From 27c31c7238a3b3b5d153bfea35eef1ef0ce4ad5f Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> Date: Thu, 20 Mar 2025 20:30:47 +0800 Subject: [PATCH] feat: forbid bad postfix completion in math mode (#1556) --- .../src/analysis/completion/field_access.rs | 51 +++++++++++++++---- .../src/fixtures/completion/dot_punc_math.typ | 3 ++ .../fixtures/completion/dot_punc_math2.typ | 3 ++ .../snaps/test@dot_punc_math.typ.snap | 12 +++++ .../snaps/test@dot_punc_math2.typ.snap | 12 +++++ 5 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 crates/tinymist-query/src/fixtures/completion/dot_punc_math.typ create mode 100644 crates/tinymist-query/src/fixtures/completion/dot_punc_math2.typ create mode 100644 crates/tinymist-query/src/fixtures/completion/snaps/test@dot_punc_math.typ.snap create mode 100644 crates/tinymist-query/src/fixtures/completion/snaps/test@dot_punc_math2.typ.snap diff --git a/crates/tinymist-query/src/analysis/completion/field_access.rs b/crates/tinymist-query/src/analysis/completion/field_access.rs index 18ca3826..ca178886 100644 --- a/crates/tinymist-query/src/analysis/completion/field_access.rs +++ b/crates/tinymist-query/src/analysis/completion/field_access.rs @@ -1,5 +1,7 @@ //! Completion for field access on nodes. +use typst::syntax::ast::MathTextKind; + use crate::analysis::completion::typst_specific::ValueCompletionInfo; use super::*; @@ -14,10 +16,11 @@ impl CompletionPair<'_, '_, '_> { fn type_dot_access_completions(&mut self, target: &LinkedNode) -> Option<()> { let mode = self.cursor.leaf_mode(); - if !matches!(mode, InterpretMode::Math) { - self.type_field_access_completions(target); + if matches!(mode, InterpretMode::Math) { + return None; } + self.type_field_access_completions(target); Some(()) } @@ -54,12 +57,19 @@ impl CompletionPair<'_, '_, '_> { let mode = self.cursor.leaf_mode(); let valid_field_access_syntax = !matches!(mode, InterpretMode::Math) || is_valid_math_field_access(target); + let valid_postfix_target = + !matches!(mode, InterpretMode::Math) || is_valid_math_postfix(target); + + if !valid_field_access_syntax && !valid_postfix_target { + return None; + } if valid_field_access_syntax { self.value_field_access_completions(&value, mode); } - - self.postfix_completions(target, Ty::Value(InsTy::new(value.clone()))); + if valid_postfix_target { + self.postfix_completions(target, Ty::Value(InsTy::new(value.clone()))); + } match value { Value::Symbol(symbol) => { @@ -74,7 +84,9 @@ impl CompletionPair<'_, '_, '_> { } } - self.ufcs_completions(target); + if valid_postfix_target { + self.ufcs_completions(target); + } } Value::Content(content) => { if valid_field_access_syntax { @@ -82,8 +94,9 @@ impl CompletionPair<'_, '_, '_> { self.value_completion(Some(name.into()), &value, false, None); } } - - self.ufcs_completions(target); + if valid_postfix_target { + self.ufcs_completions(target); + } } Value::Dict(dict) if valid_field_access_syntax => { for (name, value) in dict.iter() { @@ -168,8 +181,8 @@ fn is_func(read: &Value) -> bool { } fn is_valid_math_field_access(target: &SyntaxNode) -> bool { - if let Some(fa) = target.cast::() { - return is_valid_math_field_access(fa.target().to_untyped()); + if let Some(field_access) = target.cast::() { + return is_valid_math_field_access(field_access.target().to_untyped()); } if matches!(target.kind(), SyntaxKind::Ident | SyntaxKind::MathIdent) { return true; @@ -177,3 +190,23 @@ fn is_valid_math_field_access(target: &SyntaxNode) -> bool { false } + +fn is_valid_math_postfix(target: &SyntaxNode) -> bool { + fn bad_punc_text(punc: char) -> bool { + punc.is_ascii_punctuation() || punc.is_ascii_whitespace() + } + + if let Some(target) = target.cast::() { + return match target.get() { + MathTextKind::Character(ch) => !bad_punc_text(ch), + MathTextKind::Number(..) => true, + }; + } + + if let Some(target) = target.cast::() { + let target = target.get(); + return !target.is_empty() && target.chars().all(|ch| !bad_punc_text(ch)); + } + + true +} diff --git a/crates/tinymist-query/src/fixtures/completion/dot_punc_math.typ b/crates/tinymist-query/src/fixtures/completion/dot_punc_math.typ new file mode 100644 index 00000000..de6559f8 --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/dot_punc_math.typ @@ -0,0 +1,3 @@ +/// contains: abs + +$`a`./* range 0..1 */$ diff --git a/crates/tinymist-query/src/fixtures/completion/dot_punc_math2.typ b/crates/tinymist-query/src/fixtures/completion/dot_punc_math2.typ new file mode 100644 index 00000000..a8c8426f --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/dot_punc_math2.typ @@ -0,0 +1,3 @@ +/// contains: abs + +$(a)./* range 0..1 */$ diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@dot_punc_math.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@dot_punc_math.typ.snap new file mode 100644 index 00000000..013fc07e --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@dot_punc_math.typ.snap @@ -0,0 +1,12 @@ +--- +source: crates/tinymist-query/src/completion.rs +description: Completion on / (24..25) +expression: "JsonRepr::new_pure(results)" +input_file: crates/tinymist-query/src/fixtures/completion/dot_punc_math.typ +--- +[ + { + "isIncomplete": false, + "items": [] + } +] diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@dot_punc_math2.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@dot_punc_math2.typ.snap new file mode 100644 index 00000000..2eab725f --- /dev/null +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@dot_punc_math2.typ.snap @@ -0,0 +1,12 @@ +--- +source: crates/tinymist-query/src/completion.rs +description: Completion on / (24..25) +expression: "JsonRepr::new_pure(results)" +input_file: crates/tinymist-query/src/fixtures/completion/dot_punc_math2.typ +--- +[ + { + "isIncomplete": false, + "items": [] + } +]