feat: adjust label and ref completion range (#1444)

* feat: adjust label and ref completion range

* docs: comment
This commit is contained in:
Myriad-Dreamin 2025-03-05 14:45:53 +08:00 committed by GitHub
parent 3ef71747c9
commit 4e9460758f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 86 additions and 41 deletions

View file

@ -157,7 +157,7 @@ pub struct CompletionCursor<'a> {
/// Cache for the last lsp range conversion.
last_lsp_range_pair: Option<(Range<usize>, LspRange)>,
/// Cache for the ident cursor.
ident_cursor: OnceLock<Option<LinkedNode<'a>>>,
ident_cursor: OnceLock<Option<SelectedNode<'a>>>,
/// Cache for the arg cursor.
arg_cursor: OnceLock<Option<SyntaxNode>>,
}
@ -207,16 +207,37 @@ impl<'a> CompletionCursor<'a> {
matches!(self.syntax, Some(SyntaxClass::Callee(..)))
}
/// Gets Identifier under cursor.
fn ident_cursor(&self) -> &Option<LinkedNode> {
/// Gets selected node under cursor.
fn selected_node(&self) -> &Option<SelectedNode<'a>> {
self.ident_cursor.get_or_init(|| {
// identifier
// ^ from
let is_from_ident = matches!(
self.syntax,
Some(SyntaxClass::Callee(..) | SyntaxClass::VarAccess(..))
) && is_ident_like(&self.leaf)
&& self.leaf.offset() == self.from;
if is_from_ident {
return Some(SelectedNode::Ident(self.leaf.clone()));
}
is_from_ident.then(|| self.leaf.clone())
// <identifier
// ^ from
let is_from_label = matches!(self.syntax, Some(SyntaxClass::Label { .. }))
&& self.leaf.offset() + 1 == self.from;
if is_from_label {
return Some(SelectedNode::Label(self.leaf.clone()));
}
// @identifier
// ^ from
let is_from_ref = matches!(self.syntax, Some(SyntaxClass::Ref(..)))
&& self.leaf.offset() + 1 == self.from;
if is_from_ref {
return Some(SelectedNode::Ref(self.leaf.clone()));
}
None
})
}
@ -269,23 +290,43 @@ impl<'a> CompletionCursor<'a> {
fn lsp_item_of(&mut self, item: &Completion) -> LspCompletion {
// Determine range to replace
let mut snippet = item.apply.as_ref().unwrap_or(&item.label).clone();
let replace_range = if let Some(from_ident) = self.ident_cursor() {
let mut rng = from_ident.range();
let replace_range = match self.selected_node() {
Some(SelectedNode::Ident(from_ident)) => {
let mut rng = from_ident.range();
// if modifying some arguments, we need to truncate and add a comma
if !self.is_callee() && self.cursor != rng.end && is_arg_like_context(from_ident) {
// extend comma
if !snippet.trim_end().ends_with(',') {
snippet.push_str(", ");
// if modifying some arguments, we need to truncate and add a comma
if !self.is_callee() && self.cursor != rng.end && is_arg_like_context(from_ident) {
// extend comma
if !snippet.trim_end().ends_with(',') {
snippet.push_str(", ");
}
// Truncate
rng.end = self.cursor;
}
// Truncate
rng.end = self.cursor;
self.lsp_range_of(rng)
}
Some(SelectedNode::Label(from_label)) => {
let mut rng = from_label.range();
if from_label.text().starts_with('<') && !snippet.starts_with('<') {
rng.start += 1;
}
if from_label.text().ends_with('>') && !snippet.ends_with('>') {
rng.end -= 1;
}
self.lsp_range_of(rng)
} else {
self.lsp_range_of(self.from..self.cursor)
self.lsp_range_of(rng)
}
Some(SelectedNode::Ref(from_ref)) => {
let mut rng = from_ref.range();
if from_ref.text().starts_with('@') && !snippet.starts_with('@') {
rng.start += 1;
}
self.lsp_range_of(rng)
}
None => self.lsp_range_of(self.from..self.cursor),
};
let text_edit = EcoTextEdit::new(replace_range, snippet);
@ -309,6 +350,16 @@ impl<'a> CompletionCursor<'a> {
/// Alias for a completion cursor, [`CompletionCursor`].
type Cursor<'a> = CompletionCursor<'a>;
/// A node selected by [`CompletionCursor`].
enum SelectedNode<'a> {
/// Selects an identifier, e.g. `foo|` or `fo|o`.
Ident(LinkedNode<'a>),
/// Selects a label, e.g. `<foo|>` or `<fo|o>`.
Label(LinkedNode<'a>),
/// Selects a reference, e.g. `@foo|` or `@fo|o`.
Ref(LinkedNode<'a>),
}
/// Autocomplete a cursor position in a source file.
///
/// Returns the position from which the completions apply and a list of
@ -390,7 +441,7 @@ impl<'a> CompletionWorker<'a> {
/// Starts the completion process.
pub(crate) fn work(&mut self, cursor: &mut Cursor) -> Option<()> {
// Skip if is the let binding item *directly*
// Skips if is the let binding item *directly*
if let Some(SyntaxClass::VarAccess(var)) = &cursor.syntax {
let node = var.node();
match node.parent_kind() {
@ -411,7 +462,7 @@ impl<'a> CompletionWorker<'a> {
}
}
// Skip if an error node starts with number (e.g. `1pt`)
// Skips if an error node starts with number (e.g. `1pt`)
if matches!(
cursor.syntax,
Some(SyntaxClass::Callee(..) | SyntaxClass::VarAccess(..) | SyntaxClass::Normal(..))
@ -429,7 +480,7 @@ impl<'a> CompletionWorker<'a> {
}
}
// Exclude it self from auto completion
// Excludes it self from auto completion
// e.g. `#let x = (1.);`
let self_ty = cursor.leaf.cast::<ast::Expr>().and_then(|leaf| {
let v = self.ctx.mini_eval(leaf)?;
@ -446,8 +497,8 @@ impl<'a> CompletionWorker<'a> {
};
let _ = pair.complete_cursor();
// Filter
if let Some(from_ident) = cursor.ident_cursor() {
// Filters
if let Some(SelectedNode::Ident(from_ident)) = cursor.selected_node() {
let ident_prefix = cursor.text[from_ident.offset()..cursor.cursor].to_string();
self.completions.retain(|item| {

View file

@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs
description: Completion on t (44..45)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/complete_half_label.typ
snapshot_kind: text
---
[
{
@ -20,7 +19,7 @@ snapshot_kind: text
"newText": "test>",
"range": {
"end": {
"character": 2,
"character": 3,
"line": 3
},
"start": {

View file

@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs
description: Completion on t (56..57)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/complete_half_label_cite.typ
snapshot_kind: text
---
[
{
@ -20,7 +19,7 @@ snapshot_kind: text
"newText": "tarry>",
"range": {
"end": {
"character": 7,
"character": 8,
"line": 3
},
"start": {

View file

@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs
description: Completion on t (103..104)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/complete_purely_label.typ
snapshot_kind: text
---
[
{
@ -20,7 +19,7 @@ snapshot_kind: text
"newText": "test>",
"range": {
"end": {
"character": 2,
"character": 4,
"line": 11
},
"start": {

View file

@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs
description: Completion on @R (135..137)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/completion_title.typ
snapshot_kind: text
---
[
{
@ -23,7 +22,7 @@ snapshot_kind: text
"newText": "Russell:1908",
"range": {
"end": {
"character": 1,
"character": 2,
"line": 5
},
"start": {
@ -43,7 +42,7 @@ snapshot_kind: text
"newText": "Russell:1908",
"range": {
"end": {
"character": 1,
"character": 2,
"line": 5
},
"start": {

View file

@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs
description: Completion on 9 (109..110)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/completion_title2.typ
snapshot_kind: text
---
[
{
@ -20,7 +19,7 @@ snapshot_kind: text
"newText": "Russell:1908>",
"range": {
"end": {
"character": 16,
"character": 20,
"line": 3
},
"start": {
@ -41,7 +40,7 @@ snapshot_kind: text
"newText": "Russell:1908>",
"range": {
"end": {
"character": 16,
"character": 20,
"line": 3
},
"start": {

View file

@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs
description: Completion on e (57..58)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/label_middle.typ
snapshot_kind: text
---
[
{
@ -20,7 +19,7 @@ snapshot_kind: text
"newText": "abcd>",
"range": {
"end": {
"character": 4,
"character": 5,
"line": 3
},
"start": {
@ -41,7 +40,7 @@ snapshot_kind: text
"newText": "abef>",
"range": {
"end": {
"character": 4,
"character": 5,
"line": 3
},
"start": {
@ -62,7 +61,7 @@ snapshot_kind: text
"newText": "efg>",
"range": {
"end": {
"character": 4,
"character": 5,
"line": 3
},
"start": {

View file

@ -18,7 +18,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/ref_half.typ
"newText": "test",
"range": {
"end": {
"character": 1,
"character": 2,
"line": 11
},
"start": {

View file

@ -18,7 +18,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/ref_half2.typ
"newText": "test",
"range": {
"end": {
"character": 1,
"character": 3,
"line": 11
},
"start": {
@ -43,7 +43,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/ref_half2.typ
"newText": "test",
"range": {
"end": {
"character": 2,
"character": 3,
"line": 11
},
"start": {

View file

@ -18,7 +18,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/ref_half_colon.typ
"newText": "sec:it",
"range": {
"end": {
"character": 5,
"character": 6,
"line": 13
},
"start": {