feat: completion on func.where and func.with (#204)

This commit is contained in:
Myriad-Dreamin 2024-04-20 15:17:47 +08:00 committed by GitHub
parent b35d897919
commit b52ad52760
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 928 additions and 427 deletions

View file

@ -352,7 +352,7 @@ mod signature_tests {
use typst::foundations::Repr; use typst::foundations::Repr;
use typst::syntax::LinkedNode; use typst::syntax::LinkedNode;
use crate::analysis::{analyze_signature_v2, Signature, SignatureTarget}; use crate::analysis::{analyze_signature, Signature, SignatureTarget};
use crate::syntax::get_deref_target; use crate::syntax::get_deref_target;
use crate::tests::*; use crate::tests::*;
@ -370,7 +370,7 @@ mod signature_tests {
let callee_node = get_deref_target(callee_node, pos).unwrap(); let callee_node = get_deref_target(callee_node, pos).unwrap();
let callee_node = callee_node.node(); let callee_node = callee_node.node();
let result = analyze_signature_v2( let result = analyze_signature(
ctx, ctx,
source.clone(), source.clone(),
SignatureTarget::Syntax(callee_node.clone()), SignatureTarget::Syntax(callee_node.clone()),

View file

@ -4,7 +4,7 @@ use typst::syntax::SyntaxNode;
use super::{ParamSpec, Signature}; use super::{ParamSpec, Signature};
use crate::{ use crate::{
analysis::{analyze_signature_v2, PrimarySignature, SignatureTarget}, analysis::{analyze_signature, PrimarySignature, SignatureTarget},
prelude::*, prelude::*,
}; };
@ -74,7 +74,7 @@ pub fn analyze_call_no_cache(
callee_node: LinkedNode, callee_node: LinkedNode,
args: ast::Args<'_>, args: ast::Args<'_>,
) -> Option<CallInfo> { ) -> Option<CallInfo> {
let signature = analyze_signature_v2(ctx, source, SignatureTarget::Syntax(callee_node))?; let signature = analyze_signature(ctx, source, SignatureTarget::Syntax(callee_node))?;
trace!("got signature {signature:?}"); trace!("got signature {signature:?}");
let mut info = CallInfo { let mut info = CallInfo {

View file

@ -3,6 +3,8 @@
use std::ops::Range; use std::ops::Range;
use log::debug; use log::debug;
use once_cell::sync::Lazy;
use typst::foundations::Type;
use typst::syntax::FileId as TypstFileId; use typst::syntax::FileId as TypstFileId;
use typst::{foundations::Value, syntax::Span}; use typst::{foundations::Value, syntax::Span};
@ -166,9 +168,115 @@ pub fn find_definition(
} }
} }
/// The target of a dynamic call.
#[derive(Debug, Clone)]
pub struct DynCallTarget {
/// The function pointer.
pub func_ptr: Func,
/// The this pointer.
pub this: Option<Value>,
}
/// The calling convention of a function.
pub enum CallConvention {
/// A static function.
Static(Func),
/// A method call with a this.
Method(Value, Func),
/// A function call by with binding.
With(Func),
/// A function call by where binding.
Where(Func),
}
impl CallConvention {
/// Get the function pointer of the call.
pub fn method_this(&self) -> Option<&Value> {
match self {
CallConvention::Static(_) => None,
CallConvention::Method(this, _) => Some(this),
CallConvention::With(_) => None,
CallConvention::Where(_) => None,
}
}
/// Get the function pointer of the call.
pub fn callee(self) -> Func {
match self {
CallConvention::Static(f) => f,
CallConvention::Method(_, f) => f,
CallConvention::With(f) => f,
CallConvention::Where(f) => f,
}
}
}
fn identify_call_convention(target: DynCallTarget) -> CallConvention {
match target.this {
Some(Value::Func(func)) if is_with_func(&target.func_ptr) => CallConvention::With(func),
Some(Value::Func(func)) if is_where_func(&target.func_ptr) => CallConvention::Where(func),
Some(this) => CallConvention::Method(this, target.func_ptr),
None => CallConvention::Static(target.func_ptr),
}
}
fn is_with_func(func_ptr: &Func) -> bool {
static WITH_FUNC: Lazy<Option<&'static Func>> = Lazy::new(|| {
let fn_ty = Type::of::<Func>();
let Some(Value::Func(f)) = fn_ty.scope().get("with") else {
return None;
};
Some(f)
});
is_same_native_func(*WITH_FUNC, func_ptr)
}
fn is_where_func(func_ptr: &Func) -> bool {
static WITH_FUNC: Lazy<Option<&'static Func>> = Lazy::new(|| {
let fn_ty = Type::of::<Func>();
let Some(Value::Func(f)) = fn_ty.scope().get("where") else {
return None;
};
Some(f)
});
is_same_native_func(*WITH_FUNC, func_ptr)
}
fn is_same_native_func(x: Option<&Func>, y: &Func) -> bool {
let Some(x) = x else {
return false;
};
use typst::foundations::func::Repr;
match (x.inner(), y.inner()) {
(Repr::Native(x), Repr::Native(y)) => x == y,
(Repr::Element(x), Repr::Element(y)) => x == y,
_ => false,
}
}
// todo: merge me with resolve_callee
/// Resolve a call target to a function or a method with a this.
pub fn resolve_call_target(
ctx: &mut AnalysisContext,
callee: LinkedNode,
) -> Option<CallConvention> {
resolve_callee_(ctx, callee, true).map(identify_call_convention)
}
/// Resolve a callee expression to a function. /// Resolve a callee expression to a function.
pub fn resolve_callee(ctx: &mut AnalysisContext, callee: LinkedNode) -> Option<Func> { pub fn resolve_callee(ctx: &mut AnalysisContext, callee: LinkedNode) -> Option<Func> {
{ resolve_callee_(ctx, callee, false).map(|e| e.func_ptr)
}
fn resolve_callee_(
ctx: &mut AnalysisContext,
callee: LinkedNode,
resolve_this: bool,
) -> Option<DynCallTarget> {
None.or_else(|| {
let source = ctx.source_by_id(callee.span().id()?).ok()?; let source = ctx.source_by_id(callee.span().id()?).ok()?;
let node = source.find(callee.span())?; let node = source.find(callee.span())?;
let cursor = node.offset(); let cursor = node.offset();
@ -181,20 +289,54 @@ pub fn resolve_callee(ctx: &mut AnalysisContext, callee: LinkedNode) -> Option<F
}, },
_ => None, _ => None,
} }
} })
.or_else(|| { .or_else(|| {
resolve_global_value(ctx, callee.clone(), false).and_then(|v| match v { resolve_global_value(ctx, callee.clone(), false).and_then(|v| match v {
Value::Func(f) => Some(f), Value::Func(f) => Some(f),
_ => None, _ => None,
}) })
}) })
.map(|e| DynCallTarget {
func_ptr: e,
this: None,
})
.or_else(|| { .or_else(|| {
let values = analyze_expr(ctx.world(), &callee); let values = analyze_expr(ctx.world(), &callee);
values.into_iter().find_map(|v| match v.0 { if let Some(func) = values.into_iter().find_map(|v| match v.0 {
Value::Func(f) => Some(f), Value::Func(f) => Some(f),
_ => None, _ => None,
}) }) {
return Some(DynCallTarget {
func_ptr: func,
this: None,
});
};
if resolve_this {
if let Some(access) = match callee.cast::<ast::Expr>() {
Some(ast::Expr::FieldAccess(access)) => Some(access),
_ => None,
} {
let target = access.target();
let field = access.field().get();
let values = analyze_expr(ctx.world(), &callee.find(target.span())?);
if let Some((this, func_ptr)) = values.into_iter().find_map(|(this, _styles)| {
if let Some(Value::Func(f)) = this.ty().scope().get(field) {
return Some((this, f.clone()));
}
None
}) {
return Some(DynCallTarget {
func_ptr,
this: Some(this),
});
}
}
}
None
}) })
} }

