fix: match identifier like nodes for completion (#747)

* fix: match identifier like nodes for completion

* test: update snapshot
This commit is contained in:
Myriad-Dreamin 2024-10-29 22:09:55 +08:00 committed by GitHub
parent 9385b95c81
commit e8f5f6185a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 68 additions and 22 deletions

View file

@ -4,11 +4,12 @@ use lsp_types::{
};
use once_cell::sync::Lazy;
use regex::{Captures, Regex};
use typst_shim::syntax::LinkedNodeExt;
use crate::{
analysis::{BuiltinTy, InsTy, Ty},
prelude::*,
syntax::DerefTarget,
syntax::{is_ident_like, DerefTarget},
upstream::{autocomplete, complete_path, CompletionContext},
StatefulRequest,
};
@ -96,13 +97,14 @@ impl StatefulRequest for CompletionRequest {
}
// Do some completion specific to the deref target
let mut match_ident = None;
let mut ident_like = None;
let mut completion_result = None;
let is_callee = matches!(deref_target, Some(DerefTarget::Callee(..)));
match deref_target {
Some(DerefTarget::Callee(v) | DerefTarget::VarAccess(v)) => {
if v.is::<ast::Ident>() {
match_ident = Some(v);
Some(DerefTarget::Callee(..) | DerefTarget::VarAccess(..)) => {
let node = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
if is_ident_like(&node) {
ident_like = Some(node);
}
}
Some(DerefTarget::ImportPath(v) | DerefTarget::IncludePath(v)) => {
@ -169,9 +171,9 @@ impl StatefulRequest for CompletionRequest {
let _ = ic;
let replace_range;
if match_ident.as_ref().is_some_and(|i| i.offset() == offset) {
let match_ident = match_ident.unwrap();
let mut rng = match_ident.range();
if ident_like.as_ref().is_some_and(|i| i.offset() == offset) {
let ident_like = ident_like.unwrap();
let mut rng = ident_like.range();
let ident_prefix = source.text()[rng.start..cursor].to_string();
completions.retain(|c| {
@ -191,7 +193,7 @@ impl StatefulRequest for CompletionRequest {
});
// if modifying some arguments, we need to truncate and add a comma
if !is_callee && cursor != rng.end && is_arg_like_context(&match_ident) {
if !is_callee && cursor != rng.end && is_arg_like_context(&ident_like) {
// extend comma
for c in completions.iter_mut() {
let apply = match &mut c.apply {

View file

@ -0,0 +1,2 @@
// contains: inset
#set block(in /* range -2..-1 */)

View file

@ -0,0 +1,31 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on n (31..32)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/keyword_ident.typ
---
[
{
"isIncomplete": false,
"items": [
{
"kind": 5,
"label": "inset",
"sortText": "007",
"textEdit": {
"newText": "inset: ${1:}",
"range": {
"end": {
"character": 13,
"line": 1
},
"start": {
"character": 11,
"line": 1
}
}
}
}
]
}
]

View file

@ -1,6 +1,7 @@
pub use std::{
collections::HashMap,
iter,
ops::Range,
path::{Path, PathBuf},
sync::Arc,
};
@ -26,7 +27,7 @@ pub use typst::foundations::Value;
pub use typst::syntax::FileId as TypstFileId;
pub use typst::syntax::{
ast::{self, AstNode},
LinkedNode, Source, Spanned, SyntaxKind,
LinkedNode, Source, Spanned, SyntaxKind, SyntaxNode,
};
pub use typst::World;

View file

@ -1,14 +1,7 @@
use std::ops::Range;
use ecow::EcoVec;
use serde::Serialize;
use typst::{
foundations::{Func, ParamInfo},
syntax::{
ast::{self, AstNode},
LinkedNode, SyntaxKind,
},
};
use typst::foundations::{Func, ParamInfo};
use crate::prelude::*;
pub fn deref_expr(mut ancestor: LinkedNode) -> Option<LinkedNode> {
while !ancestor.is::<ast::Expr>() {
@ -87,6 +80,18 @@ fn is_mark(sk: SyntaxKind) -> bool {
)
}
pub fn is_ident_like(node: &SyntaxNode) -> bool {
use SyntaxKind::*;
let k = node.kind();
matches!(k, Ident | MathIdent | Underscore)
|| (matches!(k, Error) && can_be_ident(node))
|| k.is_keyword()
}
fn can_be_ident(node: &SyntaxNode) -> bool {
typst::syntax::is_ident(node.text())
}
/// A mode in which a text document is interpreted.
#[derive(Debug, Clone, Copy, Serialize)]
#[serde(rename_all = "camelCase")]

View file

@ -14,7 +14,7 @@ use typst::visualize::Color;
use super::{Completion, CompletionContext, CompletionKind};
use crate::adt::interner::Interned;
use crate::analysis::{resolve_call_target, BuiltinTy, PathPreference, Ty};
use crate::syntax::{param_index_at_leaf, CheckTarget};
use crate::syntax::{is_ident_like, param_index_at_leaf, CheckTarget};
use crate::upstream::complete::complete_code;
use crate::upstream::plain_docs_sentence;
@ -1083,6 +1083,11 @@ pub(crate) fn complete_type(ctx: &mut CompletionContext) -> Option<()> {
.literal_type_of_node(ctx.leaf.clone())
.filter(|ty| !matches!(ty, Ty::Any))?;
// adjust the completion position
if is_ident_like(&ctx.leaf) {
ctx.from = ctx.leaf.offset();
}
log::debug!("complete_type: ty {:?} -> {ty:#?}", ctx.leaf);
type_completion(ctx, &ty, None);