Skip to content

Commit 05ab753

Browse files
committed
Reshape DocumentContext and migrate hover, signature help, and completion to ArkFile
1 parent 820ae96 commit 05ab753

24 files changed

Lines changed: 376 additions & 269 deletions

crates/ark/src/fixtures/utils.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ pub fn r_test_init() {
2929
Console::test_init();
3030
}
3131

32+
pub const TEST_ENCODING: aether_lsp_utils::proto::PositionEncoding =
33+
aether_lsp_utils::proto::PositionEncoding::Wide(biome_line_index::WideEncoding::Utf16);
34+
35+
pub fn tree_sitter_parse(code: &str) -> tree_sitter::Tree {
36+
let mut parser = tree_sitter::Parser::new();
37+
parser
38+
.set_language(&tree_sitter_r::LANGUAGE.into())
39+
.unwrap();
40+
parser.parse(code, None).unwrap()
41+
}
42+
3243
pub fn point_from_cursor(x: &str) -> (String, Point) {
3344
// i.e. looking for `@` in something like `fn(x = @1, y = 2)`, and it treats the
3445
// `@` as the cursor position

crates/ark/src/lsp/completions/completion_item.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub(super) fn completion_item_from_assignment(
100100
let lhs = node.child_by_field_name("lhs").into_result()?;
101101
let rhs = node.child_by_field_name("rhs").into_result()?;
102102

103-
let label = lhs.node_as_str(&context.document.contents)?.to_string();
103+
let label = lhs.node_as_str(context.contents)?.to_string();
104104

105105
// TODO: Resolve functions that exist in-document here.
106106
let mut item = completion_item(label.clone(), CompletionData::ScopeVariable {
@@ -123,7 +123,7 @@ pub(super) fn completion_item_from_assignment(
123123
// benefit from the logic in completion_item_from_function() :(
124124
if rhs.node_type() == NodeType::FunctionDefinition {
125125
if let Some(parameters) = rhs.child_by_field_name("parameters") {
126-
let parameters = parameters.node_as_str(&context.document.contents)?;
126+
let parameters = parameters.node_as_str(context.contents)?;
127127
item.detail = Some(join!(label, parameters));
128128
}
129129

@@ -644,9 +644,7 @@ fn completion_item_from_dot_dot_dot(
644644

645645
item.kind = Some(CompletionItemKind::FIELD);
646646

647-
let position = context
648-
.document
649-
.lsp_position_from_tree_sitter_point(context.point)?;
647+
let position = context.lsp_position_from_tree_sitter_point(context.point)?;
650648

651649
let range = Range {
652650
start: position,

crates/ark/src/lsp/completions/function_context.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ impl FunctionContext {
6262
// function-flavored CompletionItem in this degenerate case, but we
6363
// return a dummy FunctionContext just to be safe.
6464
let node_end = document_context
65-
.document
6665
.lsp_position_from_tree_sitter_point(completion_node.range().end_point)?;
6766

6867
return Ok(Self {
@@ -74,10 +73,7 @@ impl FunctionContext {
7473
});
7574
};
7675

77-
let usage = determine_function_usage(
78-
&effective_function_node,
79-
&document_context.document.contents,
80-
);
76+
let usage = determine_function_usage(&effective_function_node, document_context.contents);
8177

8278
let function_name_node = if effective_function_node.is_namespace_operator() {
8379
// Note: this could be 'None', in the case of, e.g., `dplyr::@`
@@ -93,7 +89,7 @@ impl FunctionContext {
9389

9490
let name = match function_name_node {
9591
Some(node) => node
96-
.node_to_string(&document_context.document.contents)
92+
.node_to_string(document_context.contents)
9793
.unwrap_or_default(),
9894
None => String::new(),
9995
};
@@ -111,16 +107,12 @@ impl FunctionContext {
111107
Ok(Self {
112108
name,
113109
range: match function_name_node {
114-
Some(node) => document_context
115-
.document
116-
.lsp_range_from_tree_sitter_range(node.range())?,
110+
Some(node) => document_context.lsp_range_from_tree_sitter_range(node.range())?,
117111
None => {
118112
// Create a zero-width range at the end of the effective_function_node
119-
let node_end = document_context
120-
.document
121-
.lsp_position_from_tree_sitter_point(
122-
effective_function_node.range().end_point,
123-
)?;
113+
let node_end = document_context.lsp_position_from_tree_sitter_point(
114+
effective_function_node.range().end_point,
115+
)?;
124116
lsp_types::Range::new(node_end, node_end)
125117
},
126118
},

crates/ark/src/lsp/completions/provide.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub(crate) fn provide_completions(
2525
"provide_completions() - Completion node text: '{node_text}', Node type: '{node_type:?}'",
2626
node_text = document_context
2727
.node
28-
.node_as_str(&document_context.document.contents)
28+
.node_as_str(document_context.contents)
2929
.unwrap_or_default(),
3030
node_type = document_context.node.node_type()
3131
);

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,6 @@ mod tests {
220220
use crate::lsp::completions::completion_context::CompletionContext;
221221
use crate::lsp::completions::sources::composite::get_completions;
222222
use crate::lsp::completions::sources::composite::is_identifier_like;
223-
use crate::lsp::document::Document;
224223
use crate::lsp::document_context::DocumentContext;
225224
use crate::lsp::state::WorldState;
226225
use crate::r_task;
@@ -235,8 +234,9 @@ mod tests {
235234
// identifiers that we provide completions for
236235
for keyword in ["if", "for", "while"] {
237236
let (text, point) = point_from_cursor(&format!("{keyword}@"));
238-
let document = Document::new(text.as_str(), None);
239-
let context = DocumentContext::new(&document, point, None);
237+
let tree = crate::fixtures::tree_sitter_parse(&text);
238+
let context =
239+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
240240

241241
assert!(is_identifier_like(context.node));
242242
assert_eq!(
@@ -251,8 +251,9 @@ mod tests {
251251
fn test_get_completions_on_empty_document() {
252252
r_task(|| {
253253
let (text, point) = point_from_cursor("@");
254-
let document = Document::new(text.as_str(), None);
255-
let document_context = DocumentContext::new(&document, point, None);
254+
let tree = crate::fixtures::tree_sitter_parse(&text);
255+
let document_context =
256+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
256257
let state = WorldState::default();
257258
let context = CompletionContext::new(&document_context, &state);
258259

@@ -269,8 +270,9 @@ mod tests {
269270
r_task(|| {
270271
let code = "x <- 1:3\n@\nrnorm(3)";
271272
let (text, point) = point_from_cursor(code);
272-
let document = Document::new(text.as_str(), None);
273-
let document_context = DocumentContext::new(&document, point, None);
273+
let tree = crate::fixtures::tree_sitter_parse(&text);
274+
let document_context =
275+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
274276
let state = WorldState::default();
275277
let context = CompletionContext::new(&document_context, &state);
276278

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

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ fn completions_from_call(
7676
return Ok(None);
7777
};
7878

79-
let callee = callee.node_as_str(&document_context.document.contents)?;
79+
let callee = callee.node_as_str(document_context.contents)?;
8080

8181
// - Prefer `root` as the first argument if it exists
8282
// - Then fall back to looking it up, if possible
@@ -122,7 +122,7 @@ fn get_first_argument(context: &DocumentContext, node: &Node) -> anyhow::Result<
122122
return Ok(None);
123123
};
124124

125-
let text = value.node_as_str(&context.document.contents)?;
125+
let text = value.node_as_str(context.contents)?;
126126

127127
let options = RParseEvalOptions {
128128
forbid_function_calls: true,
@@ -280,7 +280,6 @@ mod tests {
280280
use crate::fixtures::point_from_cursor;
281281
use crate::lsp::completions::completion_context::CompletionContext;
282282
use crate::lsp::completions::sources::composite::call::completions_from_call;
283-
use crate::lsp::document::Document;
284283
use crate::lsp::document_context::DocumentContext;
285284
use crate::lsp::state::WorldState;
286285
use crate::r_task;
@@ -290,8 +289,9 @@ mod tests {
290289
r_task(|| {
291290
// Right after `tab`
292291
let (text, point) = point_from_cursor("match(tab@)");
293-
let document = Document::new(text.as_str(), None);
294-
let document_context = DocumentContext::new(&document, point, None);
292+
let tree = crate::fixtures::tree_sitter_parse(&text);
293+
let document_context =
294+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
295295
let state = WorldState::default();
296296
let context = CompletionContext::new(&document_context, &state);
297297
let completions = completions_from_call(&context).unwrap().unwrap();
@@ -303,8 +303,9 @@ mod tests {
303303

304304
// Right after `tab`
305305
let (text, point) = point_from_cursor("match(1, tab@)");
306-
let document = Document::new(text.as_str(), None);
307-
let document_context = DocumentContext::new(&document, point, None);
306+
let tree = crate::fixtures::tree_sitter_parse(&text);
307+
let document_context =
308+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
308309
let state = WorldState::default();
309310
let context = CompletionContext::new(&document_context, &state);
310311
let completions = completions_from_call(&context).unwrap().unwrap();
@@ -323,8 +324,9 @@ mod tests {
323324
r_task(|| {
324325
// Place cursor between `()`
325326
let (text, point) = point_from_cursor("not_a_known_function(@)");
326-
let document = Document::new(text.as_str(), None);
327-
let document_context = DocumentContext::new(&document, point, None);
327+
let tree = crate::fixtures::tree_sitter_parse(&text);
328+
let document_context =
329+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
328330
let state = WorldState::default();
329331
let context = CompletionContext::new(&document_context, &state);
330332
let completions = completions_from_call(&context).unwrap();
@@ -343,8 +345,9 @@ mod tests {
343345

344346
// Place cursor between `()`
345347
let (text, point) = point_from_cursor("my_fun(@)");
346-
let document = Document::new(text.as_str(), None);
347-
let document_context = DocumentContext::new(&document, point, None);
348+
let tree = crate::fixtures::tree_sitter_parse(&text);
349+
let document_context =
350+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
348351
let state = WorldState::default();
349352
let context = CompletionContext::new(&document_context, &state);
350353
let completions = completions_from_call(&context).unwrap().unwrap();
@@ -360,17 +363,19 @@ mod tests {
360363

361364
// Place just before the `()`
362365
let (text, point) = point_from_cursor("my_fun@()");
363-
let document = Document::new(text.as_str(), None);
364-
let document_context = DocumentContext::new(&document, point, None);
366+
let tree = crate::fixtures::tree_sitter_parse(&text);
367+
let document_context =
368+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
365369
let state = WorldState::default();
366370
let context = CompletionContext::new(&document_context, &state);
367371
let completions = completions_from_call(&context).unwrap();
368372
assert!(completions.is_none());
369373

370374
// Place just after the `()`
371375
let (text, point) = point_from_cursor("my_fun()@");
372-
let document = Document::new(text.as_str(), None);
373-
let document_context = DocumentContext::new(&document, point, None);
376+
let tree = crate::fixtures::tree_sitter_parse(&text);
377+
let document_context =
378+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
374379
let state = WorldState::default();
375380
let context = CompletionContext::new(&document_context, &state);
376381
let completions = completions_from_call(&context).unwrap();
@@ -392,8 +397,9 @@ mod tests {
392397

393398
// Place cursor between `()`
394399
let (text, point) = point_from_cursor("my_fun(@)");
395-
let document = Document::new(text.as_str(), None);
396-
let document_context = DocumentContext::new(&document, point, None);
400+
let tree = crate::fixtures::tree_sitter_parse(&text);
401+
let document_context =
402+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
397403
let state = WorldState::default();
398404
let context = CompletionContext::new(&document_context, &state);
399405
let completions = completions_from_call(&context).unwrap().unwrap();
@@ -409,8 +415,9 @@ mod tests {
409415
r_task(|| {
410416
// No arguments typed yet
411417
let (text, point) = point_from_cursor("match(\n @\n)");
412-
let document = Document::new(text.as_str(), None);
413-
let document_context = DocumentContext::new(&document, point, None);
418+
let tree = crate::fixtures::tree_sitter_parse(&text);
419+
let document_context =
420+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
414421
let state = WorldState::default();
415422
let context = CompletionContext::new(&document_context, &state);
416423
let completions = completions_from_call(&context).unwrap().unwrap();
@@ -421,8 +428,9 @@ mod tests {
421428

422429
// Partially typed argument
423430
let (text, point) = point_from_cursor("match(\n tab@\n)");
424-
let document = Document::new(text.as_str(), None);
425-
let document_context = DocumentContext::new(&document, point, None);
431+
let tree = crate::fixtures::tree_sitter_parse(&text);
432+
let document_context =
433+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
426434
let state = WorldState::default();
427435
let context = CompletionContext::new(&document_context, &state);
428436
let completions = completions_from_call(&context).unwrap().unwrap();
@@ -433,8 +441,9 @@ mod tests {
433441

434442
// Partially typed second argument
435443
let (text, point) = point_from_cursor("match(\n 1,\n tab@\n)");
436-
let document = Document::new(text.as_str(), None);
437-
let document_context = DocumentContext::new(&document, point, None);
444+
let tree = crate::fixtures::tree_sitter_parse(&text);
445+
let document_context =
446+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
438447
let state = WorldState::default();
439448
let context = CompletionContext::new(&document_context, &state);
440449
let completions = completions_from_call(&context).unwrap().unwrap();
@@ -450,8 +459,9 @@ mod tests {
450459
r_task(|| {
451460
fn assert_no_call_completions(code_with_cursor: &str) {
452461
let (text, point) = point_from_cursor(code_with_cursor);
453-
let document = Document::new(text.as_str(), None);
454-
let document_context = DocumentContext::new(&document, point, None);
462+
let tree = crate::fixtures::tree_sitter_parse(&text);
463+
let document_context =
464+
DocumentContext::new(&tree, &text, crate::fixtures::TEST_ENCODING, point, None);
455465
let state = WorldState::default();
456466
let context = CompletionContext::new(&document_context, &state);
457467
let completions = completions_from_call(&context).unwrap();

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ fn completions_from_document_function_arguments(
168168
continue;
169169
}
170170

171-
let parameter = node.node_as_str(&context.document.contents)?.to_string();
171+
let parameter = node.node_as_str(context.contents)?.to_string();
172172
match completion_item_from_scope_parameter(parameter.as_str(), context) {
173173
Ok(item) => completions.push(item),
174174
Err(err) => log::error!("{err:?}"),
@@ -185,7 +185,7 @@ fn call_uses_nse(node: &Node, context: &DocumentContext) -> bool {
185185
lhs.is_identifier_or_string().into_result()?;
186186

187187
let value = lhs
188-
.node_as_str(&context.document.contents)?
188+
.node_as_str(context.contents)?
189189
.to_string();
190190
matches!(value.as_str(), "expression" | "local" | "quote" | "enquote" | "substitute" | "with" | "within").into_result()?;
191191

0 commit comments

Comments
 (0)