View file

@ -166,7 +166,7 @@ pub(crate) fn analyze_dyn_signature(ctx: &mut AnalysisContext, func: Func) -> Si
}) })
} }
pub(crate) fn analyze_signature_v2( pub(crate) fn analyze_signature(
ctx: &mut AnalysisContext, ctx: &mut AnalysisContext,
source: Source, source: Source,
callee_node: SignatureTarget, callee_node: SignatureTarget,

View file

@ -989,7 +989,8 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
log::debug!("check method at {method_name:?} on {primary_type:?}"); log::debug!("check method at {method_name:?} on {primary_type:?}");
match primary_type { match primary_type {
FlowType::Func(v) => match method_name.as_str() { FlowType::Func(v) => match method_name.as_str() {
"with" => { // todo: process where specially
"with" | "where" => {
// log::debug!("check method at args: {v:?}.with({args:?})"); // log::debug!("check method at args: {v:?}.with({args:?})");
let f = v.as_ref(); let f = v.as_ref();
@ -1011,9 +1012,6 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
_candidates.push(self.partial_apply(f, args)); _candidates.push(self.partial_apply(f, args));
} }
"where" => {
// log::debug!("where method at args: {args:?}");
}
_ => {} _ => {}
}, },
FlowType::Array(..) => {} FlowType::Array(..) => {}

View file

@ -225,28 +225,48 @@ mod tests {
match resp { match resp {
CompletionResponse::List(l) => CompletionResponse::List(CompletionList { CompletionResponse::List(l) => CompletionResponse::List(CompletionList {
is_incomplete: l.is_incomplete, is_incomplete: l.is_incomplete,
items: l items: {
.items let mut res: Vec<_> = l
.into_iter() .items
.map(|item| CompletionItem { .into_iter()
label: item.label, .map(|item| CompletionItem {
kind: item.kind, label: item.label,
text_edit: item.text_edit, sort_text: item.sort_text,
..Default::default() kind: item.kind,
}) text_edit: item.text_edit,
.collect(), ..Default::default()
})
.collect();
res.sort_by(|a, b| {
a.sort_text
.as_ref()
.cmp(&b.sort_text.as_ref())
.then_with(|| a.label.cmp(&b.label))
});
res
},
}), }),
CompletionResponse::Array(items) => CompletionResponse::Array( CompletionResponse::Array(items) => CompletionResponse::Array({
items let mut res: Vec<_> = items
.into_iter() .into_iter()
.map(|item| CompletionItem { .map(|item| CompletionItem {
label: item.label, label: item.label,
sort_text: item.sort_text,
kind: item.kind, kind: item.kind,
text_edit: item.text_edit, text_edit: item.text_edit,
..Default::default() ..Default::default()
}) })
.collect(), .collect();
),
res.sort_by(|a, b| {
a.sort_text
.as_ref()
.cmp(&b.sort_text.as_ref())
.then_with(|| a.label.cmp(&b.label))
});
res
}),
} }
})); }));
} }

