feat: complete parameters by capture information (#1114)

* feat: complete parameters by capture information

* dev: wording
This commit is contained in:
Myriad-Dreamin 2025-01-06 06:33:49 +08:00 committed by GitHub
parent fd33024915
commit bb66b4b97a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 111 additions and 8 deletions

View file

@ -507,7 +507,7 @@ impl CompletionPair<'_, '_, '_> {
// Special completions 3, we should remove them finally
if matches!(surrounding_syntax, ParamList) {
return self.complete_params().then_some(());
return self.complete_params();
}
// Checks and completes `self.cursor.syntax_context`

View file

@ -3,10 +3,59 @@
//! Note, this is used for the completion of parameters on a function's
//! *definition* instead of the completion of arguments of some *function call*.
use typst::eval::CapturesVisitor;
use super::*;
impl CompletionPair<'_, '_, '_> {
/// Complete parameters.
pub fn complete_params(&mut self) -> bool {
true
pub fn complete_params(&mut self) -> Option<()> {
self.cursor.from = self.cursor.leaf.offset();
let leaf = self.cursor.leaf.clone();
let closure_node = node_ancestors(&leaf).find(|node| node.kind() == SyntaxKind::Closure)?;
let mut bindings = HashSet::<EcoString>::default();
let closure_node = closure_node.cast::<ast::Closure>()?;
// It is common for a function to reference itself in typst.
let name = closure_node.name();
if let Some(name) = name {
bindings.insert(name.get().clone());
}
// Collects all bindings from the parameters.
let param_list = closure_node.params();
for param in param_list.children() {
match param {
ast::Param::Pos(pos) => {
for name in pos.bindings() {
bindings.insert(name.get().clone());
}
}
ast::Param::Named(named) => {
bindings.insert(named.name().get().clone());
}
ast::Param::Spread(spread) => {
if let Some(ident) = spread.sink_ident() {
bindings.insert(ident.get().clone());
}
}
}
}
let mut visitor = CapturesVisitor::new(None, typst::foundations::Capturer::Function);
visitor.visit(closure_node.body().to_untyped());
let captures = visitor.finish();
// Converts the captures into completions.
for (name, value, _) in captures.iter() {
if !bindings.contains(name) {
let docs = "Parametrizes the captured variable.";
self.value_completion(Some(name.clone()), value, false, Some(docs));
}
}
Some(())
}
}

View file

@ -0,0 +1,5 @@
/// contains: content
#let f(
content, /* range -6..-5 */
) = content-ext

View file

@ -0,0 +1,5 @@
/// contains: function
#let function(
fun, /* range -3..-2 */
) = function

View file

@ -1,5 +1,5 @@
/// contains: content
/// contains: value
#let f(
content, /* range -6..-5 */
) = content
val, /* range -3..-2 */
) = value

View file

@ -0,0 +1,13 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on t (36..37)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/param_capture.typ
snapshot_kind: text
---
[
{
"isIncomplete": false,
"items": []
}
]

View file

@ -0,0 +1,13 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on n (43..44)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/param_capture_recursive.typ
snapshot_kind: text
---
[
{
"isIncomplete": false,
"items": []
}
]

View file

@ -1,6 +1,6 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on t (36..37)
description: Completion on l (33..34)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/param_name.typ
snapshot_kind: text
@ -8,6 +8,24 @@ snapshot_kind: text
[
{
"isIncomplete": false,
"items": []
"items": [
{
"kind": 6,
"label": "value",
"textEdit": {
"newText": "value, ",
"range": {
"end": {
"character": 4,
"line": 3
},
"start": {
"character": 2,
"line": 3
}
}
}
}
]
}
]