diff --git a/crates/tinymist-query/src/completion.rs b/crates/tinymist-query/src/completion.rs index 42e8e5be..48b9c4d4 100644 --- a/crates/tinymist-query/src/completion.rs +++ b/crates/tinymist-query/src/completion.rs @@ -46,6 +46,8 @@ pub struct CompletionRequest { pub position: LspPosition, /// Whether the completion is triggered explicitly. pub explicit: bool, + /// The character that triggered the completion, if any. + pub trigger_character: Option, /// Whether to trigger suggest completion, a.k.a. auto-completion. pub trigger_suggest: bool, /// Whether to trigger named parameter completion. @@ -145,6 +147,7 @@ impl StatefulRequest for CompletionRequest { &source, cursor, explicit, + self.trigger_character, self.trigger_suggest, self.trigger_parameter_hints, self.trigger_named_completion, @@ -336,6 +339,11 @@ mod tests { let docs = find_module_level_docs(&source).unwrap_or_default(); let properties = get_test_properties(&docs); + + let trigger_character = properties + .get("trigger_character") + .map(|v| v.chars().next().unwrap()); + let mut includes = HashSet::new(); let mut excludes = HashSet::new(); @@ -389,6 +397,7 @@ mod tests { path: ctx.path_for_id(id).unwrap(), position: ctx.to_lsp_pos(s, &source), explicit: false, + trigger_character, trigger_suggest: true, trigger_parameter_hints: true, trigger_named_completion: true, diff --git a/crates/tinymist-query/src/fixtures/completion/colon_markup.typ b/crates/tinymist-query/src/fixtures/completion/colon_markup.typ new file mode 100644 index 00000000..3c48fa30 --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/colon_markup.typ @@ -0,0 +1,2 @@ +// contains: attach +$: /* range -1..0 */$ \ No newline at end of file diff --git a/crates/tinymist-query/src/fixtures/completion/colon_markup2.typ b/crates/tinymist-query/src/fixtures/completion/colon_markup2.typ new file mode 100644 index 00000000..0ef5d022 --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/colon_markup2.typ @@ -0,0 +1,3 @@ +// contains: attach +// trigger_character: : +$: /* range -1..0 */$ \ No newline at end of file diff --git a/crates/tinymist-query/src/fixtures/completion/colon_math.typ b/crates/tinymist-query/src/fixtures/completion/colon_math.typ new file mode 100644 index 00000000..3c48fa30 --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/colon_math.typ @@ -0,0 +1,2 @@ +// contains: attach +$: /* range -1..0 */$ \ No newline at end of file diff --git a/crates/tinymist-query/src/fixtures/completion/colon_math2.typ b/crates/tinymist-query/src/fixtures/completion/colon_math2.typ new file mode 100644 index 00000000..0ef5d022 --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/colon_math2.typ @@ -0,0 +1,3 @@ +// contains: attach +// trigger_character: : +$: /* range -1..0 */$ \ No newline at end of file diff --git a/crates/tinymist-query/src/fixtures/completion/colon_param.typ b/crates/tinymist-query/src/fixtures/completion/colon_param.typ new file mode 100644 index 00000000..0ce5398c --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/colon_param.typ @@ -0,0 +1,3 @@ +// contains: length +// trigger_character: : +#set text(baseline: /* range -1..0 */) \ No newline at end of file diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_markup.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_markup.typ.snap new file mode 100644 index 00000000..c38a2010 --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_markup.typ.snap @@ -0,0 +1,33 @@ +--- +source: crates/tinymist-query/src/completion.rs +description: Completion on (22..23) +expression: "JsonRepr::new_pure(results)" +input_file: crates/tinymist-query/src/fixtures/completion/colon_markup.typ +--- +[ + { + "isIncomplete": false, + "items": [ + { + "kind": 3, + "label": "attach", + "labelDetails": { + "description": "(content, b: content | none, bl: content | none, br: content | none, t: content | none, tl: content | none, tr: content | none) => attach" + }, + "textEdit": { + "newText": "attach(${1:})", + "range": { + "end": { + "character": 2, + "line": 1 + }, + "start": { + "character": 1, + "line": 1 + } + } + } + } + ] + } +] diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_markup2.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_markup2.typ.snap new file mode 100644 index 00000000..5a6d0f5b --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_markup2.typ.snap @@ -0,0 +1,12 @@ +--- +source: crates/tinymist-query/src/completion.rs +description: Completion on (46..47) +expression: "JsonRepr::new_pure(results)" +input_file: crates/tinymist-query/src/fixtures/completion/colon_markup2.typ +--- +[ + { + "isIncomplete": false, + "items": [] + } +] diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_math.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_math.typ.snap new file mode 100644 index 00000000..ca0e9891 --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_math.typ.snap @@ -0,0 +1,33 @@ +--- +source: crates/tinymist-query/src/completion.rs +description: Completion on (22..23) +expression: "JsonRepr::new_pure(results)" +input_file: crates/tinymist-query/src/fixtures/completion/colon_math.typ +--- +[ + { + "isIncomplete": false, + "items": [ + { + "kind": 3, + "label": "attach", + "labelDetails": { + "description": "(content, b: content | none, bl: content | none, br: content | none, t: content | none, tl: content | none, tr: content | none) => attach" + }, + "textEdit": { + "newText": "attach(${1:})", + "range": { + "end": { + "character": 2, + "line": 1 + }, + "start": { + "character": 1, + "line": 1 + } + } + } + } + ] + } +] diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_math2.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_math2.typ.snap new file mode 100644 index 00000000..de7260c4 --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_math2.typ.snap @@ -0,0 +1,12 @@ +--- +source: crates/tinymist-query/src/completion.rs +description: Completion on (46..47) +expression: "JsonRepr::new_pure(results)" +input_file: crates/tinymist-query/src/fixtures/completion/colon_math2.typ +--- +[ + { + "isIncomplete": false, + "items": [] + } +] diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_param.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_param.typ.snap new file mode 100644 index 00000000..dc644b9b --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_param.typ.snap @@ -0,0 +1,31 @@ +--- +source: crates/tinymist-query/src/completion.rs +description: Completion on (63..64) +expression: "JsonRepr::new_pure(results)" +input_file: crates/tinymist-query/src/fixtures/completion/colon_param.typ +--- +[ + { + "isIncomplete": false, + "items": [ + { + "kind": 15, + "label": "length", + "sortText": "000", + "textEdit": { + "newText": " ${1:length}", + "range": { + "end": { + "character": 19, + "line": 2 + }, + "start": { + "character": 19, + "line": 2 + } + } + } + } + ] + } +] diff --git a/crates/tinymist-query/src/upstream/complete.rs b/crates/tinymist-query/src/upstream/complete.rs index 09601e86..d8ccf512 100644 --- a/crates/tinymist-query/src/upstream/complete.rs +++ b/crates/tinymist-query/src/upstream/complete.rs @@ -187,7 +187,7 @@ fn complete_markup(ctx: &mut CompletionContext) -> bool { } // Anywhere: "|". - if ctx.explicit { + if !is_triggerred_by_punc(ctx.trigger_character) && ctx.explicit { ctx.from = ctx.cursor; markup_completions(ctx); return true; @@ -316,14 +316,16 @@ fn complete_math(ctx: &mut CompletionContext) -> bool { } // Behind existing atom or identifier: "$a|$" or "$abc|$". - if matches!(ctx.leaf.kind(), SyntaxKind::Text | SyntaxKind::MathIdent) { + if !is_triggerred_by_punc(ctx.trigger_character) + && matches!(ctx.leaf.kind(), SyntaxKind::Text | SyntaxKind::MathIdent) + { ctx.from = ctx.leaf.offset(); math_completions(ctx); return true; } // Anywhere: "$|$". - if ctx.explicit { + if !is_triggerred_by_punc(ctx.trigger_character) && ctx.explicit { ctx.from = ctx.cursor; math_completions(ctx); return true; @@ -959,6 +961,7 @@ pub struct CompletionContext<'a, 'b> { pub leaf: LinkedNode<'a>, pub cursor: usize, pub explicit: bool, + pub trigger_character: Option, pub trigger_suggest: bool, pub trigger_parameter_hints: bool, pub trigger_named_completion: bool, @@ -980,6 +983,7 @@ impl<'a, 'w> CompletionContext<'a, 'w> { source: &'a Source, cursor: usize, explicit: bool, + trigger_character: Option, trigger_suggest: bool, trigger_parameter_hints: bool, trigger_named_completion: bool, @@ -996,6 +1000,7 @@ impl<'a, 'w> CompletionContext<'a, 'w> { root, leaf, cursor, + trigger_character, trigger_suggest, trigger_parameter_hints, trigger_named_completion, @@ -1298,3 +1303,7 @@ fn slice_at(s: &str, mut rng: Range) -> &str { &s[rng] } + +fn is_triggerred_by_punc(trigger_character: Option) -> bool { + trigger_character.is_some_and(|c| c.is_ascii_punctuation()) +} diff --git a/crates/tinymist/src/server.rs b/crates/tinymist/src/server.rs index ad924c5f..779f674a 100644 --- a/crates/tinymist/src/server.rs +++ b/crates/tinymist/src/server.rs @@ -746,10 +746,13 @@ impl LanguageState { fn completion(&mut self, req_id: RequestId, params: CompletionParams) -> ScheduledResult { let (path, position) = as_path_pos(params.text_document_position); - let explicit = params + let explicit = params.context.as_ref().map_or(false, |context| { + context.trigger_kind == CompletionTriggerKind::INVOKED + }); + let trigger_character = params .context - .map(|context| context.trigger_kind == CompletionTriggerKind::INVOKED) - .unwrap_or(false); + .and_then(|c| c.trigger_character) + .and_then(|c| c.chars().next()); let trigger_suggest = self.config.trigger_suggest; let trigger_parameter_hints = self.config.trigger_parameter_hints; let trigger_named_completion = self.config.trigger_named_completion; @@ -760,6 +763,7 @@ impl LanguageState { path, position, explicit, + trigger_character, trigger_suggest, trigger_parameter_hints, trigger_named_completion