-
Notifications
You must be signed in to change notification settings - Fork 14
Use smallest spanning node as the "starter" node in completions #805
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2659aed
82ed9e1
597c9a5
3eba779
1dd4dd0
6ab0bd2
e8ff62a
42ee5be
046c131
816ba0a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -302,8 +302,8 @@ fn completions_from_workspace_arguments( | |
#[cfg(test)] | ||
mod tests { | ||
use harp::eval::RParseEvalOptions; | ||
use tree_sitter::Point; | ||
|
||
use crate::fixtures::point_from_cursor; | ||
use crate::lsp::completions::sources::composite::call::completions_from_call; | ||
use crate::lsp::document_context::DocumentContext; | ||
use crate::lsp::documents::Document; | ||
|
@@ -313,8 +313,8 @@ mod tests { | |
fn test_completions_after_user_types_part_of_an_argument_name() { | ||
r_task(|| { | ||
// Right after `tab` | ||
let point = Point { row: 0, column: 9 }; | ||
let document = Document::new("match(tab)", None); | ||
let (text, point) = point_from_cursor("match(tab@)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
let completions = completions_from_call(&context, None).unwrap().unwrap(); | ||
|
||
|
@@ -324,8 +324,8 @@ mod tests { | |
assert_eq!(completions.get(1).unwrap().label, "table = "); | ||
|
||
// Right after `tab` | ||
let point = Point { row: 0, column: 12 }; | ||
let document = Document::new("match(1, tab)", None); | ||
let (text, point) = point_from_cursor("match(1, tab@)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
let completions = completions_from_call(&context, None).unwrap().unwrap(); | ||
|
||
|
@@ -342,8 +342,8 @@ mod tests { | |
// Can't find the function | ||
r_task(|| { | ||
// Place cursor between `()` | ||
let point = Point { row: 0, column: 21 }; | ||
let document = Document::new("not_a_known_function()", None); | ||
let (text, point) = point_from_cursor("not_a_known_function(@)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
let completions = completions_from_call(&context, None).unwrap(); | ||
assert!(completions.is_none()); | ||
|
@@ -360,8 +360,8 @@ mod tests { | |
harp::parse_eval("my_fun <- function(y, x) x + y", options.clone()).unwrap(); | ||
|
||
// Place cursor between `()` | ||
let point = Point { row: 0, column: 7 }; | ||
let document = Document::new("my_fun()", None); | ||
let (text, point) = point_from_cursor("my_fun(@)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
let completions = completions_from_call(&context, None).unwrap().unwrap(); | ||
|
||
|
@@ -375,15 +375,15 @@ mod tests { | |
assert_eq!(completion.label, "x = "); | ||
|
||
// Place just before the `()` | ||
let point = Point { row: 0, column: 6 }; | ||
let document = Document::new("my_fun()", None); | ||
let (text, point) = point_from_cursor("my_fun@()"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
let completions = completions_from_call(&context, None).unwrap(); | ||
assert!(completions.is_none()); | ||
|
||
// Place just after the `()` | ||
let point = Point { row: 0, column: 8 }; | ||
let document = Document::new("my_fun()", None); | ||
let (text, point) = point_from_cursor("my_fun()@"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
let completions = completions_from_call(&context, None).unwrap(); | ||
assert!(completions.is_none()); | ||
|
@@ -403,8 +403,8 @@ mod tests { | |
harp::parse_eval("my_fun <- 1", options.clone()).unwrap(); | ||
|
||
// Place cursor between `()` | ||
let point = Point { row: 0, column: 7 }; | ||
let document = Document::new("my_fun()", None); | ||
let (text, point) = point_from_cursor("my_fun(@)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
let completions = completions_from_call(&context, None).unwrap().unwrap(); | ||
assert_eq!(completions.len(), 0); | ||
|
@@ -413,4 +413,39 @@ mod tests { | |
harp::parse_eval("remove(my_fun)", options.clone()).unwrap(); | ||
}) | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here are some tests re: "the |
||
#[test] | ||
fn test_completions_multiline_call() { | ||
r_task(|| { | ||
// No arguments typed yet | ||
let (text, point) = point_from_cursor("match(\n @\n)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
let completions = completions_from_call(&context, None).unwrap().unwrap(); | ||
|
||
assert_eq!(completions.len(), 4); | ||
assert_eq!(completions.get(0).unwrap().label, "x = "); | ||
assert_eq!(completions.get(1).unwrap().label, "table = "); | ||
|
||
// Partially typed argument | ||
let (text, point) = point_from_cursor("match(\n tab@\n)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
let completions = completions_from_call(&context, None).unwrap().unwrap(); | ||
|
||
assert_eq!(completions.len(), 4); | ||
assert_eq!(completions.get(0).unwrap().label, "x = "); | ||
assert_eq!(completions.get(1).unwrap().label, "table = "); | ||
|
||
// Partially typed second argument | ||
let (text, point) = point_from_cursor("match(\n 1,\n tab@\n)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
let completions = completions_from_call(&context, None).unwrap().unwrap(); | ||
|
||
assert_eq!(completions.len(), 4); | ||
assert_eq!(completions.get(0).unwrap().label, "x = "); | ||
assert_eq!(completions.get(1).unwrap().label, "table = "); | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -302,18 +302,34 @@ mod tests { | |
let (text, point) = point_from_cursor("Sys.getenv(@)"); | ||
assert_has_ark_test_envvar_completion(text.as_str(), point); | ||
|
||
// Inside the parentheses, multiline | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here I just interleaved the multiline scenarios inside the existing test. This is the |
||
let (text, point) = point_from_cursor("Sys.getenv(\n @\n)"); | ||
assert_has_ark_test_envvar_completion(text.as_str(), point); | ||
|
||
// Named argument | ||
let (text, point) = point_from_cursor("Sys.getenv(x = @)"); | ||
assert_has_ark_test_envvar_completion(text.as_str(), point); | ||
|
||
// Named argument, multiline | ||
let (text, point) = point_from_cursor("Sys.getenv(\n x = @\n)"); | ||
assert_has_ark_test_envvar_completion(text.as_str(), point); | ||
|
||
// Typed some and then requested completions | ||
let (text, point) = point_from_cursor("Sys.getenv(ARK_@)"); | ||
assert_has_ark_test_envvar_completion(text.as_str(), point); | ||
|
||
// Typed some and then requested completions, multiline | ||
let (text, point) = point_from_cursor("Sys.getenv(\n ARK_@\n)"); | ||
assert_has_ark_test_envvar_completion(text.as_str(), point); | ||
|
||
// After a named argument | ||
let (text, point) = point_from_cursor("Sys.getenv(unset = '1', @)"); | ||
assert_has_ark_test_envvar_completion(text.as_str(), point); | ||
|
||
// After a named argument, multiline | ||
let (text, point) = point_from_cursor("Sys.getenv(\n unset = '1',\n @\n)"); | ||
assert_has_ark_test_envvar_completion(text.as_str(), point); | ||
|
||
// Should not have it here | ||
let (text, point) = point_from_cursor("Sys.getenv('foo', @)"); | ||
let document = Document::new(text.as_str(), None); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,6 +101,7 @@ pub(super) enum CallNodePositionType { | |
|
||
pub(super) fn call_node_position_type(node: &Node, point: Point) -> CallNodePositionType { | ||
match node.node_type() { | ||
NodeType::Arguments => return CallNodePositionType::Name, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fixup for argument completions inside a call and custom completions such as |
||
NodeType::Anonymous(kind) if kind == "(" => { | ||
if point.is_before_or_equal(node.start_position()) { | ||
// Before the `(` | ||
|
@@ -267,9 +268,9 @@ fn completions_from_object_names_impl( | |
#[cfg(test)] | ||
mod tests { | ||
use harp::eval::parse_eval_global; | ||
use tree_sitter::Point; | ||
|
||
use crate::fixtures::package_is_installed; | ||
use crate::fixtures::point_from_cursor; | ||
use crate::lsp::completions::sources::utils::call_node_position_type; | ||
use crate::lsp::completions::sources::utils::completions_from_evaluated_object_names; | ||
use crate::lsp::completions::sources::utils::CallNodePositionType; | ||
|
@@ -282,8 +283,8 @@ mod tests { | |
#[test] | ||
fn test_call_node_position_type() { | ||
// Before `(`, but on it | ||
let point = Point { row: 0, column: 3 }; | ||
let document = Document::new("fn ()", None); | ||
let (text, point) = point_from_cursor("fn @()"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert_eq!( | ||
context.node.node_type(), | ||
|
@@ -295,8 +296,8 @@ mod tests { | |
); | ||
|
||
// After `)`, but on it | ||
let point = Point { row: 0, column: 4 }; | ||
let document = Document::new("fn()", None); | ||
let (text, point) = point_from_cursor("fn()@"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert_eq!( | ||
context.node.node_type(), | ||
|
@@ -308,8 +309,8 @@ mod tests { | |
); | ||
|
||
// After `(`, but on it | ||
let point = Point { row: 0, column: 3 }; | ||
let document = Document::new("fn()", None); | ||
let (text, point) = point_from_cursor("fn(@)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert_eq!( | ||
context.node.node_type(), | ||
|
@@ -321,26 +322,26 @@ mod tests { | |
); | ||
|
||
// After `x` | ||
let point = Point { row: 0, column: 4 }; | ||
let document = Document::new("fn(x)", None); | ||
let (text, point) = point_from_cursor("fn(x@)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert_eq!( | ||
call_node_position_type(&context.node, context.point), | ||
CallNodePositionType::Ambiguous | ||
); | ||
|
||
// After `x` | ||
let point = Point { row: 0, column: 7 }; | ||
let document = Document::new("fn(1, x)", None); | ||
let (text, point) = point_from_cursor("fn(1, x@)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert_eq!( | ||
call_node_position_type(&context.node, context.point), | ||
CallNodePositionType::Ambiguous | ||
); | ||
|
||
// Directly after `,` | ||
let point = Point { row: 0, column: 5 }; | ||
let document = Document::new("fn(x, )", None); | ||
let (text, point) = point_from_cursor("fn(x,@ )"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert_eq!(context.node.node_type(), NodeType::Comma); | ||
assert_eq!( | ||
|
@@ -349,8 +350,8 @@ mod tests { | |
); | ||
|
||
// After `,`, but on `)` | ||
let point = Point { row: 0, column: 6 }; | ||
let document = Document::new("fn(x, )", None); | ||
let (text, point) = point_from_cursor("fn(x, @)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert_eq!( | ||
context.node.node_type(), | ||
|
@@ -362,8 +363,8 @@ mod tests { | |
); | ||
|
||
// After `=` | ||
let point = Point { row: 0, column: 6 }; | ||
let document = Document::new("fn(x =)", None); | ||
let (text, point) = point_from_cursor("fn(x =@ )"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert_eq!( | ||
context.node.node_type(), | ||
|
@@ -375,17 +376,17 @@ mod tests { | |
); | ||
|
||
// In an expression | ||
let point = Point { row: 0, column: 4 }; | ||
let document = Document::new("fn(1 + 1)", None); | ||
let (text, point) = point_from_cursor("fn(1@ + 1)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert_eq!(context.node.node_type(), NodeType::Float); | ||
assert_eq!( | ||
call_node_position_type(&context.node, context.point), | ||
CallNodePositionType::Value | ||
); | ||
|
||
let point = Point { row: 0, column: 8 }; | ||
let document = Document::new("fn(1 + 1)", None); | ||
let (text, point) = point_from_cursor("fn(1 + 1@)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert_eq!(context.node.node_type(), NodeType::Float); | ||
assert_eq!( | ||
|
@@ -395,8 +396,8 @@ mod tests { | |
|
||
// Right before an expression | ||
// (special case where we still provide argument completions) | ||
let point = Point { row: 0, column: 6 }; | ||
let document = Document::new("fn(1, 1 + 1)", None); | ||
let (text, point) = point_from_cursor("fn(1, @1 + 1)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert_eq!(context.node.node_type(), NodeType::Float); | ||
assert_eq!( | ||
|
@@ -406,8 +407,8 @@ mod tests { | |
|
||
// After an identifier, before the `)`, with whitespace between them, | ||
// but on the `)` | ||
let point = Point { row: 0, column: 5 }; | ||
let document = Document::new("fn(x )", None); | ||
let (text, point) = point_from_cursor("fn(x @)"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert_eq!( | ||
context.node.node_type(), | ||
|
@@ -420,14 +421,25 @@ mod tests { | |
|
||
// After an identifier, before the `)`, with whitespace between them, | ||
// but on the identifier | ||
let point = Point { row: 0, column: 4 }; | ||
let document = Document::new("fn(x )", None); | ||
let (text, point) = point_from_cursor("fn(x@ )"); | ||
let document = Document::new(text.as_str(), None); | ||
let context = DocumentContext::new(&document, point, None); | ||
assert!(context.node.is_identifier()); | ||
assert_eq!( | ||
call_node_position_type(&context.node, context.point), | ||
CallNodePositionType::Ambiguous | ||
); | ||
|
||
// After `(`, and on own line | ||
let (text, point) = point_from_cursor("fn(\n @\n)"); | ||
let document = Document::new(&text, None); | ||
let context = DocumentContext::new(&document, point, None); | ||
|
||
assert_eq!(context.node.node_type(), NodeType::Arguments); | ||
assert_eq!( | ||
call_node_position_type(&context.node, context.point), | ||
CallNodePositionType::Name | ||
); | ||
} | ||
|
||
#[test] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find tests that use this fixture much easier to read, so I've updated anything I touched to use this.