feat: auto complete parameters after completing a function (#150)

* feat: auto complete parameters after completing a function

* dev: update snapshot

* dev: update completion snapshot
This commit is contained in:
Myriad-Dreamin 2024-04-02 18:50:02 +08:00 committed by GitHub
parent d71dd38b98
commit bd610b2323
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 69 additions and 24 deletions

View file

@ -250,6 +250,7 @@ fn complete_path(
kind: CompletionKind::Folder, kind: CompletionKind::Folder,
apply: None, apply: None,
detail: None, detail: None,
command: None,
}); });
} else { } else {
module_completions.push(Completion { module_completions.push(Completion {
@ -257,6 +258,7 @@ fn complete_path(
kind: CompletionKind::Module, kind: CompletionKind::Module,
apply: None, apply: None,
detail: None, detail: None,
command: None,
}); });
} }
} }

View file

@ -114,7 +114,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/base.typ
"kind": 3, "kind": 3,
"label": "aa", "label": "aa",
"textEdit": { "textEdit": {
"newText": "aa", "newText": "aa(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -131,7 +131,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/base.typ
"kind": 3, "kind": 3,
"label": "aab", "label": "aab",
"textEdit": { "textEdit": {
"newText": "aab", "newText": "aab(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -148,7 +148,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/base.typ
"kind": 3, "kind": 3,
"label": "aabc", "label": "aabc",
"textEdit": { "textEdit": {
"newText": "aabc", "newText": "aabc(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -165,7 +165,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/base.typ
"kind": 3, "kind": 3,
"label": "aac", "label": "aac",
"textEdit": { "textEdit": {
"newText": "aac", "newText": "aac(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -255,7 +255,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/base.typ
"kind": 3, "kind": 3,
"label": "aabc", "label": "aabc",
"textEdit": { "textEdit": {
"newText": "aabc", "newText": "aabc(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -272,7 +272,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/base.typ
"kind": 3, "kind": 3,
"label": "aac", "label": "aac",
"textEdit": { "textEdit": {
"newText": "aac", "newText": "aac(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,

View file

@ -114,7 +114,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/func_params.typ
"kind": 3, "kind": 3,
"label": "aa", "label": "aa",
"textEdit": { "textEdit": {
"newText": "aa", "newText": "aa(${1:})",
"range": { "range": {
"end": { "end": {
"character": 5, "character": 5,

View file

@ -114,7 +114,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/item_shadow.typ
"kind": 3, "kind": 3,
"label": "aa", "label": "aa",
"textEdit": { "textEdit": {
"newText": "aa", "newText": "aa(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -131,7 +131,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/item_shadow.typ
"kind": 3, "kind": 3,
"label": "aac", "label": "aac",
"textEdit": { "textEdit": {
"newText": "aac", "newText": "aac(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -221,7 +221,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/item_shadow.typ
"kind": 3, "kind": 3,
"label": "aac", "label": "aac",
"textEdit": { "textEdit": {
"newText": "aac", "newText": "aac(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,

View file

@ -114,7 +114,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/let.typ
"kind": 3, "kind": 3,
"label": "aa", "label": "aa",
"textEdit": { "textEdit": {
"newText": "aa", "newText": "aa(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -165,7 +165,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/let.typ
"kind": 3, "kind": 3,
"label": "aac", "label": "aac",
"textEdit": { "textEdit": {
"newText": "aac", "newText": "aac(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -272,7 +272,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/let.typ
"kind": 3, "kind": 3,
"label": "aac", "label": "aac",
"textEdit": { "textEdit": {
"newText": "aac", "newText": "aac(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,

View file

@ -211,7 +211,7 @@ pub mod typst_to_lsp {
use itertools::Itertools; use itertools::Itertools;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use lsp_types::{ use lsp_types::{
CompletionTextEdit, Documentation, InsertTextFormat, LanguageString, MarkedString, Command, CompletionTextEdit, Documentation, InsertTextFormat, LanguageString, MarkedString,
MarkupContent, MarkupKind, TextEdit, MarkupContent, MarkupKind, TextEdit,
}; };
use regex::{Captures, Regex}; use regex::{Captures, Regex};
@ -312,6 +312,10 @@ pub mod typst_to_lsp {
detail: typst_completion.detail.as_ref().map(String::from), detail: typst_completion.detail.as_ref().map(String::from),
text_edit: Some(text_edit), text_edit: Some(text_edit),
insert_text_format: Some(InsertTextFormat::SNIPPET), insert_text_format: Some(InsertTextFormat::SNIPPET),
command: typst_completion.command.as_ref().map(|c| Command {
command: c.to_string(),
..Default::default()
}),
..Default::default() ..Default::default()
} }
} }

View file

@ -60,10 +60,12 @@ pub struct Completion {
pub apply: Option<EcoString>, pub apply: Option<EcoString>,
/// An optional short description, at most one sentence. /// An optional short description, at most one sentence.
pub detail: Option<EcoString>, pub detail: Option<EcoString>,
/// An optional command to run when the completion is selected.
pub command: Option<&'static str>,
} }
/// A kind of item that can be completed. /// A kind of item that can be completed.
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
pub enum CompletionKind { pub enum CompletionKind {
/// A syntactical structure. /// A syntactical structure.
@ -394,6 +396,7 @@ fn field_access_completions(ctx: &mut CompletionContext, value: &Value, styles:
eco_format!("{method}()${{}}") eco_format!("{method}()${{}}")
}), }),
detail: None, detail: None,
command: None,
}) })
} }
@ -420,6 +423,7 @@ fn field_access_completions(ctx: &mut CompletionContext, value: &Value, styles:
label: modifier.into(), label: modifier.into(),
apply: None, apply: None,
detail: None, detail: None,
command: None,
}); });
} }
} }
@ -454,6 +458,7 @@ fn field_access_completions(ctx: &mut CompletionContext, value: &Value, styles:
label: name.clone(), label: name.clone(),
apply: None, apply: None,
detail: None, detail: None,
command: None,
}) })
} }
} }
@ -987,6 +992,12 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
label: label.into(), label: label.into(),
apply: Some(snippet.into()), apply: Some(snippet.into()),
detail: Some(docs.into()), detail: Some(docs.into()),
// VS Code doesn't do that... Auto triggering suggestion only happens on typing (word
// starts or trigger characters). However, you can use editor.action.triggerSuggest as
// command on a suggestion to "manually" retrigger suggest after inserting one
//
// todo: only vscode and neovim (0.9.1) support this
command: Some("editor.action.triggerSuggest"),
}); });
} }
@ -1041,6 +1052,7 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
label: name.into(), label: name.into(),
apply: Some(tags[0].into()), apply: Some(tags[0].into()),
detail: Some(repr::separated_list(&tags, " or ").into()), detail: Some(repr::separated_list(&tags, " or ").into()),
command: None,
}); });
} }
} }
@ -1079,6 +1091,7 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
}), }),
label: label.as_str().into(), label: label.as_str().into(),
detail, detail,
command: None,
}); });
} }
} }
@ -1105,8 +1118,10 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
}); });
let mut apply = None; let mut apply = None;
let mut command = None;
if parens && matches!(value, Value::Func(_)) { if parens && matches!(value, Value::Func(_)) {
if let Value::Func(func) = value { if let Value::Func(func) = value {
command = Some("editor.action.triggerSuggest");
if func if func
.params() .params()
.is_some_and(|params| params.iter().all(|param| param.name == "self")) .is_some_and(|params| params.iter().all(|param| param.name == "self"))
@ -1134,6 +1149,7 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
label, label,
apply, apply,
detail, detail,
command,
}); });
} }
@ -1209,6 +1225,7 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
label: ty.long_name().into(), label: ty.long_name().into(),
apply: Some(eco_format!("${{{ty}}}")), apply: Some(eco_format!("${{{ty}}}")),
detail: Some(eco_format!("A value of type {ty}.")), detail: Some(eco_format!("A value of type {ty}.")),
command: None,
}); });
self.scope_completions_(false, |value| value.ty() == *ty); self.scope_completions_(false, |value| value.ty() == *ty);
} }

