refactor: change PrimarySignature structure to merge type checking info (#687)

* dev: support spread left params

* dev: stage merge

* dev: remove ParamSpecShort

* chore: remove useless code

* dev: merge all things back

* fix: testing

* fix: testing

* remove: useless method
This commit is contained in:
Myriad-Dreamin 2024-10-16 14:35:24 +08:00 committed by GitHub
parent 1f5be314a7
commit a3f100e7cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 260 additions and 218 deletions

View file

@ -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) = &param.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, ")")?;

View file

@ -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<ParamSpec>,
// 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<SyntaxNode>) {
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(),
},
);
}

View file

@ -463,6 +463,15 @@ impl<'w> AnalysisContext<'w> {
/// Get the signature of a function.
pub fn signature(&self, func: &SignatureTarget) -> Option<Signature> {
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

View file

@ -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<str>,
/// 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<EcoString>,
/// The name of the parameter.
pub name: StrRef,
/// The docstring of the parameter.
pub docs: Option<EcoString>,
/// The default value of the variable
pub default: Option<EcoString>,
/// 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<Self> {
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<Arc<ParamSpec>>,
/// The named parameters.
pub named: HashMap<Interned<str>, Arc<ParamSpec>>,
/// The documentation of the function
pub docs: Option<EcoString>,
/// The documentation of the parameter.
pub param_specs: Vec<ParamSpec>,
/// Whether the function has fill, stroke, or size parameters.
pub has_fill_or_size_or_stroke: bool,
/// The rest parameter.
pub rest: Option<Arc<ParamSpec>>,
/// The return type.
pub(crate) ret_ty: Option<Ty>,
/// The signature type.
pub(crate) sig_ty: Interned<SigTy>,
_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<Value>,
}
/// Describes a span.
#[derive(Debug, Clone)]
pub enum SpanInfo {
/// Unresolved raw span
Span(Span),
/// Resolved span
Range((TypstFileId, Range<usize>)),
}
/// Describes a function argument list.
#[derive(Debug, Clone)]
pub struct ArgsInfo {
/// The span of the argument list.
pub span: Option<SpanInfo>,
/// The arguments.
pub items: EcoVec<ArgInfo>,
}
@ -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<PrimarySignature> {
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<PrimarySignature> {
}
_ => {}
}
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,
});
}
let mut named_vec: Vec<(Interned<str>, Ty)> = named
.iter()
.map(|e| (e.0.clone(), e.1.base_type.clone()))
.collect::<Vec<_>>();
func.returns().map(|r| Ty::from_return_site(&func, r))
}
};
named_vec.sort_by(|a, b| a.0.cmp(&b.0));
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 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<LazyHash<Closure>>) -> Vec<Arc<ParamSpec>> {
let mut params = vec![];
trace!("closure signature for: {:?}", c.node.kind());
fn analyze_closure_signature(c: Arc<LazyHash<Closure>>, 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::<ast::Closure>().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>);

View file

@ -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))
});

View file

@ -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<str>, 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()))
}

View file

@ -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<EcoString>,
pub default: Option<EcoString>,
/// 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(&param.base_type)), doc_ty),
expr: param.expr.clone(),
docs: param.docs.clone().unwrap_or_default(),
cano_type: format_ty(ty.or(Some(&param.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()),
)
})

View file

@ -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" }

View file

@ -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" }

View file

@ -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" }

View file

@ -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" }

View file

@ -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" }

View file

@ -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" }

View file

@ -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" }

View file

@ -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" }

View file

@ -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 {

View file

@ -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::<Vec<_>>();
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(&param.base_type)
ty.unwrap_or(&param.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:?}");

View file

@ -609,7 +609,7 @@ pub static FLOW_RADIUS_DICT: Lazy<Interned<RecordTy>> = 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:?}");
}

View file

@ -612,20 +612,28 @@ impl SigTy {
/// Create a function type
pub fn new(
pos: impl IntoIterator<Item = Ty>,
pos: impl ExactSizeIterator<Item = Ty>,
named: impl IntoIterator<Item = (StrRef, Ty)>,
rest: Option<Ty>,
rest_left: Option<Ty>,
rest_right: Option<Ty>,
ret_ty: Option<Ty>,
) -> Self {
let named = named
.into_iter()
.map(|(name, ty)| (name, ty, Span::detached()))
.collect::<Vec<_>>();
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::<Vec<_>>();
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,
}
}

View file

@ -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

View file

@ -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 = &param.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(&param.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, &param.base_type, docs().as_deref());
type_completion(ctx, &param.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(&param.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, &param.base_type, doc.as_deref());
if !matches!(param.ty, Ty::Any) {
type_completion(ctx, &param.ty, doc.as_deref());
completed = true;
}
if !completed {
if let Some(expr) = &param.expr {
if let Some(expr) = &param.default {
ctx.completions.push(Completion {
kind: CompletionKind::Constant,
label: expr.clone(),

View file

@ -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") {