diff --git a/crates/tinymist-query/src/analysis.rs b/crates/tinymist-query/src/analysis.rs index 41220ced..471ad3ba 100644 --- a/crates/tinymist-query/src/analysis.rs +++ b/crates/tinymist-query/src/analysis.rs @@ -449,14 +449,18 @@ mod signature_tests { }; writeln!(f, "fn(")?; - for param in primary_sig.pos.iter() { + for param in primary_sig.pos() { writeln!(f, " {},", param.name)?; } - for (name, param) in primary_sig.named.iter() { - writeln!(f, " {}: {},", name, param.expr.clone().unwrap_or_default())?; + for param in primary_sig.named() { + if let Some(expr) = ¶m.default { + writeln!(f, " {}: {},", param.name, expr)?; + } else { + writeln!(f, " {},", param.name)?; + } } - if let Some(primary_sig) = &primary_sig.rest { - writeln!(f, " ...{}, ", primary_sig.name)?; + if let Some(param) = primary_sig.rest() { + writeln!(f, " ...{}, ", param.name)?; } write!(f, ")")?; diff --git a/crates/tinymist-query/src/analysis/call.rs b/crates/tinymist-query/src/analysis/call.rs index 4c124974..813d9378 100644 --- a/crates/tinymist-query/src/analysis/call.rs +++ b/crates/tinymist-query/src/analysis/call.rs @@ -2,7 +2,7 @@ use typst::syntax::SyntaxNode; -use super::{ParamSpec, Signature}; +use super::{Signature, StrRef}; use crate::{ analysis::{analyze_signature, PrimarySignature, SignatureTarget}, prelude::*, @@ -26,9 +26,8 @@ pub struct CallParamInfo { pub kind: ParamKind, /// Whether the parameter is a content block. pub is_content_block: bool, - /// The parameter's specification. - pub param: Arc, - // types: EcoVec<()>, + /// The name of the parameter. + pub param_name: StrRef, } /// Describes a function call. @@ -99,9 +98,9 @@ pub fn analyze_call_no_cache( fn advance(&mut self, info: &mut CallInfo, arg: Option) { let (kind, param) = match self.state { PosState::Init => { - if !self.signature.pos.is_empty() { + if !self.signature.pos().is_empty() { self.state = PosState::Pos(0); - } else if self.signature.rest.is_some() { + } else if self.signature.has_spread_right() { self.state = PosState::Variadic; } else { self.state = PosState::Final; @@ -110,17 +109,17 @@ pub fn analyze_call_no_cache( return; } PosState::Pos(i) => { - if i + 1 < self.signature.pos.len() { + if i + 1 < self.signature.pos_size() { self.state = PosState::Pos(i + 1); - } else if self.signature.rest.is_some() { + } else if self.signature.has_spread_right() { self.state = PosState::Variadic; } else { self.state = PosState::Final; } - (ParamKind::Positional, &self.signature.pos[i]) + (ParamKind::Positional, self.signature.get_pos(i).unwrap()) } - PosState::Variadic => (ParamKind::Rest, self.signature.rest.as_ref().unwrap()), + PosState::Variadic => (ParamKind::Rest, self.signature.rest().unwrap()), PosState::Final => return, }; @@ -132,8 +131,7 @@ pub fn analyze_call_no_cache( CallParamInfo { kind, is_content_block, - param: param.clone(), - // types: eco_vec![], + param_name: param.name.clone(), }, ); } @@ -144,7 +142,7 @@ pub fn analyze_call_no_cache( PosState::Init => unreachable!(), // todo: not precise PosState::Pos(..) => { - if self.signature.rest.is_some() { + if self.signature.has_spread_right() { self.state = PosState::Variadic; } else { self.state = PosState::Final; @@ -154,7 +152,7 @@ pub fn analyze_call_no_cache( PosState::Final => return, }; - let Some(rest) = self.signature.rest.as_ref() else { + let Some(rest) = self.signature.rest() else { return; }; @@ -166,8 +164,7 @@ pub fn analyze_call_no_cache( CallParamInfo { kind: ParamKind::Rest, is_content_block, - param: rest.clone(), - // types: eco_vec![], + param_name: rest.name.clone(), }, ); } @@ -212,14 +209,13 @@ pub fn analyze_call_no_cache( ast::Arg::Named(named) => { let n = named.name().get().into(); - if let Some(param) = signature.primary().named.get(&n) { + if let Some(param) = signature.primary().get_named(&n) { info.arg_mapping.insert( arg_tag, CallParamInfo { kind: ParamKind::Named, is_content_block: false, - param: param.clone(), - // types: eco_vec![], + param_name: param.name.clone(), }, ); } diff --git a/crates/tinymist-query/src/analysis/global.rs b/crates/tinymist-query/src/analysis/global.rs index 9cac5790..9fb64edf 100644 --- a/crates/tinymist-query/src/analysis/global.rs +++ b/crates/tinymist-query/src/analysis/global.rs @@ -463,6 +463,15 @@ impl<'w> AnalysisContext<'w> { /// Get the signature of a function. pub fn signature(&self, func: &SignatureTarget) -> Option { match func { + SignatureTarget::Def(source, r) => { + // todo: check performance on peeking signature source frequently + let cache_key = (source, r.range.start); + self.analysis + .caches + .static_signatures + .get(&hash128(&cache_key)) + .and_then(|slot| (cache_key.1 == slot.2).then_some(slot.3.clone())) + } SignatureTarget::Syntax(source, node) => { // todo: check performance on peeking signature source frequently let cache_key = (source, node.offset()); @@ -488,6 +497,16 @@ impl<'w> AnalysisContext<'w> { compute: impl FnOnce() -> Signature, ) -> Signature { match func { + SignatureTarget::Def(source, r) => { + let cache_key = (source, r.range.start); + let h = hash128(&cache_key); + let slot = self.analysis.caches.static_signatures.entry(h); + let slot = slot.or_insert_with(|| { + let sig = compute(); + (self.lifetime, cache_key.0, cache_key.1, sig) + }); + slot.3.clone() + } SignatureTarget::Syntax(source, node) => { let cache_key = (source, node.offset()); self.analysis diff --git a/crates/tinymist-query/src/analysis/signature.rs b/crates/tinymist-query/src/analysis/signature.rs index a812ad33..23f369c6 100644 --- a/crates/tinymist-query/src/analysis/signature.rs +++ b/crates/tinymist-query/src/analysis/signature.rs @@ -1,15 +1,15 @@ //! Analysis of function signatures. use core::fmt; -use std::{borrow::Cow, collections::HashMap, ops::Range, sync::Arc}; +use std::collections::BTreeMap; +use std::sync::Arc; -use ecow::{eco_vec, EcoString, EcoVec}; -use log::trace; -use typst::syntax::{FileId as TypstFileId, Source}; +use ecow::{eco_format, eco_vec, EcoString, EcoVec}; +use typst::syntax::Source; use typst::{ - foundations::{Closure, Func, ParamInfo, Value}, + foundations::{Closure, Func, Value}, syntax::{ ast::{self, AstNode}, - LinkedNode, Span, SyntaxKind, + LinkedNode, SyntaxKind, }, }; use typst_shim::syntax::LinkedNodeExt; @@ -22,21 +22,19 @@ use crate::ty::SigTy; use crate::upstream::truncated_repr; use crate::AnalysisContext; -use super::{find_definition, DefinitionLink, LexicalKind, LexicalVarKind, Ty}; - -// pub fn analyze_signature +use super::{find_definition, DefinitionLink, IdentRef, LexicalKind, LexicalVarKind, StrRef, Ty}; /// Describes a function parameter. #[derive(Debug, Clone)] pub struct ParamSpec { - /// The parameter's name. - pub name: Interned, - /// Documentation for the parameter. - pub docs: Cow<'static, str>, - /// Inferred type of the parameter. - pub(crate) base_type: Ty, - /// The parameter's default name as value. - pub expr: Option, + /// The name of the parameter. + pub name: StrRef, + /// The docstring of the parameter. + pub docs: Option, + /// The default value of the variable + pub default: Option, + /// The type of the parameter. + pub ty: Ty, /// Is the parameter positional? pub positional: bool, /// Is the parameter named? @@ -50,21 +48,6 @@ pub struct ParamSpec { pub settable: bool, } -impl ParamSpec { - fn from_static(f: &Func, p: &ParamInfo) -> Arc { - Arc::new(Self { - name: p.name.into(), - docs: Cow::Borrowed(p.docs), - base_type: Ty::from_param_site(f, p), - expr: p.default.map(|d| truncated_repr(&d())), - positional: p.positional, - named: p.named, - variadic: p.variadic, - settable: p.settable, - }) - } -} - /// Describes a function signature. #[derive(Debug, Clone)] pub enum Signature { @@ -101,16 +84,12 @@ impl Signature { /// Describes a primary function signature. #[derive(Debug, Clone)] pub struct PrimarySignature { - /// The positional parameters. - pub pos: Vec>, - /// The named parameters. - pub named: HashMap, Arc>, + /// The documentation of the function + pub docs: Option, + /// The documentation of the parameter. + pub param_specs: Vec, /// Whether the function has fill, stroke, or size parameters. pub has_fill_or_size_or_stroke: bool, - /// The rest parameter. - pub rest: Option>, - /// The return type. - pub(crate) ret_ty: Option, /// The signature type. pub(crate) sig_ty: Interned, _broken: bool, @@ -121,6 +100,42 @@ impl PrimarySignature { pub(crate) fn ty(&self) -> Ty { Ty::Func(self.sig_ty.clone()) } + + /// Returns the number of positional parameters of the function. + pub fn pos_size(&self) -> usize { + self.sig_ty.name_started as usize + } + + /// Returns the positional parameters of the function. + pub fn pos(&self) -> &[ParamSpec] { + &self.param_specs[..self.pos_size()] + } + + /// Returns the positional parameters of the function. + pub fn get_pos(&self, offset: usize) -> Option<&ParamSpec> { + self.pos().get(offset) + } + + /// Returns the named parameters of the function. + pub fn named(&self) -> &[ParamSpec] { + &self.param_specs[self.pos_size()..self.pos_size() + self.sig_ty.names.names.len()] + } + + /// Returns the named parameters of the function. + pub fn get_named(&self, name: &StrRef) -> Option<&ParamSpec> { + self.named().get(self.sig_ty.names.find(name)?) + } + + /// Returns the name of the rest parameter of the function. + pub fn has_spread_right(&self) -> bool { + self.sig_ty.spread_right + } + + /// Returns the rest parameter of the function. + pub fn rest(&self) -> Option<&ParamSpec> { + self.has_spread_right() + .then(|| &self.param_specs[self.pos_size() + self.sig_ty.names.names.len()]) + } } /// Describes a function argument instance @@ -132,20 +147,9 @@ pub struct ArgInfo { pub value: Option, } -/// Describes a span. -#[derive(Debug, Clone)] -pub enum SpanInfo { - /// Unresolved raw span - Span(Span), - /// Resolved span - Range((TypstFileId, Range)), -} - /// Describes a function argument list. #[derive(Debug, Clone)] pub struct ArgsInfo { - /// The span of the argument list. - pub span: Option, /// The arguments. pub items: EcoVec, } @@ -161,6 +165,8 @@ pub struct PartialSignature { /// The language object that the signature is being analyzed for. pub enum SignatureTarget<'a> { + /// A static node without knowing the function at runtime. + Def(Source, IdentRef), /// A static node without knowing the function at runtime. Syntax(Source, LinkedNode<'a>), /// A function that is known at runtime. @@ -182,6 +188,7 @@ pub(crate) fn analyze_signature( } let func = match callee_node { + SignatureTarget::Def(..) => todo!(), SignatureTarget::Syntax(source, node) => { let _ = resolve_callee_v2; let _ = source; @@ -210,7 +217,6 @@ pub(crate) fn analyze_signature( let mut func = func; while let Repr::With(f) = func.inner() { with_stack.push(ArgsInfo { - span: None, items: f .1 .items @@ -230,7 +236,7 @@ pub(crate) fn analyze_signature( }) .primary() .clone(); - trace!("got signature {signature:?}"); + log::trace!("got signature {signature:?}"); if with_stack.is_empty() { return Some(Signature::Primary(signature)); @@ -311,32 +317,21 @@ fn resolve_callee_v2( } fn analyze_dyn_signature_inner(func: Func) -> Arc { - use typst::foundations::func::Repr; - let (params, ret_ty) = match func.inner() { - Repr::With(..) => unreachable!(), - Repr::Closure(c) => (analyze_closure_signature(c.clone()), None), - Repr::Element(..) | Repr::Native(..) => { - let ret_ty = func.returns().map(|r| Ty::from_return_site(&func, r)); - let params = func.params().unwrap(); - ( - params - .iter() - .map(|p| ParamSpec::from_static(&func, p)) - .collect(), - ret_ty, - ) - } - }; + let mut pos_tys = vec![]; + let mut named_tys = Vec::new(); + let mut rest_ty = None; + + let mut named_specs = BTreeMap::new(); + let mut param_specs = Vec::new(); + let mut rest_spec = None; - let mut pos = vec![]; - let mut named = HashMap::new(); - let mut rest = None; let mut broken = false; let mut has_fill = false; let mut has_stroke = false; let mut has_size = false; - for param in params.into_iter() { + let mut add_param = |param: ParamSpec| { + let name = param.name.clone(); if param.named { match param.name.as_ref() { "fill" => { @@ -350,103 +345,120 @@ fn analyze_dyn_signature_inner(func: Func) -> Arc { } _ => {} } - named.insert(param.name.clone(), param.clone()); + named_tys.push((name.clone(), param.ty.clone())); + named_specs.insert(name.clone(), param.clone()); } if param.variadic { - if rest.is_some() { + if rest_ty.is_some() { broken = true; } else { - rest = Some(param.clone()); + rest_ty = Some(param.ty.clone()); + rest_spec = Some(param); } } else if param.positional { - pos.push(param); + // todo: we have some params that are both positional and named + pos_tys.push(param.ty.clone()); + param_specs.push(param); } + }; + + use typst::foundations::func::Repr; + let ret_ty = match func.inner() { + Repr::With(..) => unreachable!(), + Repr::Closure(c) => { + analyze_closure_signature(c.clone(), &mut add_param); + None + } + Repr::Element(..) | Repr::Native(..) => { + for p in func.params().unwrap() { + add_param(ParamSpec { + name: p.name.into(), + docs: Some(p.docs.into()), + default: p.default.map(|d| truncated_repr(&d())), + ty: Ty::from_param_site(&func, p), + positional: p.positional, + named: p.named, + variadic: p.variadic, + settable: p.settable, + }); + } + + func.returns().map(|r| Ty::from_return_site(&func, r)) + } + }; + + let sig_ty = SigTy::new(pos_tys.into_iter(), named_tys, None, rest_ty, ret_ty); + + for name in &sig_ty.names.names { + param_specs.push(named_specs.get(name).unwrap().clone()); + } + if let Some(doc) = rest_spec { + param_specs.push(doc); } - let mut named_vec: Vec<(Interned, Ty)> = named - .iter() - .map(|e| (e.0.clone(), e.1.base_type.clone())) - .collect::>(); - - named_vec.sort_by(|a, b| a.0.cmp(&b.0)); - - let sig_ty = SigTy::new( - pos.iter().map(|e| e.base_type.clone()), - named_vec, - rest.as_ref().map(|e| e.base_type.clone()), - ret_ty.clone(), - ); Arc::new(PrimarySignature { - pos, - named, - rest, - ret_ty, + docs: func.docs().map(From::from), + param_specs, has_fill_or_size_or_stroke: has_fill || has_stroke || has_size, sig_ty: sig_ty.into(), _broken: broken, }) } -fn analyze_closure_signature(c: Arc>) -> Vec> { - let mut params = vec![]; - - trace!("closure signature for: {:?}", c.node.kind()); +fn analyze_closure_signature(c: Arc>, add_param: &mut impl FnMut(ParamSpec)) { + log::trace!("closure signature for: {:?}", c.node.kind()); let closure = &c.node; let closure_ast = match closure.kind() { SyntaxKind::Closure => closure.cast::().unwrap(), - _ => return params, + _ => return, }; for param in closure_ast.params().children() { match param { ast::Param::Pos(e) => { let name = format!("{}", PatternDisplay(&e)); - - params.push(Arc::new(ParamSpec { + add_param(ParamSpec { name: name.as_str().into(), - base_type: Ty::Any, - // type_repr: None, - expr: None, + docs: None, + default: None, + ty: Ty::Any, positional: true, named: false, variadic: false, settable: false, - docs: Cow::Borrowed(""), - })); + }); } // todo: pattern ast::Param::Named(n) => { let expr = unwrap_expr(n.expr()).to_untyped().clone().into_text(); - params.push(Arc::new(ParamSpec { - name: n.name().into(), - base_type: Ty::Any, - expr: Some(expr.clone()), + add_param(ParamSpec { + name: n.name().get().into(), + docs: Some(eco_format!("Default value: {expr}")), + default: Some(expr), + ty: Ty::Any, positional: false, named: true, variadic: false, settable: true, - docs: Cow::Owned("Default value: ".to_owned() + expr.as_str()), - })); + }); } ast::Param::Spread(n) => { let ident = n.sink_ident().map(|e| e.as_str()); - params.push(Arc::new(ParamSpec { + add_param(ParamSpec { name: ident.unwrap_or_default().into(), - base_type: Ty::Any, - expr: None, + docs: None, + default: None, + ty: Ty::Any, positional: true, named: false, variadic: true, settable: false, - docs: Cow::Borrowed(""), - })); + }); } } } - - params } struct PatternDisplay<'a>(&'a ast::Pattern<'a>); diff --git a/crates/tinymist-query/src/analysis/tyck/docs.rs b/crates/tinymist-query/src/analysis/tyck/docs.rs index 10ed465a..dfff14b5 100644 --- a/crates/tinymist-query/src/analysis/tyck/docs.rs +++ b/crates/tinymist-query/src/analysis/tyck/docs.rs @@ -373,7 +373,7 @@ impl<'a, 'w> DocsChecker<'a, 'w> { } let body = self.check_type_expr(m, c.body())?; - let sig = SigTy::new(pos, named, rest, Some(body)).into(); + let sig = SigTy::new(pos.into_iter(), named, None, rest, Some(body)).into(); Some(Ty::Func(sig)) }); diff --git a/crates/tinymist-query/src/analysis/tyck/syntax.rs b/crates/tinymist-query/src/analysis/tyck/syntax.rs index fb33bcd6..ce3f55d8 100644 --- a/crates/tinymist-query/src/analysis/tyck/syntax.rs +++ b/crates/tinymist-query/src/analysis/tyck/syntax.rs @@ -368,7 +368,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> { } } - let args = ArgsTy::new(args_res, named, None, None); + let args = ArgsTy::new(args_res.into_iter(), named, None, None, None); Some(Ty::Args(args.into())) } @@ -444,7 +444,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> { self.weaken(rest); } - let sig = SigTy::new(pos, named, rest, Some(res_ty)).into(); + let sig = SigTy::new(pos.into_iter(), named, None, rest, Some(res_ty)).into(); let sig = Ty::Func(sig); if defaults.is_empty() { return Some(sig); @@ -453,7 +453,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> { let defaults: Vec<(Interned, Ty)> = defaults.into_iter().collect(); let with_defaults = SigWithTy { sig: sig.into(), - with: ArgsTy::new(vec![], defaults, None, None).into(), + with: ArgsTy::new([].into_iter(), defaults, None, None, None).into(), }; Some(Ty::With(with_defaults.into())) } diff --git a/crates/tinymist-query/src/docs/symbol.rs b/crates/tinymist-query/src/docs/symbol.rs index 38ec393e..6df60f49 100644 --- a/crates/tinymist-query/src/docs/symbol.rs +++ b/crates/tinymist-query/src/docs/symbol.rs @@ -169,7 +169,7 @@ impl fmt::Display for SignatureDocs { let mut name_prints = vec![]; for v in self.named.values() { let ty = v.cano_type.as_ref().map(|t| &t.0); - name_prints.push((v.name.clone(), ty, v.expr.clone())) + name_prints.push((v.name.clone(), ty, v.default.clone())) } name_prints.sort(); for (k, t, v) in name_prints { @@ -203,11 +203,11 @@ pub struct ParamDocs { /// The parameter's name. pub name: String, /// Documentation for the parameter. - pub docs: String, + pub docs: EcoString, /// Inferred type of the parameter. pub cano_type: TypeRepr, /// The parameter's default name as value. - pub expr: Option, + pub default: Option, /// Is the parameter positional? pub positional: bool, /// Is the parameter named? @@ -225,9 +225,9 @@ impl ParamDocs { fn new(param: &ParamSpec, ty: Option<&Ty>, doc_ty: Option<&mut ShowTypeRepr>) -> Self { Self { name: param.name.as_ref().to_owned(), - docs: param.docs.as_ref().to_owned(), - cano_type: format_ty(ty.or(Some(¶m.base_type)), doc_ty), - expr: param.expr.clone(), + docs: param.docs.clone().unwrap_or_default(), + cano_type: format_ty(ty.or(Some(¶m.ty)), doc_ty), + default: param.default.clone(), positional: param.positional, named: param.named, variadic: param.variadic, @@ -288,19 +288,18 @@ pub(crate) fn signature_docs( let pos_in = sig .primary() - .pos + .pos() .iter() .enumerate() .map(|(i, pos)| (pos, type_sig.as_ref().and_then(|sig| sig.pos(i)))); let named_in = sig .primary() - .named + .named() .iter() - .map(|x| (x, type_sig.as_ref().and_then(|sig| sig.named(x.0)))); + .map(|x| (x, type_sig.as_ref().and_then(|sig| sig.named(&x.name)))); let rest_in = sig .primary() - .rest - .as_ref() + .rest() .map(|x| (x, type_sig.as_ref().and_then(|sig| sig.rest_param()))); let ret_in = type_sig @@ -312,9 +311,9 @@ pub(crate) fn signature_docs( .map(|(param, ty)| ParamDocs::new(param, ty, doc_ty.as_mut())) .collect(); let named = named_in - .map(|((name, param), ty)| { + .map(|(param, ty)| { ( - name.as_ref().to_owned(), + param.name.as_ref().to_owned(), ParamDocs::new(param, ty, doc_ty.as_mut()), ) }) diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin.typ.snap index f55a0be6..67601683 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin.typ.snap @@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/builtin.typ --- -1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "angle", docs: "The angle whose sine to calculate.", base_type: (Type(angle) | Type(float) | Type(integer)), expr: None, positional: true, named: false, variadic: false, settable: false } } +1 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "angle" } diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly.typ.snap index 83bb3a54..4600f47b 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly.typ.snap @@ -3,6 +3,6 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/builtin_poly.typ --- -255 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "red", docs: "The red component.", base_type: (Type(integer) | Type(ratio)), expr: None, positional: true, named: false, variadic: false, settable: false } } -255 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "green", docs: "The green component.", base_type: (Type(integer) | Type(ratio)), expr: None, positional: true, named: false, variadic: false, settable: false } } -255 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "blue", docs: "The blue component.", base_type: (Type(integer) | Type(ratio)), expr: None, positional: true, named: false, variadic: false, settable: false } } +255 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "red" } +255 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "green" } +255 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "blue" } diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly2.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly2.typ.snap index ef7f80ba..8efac0c9 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly2.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly2.typ.snap @@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/builtin_poly2.typ --- -"#fff" -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "red", docs: "The red component.", base_type: (Type(integer) | Type(ratio)), expr: None, positional: true, named: false, variadic: false, settable: false } } +"#fff" -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "red" } diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user.typ.snap index 96d9d23f..e7adb338 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user.typ.snap @@ -3,5 +3,5 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/user.typ --- -1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "x", docs: "", base_type: Any, expr: None, positional: true, named: false, variadic: false, settable: false } } -1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "y", docs: "", base_type: Any, expr: None, positional: true, named: false, variadic: false, settable: false } } +1 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "x" } +1 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "y" } diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named.typ.snap index 36cd0b4f..06c95de2 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named.typ.snap @@ -3,5 +3,5 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/user_named.typ --- -y: 1 -> CallParamInfo { kind: Named, is_content_block: false, param: ParamSpec { name: "y", docs: "Default value: none", base_type: Any, expr: Some("none"), positional: false, named: true, variadic: false, settable: true } } -1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "x", docs: "", base_type: Any, expr: None, positional: true, named: false, variadic: false, settable: false } } +y: 1 -> CallParamInfo { kind: Named, is_content_block: false, param_name: "y" } +1 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "x" } diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named_with.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named_with.typ.snap index 89b07df7..59a169cd 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named_with.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named_with.typ.snap @@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/user_named_with.typ --- -1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "x", docs: "", base_type: Any, expr: None, positional: true, named: false, variadic: false, settable: false } } +1 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "x" } diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named_with2.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named_with2.typ.snap index 18aaaec9..588fd9ff 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named_with2.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named_with2.typ.snap @@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/user_named_with2.typ --- -y: 1 -> CallParamInfo { kind: Named, is_content_block: false, param: ParamSpec { name: "y", docs: "Default value: none", base_type: Any, expr: Some("none"), positional: false, named: true, variadic: false, settable: true } } +y: 1 -> CallParamInfo { kind: Named, is_content_block: false, param_name: "y" } diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_with.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_with.typ.snap index bcbe1aa7..641f0b95 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_with.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_with.typ.snap @@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/user_with.typ --- -1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "y", docs: "", base_type: Any, expr: None, positional: true, named: false, variadic: false, settable: false } } +1 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "y" } diff --git a/crates/tinymist-query/src/inlay_hint.rs b/crates/tinymist-query/src/inlay_hint.rs index 5b675682..8d25c0c3 100644 --- a/crates/tinymist-query/src/inlay_hint.rs +++ b/crates/tinymist-query/src/inlay_hint.rs @@ -215,7 +215,8 @@ fn inlay_hint( continue; }; - if info.param.name.is_empty() { + let name = &info.param_name; + if name.is_empty() { continue; } @@ -257,9 +258,9 @@ fn inlay_hint( typst_to_lsp::offset_to_position(pos, self.encoding, self.source); let label = InlayHintLabel::String(if info.kind == ParamKind::Rest { - format!("..{}:", info.param.name) + format!("..{name}:") } else { - format!("{}:", info.param.name) + format!("{name}:") }); self.hints.push(InlayHint { diff --git a/crates/tinymist-query/src/signature_help.rs b/crates/tinymist-query/src/signature_help.rs index 0aed0ab2..cc0015d3 100644 --- a/crates/tinymist-query/src/signature_help.rs +++ b/crates/tinymist-query/src/signature_help.rs @@ -63,16 +63,14 @@ impl SemanticRequest for SignatureHelpRequest { } let sig = analyze_dyn_signature(ctx, function.clone()); - let pos = &sig.primary().pos; - let mut named = sig.primary().named.values().collect::>(); - let rest = &sig.primary().rest; + let pos = sig.primary().pos(); + let named = sig.primary().named(); + let rest = sig.primary().rest(); let type_sig = type_sig.and_then(|type_sig| type_sig.sig_repr(true)); log::info!("got type signature {type_sig:?}"); - named.sort_by_key(|x| &x.name); - let mut active_parameter = None; let mut label = def_link.name.clone(); @@ -84,10 +82,10 @@ impl SemanticRequest for SignatureHelpRequest { .enumerate() .map(|(i, pos)| (pos, type_sig.as_ref().and_then(|sig| sig.pos(i)))); let named = named - .into_iter() + .iter() .map(|x| (x, type_sig.as_ref().and_then(|sig| sig.named(&x.name)))); let rest = rest - .iter() + .into_iter() .map(|x| (x, type_sig.as_ref().and_then(|sig| sig.rest_param()))); let mut real_offset = 0; @@ -122,7 +120,7 @@ impl SemanticRequest for SignatureHelpRequest { label.push_str(&format!( "{}: {}", param.name, - ty.unwrap_or(¶m.base_type) + ty.unwrap_or(¶m.ty) .describe() .as_deref() .unwrap_or("any") @@ -130,21 +128,19 @@ impl SemanticRequest for SignatureHelpRequest { params.push(LspParamInfo { label: lsp_types::ParameterLabel::Simple(format!("{}:", param.name)), - documentation: if !param.docs.is_empty() { - Some(Documentation::MarkupContent(MarkupContent { - value: param.docs.clone().into(), + documentation: param.docs.as_ref().map(|docs| { + Documentation::MarkupContent(MarkupContent { + value: docs.as_ref().into(), kind: MarkupKind::Markdown, - })) - } else { - None - }, + }) + }), }); } label.push(')'); let ret = type_sig .as_ref() .and_then(|sig| sig.body.as_ref()) - .or_else(|| sig.primary().ret_ty.as_ref()); + .or_else(|| sig.primary().sig_ty.body.as_ref()); if let Some(ret_ty) = ret { label.push_str(" -> "); label.push_str(ret_ty.describe().as_deref().unwrap_or("any")); @@ -152,7 +148,7 @@ impl SemanticRequest for SignatureHelpRequest { if matches!(target, ParamTarget::Positional { .. }) { active_parameter = - active_parameter.map(|x| x.min(sig.primary().pos.len().saturating_sub(1))); + active_parameter.map(|x| x.min(sig.primary().pos_size().saturating_sub(1))); } trace!("got signature info {label} {params:?}"); diff --git a/crates/tinymist-query/src/ty/builtin.rs b/crates/tinymist-query/src/ty/builtin.rs index a957de7d..7a047130 100644 --- a/crates/tinymist-query/src/ty/builtin.rs +++ b/crates/tinymist-query/src/ty/builtin.rs @@ -609,7 +609,7 @@ pub static FLOW_RADIUS_DICT: Lazy> = Lazy::new(|| { mod tests { use reflexo::vector::ir::DefId; - use super::*; + use super::{SigTy, Ty, TypeVar}; // todo: map function // Technical Note for implementing a map function: @@ -620,8 +620,9 @@ mod tests { let u = Ty::Var(TypeVar::new("u".into(), DefId(0))); let v = Ty::Var(TypeVar::new("v".into(), DefId(1))); let mapper_fn = - Ty::Func(SigTy::new([u], Option::None, Option::None, Some(v.clone())).into()); - let map_fn = Ty::Func(SigTy::new([mapper_fn], Option::None, Option::None, Some(v)).into()); + Ty::Func(SigTy::new([u].into_iter(), None, None, None, Some(v.clone())).into()); + let map_fn = + Ty::Func(SigTy::new([mapper_fn].into_iter(), None, None, None, Some(v)).into()); let _ = map_fn; // println!("{map_fn:?}"); } diff --git a/crates/tinymist-query/src/ty/def.rs b/crates/tinymist-query/src/ty/def.rs index 9f64d752..2283c2aa 100644 --- a/crates/tinymist-query/src/ty/def.rs +++ b/crates/tinymist-query/src/ty/def.rs @@ -612,20 +612,28 @@ impl SigTy { /// Create a function type pub fn new( - pos: impl IntoIterator, + pos: impl ExactSizeIterator, named: impl IntoIterator, - rest: Option, + rest_left: Option, + rest_right: Option, ret_ty: Option, ) -> Self { let named = named .into_iter() .map(|(name, ty)| (name, ty, Span::detached())) .collect::>(); - let (names, types) = RecordTy::shape_fields(named); - let spread_right = rest.is_some(); + let (names, mut named_types) = RecordTy::shape_fields(named); + let spread_left = rest_left.is_some(); + let spread_right = rest_right.is_some(); - let name_started = if spread_right { 1 } else { 0 } + types.len(); - let types = pos.into_iter().chain(types).chain(rest).collect::>(); + let name_started = if spread_right { 1 } else { 0 } + named_types.len(); + let mut types = Vec::with_capacity( + pos.len() + named_types.len() + spread_left as usize + spread_right as usize, + ); + types.extend(pos); + types.append(&mut named_types); + types.extend(rest_left); + types.extend(rest_right); let name_started = (types.len() - name_started) as u32; @@ -634,7 +642,7 @@ impl SigTy { body: ret_ty, names: Interned::new(names), name_started, - spread_left: false, + spread_left, spread_right, } } diff --git a/crates/tinymist-query/src/ty/mod.rs b/crates/tinymist-query/src/ty/mod.rs index b0299713..353ab019 100644 --- a/crates/tinymist-query/src/ty/mod.rs +++ b/crates/tinymist-query/src/ty/mod.rs @@ -106,7 +106,7 @@ mod tests { let named = named.iter().map(|(n, t)| ((*n).into(), var_ins(t))); let rest = rest.map(var_ins); let ret = ret.map(var_ins); - SigTy::new(pos, named, rest, ret).into() + SigTy::new(pos, named, None, rest, ret).into() } // args*, (keys: values)*, ...rest -> ret diff --git a/crates/tinymist-query/src/upstream/complete/ext.rs b/crates/tinymist-query/src/upstream/complete/ext.rs index b409a2e4..e4658b4e 100644 --- a/crates/tinymist-query/src/upstream/complete/ext.rs +++ b/crates/tinymist-query/src/upstream/complete/ext.rs @@ -597,7 +597,7 @@ pub fn param_completions<'a>( let mut doc = None; if let Some(pos_index) = pos_index { - let pos = primary_sig.pos.get(pos_index); + let pos = primary_sig.get_pos(pos_index); log::debug!("pos_param_completion_to: {:?}", pos); if let Some(pos) = pos { @@ -605,10 +605,12 @@ pub fn param_completions<'a>( break 'pos_check; } - doc = Some(plain_docs_sentence(&pos.docs)); + if let Some(docs) = &pos.docs { + doc = Some(plain_docs_sentence(docs)); + } if pos.positional { - type_completion(ctx, &pos.base_type, doc.as_deref()); + type_completion(ctx, &pos.ty, doc.as_deref()); } } } @@ -618,7 +620,8 @@ pub fn param_completions<'a>( } } - for (name, param) in &primary_sig.named { + for param in primary_sig.named() { + let name = ¶m.name; if ctx.seen_field(name.as_ref().into()) { continue; } @@ -632,7 +635,10 @@ pub fn param_completions<'a>( } let _d = OnceCell::new(); - let docs = || Some(_d.get_or_init(|| plain_docs_sentence(¶m.docs)).clone()); + let docs = || { + _d.get_or_init(|| param.docs.as_ref().map(|d| plain_docs_sentence(d.as_str()))) + .clone() + }; if param.named { let compl = Completion { @@ -644,7 +650,7 @@ pub fn param_completions<'a>( command: Some("tinymist.triggerNamedCompletion"), ..Completion::default() }; - match param.base_type { + match param.ty { Ty::Builtin(BuiltinTy::TextSize) => { for size_template in &[ "10.5pt", "12pt", "9pt", "14pt", "8pt", "16pt", "18pt", "20pt", "22pt", @@ -674,7 +680,7 @@ pub fn param_completions<'a>( } if param.positional { - type_completion(ctx, ¶m.base_type, docs().as_deref()); + type_completion(ctx, ¶m.ty, docs().as_deref()); } } @@ -984,14 +990,14 @@ pub fn named_param_value_completions<'a>( let primary_sig = signature.primary(); - let Some(param) = primary_sig.named.get(name) else { + let Some(param) = primary_sig.get_named(name) else { return; }; if !param.named { return; } - let doc = Some(plain_docs_sentence(¶m.docs)); + let doc = param.docs.as_ref().map(|d| plain_docs_sentence(d.as_str())); // static analysis if let Some(ty) = ty { @@ -1005,13 +1011,13 @@ pub fn named_param_value_completions<'a>( completed = true; } - if !matches!(param.base_type, Ty::Any) { - type_completion(ctx, ¶m.base_type, doc.as_deref()); + if !matches!(param.ty, Ty::Any) { + type_completion(ctx, ¶m.ty, doc.as_deref()); completed = true; } if !completed { - if let Some(expr) = ¶m.expr { + if let Some(expr) = ¶m.default { ctx.completions.push(Completion { kind: CompletionKind::Constant, label: expr.clone(), diff --git a/tools/editor-tools/src/features/docs.ts b/tools/editor-tools/src/features/docs.ts index baf07ec6..346ca456 100644 --- a/tools/editor-tools/src/features/docs.ts +++ b/tools/editor-tools/src/features/docs.ts @@ -605,7 +605,7 @@ function MakeDoc(root: DocElement) { interface DocParam { name: string; cano_type: [string, string]; - expr?: string; + default?: string; } function FuncItem(v: DocElement) { @@ -761,9 +761,9 @@ function MakeDoc(root: DocElement) { sigTypeHighlighted(param.cano_type, paramTitle); } - if (param.expr) { + if (param.default) { paramTitle.push(codeHl("op", " = ")); - paramTitle.push(code(param.expr)); + paramTitle.push(code(param.default)); } if (kind == "pos") {