diff --git a/crates/tinymist-query/src/fixtures/completion/item_shadow.typ b/crates/tinymist-query/src/fixtures/completion/item_shadow.typ new file mode 100644 index 00000000..4cedb4ee --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/item_shadow.typ @@ -0,0 +1,6 @@ + +#let aa = 1; +#let aa() = 1; +#let aac() = 1; + +#aac(/* range -2..0 */); diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@item_shadow.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@item_shadow.typ.snap new file mode 100644 index 00000000..0cdb14bd --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@item_shadow.typ.snap @@ -0,0 +1,239 @@ +--- +source: crates/tinymist-query/src/completion.rs +description: Completion on c( (48..50) +expression: "JsonRepr::new_pure(results)" +input_file: crates/tinymist-query/src/fixtures/completion/item_shadow.typ +--- +[ + { + "isIncomplete": true, + "items": [ + { + "kind": 7, + "label": "array", + "textEdit": { + "newText": "array", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + }, + { + "kind": 3, + "label": "parbreak", + "textEdit": { + "newText": "parbreak()${1:}", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + }, + { + "kind": 3, + "label": "smallcaps", + "textEdit": { + "newText": "smallcaps(${1:})", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + }, + { + "kind": 3, + "label": "pagebreak", + "textEdit": { + "newText": "pagebreak(${1:})", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + }, + { + "kind": 3, + "label": "metadata", + "textEdit": { + "newText": "metadata(${1:})", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + }, + { + "kind": 21, + "label": "aqua", + "textEdit": { + "newText": "aqua", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + }, + { + "kind": 3, + "label": "aa", + "textEdit": { + "newText": "aa", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + }, + { + "kind": 3, + "label": "aac", + "textEdit": { + "newText": "aac", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + }, + { + "kind": 15, + "label": "import package", + "textEdit": { + "newText": "import \"@${1:}\": ${2:items}", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + }, + { + "kind": 15, + "label": "include (package)", + "textEdit": { + "newText": "include \"@${1:}\"", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + }, + { + "kind": 15, + "label": "array literal", + "textEdit": { + "newText": "(${1:1, 2, 3})", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + }, + { + "kind": 15, + "label": "dictionary literal", + "textEdit": { + "newText": "(${1:a: 1, b: 2})", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + } + ] + }, + { + "isIncomplete": true, + "items": [ + { + "kind": 3, + "label": "aac", + "textEdit": { + "newText": "aac", + "range": { + "end": { + "character": 4, + "line": 4 + }, + "start": { + "character": 1, + "line": 4 + } + } + } + } + ] + } +] diff --git a/crates/tinymist-query/src/upstream/complete/ext.rs b/crates/tinymist-query/src/upstream/complete/ext.rs index 09ba61bb..358c1c65 100644 --- a/crates/tinymist-query/src/upstream/complete/ext.rs +++ b/crates/tinymist-query/src/upstream/complete/ext.rs @@ -1,6 +1,7 @@ use super::{Completion, CompletionContext, CompletionKind}; use std::collections::BTreeMap; +use ecow::EcoString; use typst::foundations::Value; use typst::syntax::{ast, SyntaxKind}; @@ -12,6 +13,11 @@ impl<'a> CompletionContext<'a> { /// Filters the global/math scope with the given filter. pub fn scope_completions_(&mut self, parens: bool, filter: impl Fn(&Value) -> bool) { let mut defined = BTreeMap::new(); + let mut try_insert = |name: EcoString, kind: CompletionKind| { + if let std::collections::btree_map::Entry::Vacant(entry) = defined.entry(name) { + entry.insert(kind); + } + }; let mut ancestor = Some(self.leaf.clone()); while let Some(node) = &ancestor { @@ -23,7 +29,7 @@ impl<'a> CompletionContext<'a> { ast::LetBindingKind::Normal(..) => CompletionKind::Variable, }; for ident in v.kind().bindings() { - defined.insert(ident.get().clone(), kind.clone()); + try_insert(ident.get().clone(), kind.clone()); } } @@ -39,13 +45,9 @@ impl<'a> CompletionContext<'a> { } if let Some(value) = analyzed { if imports.is_none() { - // todo: correct kind - defined.extend( - value - .name() - .map(Into::into) - .map(|e| (e, CompletionKind::Module)), - ); + if let Some(name) = value.name() { + try_insert(name.into(), CompletionKind::Module); + } } else if let Some(scope) = value.scope() { for (name, v) in scope.iter() { let kind = match v { @@ -54,7 +56,7 @@ impl<'a> CompletionContext<'a> { Value::Type(..) => CompletionKind::Type, _ => CompletionKind::Constant, }; - defined.insert(name.clone(), kind); + try_insert(name.clone(), kind); } } } @@ -68,7 +70,15 @@ impl<'a> CompletionContext<'a> { if node.prev_sibling_kind() != Some(SyntaxKind::In) { let pattern = v.pattern(); for ident in pattern.bindings() { - defined.insert(ident.get().clone(), CompletionKind::Variable); + try_insert(ident.get().clone(), CompletionKind::Variable); + } + } + } + if let Some(v) = parent.cast::() { + if node.prev_sibling_kind() != Some(SyntaxKind::In) { + let pattern = v.pattern(); + for ident in pattern.bindings() { + try_insert(ident.get().clone(), CompletionKind::Variable); } } }