@@ -71,9 +71,12 @@ pub(crate) fn get_completions(
71
71
// subset completions (`[` or `[[`)
72
72
push_completions ( subset:: SubsetSource , completion_context, & mut completions) ?;
73
73
74
- // For the rest of the general completions, we require an identifier to
75
- // begin showing anything.
76
- if is_identifier_like ( completion_context. document_context . node ) {
74
+ // To offer the rest of the general completions, we should be completing:
75
+ // * on an empty line, outside of any function or expression, or
76
+ // * something that looks like an identifier
77
+ if completion_context. document_context . node . is_program ( ) ||
78
+ is_identifier_like ( completion_context. document_context . node )
79
+ {
77
80
push_completions ( keyword:: KeywordSource , completion_context, & mut completions) ?;
78
81
79
82
push_completions (
@@ -206,11 +209,13 @@ fn is_identifier_like(x: Node) -> bool {
206
209
207
210
#[ cfg( test) ]
208
211
mod tests {
209
- use tree_sitter:: Point ;
210
-
212
+ use crate :: fixtures:: point_from_cursor;
213
+ use crate :: lsp:: completions:: completion_context:: CompletionContext ;
214
+ use crate :: lsp:: completions:: sources:: composite:: get_completions;
211
215
use crate :: lsp:: completions:: sources:: composite:: is_identifier_like;
212
216
use crate :: lsp:: document_context:: DocumentContext ;
213
217
use crate :: lsp:: documents:: Document ;
218
+ use crate :: lsp:: state:: WorldState ;
214
219
use crate :: r_task;
215
220
use crate :: treesitter:: NodeType ;
216
221
use crate :: treesitter:: NodeTypeExt ;
@@ -222,9 +227,10 @@ mod tests {
222
227
// anonymous nodes and keywords, so they need to look like
223
228
// identifiers that we provide completions for
224
229
for keyword in [ "if" , "for" , "while" ] {
225
- let point = Point { row : 0 , column : 0 } ;
226
- let document = Document :: new ( keyword , None ) ;
230
+ let ( text , point) = point_from_cursor ( & format ! ( "{keyword}@" ) ) ;
231
+ let document = Document :: new ( text . as_str ( ) , None ) ;
227
232
let context = DocumentContext :: new ( & document, point, None ) ;
233
+
228
234
assert ! ( is_identifier_like( context. node) ) ;
229
235
assert_eq ! (
230
236
context. node. node_type( ) ,
@@ -233,4 +239,39 @@ mod tests {
233
239
}
234
240
} )
235
241
}
242
+
243
+ #[ test]
244
+ fn test_get_completions_on_empty_document ( ) {
245
+ r_task ( || {
246
+ let ( text, point) = point_from_cursor ( "@" ) ;
247
+ let document = Document :: new ( text. as_str ( ) , None ) ;
248
+ let document_context = DocumentContext :: new ( & document, point, None ) ;
249
+ let state = WorldState :: default ( ) ;
250
+ let context = CompletionContext :: new ( & document_context, & state) ;
251
+
252
+ assert ! ( context. document_context. node. is_program( ) ) ;
253
+
254
+ let completions = get_completions ( & context) . unwrap ( ) ;
255
+ assert ! ( completions. is_some( ) ) ;
256
+ assert ! ( !completions. unwrap( ) . is_empty( ) ) ;
257
+ } ) ;
258
+ }
259
+
260
+ #[ test]
261
+ fn test_get_completions_on_empty_line_in_non_empty_document ( ) {
262
+ r_task ( || {
263
+ let code = "x <- 1:3\n @\n rnorm(3)" ;
264
+ let ( text, point) = point_from_cursor ( code) ;
265
+ let document = Document :: new ( text. as_str ( ) , None ) ;
266
+ let document_context = DocumentContext :: new ( & document, point, None ) ;
267
+ let state = WorldState :: default ( ) ;
268
+ let context = CompletionContext :: new ( & document_context, & state) ;
269
+
270
+ assert ! ( context. document_context. node. is_program( ) ) ;
271
+
272
+ let completions = get_completions ( & context) . unwrap ( ) ;
273
+ assert ! ( completions. is_some( ) ) ;
274
+ assert ! ( !completions. unwrap( ) . is_empty( ) ) ;
275
+ } ) ;
276
+ }
236
277
}
0 commit comments