View file

@ -0,0 +1 @@
#figure.where(/* range 0..1 */)

View file

@ -0,0 +1,15 @@
#let tmpl(content, authors: (), font: none, class: "article") = {
if class != "article" and class != "letter" {
panic("")
}
set document(author: authors)
set text(font: font)
set page(paper: "a4") if class == "article"
set page(paper: "us-letter") if class == "letter"
content
}
#tmpl(/* range 0..1 */)

View file

@ -0,0 +1,15 @@
#let tmpl(content, authors: (), font: none, class: "article") = {
if class != "article" and class != "letter" {
panic("")
}
set document(author: authors)
set text(font: font)
set page(paper: "a4") if class == "article"
set page(paper: "us-letter") if class == "letter"
content
}
#tmpl(authors: (),/* range 0..1 */)

View file

@ -0,0 +1,15 @@
#let tmpl(content, authors: (), font: none, class: "article") = {
if class != "article" and class != "letter" {
panic("")
}
set document(author: authors)
set text(font: font)
set page(paper: "a4") if class == "article"
set page(paper: "us-letter") if class == "letter"
content
}
#tmpl.with(/* range 0..1 */)

View file

@ -8,108 +8,6 @@ input_file: crates/tinymist-query/src/fixtures/completion/base.typ
{ {
"isIncomplete": false, "isIncomplete": false,
"items": [ "items": [
{
"kind": 7,
"label": "array",
"textEdit": {
"newText": "array",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "parbreak",
"textEdit": {
"newText": "parbreak()${1:}",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "smallcaps",
"textEdit": {
"newText": "smallcaps(${1:})",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "pagebreak",
"textEdit": {
"newText": "pagebreak(${1:})",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "metadata",
"textEdit": {
"newText": "metadata(${1:})",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 21,
"label": "aqua",
"textEdit": {
"newText": "aqua",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{ {
"kind": 3, "kind": 3,
"label": "aa", "label": "aa",
@ -179,10 +77,10 @@ input_file: crates/tinymist-query/src/fixtures/completion/base.typ
} }
}, },
{ {
"kind": 15, "kind": 21,
"label": "import package", "label": "aqua",
"textEdit": { "textEdit": {
"newText": "import \"@${1:}\": ${2:items}", "newText": "aqua",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -196,10 +94,10 @@ input_file: crates/tinymist-query/src/fixtures/completion/base.typ
} }
}, },
{ {
"kind": 15, "kind": 7,
"label": "include (package)", "label": "array",
"textEdit": { "textEdit": {
"newText": "include \"@${1:}\"", "newText": "array",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -245,6 +143,108 @@ input_file: crates/tinymist-query/src/fixtures/completion/base.typ
} }
} }
} }
},
{
"kind": 15,
"label": "import package",
"textEdit": {
"newText": "import \"@${1:}\": ${2:items}",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 15,
"label": "include (package)",
"textEdit": {
"newText": "include \"@${1:}\"",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "metadata",
"textEdit": {
"newText": "metadata(${1:})",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "pagebreak",
"textEdit": {
"newText": "pagebreak(${1:})",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "parbreak",
"textEdit": {
"newText": "parbreak()${1:}",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "smallcaps",
"textEdit": {
"newText": "smallcaps(${1:})",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
} }
] ]
}, },

View file

@ -0,0 +1,149 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on / (14..15)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/element_where.typ
---
[
{
"isIncomplete": false,
"items": [
{
"kind": 6,
"label": "caption",
"textEdit": {
"newText": "caption: ${1:}",
"range": {
"end": {
"character": 14,
"line": 0
},
"start": {
"character": 14,
"line": 0
}
}
}
},
{
"kind": 15,
"label": "content",
"textEdit": {
"newText": "${1:content}",
"range": {
"end": {
"character": 14,
"line": 0
},
"start": {
"character": 14,
"line": 0
}
}
}
},
{
"kind": 6,
"label": "gap",
"textEdit": {
"newText": "gap: ${1:}",
"range": {
"end": {
"character": 14,
"line": 0
},
"start": {
"character": 14,
"line": 0
}
}
}
},
{
"kind": 6,
"label": "kind",
"textEdit": {
"newText": "kind: ${1:}",
"range": {
"end": {
"character": 14,
"line": 0
},
"start": {
"character": 14,
"line": 0
}
}
}
},
{
"kind": 6,
"label": "numbering",
"textEdit": {
"newText": "numbering: ${1:}",
"range": {
"end": {
"character": 14,
"line": 0
},
"start": {
"character": 14,
"line": 0
}
}
}
},
{
"kind": 6,
"label": "outlined",
"textEdit": {
"newText": "outlined: ${1:}",
"range": {
"end": {
"character": 14,
"line": 0
},
"start": {
"character": 14,
"line": 0
}
}
}
},
{
"kind": 6,
"label": "placement",
"textEdit": {
"newText": "placement: ${1:}",
"range": {
"end": {
"character": 14,
"line": 0
},
"start": {
"character": 14,
"line": 0
}
}
}
},
{
"kind": 6,
"label": "supplement",
"textEdit": {
"newText": "supplement: ${1:}",
"range": {
"end": {
"character": 14,
"line": 0
},
"start": {
"character": 14,
"line": 0
}
}
}
}
]
}
]

View file

@ -0,0 +1,64 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on / (307..308)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/func_args.typ
---
[
{
"isIncomplete": false,
"items": [
{
"kind": 6,
"label": "authors",
"textEdit": {
"newText": "authors: ${1:}",
"range": {
"end": {
"character": 6,
"line": 14
},
"start": {
"character": 6,
"line": 14
}
}
}
},
{
"kind": 6,
"label": "class",
"textEdit": {
"newText": "class: ${1:}",
"range": {
"end": {
"character": 6,
"line": 14
},
"start": {
"character": 6,
"line": 14
}
}
}
},
{
"kind": 6,
"label": "font",
"textEdit": {
"newText": "font: ${1:}",
"range": {
"end": {
"character": 6,
"line": 14
},
"start": {
"character": 6,
"line": 14
}
}
}
}
]
}
]

View file

@ -0,0 +1,12 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on / (319..320)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/func_args2.typ
---
[
{
"isIncomplete": false,
"items": []
}
]

View file

@ -8,108 +8,6 @@ input_file: crates/tinymist-query/src/fixtures/completion/func_params.typ
{ {
"isIncomplete": false, "isIncomplete": false,
"items": [ "items": [
{
"kind": 7,
"label": "array",
"textEdit": {
"newText": "array",
"range": {
"end": {
"character": 5,
"line": 1
},
"start": {
"character": 2,
"line": 1
}
}
}
},
{
"kind": 3,
"label": "parbreak",
"textEdit": {
"newText": "parbreak()${1:}",
"range": {
"end": {
"character": 5,
"line": 1
},
"start": {
"character": 2,
"line": 1
}
}
}
},
{
"kind": 3,
"label": "smallcaps",
"textEdit": {
"newText": "smallcaps(${1:})",
"range": {
"end": {
"character": 5,
"line": 1
},
"start": {
"character": 2,
"line": 1
}
}
}
},
{
"kind": 3,
"label": "pagebreak",
"textEdit": {
"newText": "pagebreak(${1:})",
"range": {
"end": {
"character": 5,
"line": 1
},
"start": {
"character": 2,
"line": 1
}
}
}
},
{
"kind": 3,
"label": "metadata",
"textEdit": {
"newText": "metadata(${1:})",
"range": {
"end": {
"character": 5,
"line": 1
},
"start": {
"character": 2,
"line": 1
}
}
}
},
{
"kind": 21,
"label": "aqua",
"textEdit": {
"newText": "aqua",
"range": {
"end": {
"character": 5,
"line": 1
},
"start": {
"character": 2,
"line": 1
}
}
}
},
{ {
"kind": 3, "kind": 3,
"label": "aa", "label": "aa",
@ -179,10 +77,10 @@ input_file: crates/tinymist-query/src/fixtures/completion/func_params.typ
} }
}, },
{ {
"kind": 15, "kind": 21,
"label": "import package", "label": "aqua",
"textEdit": { "textEdit": {
"newText": "import \"@${1:}\": ${2:items}", "newText": "aqua",
"range": { "range": {
"end": { "end": {
"character": 5, "character": 5,
@ -196,10 +94,10 @@ input_file: crates/tinymist-query/src/fixtures/completion/func_params.typ
} }
}, },
{ {
"kind": 15, "kind": 7,
"label": "include (package)", "label": "array",
"textEdit": { "textEdit": {
"newText": "include \"@${1:}\"", "newText": "array",
"range": { "range": {
"end": { "end": {
"character": 5, "character": 5,
@ -245,6 +143,108 @@ input_file: crates/tinymist-query/src/fixtures/completion/func_params.typ
} }
} }
} }
},
{
"kind": 15,
"label": "import package",
"textEdit": {
"newText": "import \"@${1:}\": ${2:items}",
"range": {
"end": {
"character": 5,
"line": 1
},
"start": {
"character": 2,
"line": 1
}
}
}
},
{
"kind": 15,
"label": "include (package)",
"textEdit": {
"newText": "include \"@${1:}\"",
"range": {
"end": {
"character": 5,
"line": 1
},
"start": {
"character": 2,
"line": 1
}
}
}
},
{
"kind": 3,
"label": "metadata",
"textEdit": {
"newText": "metadata(${1:})",
"range": {
"end": {
"character": 5,
"line": 1
},
"start": {
"character": 2,
"line": 1
}
}
}
},
{
"kind": 3,
"label": "pagebreak",
"textEdit": {
"newText": "pagebreak(${1:})",
"range": {
"end": {
"character": 5,
"line": 1
},
"start": {
"character": 2,
"line": 1
}
}
}
},
{
"kind": 3,
"label": "parbreak",
"textEdit": {
"newText": "parbreak()${1:}",
"range": {
"end": {
"character": 5,
"line": 1
},
"start": {
"character": 2,
"line": 1
}
}
}
},
{
"kind": 3,
"label": "smallcaps",
"textEdit": {
"newText": "smallcaps(${1:})",
"range": {
"end": {
"character": 5,
"line": 1
},
"start": {
"character": 2,
"line": 1
}
}
}
} }
] ]
}, },

View file

@ -0,0 +1,64 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on / (312..313)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/func_with_args.typ
---
[
{
"isIncomplete": false,
"items": [
{
"kind": 6,
"label": "authors",
"textEdit": {
"newText": "authors: ${1:}",
"range": {
"end": {
"character": 11,
"line": 14
},
"start": {
"character": 11,
"line": 14
}
}
}
},
{
"kind": 6,
"label": "class",
"textEdit": {
"newText": "class: ${1:}",
"range": {
"end": {
"character": 11,
"line": 14
},
"start": {
"character": 11,
"line": 14
}
}
}
},
{
"kind": 6,
"label": "font",
"textEdit": {
"newText": "font: ${1:}",
"range": {
"end": {
"character": 11,
"line": 14
},
"start": {
"character": 11,
"line": 14
}
}
}
}
]
}
]

View file

@ -9,10 +9,10 @@ input_file: crates/tinymist-query/src/fixtures/completion/item_shadow.typ
"isIncomplete": false, "isIncomplete": false,
"items": [ "items": [
{ {
"kind": 7, "kind": 3,
"label": "array", "label": "aa",
"textEdit": { "textEdit": {
"newText": "array", "newText": "aa(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -27,60 +27,9 @@ input_file: crates/tinymist-query/src/fixtures/completion/item_shadow.typ
}, },
{ {
"kind": 3, "kind": 3,
"label": "parbreak", "label": "aac",
"textEdit": { "textEdit": {
"newText": "parbreak()${1:}", "newText": "aac(${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": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -111,10 +60,10 @@ input_file: crates/tinymist-query/src/fixtures/completion/item_shadow.typ
} }
}, },
{ {
"kind": 3, "kind": 7,
"label": "aa", "label": "array",
"textEdit": { "textEdit": {
"newText": "aa(${1:})", "newText": "array",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -128,10 +77,27 @@ input_file: crates/tinymist-query/src/fixtures/completion/item_shadow.typ
} }
}, },
{ {
"kind": 3, "kind": 15,
"label": "aac", "label": "array literal",
"textEdit": { "textEdit": {
"newText": "aac(${1:})", "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": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -179,10 +145,10 @@ input_file: crates/tinymist-query/src/fixtures/completion/item_shadow.typ
} }
}, },
{ {
"kind": 15, "kind": 3,
"label": "array literal", "label": "metadata",
"textEdit": { "textEdit": {
"newText": "(${1:1, 2, 3})", "newText": "metadata(${1:})",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -196,10 +162,44 @@ input_file: crates/tinymist-query/src/fixtures/completion/item_shadow.typ
} }
}, },
{ {
"kind": 15, "kind": 3,
"label": "dictionary literal", "label": "pagebreak",
"textEdit": { "textEdit": {
"newText": "(${1:a: 1, b: 2})", "newText": "pagebreak(${1:})",
"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": { "range": {
"end": { "end": {
"character": 4, "character": 4,

View file

@ -8,108 +8,6 @@ input_file: crates/tinymist-query/src/fixtures/completion/let.typ
{ {
"isIncomplete": false, "isIncomplete": false,
"items": [ "items": [
{
"kind": 7,
"label": "array",
"textEdit": {
"newText": "array",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "parbreak",
"textEdit": {
"newText": "parbreak()${1:}",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "smallcaps",
"textEdit": {
"newText": "smallcaps(${1:})",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "pagebreak",
"textEdit": {
"newText": "pagebreak(${1:})",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "metadata",
"textEdit": {
"newText": "metadata(${1:})",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 21,
"label": "aqua",
"textEdit": {
"newText": "aqua",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{ {
"kind": 3, "kind": 3,
"label": "aa", "label": "aa",
@ -179,10 +77,10 @@ input_file: crates/tinymist-query/src/fixtures/completion/let.typ
} }
}, },
{ {
"kind": 15, "kind": 21,
"label": "import package", "label": "aqua",
"textEdit": { "textEdit": {
"newText": "import \"@${1:}\": ${2:items}", "newText": "aqua",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -196,10 +94,10 @@ input_file: crates/tinymist-query/src/fixtures/completion/let.typ
} }
}, },
{ {
"kind": 15, "kind": 7,
"label": "include (package)", "label": "array",
"textEdit": { "textEdit": {
"newText": "include \"@${1:}\"", "newText": "array",
"range": { "range": {
"end": { "end": {
"character": 4, "character": 4,
@ -245,6 +143,108 @@ input_file: crates/tinymist-query/src/fixtures/completion/let.typ
} }
} }
} }
},
{
"kind": 15,
"label": "import package",
"textEdit": {
"newText": "import \"@${1:}\": ${2:items}",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 15,
"label": "include (package)",
"textEdit": {
"newText": "include \"@${1:}\"",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "metadata",
"textEdit": {
"newText": "metadata(${1:})",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "pagebreak",
"textEdit": {
"newText": "pagebreak(${1:})",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "parbreak",
"textEdit": {
"newText": "parbreak()${1:}",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
},
{
"kind": 3,
"label": "smallcaps",
"textEdit": {
"newText": "smallcaps(${1:})",
"range": {
"end": {
"character": 4,
"line": 5
},
"start": {
"character": 1,
"line": 5
}
}
}
} }
] ]
}, },

View file

@ -12,9 +12,9 @@ use typst::visualize::Color;
use super::{Completion, CompletionContext, CompletionKind}; use super::{Completion, CompletionContext, CompletionKind};
use crate::analysis::{ use crate::analysis::{
analyze_dyn_signature, analyze_import, resolve_callee, FlowBuiltinType, FlowRecord, FlowType, analyze_dyn_signature, analyze_import, resolve_call_target, FlowBuiltinType, FlowRecord,
PathPreference, FLOW_INSET_DICT, FLOW_MARGIN_DICT, FLOW_OUTSET_DICT, FLOW_RADIUS_DICT, FlowType, PathPreference, FLOW_INSET_DICT, FLOW_MARGIN_DICT, FLOW_OUTSET_DICT,
FLOW_STROKE_DICT, FLOW_RADIUS_DICT, FLOW_STROKE_DICT,
}; };
use crate::syntax::{get_non_strict_def_target, param_index_at_leaf, DefTarget}; use crate::syntax::{get_non_strict_def_target, param_index_at_leaf, DefTarget};
use crate::upstream::plain_docs_sentence; use crate::upstream::plain_docs_sentence;
@ -185,13 +185,16 @@ pub fn param_completions<'a>(
set: bool, set: bool,
args: ast::Args<'a>, args: ast::Args<'a>,
) { ) {
let Some(func) = ctx let Some(cc) = ctx
.root .root
.find(callee.span()) .find(callee.span())
.and_then(|callee| resolve_callee(ctx.ctx, callee)) .and_then(|callee| resolve_call_target(ctx.ctx, callee))
else { else {
return; return;
}; };
// todo: regards call convention
let this = cc.method_this().cloned();
let func = cc.callee();
use typst::foundations::func::Repr; use typst::foundations::func::Repr;
let mut func = func; let mut func = func;
@ -201,7 +204,8 @@ pub fn param_completions<'a>(
func = f.0.clone(); func = f.0.clone();
} }
let pos_index = param_index_at_leaf(&ctx.leaf, &func, args); let pos_index =
param_index_at_leaf(&ctx.leaf, &func, args).map(|i| if this.is_some() { i + 1 } else { i });
let signature = analyze_dyn_signature(ctx.ctx, func.clone()); let signature = analyze_dyn_signature(ctx.ctx, func.clone());
@ -585,10 +589,10 @@ pub fn named_param_value_completions<'a>(
name: &str, name: &str,
ty: Option<&FlowType>, ty: Option<&FlowType>,
) { ) {
let Some(func) = ctx let Some(cc) = ctx
.root .root
.find(callee.span()) .find(callee.span())
.and_then(|callee| resolve_callee(ctx.ctx, callee)) .and_then(|callee| resolve_call_target(ctx.ctx, callee))
else { else {
// static analysis // static analysis
if let Some(ty) = ty { if let Some(ty) = ty {
@ -597,6 +601,8 @@ pub fn named_param_value_completions<'a>(
return; return;
}; };
// todo: regards call convention
let func = cc.callee();
let def = func.span(); let def = func.span();
let type_sig = def.id().and_then(|id| { let type_sig = def.id().and_then(|id| {