@@ -15,18 +15,8 @@ pub fn get(
1515 tree : & tree_sitter:: Tree ,
1616 symbols_map : & impl SymbolsMap ,
1717) -> Option < CompletionResponse > {
18- let line = rope. get_line ( position. line as usize ) ?;
19- let mut query = String :: new ( ) ;
20- for ( i, char) in line. chars ( ) . enumerate ( ) {
21- if char. is_alphabetic ( ) || char == '.' || ( !query. is_empty ( ) && char. is_numeric ( ) ) {
22- query. push ( char)
23- } else {
24- query. clear ( ) ;
25- }
26- if i == ( position. character - 1 ) as usize {
27- break ;
28- }
29- }
18+ let query = extract_query ( position, rope) ?;
19+
3020 tracing:: debug!( "completion query: {query}" ) ;
3121
3222 let symbol_kind_to_completion_kind = |kind : SymbolKind | match kind {
@@ -36,61 +26,67 @@ pub fn get(
3626 _ => CompletionItemKind :: VARIABLE ,
3727 } ;
3828
39- let point = Point {
40- row : position. line as usize ,
41- column : position. character as usize ,
42- } ;
29+ let local_symbols: Vec < CompletionItem > = {
30+ let point = Point {
31+ row : position. line as usize ,
32+ column : position. character as usize ,
33+ } ;
34+ tree. root_node ( )
35+ . descendant_for_point_range ( point, point)
36+ . map ( |node| {
37+ std:: iter:: successors ( Some ( node) , |node| node. parent ( ) )
38+ . filter ( |node| node. kind ( ) == "function_definition" )
39+ . flat_map ( |func_node| {
40+ let mut items = Vec :: new ( ) ;
4341
44- // TODO: filter for query
45- let local_symbols: Vec < CompletionItem > = tree
46- . root_node ( )
47- . descendant_for_point_range ( point, point)
48- . map ( |node| {
49- std:: iter:: successors ( Some ( node) , |node| node. parent ( ) )
50- . filter ( |n| n. kind ( ) == "function_definition" )
51- . flat_map ( |func_node| {
52- let mut items = Vec :: new ( ) ;
53-
54- if let Some ( parameters) = func_node. child_by_field_name ( "parameters" ) {
55- items. extend (
56- parameters
57- . children_by_field_name ( "parameter" , & mut parameters. walk ( ) )
58- . filter_map ( |parameter| {
59- parameter. child_by_field_name ( "name" ) . map ( |name| {
60- CompletionItem {
61- label : rope
62- . byte_slice ( name. start_byte ( ) ..name. end_byte ( ) )
63- . to_string ( ) ,
64- kind : Some ( CompletionItemKind :: VARIABLE ) ,
65- ..Default :: default ( )
66- }
42+ if let Some ( parameters) = func_node. child_by_field_name ( "parameters" ) {
43+ items. extend (
44+ parameters
45+ . children_by_field_name ( "parameter" , & mut parameters. walk ( ) )
46+ . filter_map ( |parameter| {
47+ parameter. child_by_field_name ( "name" ) . map ( |name| {
48+ rope. byte_slice ( name. start_byte ( ) ..name. end_byte ( ) )
49+ . to_string ( )
50+ } )
6751 } )
68- } ) ,
69- ) ;
70- }
52+ . filter ( |name| utils:: starts_with_lowercase ( name, & query) )
53+ . map ( |label| CompletionItem {
54+ label,
55+ kind : Some ( CompletionItemKind :: VARIABLE ) ,
56+ ..Default :: default ( )
57+ } ) ,
58+ ) ;
59+ }
7160
72- if let Some ( body) = func_node. child_by_field_name ( "body" ) {
73- items. extend ( index:: symbols_for_block ( body, rope) . into_iter ( ) . map (
74- |symbol| CompletionItem {
75- label : symbol. name ,
76- kind : Some ( symbol_kind_to_completion_kind ( symbol. kind ) ) ,
77- ..Default :: default ( )
78- } ,
79- ) ) ;
80- }
61+ if let Some ( body) = func_node. child_by_field_name ( "body" ) {
62+ items. extend (
63+ index:: symbols_for_block ( body, rope)
64+ . into_iter ( )
65+ . filter ( |symbol| {
66+ utils:: starts_with_lowercase ( & symbol. name , & query)
67+ } )
68+ . map ( |symbol| CompletionItem {
69+ label : symbol. name ,
70+ kind : Some ( symbol_kind_to_completion_kind ( symbol. kind ) ) ,
71+ ..Default :: default ( )
72+ } ) ,
73+ ) ;
74+ }
8175
82- items
83- } )
84- . collect ( )
85- } )
86- . unwrap_or_default ( ) ;
76+ items
77+ } )
78+ . collect ( )
79+ } )
80+ . unwrap_or_default ( )
81+ } ;
8782
8883 let workspace_symbols = symbols_map. filter_map (
8984 |_, symbols| {
9085 symbols
9186 . iter ( )
9287 . filter ( |symbol| utils:: starts_with_lowercase ( & symbol. name , & query) )
9388 } ,
89+ // TODO: use CompletionResponse::List.is_incomplete and only limit for short queries?
9490 1024 ,
9591 ) ;
9692
@@ -119,7 +115,7 @@ pub fn get(
119115 Some ( CompletionResponse :: Array (
120116 RESERVED_WORDS
121117 . iter ( )
122- // TODO: filter keywords
118+ . filter ( |keyword| utils :: starts_with_lowercase ( keyword , & query ) )
123119 . map ( |reserved_word| CompletionItem {
124120 label : reserved_word. to_string ( ) ,
125121 kind : Some ( CompletionItemKind :: KEYWORD ) ,
@@ -135,77 +131,137 @@ pub fn get(
135131 ) )
136132}
137133
134+ // TODO: consider throwing error instead of optional
135+ // TODO: alternatively use tree-sitter to extract nearest identifer (to avoid reimplementing nearest identifer)
136+ fn extract_query ( position : Position , rope : & Rope ) -> Option < String > {
137+ let line = rope. get_line ( position. line as usize ) ?;
138+ Some (
139+ line. chars ( )
140+ . take ( position. character as usize )
141+ . fold ( String :: new ( ) , |mut acc, item| {
142+ // see: https://cran.r-project.org/doc/manuals/r-release/R-lang.html#Identifiers-1
143+ // note: we can be less strict than R otherwise its already an parser
144+ if item. is_alphabetic ( )
145+ || item == '.'
146+ || item == '_'
147+ || ( !acc. is_empty ( ) && item. is_numeric ( ) )
148+ {
149+ acc. push ( item) ;
150+ } else {
151+ acc. clear ( ) ;
152+ }
153+ acc
154+ } ) ,
155+ )
156+ }
157+
138158#[ cfg( test) ]
139159mod tests {
140- use {
141- super :: * ,
142- crate :: tree,
143- async_lsp:: lsp_types:: { CompletionItemKind , Position } ,
144- indoc:: indoc,
145- ropey:: Rope ,
146- } ;
160+ use { super :: * , crate :: tree, indoc:: indoc, ropey:: Rope , std:: collections:: HashMap } ;
147161
148- fn get_labels (
162+ fn setup (
149163 text : & str ,
150- line : u32 ,
151- character : u32 ,
152- ) -> Vec < ( String , Option < CompletionItemKind > ) > {
164+ ( line, character) : ( u32 , u32 ) ,
165+ ) -> ( String , Vec < ( String , CompletionItemKind ) > ) {
153166 let rope = Rope :: from_str ( text) ;
154167 let tree = tree:: parse ( text, None ) ;
155- let dummy_symbols = std :: collections :: HashMap :: new ( ) ;
156- let result = get ( Position { line , character } , & rope, & tree , & dummy_symbols ) . unwrap ( ) ;
157- match result {
158- async_lsp :: lsp_types :: CompletionResponse :: Array ( items) => items
168+ let position = Position :: new ( line , character ) ;
169+ let query = extract_query ( position , & rope) . unwrap ( ) ;
170+ let items = match get ( position , & rope , & tree , & HashMap :: new ( ) ) . unwrap ( ) {
171+ CompletionResponse :: Array ( items) => items
159172 . into_iter ( )
160- . map ( |item| ( item. label , item. kind ) )
173+ . map ( |item| ( item. label , item. kind . unwrap ( ) ) )
161174 . collect ( ) ,
162175 _ => unreachable ! ( ) ,
163- }
176+ } ;
177+ ( query, items)
164178 }
165179
166180 #[ test]
167181 fn completes_local_variables ( ) {
168- let text = indoc ! { "
169- foo <- function(x) {
170- local_var <- 42
171- loc
172- }
173- " } ;
174-
175- let labels = get_labels ( text, 2 , 7 ) ;
182+ let ( query, items) = setup (
183+ indoc ! { "
184+ function(x) {
185+ local_var <- 42
186+ loc
187+ }
188+ " } ,
189+ ( 2 , 7 ) ,
190+ ) ;
176191
177- assert ! ( labels. contains( & ( "local_var" . into( ) , Some ( CompletionItemKind :: VARIABLE ) ) ) ) ;
192+ assert_eq ! ( query, "loc" ) ;
193+ assert_eq ! ( items. len( ) , 1 ) ;
194+ assert ! ( items. contains( & ( "local_var" . into( ) , CompletionItemKind :: VARIABLE ) ) ) ;
178195 }
179196
180197 #[ test]
181198 fn completes_function_parameters ( ) {
182- let text = indoc ! { "
183- foo <- function(param1, param2) {
184- par
185- }
186- " } ;
187-
188- let labels = get_labels ( text, 1 , 7 ) ;
199+ let ( query, items) = setup (
200+ indoc ! { "
201+ function(param1, param2) {
202+ par
203+ }
204+ " } ,
205+ ( 1 , 7 ) ,
206+ ) ;
189207
190- assert ! ( labels. contains( & ( "param1" . into( ) , Some ( CompletionItemKind :: VARIABLE ) ) ) ) ;
191- assert ! ( labels. contains( & ( "param2" . into( ) , Some ( CompletionItemKind :: VARIABLE ) ) ) ) ;
208+ assert_eq ! ( query, "par" ) ;
209+ assert_eq ! ( items. len( ) , 2 ) ;
210+ assert ! ( items. contains( & ( "param1" . into( ) , CompletionItemKind :: VARIABLE ) ) ) ;
211+ assert ! ( items. contains( & ( "param2" . into( ) , CompletionItemKind :: VARIABLE ) ) ) ;
192212 }
193213
194214 #[ test]
195215 fn completes_nested_function_variable ( ) {
196- let text = indoc ! { "
197- foo <- function(x) {
198- var_outer <- 1
199- bar <- function(y) {
200- var_inner <- 2
201- var
216+ let ( query, items) = setup (
217+ indoc ! { "
218+ function(x) {
219+ var_a <- 1
220+ function(y) {
221+ var_b <- 2
222+ function(y) {
223+ var_c <- 3
224+ var
225+ }
226+ }
202227 }
203- }
204- " } ;
228+ " } ,
229+ ( 6 , 15 ) ,
230+ ) ;
231+
232+ assert_eq ! ( query, "var" ) ;
233+ assert_eq ! ( items. len( ) , 3 ) ;
234+ assert ! ( items. contains( & ( "var_a" . into( ) , CompletionItemKind :: VARIABLE ) ) ) ;
235+ assert ! ( items. contains( & ( "var_b" . into( ) , CompletionItemKind :: VARIABLE ) ) ) ;
236+ assert ! ( items. contains( & ( "var_c" . into( ) , CompletionItemKind :: VARIABLE ) ) ) ;
237+ }
205238
206- let labels = get_labels ( text, 4 , 11 ) ;
239+ #[ test]
240+ fn completes_keywords ( ) {
241+ let ( query, items) = setup ( "i" , ( 0 , 1 ) ) ;
242+
243+ assert_eq ! ( query, "i" ) ;
244+ assert_eq ! ( items. len( ) , 3 ) ;
245+ assert ! ( items. contains( & ( "if" . into( ) , CompletionItemKind :: KEYWORD ) ) ) ;
246+ assert ! ( items. contains( & ( "in" . into( ) , CompletionItemKind :: KEYWORD ) ) ) ;
247+ assert ! ( items. contains( & ( "Inf" . into( ) , CompletionItemKind :: KEYWORD ) ) ) ;
248+
249+ let ( query, items) = setup ( "na_ " , ( 0 , 3 ) ) ;
250+
251+ assert_eq ! ( query, "na_" ) ;
252+ assert_eq ! ( items. len( ) , 4 ) ;
253+ }
254+
255+ #[ test]
256+ fn extract_query_edge_cases ( ) {
257+ fn setup ( pos : u32 , text : & str ) -> String {
258+ extract_query ( Position :: new ( 0 , pos) , & Rope :: from_str ( text) ) . unwrap ( )
259+ }
207260
208- assert ! ( labels. contains( & ( "var_inner" . into( ) , Some ( CompletionItemKind :: VARIABLE ) ) ) ) ;
209- assert ! ( labels. contains( & ( "var_outer" . into( ) , Some ( CompletionItemKind :: VARIABLE ) ) ) ) ;
261+ assert_eq ! ( setup( 11 , "foo.bar_123" ) , "foo.bar_123" ) ;
262+ assert_eq ! ( setup( 4 , ".foo" ) , ".foo" ) ;
263+ assert_eq ! ( setup( 4 , "1foo" ) , "foo" ) ;
264+ assert_eq ! ( setup( 4 , "_foo" ) , "_foo" ) ;
265+ assert_eq ! ( setup( 5 , ".1foo" ) , ".1foo" ) ;
210266 }
211267}
0 commit comments