mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-19 18:55:01 +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.
|
||||
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| {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue