Skip to content

Commit 3e41b3e

Browse files
authored
Be willing to complete in the face of emptiness (#772)
* Consider the 'Program' node to be completion-worthy * Move this check into the condition * Touch up this test * Add tests for empty line completions
1 parent 498ccfe commit 3e41b3e

File tree

1 file changed

+48
-7
lines changed

1 file changed

+48
-7
lines changed

crates/ark/src/lsp/completions/sources/composite.rs

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,12 @@ pub(crate) fn get_completions(
7171
// subset completions (`[` or `[[`)
7272
push_completions(subset::SubsetSource, completion_context, &mut completions)?;
7373

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+
{
7780
push_completions(keyword::KeywordSource, completion_context, &mut completions)?;
7881

7982
push_completions(
@@ -206,11 +209,13 @@ fn is_identifier_like(x: Node) -> bool {
206209

207210
#[cfg(test)]
208211
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;
211215
use crate::lsp::completions::sources::composite::is_identifier_like;
212216
use crate::lsp::document_context::DocumentContext;
213217
use crate::lsp::documents::Document;
218+
use crate::lsp::state::WorldState;
214219
use crate::r_task;
215220
use crate::treesitter::NodeType;
216221
use crate::treesitter::NodeTypeExt;
@@ -222,9 +227,10 @@ mod tests {
222227
// anonymous nodes and keywords, so they need to look like
223228
// identifiers that we provide completions for
224229
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);
227232
let context = DocumentContext::new(&document, point, None);
233+
228234
assert!(is_identifier_like(context.node));
229235
assert_eq!(
230236
context.node.node_type(),
@@ -233,4 +239,39 @@ mod tests {
233239
}
234240
})
235241
}
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@\nrnorm(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+
}
236277
}

0 commit comments

Comments
 (0)