mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-24 13:13:43 +00:00
feat: detect explicit completion from vscode (#1496)
* fix: prevent completion list from showing on bad pos * feat: detect explicit completion from vscode * fix: cases * test: update snapshot
This commit is contained in:
parent
8424df13f9
commit
7399fccd5a
21 changed files with 262 additions and 19 deletions
|
@ -10,7 +10,7 @@ use lsp_types::InsertTextFormat;
|
|||
use once_cell::sync::Lazy;
|
||||
use regex::{Captures, Regex};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tinymist_analysis::syntax::bad_completion_cursor;
|
||||
use tinymist_analysis::syntax::{bad_completion_cursor, BadCompletionCursor};
|
||||
use tinymist_derive::BindTyCtx;
|
||||
use tinymist_project::LspWorld;
|
||||
use tinymist_std::path::unix_slash;
|
||||
|
@ -175,10 +175,6 @@ impl<'a> CompletionCursor<'a> {
|
|||
let syntax_context = classify_context(leaf.clone(), Some(cursor));
|
||||
let surrounding_syntax = surrounding_syntax(&leaf);
|
||||
|
||||
if bad_completion_cursor(syntax.as_ref(), syntax_context.as_ref(), &leaf).is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
crate::log_debug_ct!("CompletionCursor: syntax {leaf:?} -> {syntax:#?}");
|
||||
crate::log_debug_ct!("CompletionCursor: context {leaf:?} -> {syntax_context:#?}");
|
||||
crate::log_debug_ct!("CompletionCursor: surrounding {leaf:?} -> {surrounding_syntax:#?}");
|
||||
|
@ -637,12 +633,21 @@ impl CompletionPair<'_, '_, '_> {
|
|||
| None => {}
|
||||
}
|
||||
|
||||
let cursor_pos = bad_completion_cursor(
|
||||
self.cursor.syntax.as_ref(),
|
||||
self.cursor.syntax_context.as_ref(),
|
||||
&self.cursor.leaf,
|
||||
);
|
||||
|
||||
// Triggers a complete type checking.
|
||||
let ty = self
|
||||
.worker
|
||||
.ctx
|
||||
.post_type_of_node(self.cursor.leaf.clone())
|
||||
.filter(|ty| !matches!(ty, Ty::Any));
|
||||
.filter(|ty| !matches!(ty, Ty::Any))
|
||||
// Forbids argument completion list if the cursor is in a bad position. This will
|
||||
// prevent the completion list from showing up.
|
||||
.filter(|_| !matches!(cursor_pos, Some(BadCompletionCursor::ArgListPos)));
|
||||
|
||||
crate::log_debug_ct!(
|
||||
"complete_type: {:?} -> ({surrounding_syntax:?}, {ty:#?})",
|
||||
|
|
|
@ -55,6 +55,10 @@ impl StatefulRequest for CompletionRequest {
|
|||
return None;
|
||||
}
|
||||
|
||||
let document = doc.as_ref().map(|doc| &doc.document);
|
||||
let source = ctx.source_by_path(&self.path).ok()?;
|
||||
let cursor = ctx.to_typst_pos_offset(&source, self.position, 0)?;
|
||||
|
||||
// Please see <https://github.com/nvarner/typst-lsp/commit/2d66f26fb96ceb8e485f492e5b81e9db25c3e8ec>
|
||||
//
|
||||
// FIXME: correctly identify a completion which is triggered
|
||||
|
@ -68,11 +72,17 @@ impl StatefulRequest for CompletionRequest {
|
|||
//
|
||||
// Hence, we cannot distinguish between the two cases. Conservatively, we
|
||||
// assume that the completion is not explicit.
|
||||
let explicit = false;
|
||||
|
||||
let document = doc.as_ref().map(|doc| &doc.document);
|
||||
let source = ctx.source_by_path(&self.path).ok()?;
|
||||
let cursor = ctx.to_typst_pos_offset(&source, self.position, 0)?;
|
||||
//
|
||||
// Second try: According to VSCode:
|
||||
// - <https://github.com/microsoft/vscode/issues/130953>
|
||||
// - <https://github.com/microsoft/vscode/commit/0984071fe0d8a3c157a1ba810c244752d69e5689>
|
||||
// Checks the previous text to filter out letter explicit completions.
|
||||
let explicit = self.explicit
|
||||
&& (self.trigger_character.is_none() && {
|
||||
let prev_text = &source.text()[..cursor];
|
||||
let prev_char = prev_text.chars().next_back();
|
||||
prev_char.is_none_or(|c| !c.is_alphabetic())
|
||||
});
|
||||
let mut cursor = CompletionCursor::new(ctx.shared_(), &source, cursor)?;
|
||||
|
||||
let mut worker = CompletionWorker::new(ctx, document, explicit, self.trigger_character)?;
|
||||
|
@ -118,6 +128,11 @@ mod tests {
|
|||
let trigger_character = properties
|
||||
.get("trigger_character")
|
||||
.map(|v| v.chars().next().unwrap());
|
||||
let explicit = match properties.get("explicit").copied().map(str::trim) {
|
||||
Some("true") => true,
|
||||
Some("false") | None => false,
|
||||
Some(v) => panic!("invalid value for 'explicit' property: {v}"),
|
||||
};
|
||||
|
||||
let mut includes = HashSet::new();
|
||||
let mut excludes = HashSet::new();
|
||||
|
@ -171,7 +186,7 @@ mod tests {
|
|||
let request = CompletionRequest {
|
||||
path: ctx.path_for_id(id).unwrap().as_path().to_owned(),
|
||||
position: ctx.to_lsp_pos(s, &source),
|
||||
explicit: false,
|
||||
explicit,
|
||||
trigger_character,
|
||||
};
|
||||
let result = request
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
/// contains: AA
|
||||
/// explicit: true
|
||||
$norm(x)/* range 0..1 */$
|
|
@ -0,0 +1,2 @@
|
|||
/// contains: AA
|
||||
$norm(x)A/* range 0..1 */$
|
|
@ -0,0 +1,3 @@
|
|||
/// contains: AA
|
||||
/// explicit: false
|
||||
$norm(x)/* range 0..1 */$
|
|
@ -0,0 +1,4 @@
|
|||
/// contains: label
|
||||
/// explicit: true
|
||||
|
||||
/* range 0..1 */
|
|
@ -0,0 +1,4 @@
|
|||
/// contains: label
|
||||
/// explicit: true
|
||||
|
||||
te/* range 0..1 */
|
|
@ -0,0 +1,2 @@
|
|||
/// contains: tanh
|
||||
$tan/* range 0..1 */$
|
|
@ -0,0 +1,3 @@
|
|||
/// contains: AA
|
||||
/// explicit: true
|
||||
$/* range 0..1 */$
|
|
@ -5,5 +5,8 @@ expression: "JsonRepr::new_pure(results)"
|
|||
input_file: crates/tinymist-query/src/fixtures/completion/arg_range.typ
|
||||
---
|
||||
[
|
||||
null
|
||||
{
|
||||
"isIncomplete": false,
|
||||
"items": []
|
||||
}
|
||||
]
|
||||
|
|
|
@ -5,5 +5,8 @@ expression: "JsonRepr::new_pure(results)"
|
|||
input_file: crates/tinymist-query/src/fixtures/completion/arg_range3.typ
|
||||
---
|
||||
[
|
||||
null
|
||||
{
|
||||
"isIncomplete": false,
|
||||
"items": []
|
||||
}
|
||||
]
|
||||
|
|
|
@ -5,5 +5,8 @@ expression: "JsonRepr::new_pure(results)"
|
|||
input_file: crates/tinymist-query/src/fixtures/completion/arg_range4.typ
|
||||
---
|
||||
[
|
||||
null
|
||||
{
|
||||
"isIncomplete": false,
|
||||
"items": []
|
||||
}
|
||||
]
|
||||
|
|
|
@ -5,5 +5,8 @@ expression: "JsonRepr::new_pure(results)"
|
|||
input_file: crates/tinymist-query/src/fixtures/completion/arg_range_math.typ
|
||||
---
|
||||
[
|
||||
null
|
||||
{
|
||||
"isIncomplete": false,
|
||||
"items": []
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/completion.rs
|
||||
description: Completion on / (44..45)
|
||||
expression: "JsonRepr::new_pure(results)"
|
||||
input_file: crates/tinymist-query/src/fixtures/completion/arg_range_math2.typ
|
||||
---
|
||||
[
|
||||
{
|
||||
"isIncomplete": false,
|
||||
"items": [
|
||||
{
|
||||
"kind": 5,
|
||||
"label": "AA",
|
||||
"labelDetails": {
|
||||
"description": "𝔸"
|
||||
},
|
||||
"sortText": "000",
|
||||
"textEdit": {
|
||||
"newText": "AA",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 8,
|
||||
"line": 2
|
||||
},
|
||||
"start": {
|
||||
"character": 8,
|
||||
"line": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/completion.rs
|
||||
description: Completion on / (26..27)
|
||||
expression: "JsonRepr::new_pure(results)"
|
||||
input_file: crates/tinymist-query/src/fixtures/completion/arg_range_math3.typ
|
||||
---
|
||||
[
|
||||
{
|
||||
"isIncomplete": false,
|
||||
"items": [
|
||||
{
|
||||
"kind": 5,
|
||||
"label": "AA",
|
||||
"labelDetails": {
|
||||
"description": "𝔸"
|
||||
},
|
||||
"sortText": "000",
|
||||
"textEdit": {
|
||||
"newText": "AA",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 9,
|
||||
"line": 1
|
||||
},
|
||||
"start": {
|
||||
"character": 8,
|
||||
"line": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/completion.rs
|
||||
description: Completion on / (45..46)
|
||||
expression: "JsonRepr::new_pure(results)"
|
||||
input_file: crates/tinymist-query/src/fixtures/completion/arg_range_math4.typ
|
||||
---
|
||||
[
|
||||
{
|
||||
"isIncomplete": false,
|
||||
"items": []
|
||||
}
|
||||
]
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/completion.rs
|
||||
description: Completion on / (40..41)
|
||||
expression: "JsonRepr::new_pure(results)"
|
||||
input_file: crates/tinymist-query/src/fixtures/completion/mode_markup.typ
|
||||
---
|
||||
[
|
||||
{
|
||||
"isIncomplete": false,
|
||||
"items": [
|
||||
{
|
||||
"kind": 15,
|
||||
"label": "label",
|
||||
"sortText": "007",
|
||||
"textEdit": {
|
||||
"newText": "<${1:name}>",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 0,
|
||||
"line": 3
|
||||
},
|
||||
"start": {
|
||||
"character": 0,
|
||||
"line": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/completion.rs
|
||||
description: Completion on / (42..43)
|
||||
expression: "JsonRepr::new_pure(results)"
|
||||
input_file: crates/tinymist-query/src/fixtures/completion/mode_markup2.typ
|
||||
---
|
||||
[
|
||||
{
|
||||
"isIncomplete": false,
|
||||
"items": []
|
||||
}
|
||||
]
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/completion.rs
|
||||
description: Completion on / (23..24)
|
||||
expression: "JsonRepr::new_pure(results)"
|
||||
input_file: crates/tinymist-query/src/fixtures/completion/mode_math.typ
|
||||
---
|
||||
[
|
||||
{
|
||||
"isIncomplete": false,
|
||||
"items": [
|
||||
{
|
||||
"kind": 6,
|
||||
"label": "tanh",
|
||||
"labelDetails": {
|
||||
"description": "op(text: [tanh], limits: false)"
|
||||
},
|
||||
"sortText": "318",
|
||||
"textEdit": {
|
||||
"newText": "tanh",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 4,
|
||||
"line": 1
|
||||
},
|
||||
"start": {
|
||||
"character": 1,
|
||||
"line": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/completion.rs
|
||||
description: Completion on / (37..38)
|
||||
expression: "JsonRepr::new_pure(results)"
|
||||
input_file: crates/tinymist-query/src/fixtures/completion/mode_math2.typ
|
||||
---
|
||||
[
|
||||
{
|
||||
"isIncomplete": false,
|
||||
"items": [
|
||||
{
|
||||
"kind": 5,
|
||||
"label": "AA",
|
||||
"labelDetails": {
|
||||
"description": "𝔸"
|
||||
},
|
||||
"sortText": "000",
|
||||
"textEdit": {
|
||||
"newText": "AA",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 1,
|
||||
"line": 2
|
||||
},
|
||||
"start": {
|
||||
"character": 1,
|
||||
"line": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
Loading…
Add table
Add a link
Reference in a new issue