View file

@ -134,12 +134,26 @@ impl<'a, 'w> CompletionContext<'a, 'w> {
for (name, kind) in defined { for (name, kind) in defined {
if !name.is_empty() { if !name.is_empty() {
self.completions.push(Completion { if kind == CompletionKind::Func {
kind, // todo: check arguments, if empty, jump to after the parens
label: name, let apply = eco_format!("{}(${{}})", name);
apply: None, self.completions.push(Completion {
detail: None, kind,
}); label: name,
apply: Some(apply),
detail: None,
// todo: only vscode and neovim (0.9.1) support this
command: Some("editor.action.triggerSuggest"),
});
} else {
self.completions.push(Completion {
kind,
label: name,
apply: None,
detail: None,
command: None,
});
}
} }
} }
} }
@ -190,6 +204,13 @@ pub fn param_completions<'a>(
label: param.name.clone().into(), label: param.name.clone().into(),
apply: Some(eco_format!("{}: ${{}}", param.name)), apply: Some(eco_format!("{}: ${{}}", param.name)),
detail: Some(plain_docs_sentence(&param.docs)), detail: Some(plain_docs_sentence(&param.docs)),
// todo: only vscode and neovim (0.9.1) support this
//
// VS Code doesn't do that... Auto triggering suggestion only happens on typing
// (word starts or trigger characters). However, you can use
// editor.action.triggerSuggest as command on a suggestion to
// "manually" retrigger suggest after inserting one
command: Some("editor.action.triggerSuggest"),
}); });
} }
@ -230,12 +251,13 @@ pub fn named_param_value_completions<'a>(
return; return;
} }
if let Some(expr) = &param.type_repr { if let Some(expr) = &param.expr {
ctx.completions.push(Completion { ctx.completions.push(Completion {
kind: CompletionKind::Constant, kind: CompletionKind::Constant,
label: expr.clone(), label: expr.clone(),
apply: None, apply: None,
detail: Some(plain_docs_sentence(&param.docs)), detail: Some(plain_docs_sentence(&param.docs)),
command: None,
}); });
} }

View file

@ -375,7 +375,7 @@ fn e2e() {
}); });
let hash = replay_log(&tinymist_binary, &root.join("neovim")); let hash = replay_log(&tinymist_binary, &root.join("neovim"));
insta::assert_snapshot!(hash, @"siphash128_13:db5f6b70fa2cff5f0a58b4d11de387f9"); insta::assert_snapshot!(hash, @"siphash128_13:3a5dda1ab162b4cb843916a23f05ac0");
} }
{ {
@ -386,7 +386,7 @@ fn e2e() {
}); });
let hash = replay_log(&tinymist_binary, &root.join("vscode")); let hash = replay_log(&tinymist_binary, &root.join("vscode"));
insta::assert_snapshot!(hash, @"siphash128_13:a4bb5a2f5705857f8fbd7f8f71ac86c9"); insta::assert_snapshot!(hash, @"siphash128_13:75e8e25d2b63fc959c024b5a509466d7");
} }
} }