feat: detect explicit completion from vscode (#1496)

* fix: prevent completion list from showing on bad pos

* feat: detect explicit completion from vscode

* fix: cases

* test: update snapshot
This commit is contained in:
Myriad-Dreamin 2025-03-13 12:05:47 +08:00 committed by GitHub
parent 8424df13f9
commit 7399fccd5a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 262 additions and 19 deletions

View file

@ -55,6 +55,10 @@ impl StatefulRequest for CompletionRequest {
return None;
}
let document = doc.as_ref().map(|doc| &doc.document);
let source = ctx.source_by_path(&self.path).ok()?;
let cursor = ctx.to_typst_pos_offset(&source, self.position, 0)?;
// Please see <https://github.com/nvarner/typst-lsp/commit/2d66f26fb96ceb8e485f492e5b81e9db25c3e8ec>
//
// FIXME: correctly identify a completion which is triggered
@ -68,11 +72,17 @@ impl StatefulRequest for CompletionRequest {
//
// Hence, we cannot distinguish between the two cases. Conservatively, we
// assume that the completion is not explicit.
let explicit = false;
let document = doc.as_ref().map(|doc| &doc.document);
let source = ctx.source_by_path(&self.path).ok()?;
let cursor = ctx.to_typst_pos_offset(&source, self.position, 0)?;
//
// Second try: According to VSCode:
// - <https://github.com/microsoft/vscode/issues/130953>
// - <https://github.com/microsoft/vscode/commit/0984071fe0d8a3c157a1ba810c244752d69e5689>
// Checks the previous text to filter out letter explicit completions.
let explicit = self.explicit
&& (self.trigger_character.is_none() && {
let prev_text = &source.text()[..cursor];
let prev_char = prev_text.chars().next_back();
prev_char.is_none_or(|c| !c.is_alphabetic())
});
let mut cursor = CompletionCursor::new(ctx.shared_(), &source, cursor)?;
let mut worker = CompletionWorker::new(ctx, document, explicit, self.trigger_character)?;
@ -118,6 +128,11 @@ mod tests {
let trigger_character = properties
.get("trigger_character")
.map(|v| v.chars().next().unwrap());
let explicit = match properties.get("explicit").copied().map(str::trim) {
Some("true") => true,
Some("false") | None => false,
Some(v) => panic!("invalid value for 'explicit' property: {v}"),
};
let mut includes = HashSet::new();
let mut excludes = HashSet::new();
@ -171,7 +186,7 @@ mod tests {
let request = CompletionRequest {
path: ctx.path_for_id(id).unwrap().as_path().to_owned(),
position: ctx.to_lsp_pos(s, &source),
explicit: false,
explicit,
trigger_character,
};
let result = request