mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-24 05:05:00 +00:00
feat: adjust label and ref completion range (#1444)
* feat: adjust label and ref completion range * docs: comment
This commit is contained in:
parent
3ef71747c9
commit
4e9460758f
10 changed files with 86 additions and 41 deletions
|
@ -157,7 +157,7 @@ pub struct CompletionCursor<'a> {
|
||||||
/// Cache for the last lsp range conversion.
|
/// Cache for the last lsp range conversion.
|
||||||
last_lsp_range_pair: Option<(Range<usize>, LspRange)>,
|
last_lsp_range_pair: Option<(Range<usize>, LspRange)>,
|
||||||
/// Cache for the ident cursor.
|
/// Cache for the ident cursor.
|
||||||
ident_cursor: OnceLock<Option<LinkedNode<'a>>>,
|
ident_cursor: OnceLock<Option<SelectedNode<'a>>>,
|
||||||
/// Cache for the arg cursor.
|
/// Cache for the arg cursor.
|
||||||
arg_cursor: OnceLock<Option<SyntaxNode>>,
|
arg_cursor: OnceLock<Option<SyntaxNode>>,
|
||||||
}
|
}
|
||||||
|
@ -207,16 +207,37 @@ impl<'a> CompletionCursor<'a> {
|
||||||
matches!(self.syntax, Some(SyntaxClass::Callee(..)))
|
matches!(self.syntax, Some(SyntaxClass::Callee(..)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets Identifier under cursor.
|
/// Gets selected node under cursor.
|
||||||
fn ident_cursor(&self) -> &Option<LinkedNode> {
|
fn selected_node(&self) -> &Option<SelectedNode<'a>> {
|
||||||
self.ident_cursor.get_or_init(|| {
|
self.ident_cursor.get_or_init(|| {
|
||||||
|
// identifier
|
||||||
|
// ^ from
|
||||||
let is_from_ident = matches!(
|
let is_from_ident = matches!(
|
||||||
self.syntax,
|
self.syntax,
|
||||||
Some(SyntaxClass::Callee(..) | SyntaxClass::VarAccess(..))
|
Some(SyntaxClass::Callee(..) | SyntaxClass::VarAccess(..))
|
||||||
) && is_ident_like(&self.leaf)
|
) && is_ident_like(&self.leaf)
|
||||||
&& self.leaf.offset() == self.from;
|
&& 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 {
|
fn lsp_item_of(&mut self, item: &Completion) -> LspCompletion {
|
||||||
// Determine range to replace
|
// Determine range to replace
|
||||||
let mut snippet = item.apply.as_ref().unwrap_or(&item.label).clone();
|
let mut snippet = item.apply.as_ref().unwrap_or(&item.label).clone();
|
||||||
let replace_range = if let Some(from_ident) = self.ident_cursor() {
|
let replace_range = match self.selected_node() {
|
||||||
let mut rng = from_ident.range();
|
Some(SelectedNode::Ident(from_ident)) => {
|
||||||
|
let mut rng = from_ident.range();
|
||||||
|
|
||||||
// if modifying some arguments, we need to truncate and add a comma
|
// 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) {
|
if !self.is_callee() && self.cursor != rng.end && is_arg_like_context(from_ident) {
|
||||||
// extend comma
|
// extend comma
|
||||||
if !snippet.trim_end().ends_with(',') {
|
if !snippet.trim_end().ends_with(',') {
|
||||||
snippet.push_str(", ");
|
snippet.push_str(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate
|
||||||
|
rng.end = self.cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate
|
self.lsp_range_of(rng)
|
||||||
rng.end = self.cursor;
|
|
||||||
}
|
}
|
||||||
|
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)
|
self.lsp_range_of(rng)
|
||||||
} else {
|
}
|
||||||
self.lsp_range_of(self.from..self.cursor)
|
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);
|
let text_edit = EcoTextEdit::new(replace_range, snippet);
|
||||||
|
@ -309,6 +350,16 @@ impl<'a> CompletionCursor<'a> {
|
||||||
/// Alias for a completion cursor, [`CompletionCursor`].
|
/// Alias for a completion cursor, [`CompletionCursor`].
|
||||||
type Cursor<'a> = CompletionCursor<'a>;
|
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.
|
/// Autocomplete a cursor position in a source file.
|
||||||
///
|
///
|
||||||
/// Returns the position from which the completions apply and a list of
|
/// Returns the position from which the completions apply and a list of
|
||||||
|
@ -390,7 +441,7 @@ impl<'a> CompletionWorker<'a> {
|
||||||
|
|
||||||
/// Starts the completion process.
|
/// Starts the completion process.
|
||||||
pub(crate) fn work(&mut self, cursor: &mut Cursor) -> Option<()> {
|
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 {
|
if let Some(SyntaxClass::VarAccess(var)) = &cursor.syntax {
|
||||||
let node = var.node();
|
let node = var.node();
|
||||||
match node.parent_kind() {
|
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!(
|
if matches!(
|
||||||
cursor.syntax,
|
cursor.syntax,
|
||||||
Some(SyntaxClass::Callee(..) | SyntaxClass::VarAccess(..) | SyntaxClass::Normal(..))
|
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.);`
|
// e.g. `#let x = (1.);`
|
||||||
let self_ty = cursor.leaf.cast::<ast::Expr>().and_then(|leaf| {
|
let self_ty = cursor.leaf.cast::<ast::Expr>().and_then(|leaf| {
|
||||||
let v = self.ctx.mini_eval(leaf)?;
|
let v = self.ctx.mini_eval(leaf)?;
|
||||||
|
@ -446,8 +497,8 @@ impl<'a> CompletionWorker<'a> {
|
||||||
};
|
};
|
||||||
let _ = pair.complete_cursor();
|
let _ = pair.complete_cursor();
|
||||||
|
|
||||||
// Filter
|
// Filters
|
||||||
if let Some(from_ident) = cursor.ident_cursor() {
|
if let Some(SelectedNode::Ident(from_ident)) = cursor.selected_node() {
|
||||||
let ident_prefix = cursor.text[from_ident.offset()..cursor.cursor].to_string();
|
let ident_prefix = cursor.text[from_ident.offset()..cursor.cursor].to_string();
|
||||||
|
|
||||||
self.completions.retain(|item| {
|
self.completions.retain(|item| {
|
||||||
|
|
|
@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs
|
||||||
description: Completion on t (44..45)
|
description: Completion on t (44..45)
|
||||||
expression: "JsonRepr::new_pure(results)"
|
expression: "JsonRepr::new_pure(results)"
|
||||||
input_file: crates/tinymist-query/src/fixtures/completion/complete_half_label.typ
|
input_file: crates/tinymist-query/src/fixtures/completion/complete_half_label.typ
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -20,7 +19,7 @@ snapshot_kind: text
|
||||||
"newText": "test>",
|
"newText": "test>",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 2,
|
"character": 3,
|
||||||
"line": 3
|
"line": 3
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
|
|
@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs
|
||||||
description: Completion on t (56..57)
|
description: Completion on t (56..57)
|
||||||
expression: "JsonRepr::new_pure(results)"
|
expression: "JsonRepr::new_pure(results)"
|
||||||
input_file: crates/tinymist-query/src/fixtures/completion/complete_half_label_cite.typ
|
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>",
|
"newText": "tarry>",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 7,
|
"character": 8,
|
||||||
"line": 3
|
"line": 3
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
|
|
@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs
|
||||||
description: Completion on t (103..104)
|
description: Completion on t (103..104)
|
||||||
expression: "JsonRepr::new_pure(results)"
|
expression: "JsonRepr::new_pure(results)"
|
||||||
input_file: crates/tinymist-query/src/fixtures/completion/complete_purely_label.typ
|
input_file: crates/tinymist-query/src/fixtures/completion/complete_purely_label.typ
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -20,7 +19,7 @@ snapshot_kind: text
|
||||||
"newText": "test>",
|
"newText": "test>",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 2,
|
"character": 4,
|
||||||
"line": 11
|
"line": 11
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
|
|
@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs
|
||||||
description: Completion on @R (135..137)
|
description: Completion on @R (135..137)
|
||||||
expression: "JsonRepr::new_pure(results)"
|
expression: "JsonRepr::new_pure(results)"
|
||||||
input_file: crates/tinymist-query/src/fixtures/completion/completion_title.typ
|
input_file: crates/tinymist-query/src/fixtures/completion/completion_title.typ
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -23,7 +22,7 @@ snapshot_kind: text
|
||||||
"newText": "Russell:1908",
|
"newText": "Russell:1908",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 1,
|
"character": 2,
|
||||||
"line": 5
|
"line": 5
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@ -43,7 +42,7 @@ snapshot_kind: text
|
||||||
"newText": "Russell:1908",
|
"newText": "Russell:1908",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 1,
|
"character": 2,
|
||||||
"line": 5
|
"line": 5
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
|
|
@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs
|
||||||
description: Completion on 9 (109..110)
|
description: Completion on 9 (109..110)
|
||||||
expression: "JsonRepr::new_pure(results)"
|
expression: "JsonRepr::new_pure(results)"
|
||||||
input_file: crates/tinymist-query/src/fixtures/completion/completion_title2.typ
|
input_file: crates/tinymist-query/src/fixtures/completion/completion_title2.typ
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -20,7 +19,7 @@ snapshot_kind: text
|
||||||
"newText": "Russell:1908>",
|
"newText": "Russell:1908>",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 16,
|
"character": 20,
|
||||||
"line": 3
|
"line": 3
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@ -41,7 +40,7 @@ snapshot_kind: text
|
||||||
"newText": "Russell:1908>",
|
"newText": "Russell:1908>",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 16,
|
"character": 20,
|
||||||
"line": 3
|
"line": 3
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
|
|
@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs
|
||||||
description: Completion on e (57..58)
|
description: Completion on e (57..58)
|
||||||
expression: "JsonRepr::new_pure(results)"
|
expression: "JsonRepr::new_pure(results)"
|
||||||
input_file: crates/tinymist-query/src/fixtures/completion/label_middle.typ
|
input_file: crates/tinymist-query/src/fixtures/completion/label_middle.typ
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -20,7 +19,7 @@ snapshot_kind: text
|
||||||
"newText": "abcd>",
|
"newText": "abcd>",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 4,
|
"character": 5,
|
||||||
"line": 3
|
"line": 3
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@ -41,7 +40,7 @@ snapshot_kind: text
|
||||||
"newText": "abef>",
|
"newText": "abef>",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 4,
|
"character": 5,
|
||||||
"line": 3
|
"line": 3
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@ -62,7 +61,7 @@ snapshot_kind: text
|
||||||
"newText": "efg>",
|
"newText": "efg>",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 4,
|
"character": 5,
|
||||||
"line": 3
|
"line": 3
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
|
|
@ -18,7 +18,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/ref_half.typ
|
||||||
"newText": "test",
|
"newText": "test",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 1,
|
"character": 2,
|
||||||
"line": 11
|
"line": 11
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
|
|
@ -18,7 +18,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/ref_half2.typ
|
||||||
"newText": "test",
|
"newText": "test",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 1,
|
"character": 3,
|
||||||
"line": 11
|
"line": 11
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@ -43,7 +43,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/ref_half2.typ
|
||||||
"newText": "test",
|
"newText": "test",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 2,
|
"character": 3,
|
||||||
"line": 11
|
"line": 11
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
|
|
@ -18,7 +18,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/ref_half_colon.typ
|
||||||
"newText": "sec:it",
|
"newText": "sec:it",
|
||||||
"range": {
|
"range": {
|
||||||
"end": {
|
"end": {
|
||||||
"character": 5,
|
"character": 6,
|
||||||
"line": 13
|
"line": 13
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue