@@ -19,7 +19,6 @@ use std::collections::HashMap;
19
19
use stdext:: * ;
20
20
use tower_lsp:: lsp_types:: CompletionItem ;
21
21
use tower_lsp:: lsp_types:: CompletionItemKind ;
22
- use tree_sitter:: Node ;
23
22
24
23
use crate :: lsp:: completions:: completion_context:: CompletionContext ;
25
24
use crate :: lsp:: completions:: sources:: collect_completions;
@@ -57,7 +56,7 @@ pub(crate) fn get_completions(
57
56
58
57
// For the rest of the general completions, we require an identifier to
59
58
// begin showing anything.
60
- if is_identifier_like ( completion_context. document_context . node ) {
59
+ if is_identifier_like ( completion_context) {
61
60
push_completions ( keyword:: KeywordSource , completion_context, & mut completions) ?;
62
61
63
62
push_completions (
@@ -172,8 +171,10 @@ fn sort_completions(completions: &mut Vec<CompletionItem>) {
172
171
}
173
172
}
174
173
175
- fn is_identifier_like ( x : Node ) -> bool {
176
- if x. is_identifier ( ) {
174
+ fn is_identifier_like ( completion_context : & CompletionContext ) -> bool {
175
+ let node = completion_context. document_context . node ;
176
+
177
+ if node. is_identifier ( ) {
177
178
// Obvious case
178
179
return true ;
179
180
}
@@ -185,12 +186,29 @@ fn is_identifier_like(x: Node) -> bool {
185
186
// - `for<tab>` should provide completions for things like `forcats`
186
187
// - `for<tab>` should provide snippet completions for the `for` snippet
187
188
// The keywords here come from matching snippets in `r.code-snippets`.
188
- if matches ! ( x . node_type( ) , NodeType :: Anonymous ( kind) if matches!( kind. as_str( ) , "if" | "for" | "while" ) )
189
+ if matches ! ( node . node_type( ) , NodeType :: Anonymous ( kind) if matches!( kind. as_str( ) , "if" | "for" | "while" ) )
189
190
{
190
191
return true ;
191
192
}
192
193
193
- return false ;
194
+ // Consider when the user asks for completions with no existing
195
+ // text-to-complete, such as at the R prompt in the Console or in an empty R
196
+ // file.
197
+ // Gesture-wise, a Positron user could do this with Ctrl + Space, which
198
+ // invokes the command editor.action.triggerSuggest.
199
+ // The nominal completion node in these cases is basically degenerate, i.e.
200
+ // it's just the root node of the AST.
201
+ // In this case, we should just provide "all" completions, for some
202
+ // reasonable definition of "all".
203
+ // TODO: Handle the related case of asking for completions on an empty line
204
+ // of a non-empty R file. In this case, we will have latched on to some
205
+ // neighboring node, but perhaps should not. The text extracted from this
206
+ // node is usually not an identifier and this function will return false.
207
+ if node. node_type ( ) == NodeType :: Program {
208
+ return true ;
209
+ }
210
+
211
+ false
194
212
}
195
213
196
214
#[ cfg( test) ]
@@ -206,6 +224,9 @@ mod tests {
206
224
207
225
#[ test]
208
226
fn test_completions_on_anonymous_node_keywords ( ) {
227
+ use crate :: lsp:: completions:: completion_context:: CompletionContext ;
228
+ use crate :: lsp:: state:: WorldState ;
229
+
209
230
r_task ( || {
210
231
// `if`, `for`, and `while` in particular are both tree-sitter
211
232
// anonymous nodes and snippet keywords, so they need to look like
@@ -214,7 +235,9 @@ mod tests {
214
235
let point = Point { row : 0 , column : 0 } ;
215
236
let document = Document :: new ( keyword, None ) ;
216
237
let context = DocumentContext :: new ( & document, point, None ) ;
217
- assert ! ( is_identifier_like( context. node) ) ;
238
+ let state = WorldState :: default ( ) ;
239
+ let completion_context = CompletionContext :: new ( & context, & state) ;
240
+ assert ! ( is_identifier_like( & completion_context) ) ;
218
241
assert_eq ! (
219
242
context. node. node_type( ) ,
220
243
NodeType :: Anonymous ( keyword. to_string( ) )
0 commit comments