diff --git a/Cargo.lock b/Cargo.lock index 40ad67f8..94095e97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4086,15 +4086,31 @@ dependencies = [ name = "tinymist-analysis" version = "0.13.10" dependencies = [ + "comemo", + "dashmap", "ecow", + "ena", + "hashbrown 0.14.5", + "if_chain", "insta", + "itertools 0.13.0", "log", + "lsp-types", + "parking_lot", "regex", + "rpds", + "rustc-hash 2.1.1", "serde", + "serde_yaml", "strum", + "tinymist-derive", + "tinymist-std", "tinymist-world", "toml", + "triomphe", "typst", + "typst-shim", + "unscanny", ] [[package]] @@ -4226,7 +4242,6 @@ dependencies = [ "dirs", "ecow", "ena", - "hashbrown 0.14.5", "hex", "if_chain", "indexmap 2.8.0", @@ -4255,7 +4270,6 @@ dependencies = [ "tinymist-std", "tinymist-world", "toml", - "triomphe", "ttf-parser", "typlite", "typst", diff --git a/crates/tinymist-analysis/Cargo.toml b/crates/tinymist-analysis/Cargo.toml index a5b0fd22..55decbd3 100644 --- a/crates/tinymist-analysis/Cargo.toml +++ b/crates/tinymist-analysis/Cargo.toml @@ -13,13 +13,30 @@ repository.workspace = true rust-version.workspace = true [dependencies] +comemo.workspace = true +dashmap.workspace = true ecow.workspace = true +ena.workspace = true +hashbrown.workspace = true +if_chain.workspace = true +itertools.workspace = true log.workspace = true +lsp-types.workspace = true +parking_lot.workspace = true +regex.workspace = true +rpds.workspace = true +rustc-hash.workspace = true serde.workspace = true +serde_yaml.workspace = true strum.workspace = true -toml.workspace = true +tinymist-derive.workspace = true +tinymist-std.workspace = true tinymist-world.workspace = true +toml.workspace = true +triomphe.workspace = true typst.workspace = true +typst-shim.workspace = true +unscanny.workspace = true [dev-dependencies] insta.workspace = true diff --git a/crates/tinymist-query/src/adt/interner.rs b/crates/tinymist-analysis/src/adt/interner.rs similarity index 98% rename from crates/tinymist-query/src/adt/interner.rs rename to crates/tinymist-analysis/src/adt/interner.rs index 74b84cea..7cedc260 100644 --- a/crates/tinymist-query/src/adt/interner.rs +++ b/crates/tinymist-analysis/src/adt/interner.rs @@ -341,8 +341,7 @@ impl Display for Interned { } } -pub(crate) static MAPS: Mutex)>> = - Mutex::new(EcoVec::new()); +pub static MAPS: Mutex)>> = Mutex::new(EcoVec::new()); pub struct InternStorage { alloc: OnceLock>, @@ -407,6 +406,6 @@ macro_rules! _impl_internable { } pub use crate::_impl_internable as impl_internable; -use crate::analysis::AllocStats; +use crate::stats::AllocStats; impl_internable!(str,); diff --git a/crates/tinymist-analysis/src/adt/mod.rs b/crates/tinymist-analysis/src/adt/mod.rs new file mode 100644 index 00000000..9223f917 --- /dev/null +++ b/crates/tinymist-analysis/src/adt/mod.rs @@ -0,0 +1,4 @@ +#![allow(missing_docs)] + +pub mod interner; +pub mod snapshot_map; diff --git a/crates/tinymist-query/src/adt/snapshot_map.rs b/crates/tinymist-analysis/src/adt/snapshot_map.rs similarity index 100% rename from crates/tinymist-query/src/adt/snapshot_map.rs rename to crates/tinymist-analysis/src/adt/snapshot_map.rs diff --git a/crates/tinymist-analysis/src/docs.rs b/crates/tinymist-analysis/src/docs.rs new file mode 100644 index 00000000..0fb48253 --- /dev/null +++ b/crates/tinymist-analysis/src/docs.rs @@ -0,0 +1,6 @@ +#![allow(missing_docs)] + +mod def; +pub use def::*; +mod tidy; +pub use tidy::*; diff --git a/crates/tinymist-analysis/src/docs/def.rs b/crates/tinymist-analysis/src/docs/def.rs new file mode 100644 index 00000000..6d7be618 --- /dev/null +++ b/crates/tinymist-analysis/src/docs/def.rs @@ -0,0 +1,296 @@ +use core::fmt; +use std::collections::BTreeMap; +use std::sync::OnceLock; + +use ecow::{eco_format, EcoString}; +use serde::{Deserialize, Serialize}; + +use super::tidy::*; +use crate::ty::{Interned, ParamAttrs, ParamTy, Ty}; +use crate::upstream::plain_docs_sentence; + +type TypeRepr = Option<( + /* short */ EcoString, + /* long */ EcoString, + /* value */ EcoString, +)>; + +/// Documentation about a definition (without type information). +pub type UntypedDefDocs = DefDocsT<()>; +/// Documentation about a definition. +pub type DefDocs = DefDocsT; + +/// Documentation about a definition. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "kind")] +pub enum DefDocsT { + /// Documentation about a function. + #[serde(rename = "func")] + Function(Box>), + /// Documentation about a variable. + #[serde(rename = "var")] + Variable(VarDocsT), + /// Documentation about a module. + #[serde(rename = "module")] + Module(TidyModuleDocs), + /// Other kinds of documentation. + #[serde(rename = "plain")] + Plain { + /// The content of the documentation. + docs: EcoString, + }, +} + +impl DefDocsT { + /// Get the markdown representation of the documentation. + pub fn docs(&self) -> &EcoString { + match self { + Self::Function(docs) => &docs.docs, + Self::Variable(docs) => &docs.docs, + Self::Module(docs) => &docs.docs, + Self::Plain { docs } => docs, + } + } +} + +impl DefDocs { + /// Get full documentation for the signature. + pub fn hover_docs(&self) -> EcoString { + match self { + DefDocs::Function(docs) => docs.hover_docs().clone(), + _ => plain_docs_sentence(self.docs()), + } + } +} + +/// Describes a primary function signature. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SignatureDocsT { + /// Documentation for the function. + pub docs: EcoString, + /// The positional parameters. + pub pos: Vec>, + /// The named parameters. + pub named: BTreeMap, ParamDocsT>, + /// The rest parameter. + pub rest: Option>, + /// The return type. + pub ret_ty: T, + /// The full documentation for the signature. + #[serde(skip)] + pub hover_docs: OnceLock, +} + +impl SignatureDocsT { + /// Get full documentation for the signature. + pub fn hover_docs(&self) -> &EcoString { + self.hover_docs + .get_or_init(|| plain_docs_sentence(&format!("{}", SigHoverDocs(self)))) + } +} + +struct SigHoverDocs<'a>(&'a SignatureDocs); + +impl fmt::Display for SigHoverDocs<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let docs = self.0; + let base_docs = docs.docs.trim(); + + if !base_docs.is_empty() { + f.write_str(base_docs)?; + } + + fn write_param_docs( + f: &mut fmt::Formatter<'_>, + docs: &ParamDocsT, + kind: &str, + is_first: &mut bool, + ) -> fmt::Result { + if *is_first { + *is_first = false; + write!(f, "\n\n## {}\n\n", docs.name)?; + } else { + write!(f, "\n\n## {} ({kind})\n\n", docs.name)?; + } + + // p.cano_type.0 + if let Some(t) = &docs.cano_type { + write!(f, "```typc\ntype: {}\n```\n\n", t.2)?; + } + + f.write_str(docs.docs.trim())?; + + Ok(()) + } + + if !docs.pos.is_empty() { + f.write_str("\n\n# Positional Parameters")?; + + let mut is_first = true; + for pos_docs in &docs.pos { + write_param_docs(f, pos_docs, "positional", &mut is_first)?; + } + } + + if docs.rest.is_some() { + f.write_str("\n\n# Rest Parameters")?; + + let mut is_first = true; + if let Some(rest) = &docs.rest { + write_param_docs(f, rest, "spread right", &mut is_first)?; + } + } + + if !docs.named.is_empty() { + f.write_str("\n\n# Named Parameters")?; + + let mut is_first = true; + for named_docs in docs.named.values() { + write_param_docs(f, named_docs, "named", &mut is_first)?; + } + } + + Ok(()) + } +} + +/// Documentation about a signature. +pub type UntypedSignatureDocs = SignatureDocsT<()>; +/// Documentation about a signature. +pub type SignatureDocs = SignatureDocsT; + +impl SignatureDocs { + /// Get the markdown representation of the documentation. + pub fn print(&self, f: &mut impl std::fmt::Write) -> fmt::Result { + let mut is_first = true; + let mut write_sep = |f: &mut dyn std::fmt::Write| { + if is_first { + is_first = false; + return f.write_str("\n "); + } + f.write_str(",\n ") + }; + + f.write_char('(')?; + for pos_docs in &self.pos { + write_sep(f)?; + f.write_str(&pos_docs.name)?; + if let Some(t) = &pos_docs.cano_type { + write!(f, ": {}", t.0)?; + } + } + if let Some(rest) = &self.rest { + write_sep(f)?; + f.write_str("..")?; + f.write_str(&rest.name)?; + if let Some(t) = &rest.cano_type { + write!(f, ": {}", t.0)?; + } + } + + if !self.named.is_empty() { + 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.default.clone())) + } + name_prints.sort(); + for (name, ty, val) in name_prints { + write_sep(f)?; + let val = val.as_deref().unwrap_or("any"); + let mut default = val.trim(); + if default.starts_with('{') && default.ends_with('}') && default.len() > 30 { + default = "{ .. }" + } + if default.starts_with('`') && default.ends_with('`') && default.len() > 30 { + default = "raw" + } + if default.starts_with('[') && default.ends_with(']') && default.len() > 30 { + default = "content" + } + f.write_str(&name)?; + if let Some(ty) = ty { + write!(f, ": {ty}")?; + } + if default.contains('\n') { + write!(f, " = {}", default.replace("\n", "\n "))?; + } else { + write!(f, " = {default}")?; + } + } + } + if !is_first { + f.write_str(",\n")?; + } + f.write_char(')')?; + + Ok(()) + } +} + +/// Documentation about a variable (without type information). +pub type UntypedVarDocs = VarDocsT<()>; +/// Documentation about a variable. +pub type VarDocs = VarDocsT>; + +/// Describes a primary pattern binding. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VarDocsT { + /// Documentation for the pattern binding. + pub docs: EcoString, + /// The inferred type of the pattern binding source. + pub return_ty: T, + /// Cached documentation for the definition. + #[serde(skip)] + pub def_docs: OnceLock, +} + +impl VarDocs { + /// Get the markdown representation of the documentation. + pub fn def_docs(&self) -> &String { + self.def_docs + .get_or_init(|| plain_docs_sentence(&self.docs).into()) + } +} + +/// Documentation about a parameter (without type information). +pub type TypelessParamDocs = ParamDocsT<()>; +/// Documentation about a parameter. +pub type ParamDocs = ParamDocsT; + +/// Describes a function parameter. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct ParamDocsT { + /// The parameter's name. + pub name: Interned, + /// Documentation for the parameter. + pub docs: EcoString, + /// Inferred type of the parameter. + pub cano_type: T, + /// The parameter's default name as value. + pub default: Option, + /// The attribute of the parameter. + #[serde(flatten)] + pub attrs: ParamAttrs, +} + +impl ParamDocs { + pub fn new(param: &ParamTy, ty: Option<&Ty>) -> Self { + Self { + name: param.name.as_ref().into(), + docs: param.docs.clone().unwrap_or_default(), + cano_type: format_ty(ty.or(Some(¶m.ty))), + default: param.default.clone(), + attrs: param.attrs, + } + } +} + +pub fn format_ty(ty: Option<&Ty>) -> TypeRepr { + let ty = ty?; + let short = ty.repr().unwrap_or_else(|| "any".into()); + let long = eco_format!("{ty:?}"); + let value = ty.value_repr().unwrap_or_else(|| "".into()); + + Some((short, long, value)) +} diff --git a/crates/tinymist-query/src/docs/tidy.rs b/crates/tinymist-analysis/src/docs/tidy.rs similarity index 100% rename from crates/tinymist-query/src/docs/tidy.rs rename to crates/tinymist-analysis/src/docs/tidy.rs diff --git a/crates/tinymist-analysis/src/lib.rs b/crates/tinymist-analysis/src/lib.rs index 7a4f841d..aa8f43f2 100644 --- a/crates/tinymist-analysis/src/lib.rs +++ b/crates/tinymist-analysis/src/lib.rs @@ -1,8 +1,19 @@ //! Tinymist Analysis +pub mod adt; +pub mod docs; pub mod location; -mod prelude; +mod sig; +pub mod stats; pub mod syntax; +pub mod ty; +pub mod upstream; + +pub use sig::*; +pub use track_values::*; + +mod prelude; +mod track_values; /// Completely disabled log #[macro_export] diff --git a/crates/tinymist-analysis/src/sig.rs b/crates/tinymist-analysis/src/sig.rs new file mode 100644 index 00000000..b03f1ff6 --- /dev/null +++ b/crates/tinymist-analysis/src/sig.rs @@ -0,0 +1,377 @@ +//! Analysis of function signatures. + +use core::fmt; +use std::collections::BTreeMap; +use std::sync::Arc; + +use ecow::{eco_format, eco_vec, EcoString, EcoVec}; +use typst::foundations::{Closure, Func}; +use typst::syntax::ast::AstNode; +use typst::syntax::{ast, SyntaxKind}; +use typst::utils::LazyHash; + +// use super::{BoundChecker, Definition}; +use crate::ty::{InsTy, ParamTy, SigTy, StrRef, Ty}; +use crate::ty::{Interned, ParamAttrs}; +use crate::upstream::truncated_repr; +// use crate::upstream::truncated_repr; + +/// Describes a function signature. +#[derive(Debug, Clone)] +pub enum Signature { + /// A primary function signature. + Primary(Arc), + /// A partially applied function signature. + Partial(Arc), +} + +impl Signature { + /// Returns the primary signature if it is one. + pub fn primary(&self) -> &Arc { + match self { + Signature::Primary(sig) => sig, + Signature::Partial(sig) => &sig.signature, + } + } + + /// Returns the with bindings of the signature. + pub fn bindings(&self) -> &[ArgsInfo] { + match self { + Signature::Primary(_) => &[], + Signature::Partial(sig) => &sig.with_stack, + } + } + + /// Returns the all parameters of the signature. + pub fn params(&self) -> impl Iterator, Option<&Ty>)> { + let primary = self.primary().params(); + // todo: with stack + primary + } + + /// Returns the type of the signature. + pub fn type_sig(&self) -> Interned { + let primary = self.primary().sig_ty.clone(); + // todo: with stack + primary + } + + /// Returns the shift applied to the signature. + pub fn param_shift(&self) -> usize { + match self { + Signature::Primary(_) => 0, + Signature::Partial(sig) => sig + .with_stack + .iter() + .map(|ws| ws.items.len()) + .sum::(), + } + } +} + +/// Describes a primary function signature. +#[derive(Debug, Clone)] +pub struct PrimarySignature { + /// 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 associated signature type. + pub sig_ty: Interned, + /// Whether the signature is broken. + pub _broken: bool, +} + +impl PrimarySignature { + /// 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) -> &[Interned] { + &self.param_specs[..self.pos_size()] + } + + /// Returns the positional parameters of the function. + pub fn get_pos(&self, offset: usize) -> Option<&Interned> { + self.pos().get(offset) + } + + /// Returns the named parameters of the function. + pub fn named(&self) -> &[Interned] { + &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<&Interned> { + 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<&Interned> { + self.has_spread_right() + .then(|| &self.param_specs[self.pos_size() + self.sig_ty.names.names.len()]) + } + + /// Returns the all parameters of the function. + pub fn params(&self) -> impl Iterator, Option<&Ty>)> { + let pos = self.pos(); + let named = self.named(); + let rest = self.rest(); + let type_sig = &self.sig_ty; + let pos = pos + .iter() + .enumerate() + .map(|(idx, pos)| (pos, type_sig.pos(idx))); + let named = named.iter().map(|x| (x, type_sig.named(&x.name))); + let rest = rest.into_iter().map(|x| (x, type_sig.rest_param())); + + pos.chain(named).chain(rest) + } +} + +/// Describes a function argument instance +#[derive(Debug, Clone)] +pub struct ArgInfo { + /// The argument's name. + pub name: Option, + /// The argument's term. + pub term: Option, +} + +/// Describes a function argument list. +#[derive(Debug, Clone)] +pub struct ArgsInfo { + /// The arguments. + pub items: EcoVec, +} + +/// Describes a function signature that is already partially applied. +#[derive(Debug, Clone)] +pub struct PartialSignature { + /// The positional parameters. + pub signature: Arc, + /// The stack of `fn.with(..)` calls. + pub with_stack: EcoVec, +} + +/// Gets the signature of a function. +#[comemo::memoize] +pub fn func_signature(func: Func) -> Signature { + use typst::foundations::func::Repr; + let mut with_stack = eco_vec![]; + let mut func = func; + while let Repr::With(with) = func.inner() { + let (inner, args) = with.as_ref(); + with_stack.push(ArgsInfo { + items: args + .items + .iter() + .map(|arg| ArgInfo { + name: arg.name.clone().map(From::from), + term: Some(Ty::Value(InsTy::new(arg.value.v.clone()))), + }) + .collect(), + }); + func = inner.clone(); + } + + 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 broken = false; + let mut has_fill_or_size_or_stroke = false; + + let mut add_param = |param: Interned| { + let name = param.name.clone(); + if param.attrs.named { + if matches!(name.as_ref(), "fill" | "stroke" | "size") { + has_fill_or_size_or_stroke = true; + } + named_tys.push((name.clone(), param.ty.clone())); + named_specs.insert(name.clone(), param.clone()); + } + + if param.attrs.variadic { + if rest_ty.is_some() { + broken = true; + } else { + rest_ty = Some(param.ty.clone()); + rest_spec = Some(param); + } + } else if param.attrs.positional { + // todo: we have some params that are both positional and named + pos_tys.push(param.ty.clone()); + param_specs.push(param); + } + }; + + let ret_ty = match func.inner() { + Repr::With(..) => unreachable!(), + Repr::Closure(closure) => { + analyze_closure_signature(closure.clone(), &mut add_param); + None + } + Repr::Element(..) | Repr::Native(..) | Repr::Plugin(..) => { + for param in func.params().unwrap_or_default() { + add_param(Interned::new(ParamTy { + name: param.name.into(), + docs: Some(param.docs.into()), + default: param.default.map(|default| truncated_repr(&default())), + ty: Ty::from_param_site(&func, param), + attrs: param.into(), + })); + } + + 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 { + let Some(param) = named_specs.get(name) else { + continue; + }; + param_specs.push(param.clone()); + } + if let Some(doc) = rest_spec { + param_specs.push(doc); + } + + let signature = Arc::new(PrimarySignature { + docs: func.docs().map(From::from), + param_specs, + has_fill_or_size_or_stroke, + sig_ty: sig_ty.into(), + _broken: broken, + }); + + log::trace!("got signature {signature:?}"); + + if with_stack.is_empty() { + return Signature::Primary(signature); + } + + Signature::Partial(Arc::new(PartialSignature { + signature, + with_stack, + })) +} + +fn analyze_closure_signature( + closure: Arc>, + add_param: &mut impl FnMut(Interned), +) { + log::trace!("closure signature for: {:?}", closure.node.kind()); + + let closure = &closure.node; + let closure_ast = match closure.kind() { + SyntaxKind::Closure => closure.cast::().unwrap(), + _ => return, + }; + + for param in closure_ast.params().children() { + match param { + ast::Param::Pos(pos) => { + let name = format!("{}", PatternDisplay(&pos)); + add_param(Interned::new(ParamTy { + name: name.as_str().into(), + docs: None, + default: None, + ty: Ty::Any, + attrs: ParamAttrs::positional(), + })); + } + // todo: pattern + ast::Param::Named(named) => { + let default = unwrap_parens(named.expr()).to_untyped().clone().into_text(); + add_param(Interned::new(ParamTy { + name: named.name().get().into(), + docs: Some(eco_format!("Default value: {default}")), + default: Some(default), + ty: Ty::Any, + attrs: ParamAttrs::named(), + })); + } + ast::Param::Spread(spread) => { + let sink = spread.sink_ident().map(|sink| sink.as_str()); + add_param(Interned::new(ParamTy { + name: sink.unwrap_or_default().into(), + docs: None, + default: None, + ty: Ty::Any, + attrs: ParamAttrs::variadic(), + })); + } + } + } +} + +struct PatternDisplay<'a>(&'a ast::Pattern<'a>); + +impl fmt::Display for PatternDisplay<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + ast::Pattern::Normal(ast::Expr::Ident(ident)) => f.write_str(ident.as_str()), + ast::Pattern::Normal(_) => f.write_str("?"), // unreachable? + ast::Pattern::Placeholder(_) => f.write_str("_"), + ast::Pattern::Parenthesized(paren_expr) => { + write!(f, "{}", PatternDisplay(&paren_expr.pattern())) + } + ast::Pattern::Destructuring(destructing) => { + write!(f, "(")?; + let mut first = true; + for item in destructing.items() { + if first { + first = false; + } else { + write!(f, ", ")?; + } + match item { + ast::DestructuringItem::Pattern(pos) => { + write!(f, "{}", PatternDisplay(&pos))? + } + ast::DestructuringItem::Named(named) => write!( + f, + "{}: {}", + named.name().as_str(), + unwrap_parens(named.expr()).to_untyped().text() + )?, + ast::DestructuringItem::Spread(spread) => write!( + f, + "..{}", + spread + .sink_ident() + .map(|sink| sink.as_str()) + .unwrap_or_default() + )?, + } + } + write!(f, ")")?; + Ok(()) + } + } + } +} + +fn unwrap_parens(mut expr: ast::Expr) -> ast::Expr { + while let ast::Expr::Parenthesized(paren_expr) = expr { + expr = paren_expr.expr(); + } + + expr +} diff --git a/crates/tinymist-analysis/src/stats.rs b/crates/tinymist-analysis/src/stats.rs new file mode 100644 index 00000000..805ed8ec --- /dev/null +++ b/crates/tinymist-analysis/src/stats.rs @@ -0,0 +1,80 @@ +//! Tinymist Analysis Statistics + +use std::sync::atomic::{AtomicUsize, Ordering}; + +/// Statistics about the allocation + +#[derive(Debug, Default)] +pub struct AllocStats { + /// The number of allocated objects. + pub allocated: AtomicUsize, + /// The number of dropped objects. + pub dropped: AtomicUsize, +} + +impl AllocStats { + /// increment the statistics. + pub fn increment(&self) { + self.allocated.fetch_add(1, Ordering::Relaxed); + } + + /// decrement the statistics. + pub fn decrement(&self) { + self.dropped.fetch_add(1, Ordering::Relaxed); + } + + /// Report the statistics of the allocation. + pub fn report() -> String { + let maps = crate::adt::interner::MAPS.lock().clone(); + let mut data = Vec::new(); + for (name, sz, map) in maps { + let allocated = map.allocated.load(std::sync::atomic::Ordering::Relaxed); + let dropped = map.dropped.load(std::sync::atomic::Ordering::Relaxed); + let alive = allocated.saturating_sub(dropped); + data.push((name, sz * alive, allocated, dropped, alive)); + } + + // sort by total + data.sort_by(|x, y| y.4.cmp(&x.4)); + + // format to html + + let mut html = String::new(); + html.push_str(r#"
+ +"#); + + for (name, sz, allocated, dropped, alive) in data { + html.push_str(""); + html.push_str(&format!(r#""#)); + html.push_str(&format!("")); + html.push_str(&format!("")); + html.push_str(&format!("")); + html.push_str(&format!("", human_size(sz))); + html.push_str(""); + } + html.push_str("
NameAliveAllocatedDroppedSize
{name}{alive}{allocated}{dropped}{}
"); + html.push_str("
"); + + html + } +} + +fn human_size(size: usize) -> String { + let units = ["B", "KB", "MB", "GB", "TB"]; + let mut unit = 0; + let mut size = size as f64; + while size >= 768.0 && unit < units.len() { + size /= 1024.0; + unit += 1; + } + format!("{:.2} {}", size, units[unit]) +} diff --git a/crates/tinymist-analysis/src/syntax.rs b/crates/tinymist-analysis/src/syntax.rs index 3f8dd0bd..81c9c0a5 100644 --- a/crates/tinymist-analysis/src/syntax.rs +++ b/crates/tinymist-analysis/src/syntax.rs @@ -2,9 +2,17 @@ //! //! This module must hide all **AST details** from the rest of the codebase. +// todo: remove this +#![allow(missing_docs)] + pub mod import; pub use import::*; pub mod comment; pub use comment::*; pub mod matcher; pub use matcher::*; + +pub mod def; +pub use def::*; +pub(crate) mod repr; +use repr::*; diff --git a/crates/tinymist-query/src/syntax/def.rs b/crates/tinymist-analysis/src/syntax/def.rs similarity index 96% rename from crates/tinymist-query/src/syntax/def.rs rename to crates/tinymist-analysis/src/syntax/def.rs index f7bce8c7..7805a214 100644 --- a/crates/tinymist-query/src/syntax/def.rs +++ b/crates/tinymist-analysis/src/syntax/def.rs @@ -3,6 +3,7 @@ use std::{collections::BTreeMap, ops::Range}; use serde::{Deserialize, Serialize}; use tinymist_derive::DeclEnum; +use tinymist_std::DefId; use tinymist_world::package::PackageSpec; use typst::{ foundations::{Element, Func, Module, Type, Value}, @@ -11,7 +12,6 @@ use typst::{ use crate::{ adt::interner::impl_internable, - analysis::SharedContext, prelude::*, ty::{InsTy, Interned, SelectTy, Ty, TypeVar}, }; @@ -73,13 +73,13 @@ pub enum Expr { } impl Expr { - pub(crate) fn repr(&self) -> EcoString { + pub fn repr(&self) -> EcoString { let mut s = EcoString::new(); let _ = ExprDescriber::new(&mut s).write_expr(self); s } - pub(crate) fn span(&self) -> Span { + pub fn span(&self) -> Span { match self { Expr::Decl(decl) => decl.span(), Expr::Select(select) => select.span, @@ -88,7 +88,7 @@ impl Expr { } } - pub(crate) fn file_id(&self) -> Option { + pub fn file_id(&self) -> Option { match self { Expr::Decl(decl) => decl.file_id(), _ => self.span().id(), @@ -409,7 +409,7 @@ impl Decl { }) } - pub(crate) fn is_def(&self) -> bool { + pub fn is_def(&self) -> bool { matches!( self, Self::Func(..) @@ -452,25 +452,6 @@ impl Decl { } } - /// Gets name range of the declaration. - pub fn name_range(&self, ctx: &SharedContext) -> Option> { - if let Decl::BibEntry(decl) = self { - return Some(decl.at.1.clone()); - } - - if !self.is_def() { - return None; - } - - let span = self.span(); - if let Some(range) = span.range() { - return Some(range.clone()); - } - - let src = ctx.source_by_id(self.file_id()?).ok()?; - src.range(span) - } - /// Gets full range of the declaration. pub fn full_range(&self) -> Option> { if let Decl::BibEntry(decl) = self { @@ -595,8 +576,8 @@ impl fmt::Debug for SpannedDecl { #[derive(Clone, PartialEq, Eq, Hash)] pub struct NameRangeDecl { - name: Interned, - at: Box<(TypstFileId, Range, Option>)>, + pub name: Interned, + pub at: Box<(TypstFileId, Range, Option>)>, } impl NameRangeDecl { @@ -724,7 +705,7 @@ impl fmt::Display for Pattern { } impl Pattern { - pub(crate) fn repr(&self) -> EcoString { + pub fn repr(&self) -> EcoString { let mut s = EcoString::new(); let _ = ExprDescriber::new(&mut s).write_pattern(self); s diff --git a/crates/tinymist-query/src/syntax/repr.rs b/crates/tinymist-analysis/src/syntax/repr.rs similarity index 100% rename from crates/tinymist-query/src/syntax/repr.rs rename to crates/tinymist-analysis/src/syntax/repr.rs diff --git a/crates/tinymist-query/src/analysis/track_values.rs b/crates/tinymist-analysis/src/track_values.rs similarity index 100% rename from crates/tinymist-query/src/analysis/track_values.rs rename to crates/tinymist-analysis/src/track_values.rs diff --git a/crates/tinymist-query/src/ty/apply.rs b/crates/tinymist-analysis/src/ty/apply.rs similarity index 100% rename from crates/tinymist-query/src/ty/apply.rs rename to crates/tinymist-analysis/src/ty/apply.rs diff --git a/crates/tinymist-query/src/ty/bound.rs b/crates/tinymist-analysis/src/ty/bound.rs similarity index 100% rename from crates/tinymist-query/src/ty/bound.rs rename to crates/tinymist-analysis/src/ty/bound.rs diff --git a/crates/tinymist-query/src/ty/builtin.rs b/crates/tinymist-analysis/src/ty/builtin.rs similarity index 99% rename from crates/tinymist-query/src/ty/builtin.rs rename to crates/tinymist-analysis/src/ty/builtin.rs index 24d54ab7..d3225e23 100644 --- a/crates/tinymist-query/src/ty/builtin.rs +++ b/crates/tinymist-analysis/src/ty/builtin.rs @@ -109,7 +109,7 @@ impl PathPreference { } impl Ty { - pub(crate) fn from_cast_info(ty: &CastInfo) -> Ty { + pub fn from_cast_info(ty: &CastInfo) -> Ty { match &ty { CastInfo::Any => Ty::Any, CastInfo::Value(val, doc) => Ty::Value(InsTy::new_doc(val.clone(), *doc)), @@ -120,7 +120,7 @@ impl Ty { } } - pub(crate) fn from_param_site(func: &Func, param: &ParamInfo) -> Ty { + pub fn from_param_site(func: &Func, param: &ParamInfo) -> Ty { use typst::foundations::func::Repr; match func.inner() { Repr::Element(..) | Repr::Native(..) | Repr::Plugin(..) => { diff --git a/crates/tinymist-query/src/analysis/tyck/convert.rs b/crates/tinymist-analysis/src/ty/convert.rs similarity index 98% rename from crates/tinymist-query/src/analysis/tyck/convert.rs rename to crates/tinymist-analysis/src/ty/convert.rs index 8692cce9..aa1a5343 100644 --- a/crates/tinymist-query/src/analysis/tyck/convert.rs +++ b/crates/tinymist-analysis/src/ty/convert.rs @@ -1,4 +1,6 @@ -use crate::analysis::func_signature; +use typst::syntax::Span; + +use crate::func_signature; use super::*; diff --git a/crates/tinymist-query/src/ty/def.rs b/crates/tinymist-analysis/src/ty/def.rs similarity index 98% rename from crates/tinymist-query/src/ty/def.rs rename to crates/tinymist-analysis/src/ty/def.rs index bbb3b9f9..cdcfd2f1 100644 --- a/crates/tinymist-query/src/ty/def.rs +++ b/crates/tinymist-analysis/src/ty/def.rs @@ -17,10 +17,9 @@ use typst::{ syntax::{ast, FileId, Span, SyntaxKind, SyntaxNode}, }; -use super::{BoundPred, PackageId}; +use super::{BoundPred, BuiltinTy, PackageId}; use crate::{ adt::{interner::impl_internable, snapshot_map}, - analysis::BuiltinTy, docs::UntypedDefDocs, syntax::{DeclExpr, UnaryOp}, }; @@ -142,7 +141,7 @@ impl Ty { matches!(self, Ty::Dict(..)) } - pub(crate) fn union(lhs: Option, rhs: Option) -> Option { + pub fn union(lhs: Option, rhs: Option) -> Option { Some(match (lhs, rhs) { (Some(lhs), Some(rhs)) => Ty::from_types([lhs, rhs].into_iter()), (Some(ty), None) | (None, Some(ty)) => ty, @@ -231,11 +230,11 @@ impl Ty { } } - pub(crate) fn satisfy(&self, ctx: &T, f: impl FnMut(&Ty, bool)) { + pub fn satisfy(&self, ctx: &T, f: impl FnMut(&Ty, bool)) { self.bounds(true, &mut BoundPred::new(ctx, f)); } - pub(crate) fn is_content(&self, ctx: &T) -> bool { + pub fn is_content(&self, ctx: &T) -> bool { let mut res = false; self.satisfy(ctx, |ty: &Ty, _pol| { res = res || { @@ -561,7 +560,7 @@ pub struct ParamAttrs { } impl ParamAttrs { - pub(crate) fn positional() -> ParamAttrs { + pub fn positional() -> ParamAttrs { ParamAttrs { positional: true, named: false, @@ -570,7 +569,7 @@ impl ParamAttrs { } } - pub(crate) fn named() -> ParamAttrs { + pub fn named() -> ParamAttrs { ParamAttrs { positional: false, named: true, @@ -579,7 +578,7 @@ impl ParamAttrs { } } - pub(crate) fn variadic() -> ParamAttrs { + pub fn variadic() -> ParamAttrs { ParamAttrs { positional: true, named: false, @@ -835,7 +834,7 @@ impl SigTy { }) } - pub(crate) fn with_body(mut self, res_ty: Ty) -> Self { + pub fn with_body(mut self, res_ty: Ty) -> Self { self.body = Some(res_ty); self } @@ -964,7 +963,7 @@ impl SigTy { pub fn matches<'a>( &'a self, args: &'a SigTy, - with: Option<&'a Vec>>, + with: Option<&'a Vec>>, ) -> impl Iterator + 'a { let with_len = with .map(|w| w.iter().map(|w| w.positional_params().len()).sum::()) diff --git a/crates/tinymist-query/src/ty/describe.rs b/crates/tinymist-analysis/src/ty/describe.rs similarity index 98% rename from crates/tinymist-query/src/ty/describe.rs rename to crates/tinymist-analysis/src/ty/describe.rs index 20f9ad91..d77013b8 100644 --- a/crates/tinymist-query/src/ty/describe.rs +++ b/crates/tinymist-analysis/src/ty/describe.rs @@ -3,11 +3,8 @@ use tinymist_std::hash::hash128; use tinymist_world::vfs::WorkspaceResolver; use typst::foundations::Repr; -use crate::{ - analysis::{is_plain_value, term_value}, - ty::prelude::*, - upstream::truncated_repr_, -}; +use super::{is_plain_value, term_value}; +use crate::{ty::prelude::*, upstream::truncated_repr_}; impl Ty { /// Describe the given type. diff --git a/crates/tinymist-query/src/ty/iface.rs b/crates/tinymist-analysis/src/ty/iface.rs similarity index 100% rename from crates/tinymist-query/src/ty/iface.rs rename to crates/tinymist-analysis/src/ty/iface.rs diff --git a/crates/tinymist-query/src/ty/mod.rs b/crates/tinymist-analysis/src/ty/mod.rs similarity index 95% rename from crates/tinymist-query/src/ty/mod.rs rename to crates/tinymist-analysis/src/ty/mod.rs index 3c4d9c42..e421c5dd 100644 --- a/crates/tinymist-query/src/ty/mod.rs +++ b/crates/tinymist-analysis/src/ty/mod.rs @@ -1,8 +1,11 @@ //! Types and type operations for Typst. +#![allow(missing_docs)] + mod apply; mod bound; mod builtin; +mod convert; mod def; mod describe; mod iface; @@ -13,14 +16,16 @@ mod sig; mod simplify; mod subst; -pub(crate) use apply::*; -pub(crate) use bound::*; -pub(crate) use builtin::*; +pub use apply::*; +pub use bound::*; +pub use builtin::*; +pub use convert::*; pub use def::*; -pub(crate) use iface::*; -pub(crate) use mutate::*; -pub(crate) use select::*; -pub(crate) use sig::*; +pub use iface::*; +pub use mutate::*; +pub use select::*; +pub use sig::*; + use typst::foundations::{self, Func, Module, Value}; use typst::syntax::FileId; diff --git a/crates/tinymist-query/src/ty/mutate.rs b/crates/tinymist-analysis/src/ty/mutate.rs similarity index 100% rename from crates/tinymist-query/src/ty/mutate.rs rename to crates/tinymist-analysis/src/ty/mutate.rs diff --git a/crates/tinymist-query/src/ty/prelude.rs b/crates/tinymist-analysis/src/ty/prelude.rs similarity index 100% rename from crates/tinymist-query/src/ty/prelude.rs rename to crates/tinymist-analysis/src/ty/prelude.rs diff --git a/crates/tinymist-query/src/ty/select.rs b/crates/tinymist-analysis/src/ty/select.rs similarity index 100% rename from crates/tinymist-query/src/ty/select.rs rename to crates/tinymist-analysis/src/ty/select.rs diff --git a/crates/tinymist-query/src/ty/sig.rs b/crates/tinymist-analysis/src/ty/sig.rs similarity index 100% rename from crates/tinymist-query/src/ty/sig.rs rename to crates/tinymist-analysis/src/ty/sig.rs diff --git a/crates/tinymist-query/src/ty/simplify.rs b/crates/tinymist-analysis/src/ty/simplify.rs similarity index 100% rename from crates/tinymist-query/src/ty/simplify.rs rename to crates/tinymist-analysis/src/ty/simplify.rs diff --git a/crates/tinymist-query/src/ty/subst.rs b/crates/tinymist-analysis/src/ty/subst.rs similarity index 100% rename from crates/tinymist-query/src/ty/subst.rs rename to crates/tinymist-analysis/src/ty/subst.rs diff --git a/crates/tinymist-query/src/upstream/groups.yml b/crates/tinymist-analysis/src/upstream/groups.yml similarity index 100% rename from crates/tinymist-query/src/upstream/groups.yml rename to crates/tinymist-analysis/src/upstream/groups.yml diff --git a/crates/tinymist-query/src/upstream/mod.rs b/crates/tinymist-analysis/src/upstream/mod.rs similarity index 98% rename from crates/tinymist-query/src/upstream/mod.rs rename to crates/tinymist-analysis/src/upstream/mod.rs index cd67efa2..cf743d5a 100644 --- a/crates/tinymist-query/src/upstream/mod.rs +++ b/crates/tinymist-analysis/src/upstream/mod.rs @@ -1,3 +1,5 @@ +//! Functions from typst-ide + use std::{collections::HashMap, fmt::Write, sync::LazyLock}; use comemo::Tracked; @@ -354,6 +356,7 @@ pub(crate) fn urlify(title: &str) -> EcoString { .collect() } +/// Get the route of a value. pub fn route_of_value(val: &Value) -> Option<&'static String> { // ROUTE_MAPS.get(&CatKey::Func(k.clone())) let key = match val { @@ -396,6 +399,7 @@ pub fn summarize_font_family<'a>(variants: impl Iterator) - detail } +/// Get the representation but truncated to a certain size. pub fn truncated_repr_(value: &Value) -> EcoString { use typst::foundations::Repr; @@ -416,6 +420,7 @@ pub fn truncated_repr_(value: &Value) -> EcoString { } } +/// Get the representation but truncated to a certain size. pub fn truncated_repr(value: &Value) -> EcoString { const _10MB: usize = 100 * 1024 * 1024; truncated_repr_::<_10MB>(value) diff --git a/crates/tinymist-query/src/upstream/tooltip.rs b/crates/tinymist-analysis/src/upstream/tooltip.rs similarity index 99% rename from crates/tinymist-query/src/upstream/tooltip.rs rename to crates/tinymist-analysis/src/upstream/tooltip.rs index 57219ecc..24da00b1 100644 --- a/crates/tinymist-query/src/upstream/tooltip.rs +++ b/crates/tinymist-analysis/src/upstream/tooltip.rs @@ -12,7 +12,7 @@ use typst_shim::syntax::LinkedNodeExt; use typst_shim::utils::{round_2, Numeric}; use super::{plain_docs_sentence, summarize_font_family, truncated_repr}; -use crate::analysis::analyze_expr; +use crate::analyze_expr; /// Describe the item under the cursor. /// diff --git a/crates/tinymist-query/Cargo.toml b/crates/tinymist-query/Cargo.toml index 89064b45..91357be6 100644 --- a/crates/tinymist-query/Cargo.toml +++ b/crates/tinymist-query/Cargo.toml @@ -14,43 +14,33 @@ rust-version.workspace = true [dependencies] anyhow.workspace = true -comemo.workspace = true -dirs.workspace = true -regex.workspace = true -yaml-rust2.workspace = true +base64.workspace = true biblatex.workspace = true -serde_yaml.workspace = true -itertools.workspace = true -strum.workspace = true -log.workspace = true -serde.workspace = true -serde_json.workspace = true -parking_lot.workspace = true +comemo.workspace = true +dashmap.workspace = true +dirs.workspace = true ena.workspace = true -toml.workspace = true -walkdir.workspace = true -indexmap.workspace = true ecow.workspace = true -siphasher.workspace = true -rpds.workspace = true -rayon.workspace = true - -typst.workspace = true - -typst-shim.workspace = true - -lsp-types.workspace = true if_chain.workspace = true +itertools.workspace = true +indexmap.workspace = true +log.workspace = true +lsp-types.workspace = true +parking_lot.workspace = true percent-encoding.workspace = true -unscanny.workspace = true -ttf-parser.workspace = true +rayon.workspace = true +regex.workspace = true +rpds.workspace = true rust_iso639.workspace = true rust_iso3166.workspace = true -dashmap.workspace = true rustc-hash.workspace = true -hashbrown.workspace = true -triomphe.workspace = true -base64.workspace = true +serde.workspace = true +serde_json.workspace = true +serde_yaml.workspace = true +siphasher.workspace = true +strum.workspace = true +toml.workspace = true +ttf-parser.workspace = true typlite.workspace = true tinymist-world = { workspace = true } tinymist-project = { workspace = true, features = ["lsp"] } @@ -58,6 +48,12 @@ tinymist-analysis.workspace = true tinymist-derive.workspace = true tinymist-std.workspace = true tinymist-l10n.workspace = true +typst.workspace = true +typst-shim.workspace = true +unscanny.workspace = true +walkdir.workspace = true +yaml-rust2.workspace = true + [dev-dependencies] insta.workspace = true diff --git a/crates/tinymist-query/src/adt/mod.rs b/crates/tinymist-query/src/adt/mod.rs index 74dfb870..287e1a8b 100644 --- a/crates/tinymist-query/src/adt/mod.rs +++ b/crates/tinymist-query/src/adt/mod.rs @@ -1,3 +1,2 @@ -pub mod interner; pub mod revision; -pub mod snapshot_map; +pub use tinymist_analysis::adt::*; diff --git a/crates/tinymist-query/src/analysis.rs b/crates/tinymist-query/src/analysis.rs index 2d51e9f2..cf1fbd13 100644 --- a/crates/tinymist-query/src/analysis.rs +++ b/crates/tinymist-query/src/analysis.rs @@ -34,8 +34,6 @@ mod tyck; pub(crate) use crate::ty::*; pub(crate) use post_tyck::*; pub(crate) use tyck::*; -pub mod track_values; -pub use track_values::*; mod prelude; mod global; diff --git a/crates/tinymist-query/src/analysis/call.rs b/crates/tinymist-query/src/analysis/call.rs index 9d14b34e..6c8b8dd9 100644 --- a/crates/tinymist-query/src/analysis/call.rs +++ b/crates/tinymist-query/src/analysis/call.rs @@ -1,7 +1,7 @@ //! Hybrid analysis for function calls. use super::prelude::*; -use super::{Signature, StrRef}; +use super::Signature; use crate::analysis::{analyze_signature, PrimarySignature, SignatureTarget}; /// Describes kind of a parameter. diff --git a/crates/tinymist-query/src/analysis/completion.rs b/crates/tinymist-query/src/analysis/completion.rs index 65be27f9..f83cb5ff 100644 --- a/crates/tinymist-query/src/analysis/completion.rs +++ b/crates/tinymist-query/src/analysis/completion.rs @@ -10,6 +10,7 @@ use lsp_types::InsertTextFormat; use regex::{Captures, Regex}; use serde::{Deserialize, Serialize}; use tinymist_analysis::syntax::{bad_completion_cursor, BadCompletionCursor}; +use tinymist_analysis::{analyze_labels, func_signature, DynLabel}; use tinymist_derive::BindTyCtx; use tinymist_project::LspWorld; use tinymist_std::path::unix_slash; @@ -27,9 +28,7 @@ use typst_shim::{syntax::LinkedNodeExt, utils::hash128}; use unscanny::Scanner; use crate::adt::interner::Interned; -use crate::analysis::{ - analyze_labels, func_signature, BuiltinTy, DynLabel, LocalContext, PathPreference, Ty, -}; +use crate::analysis::{BuiltinTy, LocalContext, PathPreference, Ty}; use crate::completion::{ Completion, CompletionCommand, CompletionContextKey, CompletionItem, CompletionKind, EcoTextEdit, ParsedSnippet, PostfixSnippet, PostfixSnippetScope, PrefixSnippet, diff --git a/crates/tinymist-query/src/analysis/definition.rs b/crates/tinymist-query/src/analysis/definition.rs index 40edaf79..bae64f59 100644 --- a/crates/tinymist-query/src/analysis/definition.rs +++ b/crates/tinymist-query/src/analysis/definition.rs @@ -55,6 +55,31 @@ impl Definition { } } +trait HasNameRange { + /// Gets name range of the item. + fn name_range(&self, ctx: &SharedContext) -> Option>; +} + +impl HasNameRange for Decl { + fn name_range(&self, ctx: &SharedContext) -> Option> { + if let Decl::BibEntry(decl) = self { + return Some(decl.at.1.clone()); + } + + if !self.is_def() { + return None; + } + + let span = self.span(); + if let Some(range) = span.range() { + return Some(range.clone()); + } + + let src = ctx.source_by_id(self.file_id()?).ok()?; + src.range(span) + } +} + // todo: field definition /// Finds the definition of a symbol. pub fn definition( diff --git a/crates/tinymist-query/src/analysis/global.rs b/crates/tinymist-query/src/analysis/global.rs index ba519d2b..e2387be0 100644 --- a/crates/tinymist-query/src/analysis/global.rs +++ b/crates/tinymist-query/src/analysis/global.rs @@ -8,6 +8,9 @@ use comemo::{Track, Tracked}; use lsp_types::Url; use parking_lot::Mutex; use rustc_hash::FxHashMap; +use tinymist_analysis::stats::AllocStats; +use tinymist_analysis::ty::term_value; +use tinymist_analysis::{analyze_expr_, analyze_import_}; use tinymist_project::LspWorld; use tinymist_std::hash::{hash128, FxDashMap}; use tinymist_std::typst::TypstDocument; @@ -24,10 +27,9 @@ use typst_shim::eval::{eval_compat, Eval}; use crate::adt::revision::{RevisionLock, RevisionManager, RevisionManagerLike, RevisionSlot}; use crate::analysis::prelude::*; use crate::analysis::{ - analyze_expr_, analyze_import_, analyze_signature, bib_info, definition, post_type_check, - AllocStats, AnalysisStats, BibInfo, CompletionFeat, Definition, PathPreference, QueryStatGuard, - SemanticTokenCache, SemanticTokenContext, SemanticTokens, Signature, SignatureTarget, Ty, - TypeInfo, + analyze_signature, bib_info, definition, post_type_check, AnalysisStats, BibInfo, + CompletionFeat, Definition, PathPreference, QueryStatGuard, SemanticTokenCache, + SemanticTokenContext, SemanticTokens, Signature, SignatureTarget, Ty, TypeInfo, }; use crate::docs::{DefDocs, TidyModuleDocs}; use crate::syntax::{ @@ -147,7 +149,7 @@ impl Analysis { /// Report the statistics of the allocation. pub fn report_alloc_stats(&self) -> String { - AllocStats::report(self) + AllocStats::report() } /// Get configured trigger suggest command. @@ -781,7 +783,7 @@ impl SharedContext { return cached; } - let res = crate::analysis::term_value(val); + let res = term_value(val); self.analysis .caches diff --git a/crates/tinymist-query/src/analysis/post_tyck.rs b/crates/tinymist-query/src/analysis/post_tyck.rs index 7a0653db..69b83f5f 100644 --- a/crates/tinymist-query/src/analysis/post_tyck.rs +++ b/crates/tinymist-query/src/analysis/post_tyck.rs @@ -1,6 +1,6 @@ //! Infer more than the principal type of some expression. -use hashbrown::HashSet; +use std::collections::HashSet; use tinymist_derive::BindTyCtx; use super::{prelude::*, DynTypeBounds, ParamAttrs, ParamTy, SharedContext}; diff --git a/crates/tinymist-query/src/analysis/prelude.rs b/crates/tinymist-query/src/analysis/prelude.rs index 95ee2853..fba07803 100644 --- a/crates/tinymist-query/src/analysis/prelude.rs +++ b/crates/tinymist-query/src/analysis/prelude.rs @@ -1,4 +1,3 @@ -pub use core::fmt; pub use std::collections::{BTreeMap, HashMap}; pub use std::hash::{Hash, Hasher}; pub use std::ops::Range; @@ -14,7 +13,7 @@ pub use typst::World; pub use typst_shim::syntax::LinkedNodeExt; pub use typst_shim::utils::LazyHash; -pub(crate) use super::StrRef; pub(crate) use super::{LocalContext, ToFunc}; pub(crate) use crate::adt::interner::Interned; pub use crate::ty::Ty; +pub(crate) use crate::StrRef; diff --git a/crates/tinymist-query/src/analysis/semantic_tokens.rs b/crates/tinymist-query/src/analysis/semantic_tokens.rs index e12c9855..a7c73809 100644 --- a/crates/tinymist-query/src/analysis/semantic_tokens.rs +++ b/crates/tinymist-query/src/analysis/semantic_tokens.rs @@ -1,5 +1,6 @@ //! Semantic tokens (highlighting) support for LSP. +use std::collections::HashMap; use std::{ num::NonZeroUsize, ops::Range, @@ -7,7 +8,6 @@ use std::{ sync::{Arc, OnceLock}, }; -use hashbrown::HashMap; use lsp_types::SemanticToken; use lsp_types::{SemanticTokenModifier, SemanticTokenType}; use parking_lot::Mutex; diff --git a/crates/tinymist-query/src/analysis/signature.rs b/crates/tinymist-query/src/analysis/signature.rs index 91efc3e0..1bccf8fd 100644 --- a/crates/tinymist-query/src/analysis/signature.rs +++ b/crates/tinymist-query/src/analysis/signature.rs @@ -1,163 +1,19 @@ //! Analysis of function signatures. use itertools::Either; +use tinymist_analysis::{func_signature, ArgInfo, ArgsInfo, PartialSignature}; use tinymist_derive::BindTyCtx; -use typst::foundations::Closure; -use super::{ - prelude::*, BoundChecker, Definition, DocSource, ParamTy, SharedContext, SigTy, SigWithTy, - TypeInfo, TypeVar, -}; +use super::{prelude::*, Definition, SharedContext}; use crate::analysis::PostTypeChecker; use crate::docs::{UntypedDefDocs, UntypedSignatureDocs, UntypedVarDocs}; use crate::syntax::classify_def_loosely; -use crate::ty::{DynTypeBounds, ParamAttrs}; -use crate::ty::{InsTy, TyCtx}; -use crate::upstream::truncated_repr; +use crate::ty::{ + BoundChecker, DocSource, DynTypeBounds, ParamAttrs, ParamTy, SigWithTy, TyCtx, TypeInfo, + TypeVar, +}; -/// Describes a function signature. -#[derive(Debug, Clone)] -pub enum Signature { - /// A primary function signature. - Primary(Arc), - /// A partially applied function signature. - Partial(Arc), -} - -impl Signature { - /// Returns the primary signature if it is one. - pub fn primary(&self) -> &Arc { - match self { - Signature::Primary(sig) => sig, - Signature::Partial(sig) => &sig.signature, - } - } - - /// Returns the with bindings of the signature. - pub fn bindings(&self) -> &[ArgsInfo] { - match self { - Signature::Primary(_) => &[], - Signature::Partial(sig) => &sig.with_stack, - } - } - - /// Returns the all parameters of the function. - pub(crate) fn params(&self) -> impl Iterator, Option<&Ty>)> { - let primary = self.primary().params(); - // todo: with stack - primary - } - - pub(crate) fn type_sig(&self) -> Interned { - let primary = self.primary().sig_ty.clone(); - // todo: with stack - primary - } - - pub(crate) fn param_shift(&self) -> usize { - match self { - Signature::Primary(_) => 0, - Signature::Partial(sig) => sig - .with_stack - .iter() - .map(|ws| ws.items.len()) - .sum::(), - } - } -} - -/// Describes a primary function signature. -#[derive(Debug, Clone)] -pub struct PrimarySignature { - /// 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 associated signature type. - pub(crate) sig_ty: Interned, - _broken: bool, -} - -impl PrimarySignature { - /// 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) -> &[Interned] { - &self.param_specs[..self.pos_size()] - } - - /// Returns the positional parameters of the function. - pub fn get_pos(&self, offset: usize) -> Option<&Interned> { - self.pos().get(offset) - } - - /// Returns the named parameters of the function. - pub fn named(&self) -> &[Interned] { - &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<&Interned> { - 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<&Interned> { - self.has_spread_right() - .then(|| &self.param_specs[self.pos_size() + self.sig_ty.names.names.len()]) - } - - /// Returns the all parameters of the function. - pub fn params(&self) -> impl Iterator, Option<&Ty>)> { - let pos = self.pos(); - let named = self.named(); - let rest = self.rest(); - let type_sig = &self.sig_ty; - let pos = pos - .iter() - .enumerate() - .map(|(idx, pos)| (pos, type_sig.pos(idx))); - let named = named.iter().map(|x| (x, type_sig.named(&x.name))); - let rest = rest.into_iter().map(|x| (x, type_sig.rest_param())); - - pos.chain(named).chain(rest) - } -} - -/// Describes a function argument instance -#[derive(Debug, Clone)] -pub struct ArgInfo { - /// The argument's name. - pub name: Option, - /// The argument's term. - pub term: Option, -} - -/// Describes a function argument list. -#[derive(Debug, Clone)] -pub struct ArgsInfo { - /// The arguments. - pub items: EcoVec, -} - -/// Describes a function signature that is already partially applied. -#[derive(Debug, Clone)] -pub struct PartialSignature { - /// The positional parameters. - pub signature: Arc, - /// The stack of `fn.with(..)` calls. - pub with_stack: EcoVec, -} +pub use tinymist_analysis::{PrimarySignature, Signature}; /// The language object that the signature is being analyzed for. #[derive(Debug, Clone)] @@ -475,216 +331,3 @@ fn analyze_dyn_signature( Some(func_signature(func)) } - -/// Gets the signature of a function. -#[comemo::memoize] -pub fn func_signature(func: Func) -> Signature { - use typst::foundations::func::Repr; - let mut with_stack = eco_vec![]; - let mut func = func; - while let Repr::With(with) = func.inner() { - let (inner, args) = with.as_ref(); - with_stack.push(ArgsInfo { - items: args - .items - .iter() - .map(|arg| ArgInfo { - name: arg.name.clone().map(From::from), - term: Some(Ty::Value(InsTy::new(arg.value.v.clone()))), - }) - .collect(), - }); - func = inner.clone(); - } - - 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 broken = false; - let mut has_fill_or_size_or_stroke = false; - - let mut add_param = |param: Interned| { - let name = param.name.clone(); - if param.attrs.named { - if matches!(name.as_ref(), "fill" | "stroke" | "size") { - has_fill_or_size_or_stroke = true; - } - named_tys.push((name.clone(), param.ty.clone())); - named_specs.insert(name.clone(), param.clone()); - } - - if param.attrs.variadic { - if rest_ty.is_some() { - broken = true; - } else { - rest_ty = Some(param.ty.clone()); - rest_spec = Some(param); - } - } else if param.attrs.positional { - // todo: we have some params that are both positional and named - pos_tys.push(param.ty.clone()); - param_specs.push(param); - } - }; - - let ret_ty = match func.inner() { - Repr::With(..) => unreachable!(), - Repr::Closure(closure) => { - analyze_closure_signature(closure.clone(), &mut add_param); - None - } - Repr::Element(..) | Repr::Native(..) | Repr::Plugin(..) => { - for param in func.params().unwrap_or_default() { - add_param(Interned::new(ParamTy { - name: param.name.into(), - docs: Some(param.docs.into()), - default: param.default.map(|default| truncated_repr(&default())), - ty: Ty::from_param_site(&func, param), - attrs: param.into(), - })); - } - - 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 { - let Some(param) = named_specs.get(name) else { - continue; - }; - param_specs.push(param.clone()); - } - if let Some(doc) = rest_spec { - param_specs.push(doc); - } - - let signature = Arc::new(PrimarySignature { - docs: func.docs().map(From::from), - param_specs, - has_fill_or_size_or_stroke, - sig_ty: sig_ty.into(), - _broken: broken, - }); - - log::trace!("got signature {signature:?}"); - - if with_stack.is_empty() { - return Signature::Primary(signature); - } - - Signature::Partial(Arc::new(PartialSignature { - signature, - with_stack, - })) -} - -fn analyze_closure_signature( - closure: Arc>, - add_param: &mut impl FnMut(Interned), -) { - log::trace!("closure signature for: {:?}", closure.node.kind()); - - let closure = &closure.node; - let closure_ast = match closure.kind() { - SyntaxKind::Closure => closure.cast::().unwrap(), - _ => return, - }; - - for param in closure_ast.params().children() { - match param { - ast::Param::Pos(pos) => { - let name = format!("{}", PatternDisplay(&pos)); - add_param(Interned::new(ParamTy { - name: name.as_str().into(), - docs: None, - default: None, - ty: Ty::Any, - attrs: ParamAttrs::positional(), - })); - } - // todo: pattern - ast::Param::Named(named) => { - let default = unwrap_parens(named.expr()).to_untyped().clone().into_text(); - add_param(Interned::new(ParamTy { - name: named.name().get().into(), - docs: Some(eco_format!("Default value: {default}")), - default: Some(default), - ty: Ty::Any, - attrs: ParamAttrs::named(), - })); - } - ast::Param::Spread(spread) => { - let sink = spread.sink_ident().map(|sink| sink.as_str()); - add_param(Interned::new(ParamTy { - name: sink.unwrap_or_default().into(), - docs: None, - default: None, - ty: Ty::Any, - attrs: ParamAttrs::variadic(), - })); - } - } - } -} - -struct PatternDisplay<'a>(&'a ast::Pattern<'a>); - -impl fmt::Display for PatternDisplay<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 { - ast::Pattern::Normal(ast::Expr::Ident(ident)) => f.write_str(ident.as_str()), - ast::Pattern::Normal(_) => f.write_str("?"), // unreachable? - ast::Pattern::Placeholder(_) => f.write_str("_"), - ast::Pattern::Parenthesized(paren_expr) => { - write!(f, "{}", PatternDisplay(&paren_expr.pattern())) - } - ast::Pattern::Destructuring(destructing) => { - write!(f, "(")?; - let mut first = true; - for item in destructing.items() { - if first { - first = false; - } else { - write!(f, ", ")?; - } - match item { - ast::DestructuringItem::Pattern(pos) => { - write!(f, "{}", PatternDisplay(&pos))? - } - ast::DestructuringItem::Named(named) => write!( - f, - "{}: {}", - named.name().as_str(), - unwrap_parens(named.expr()).to_untyped().text() - )?, - ast::DestructuringItem::Spread(spread) => write!( - f, - "..{}", - spread - .sink_ident() - .map(|sink| sink.as_str()) - .unwrap_or_default() - )?, - } - } - write!(f, ")")?; - Ok(()) - } - } - } -} - -fn unwrap_parens(mut expr: ast::Expr) -> ast::Expr { - while let ast::Expr::Parenthesized(paren_expr) = expr { - expr = paren_expr.expr(); - } - - expr -} diff --git a/crates/tinymist-query/src/analysis/stats.rs b/crates/tinymist-query/src/analysis/stats.rs index 4e3f142d..6b913183 100644 --- a/crates/tinymist-query/src/analysis/stats.rs +++ b/crates/tinymist-query/src/analysis/stats.rs @@ -1,16 +1,11 @@ //! Statistics about the analyzers -use std::{ - sync::{atomic::AtomicUsize, Arc}, - time::Duration, -}; +use std::{sync::Arc, time::Duration}; use parking_lot::Mutex; use tinymist_std::hash::FxDashMap; use typst::syntax::FileId; -use super::Analysis; - #[derive(Clone)] pub(crate) struct QueryStatBucketData { pub query: u64, @@ -117,84 +112,3 @@ table.analysis-stats tr:nth-child(odd) { background-color: rgba(242, 242, 242, 0 html } } - -/// Statistics about the allocation - -#[derive(Debug, Default)] -pub struct AllocStats { - /// The number of allocated objects. - pub allocated: AtomicUsize, - /// The number of dropped objects. - pub dropped: AtomicUsize, -} - -impl AllocStats { - /// increment the statistics. - pub fn increment(&self) { - self.allocated - .fetch_add(1, std::sync::atomic::Ordering::Relaxed); - } - - /// decrement the statistics. - pub fn decrement(&self) { - self.dropped - .fetch_add(1, std::sync::atomic::Ordering::Relaxed); - } -} - -impl AllocStats { - /// Report the statistics of the allocation. - pub fn report(_a: &Analysis) -> String { - let maps = crate::adt::interner::MAPS.lock().clone(); - let mut data = Vec::new(); - for (name, sz, map) in maps { - let allocated = map.allocated.load(std::sync::atomic::Ordering::Relaxed); - let dropped = map.dropped.load(std::sync::atomic::Ordering::Relaxed); - let alive = allocated.saturating_sub(dropped); - data.push((name, sz * alive, allocated, dropped, alive)); - } - - // sort by total - data.sort_by(|x, y| y.4.cmp(&x.4)); - - // format to html - - let mut html = String::new(); - html.push_str(r#"
- -"#); - - for (name, sz, allocated, dropped, alive) in data { - html.push_str(""); - html.push_str(&format!(r#""#)); - html.push_str(&format!("")); - html.push_str(&format!("")); - html.push_str(&format!("")); - html.push_str(&format!("", human_size(sz))); - html.push_str(""); - } - html.push_str("
NameAliveAllocatedDroppedSize
{name}{alive}{allocated}{dropped}{}
"); - html.push_str("
"); - - html - } -} - -fn human_size(size: usize) -> String { - let units = ["B", "KB", "MB", "GB", "TB"]; - let mut unit = 0; - let mut size = size as f64; - while size >= 768.0 && unit < units.len() { - size /= 1024.0; - unit += 1; - } - format!("{:.2} {}", size, units[unit]) -} diff --git a/crates/tinymist-query/src/analysis/tyck.rs b/crates/tinymist-query/src/analysis/tyck.rs index c9bb5563..c4cb9695 100644 --- a/crates/tinymist-query/src/analysis/tyck.rs +++ b/crates/tinymist-query/src/analysis/tyck.rs @@ -15,13 +15,11 @@ use crate::{ }; mod apply; -mod convert; mod docs; mod select; mod syntax; pub(crate) use apply::*; -pub(crate) use convert::*; pub(crate) use select::*; #[derive(Default)] diff --git a/crates/tinymist-query/src/code_context.rs b/crates/tinymist-query/src/code_context.rs index 16bac8a8..daab0840 100644 --- a/crates/tinymist-query/src/code_context.rs +++ b/crates/tinymist-query/src/code_context.rs @@ -1,10 +1,10 @@ use serde::{Deserialize, Serialize}; +use tinymist_analysis::analyze_expr; use tinymist_world::ShadowApi; use typst::foundations::{Bytes, IntoValue, StyleChain}; use typst_shim::syntax::LinkedNodeExt; use crate::{ - analysis::analyze_expr, prelude::*, syntax::{interpret_mode_at, InterpretMode}, }; diff --git a/crates/tinymist-query/src/completion/proto.rs b/crates/tinymist-query/src/completion/proto.rs index 675443b5..b6a784a9 100644 --- a/crates/tinymist-query/src/completion/proto.rs +++ b/crates/tinymist-query/src/completion/proto.rs @@ -2,7 +2,7 @@ use ecow::EcoString; use lsp_types::InsertTextFormat; use serde::{Deserialize, Serialize}; -use crate::ty::Interned; +use crate::StrRef; use super::LspRange; @@ -284,11 +284,11 @@ pub struct LspCompletionCommand { /// The title of command. pub title: EcoString, /// The identifier of the actual command handler. - pub command: Interned, + pub command: StrRef, } -impl From> for LspCompletionCommand { - fn from(command: Interned) -> Self { +impl From for LspCompletionCommand { + fn from(command: StrRef) -> Self { Self { title: EcoString::default(), command, diff --git a/crates/tinymist-query/src/completion/snippet.rs b/crates/tinymist-query/src/completion/snippet.rs index 5b3e18f1..0d8cd7c3 100644 --- a/crates/tinymist-query/src/completion/snippet.rs +++ b/crates/tinymist-query/src/completion/snippet.rs @@ -1,14 +1,14 @@ +use std::collections::HashSet; use std::sync::OnceLock; use ecow::{eco_format, EcoString}; -use hashbrown::HashSet; use serde::de::DeserializeOwned; use serde::{Deserialize, Deserializer, Serialize}; use strum::IntoEnumIterator; +use crate::adt::interner::Interned; use crate::prelude::*; use crate::syntax::{InterpretMode, SurroundingSyntax}; -use crate::ty::Interned; /// This is the poorman's type filter, which is less powerful but more steady. #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/crates/tinymist-query/src/docs/def.rs b/crates/tinymist-query/src/docs/def.rs index ddb2cde8..5015a724 100644 --- a/crates/tinymist-query/src/docs/def.rs +++ b/crates/tinymist-query/src/docs/def.rs @@ -1,303 +1,11 @@ -use core::fmt; -use std::collections::BTreeMap; use std::sync::OnceLock; -use ecow::{eco_format, EcoString}; -use serde::{Deserialize, Serialize}; +use tinymist_analysis::docs::{format_ty, ParamDocs, SignatureDocs, VarDocs}; +use tinymist_analysis::ty::DocSource; +use tinymist_analysis::Signature; use typst::syntax::Span; -use super::tidy::*; -use crate::analysis::{ParamAttrs, ParamTy, Signature}; -use crate::prelude::*; -use crate::ty::Ty; -use crate::ty::{DocSource, Interned}; -use crate::upstream::plain_docs_sentence; - -type TypeRepr = Option<( - /* short */ EcoString, - /* long */ EcoString, - /* value */ EcoString, -)>; - -/// Documentation about a definition (without type information). -pub type UntypedDefDocs = DefDocsT<()>; -/// Documentation about a definition. -pub type DefDocs = DefDocsT; - -/// Documentation about a definition. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "kind")] -pub enum DefDocsT { - /// Documentation about a function. - #[serde(rename = "func")] - Function(Box>), - /// Documentation about a variable. - #[serde(rename = "var")] - Variable(VarDocsT), - /// Documentation about a module. - #[serde(rename = "module")] - Module(TidyModuleDocs), - /// Other kinds of documentation. - #[serde(rename = "plain")] - Plain { - /// The content of the documentation. - docs: EcoString, - }, -} - -impl DefDocsT { - /// Get the markdown representation of the documentation. - pub fn docs(&self) -> &EcoString { - match self { - Self::Function(docs) => &docs.docs, - Self::Variable(docs) => &docs.docs, - Self::Module(docs) => &docs.docs, - Self::Plain { docs } => docs, - } - } -} - -impl DefDocs { - /// Get full documentation for the signature. - pub fn hover_docs(&self) -> EcoString { - match self { - DefDocs::Function(docs) => docs.hover_docs().clone(), - _ => plain_docs_sentence(self.docs()), - } - } -} - -/// Describes a primary function signature. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SignatureDocsT { - /// Documentation for the function. - pub docs: EcoString, - /// The positional parameters. - pub pos: Vec>, - /// The named parameters. - pub named: BTreeMap, ParamDocsT>, - /// The rest parameter. - pub rest: Option>, - /// The return type. - pub ret_ty: T, - /// The full documentation for the signature. - #[serde(skip)] - pub hover_docs: OnceLock, -} - -impl SignatureDocsT { - /// Get full documentation for the signature. - pub fn hover_docs(&self) -> &EcoString { - self.hover_docs - .get_or_init(|| plain_docs_sentence(&format!("{}", SigHoverDocs(self)))) - } -} - -struct SigHoverDocs<'a>(&'a SignatureDocs); - -impl fmt::Display for SigHoverDocs<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let docs = self.0; - let base_docs = docs.docs.trim(); - - if !base_docs.is_empty() { - f.write_str(base_docs)?; - } - - fn write_param_docs( - f: &mut fmt::Formatter<'_>, - docs: &ParamDocsT, - kind: &str, - is_first: &mut bool, - ) -> fmt::Result { - if *is_first { - *is_first = false; - write!(f, "\n\n## {}\n\n", docs.name)?; - } else { - write!(f, "\n\n## {} ({kind})\n\n", docs.name)?; - } - - // p.cano_type.0 - if let Some(t) = &docs.cano_type { - write!(f, "```typc\ntype: {}\n```\n\n", t.2)?; - } - - f.write_str(docs.docs.trim())?; - - Ok(()) - } - - if !docs.pos.is_empty() { - f.write_str("\n\n# Positional Parameters")?; - - let mut is_first = true; - for pos_docs in &docs.pos { - write_param_docs(f, pos_docs, "positional", &mut is_first)?; - } - } - - if docs.rest.is_some() { - f.write_str("\n\n# Rest Parameters")?; - - let mut is_first = true; - if let Some(rest) = &docs.rest { - write_param_docs(f, rest, "spread right", &mut is_first)?; - } - } - - if !docs.named.is_empty() { - f.write_str("\n\n# Named Parameters")?; - - let mut is_first = true; - for named_docs in docs.named.values() { - write_param_docs(f, named_docs, "named", &mut is_first)?; - } - } - - Ok(()) - } -} - -/// Documentation about a signature. -pub type UntypedSignatureDocs = SignatureDocsT<()>; -/// Documentation about a signature. -pub type SignatureDocs = SignatureDocsT; - -impl SignatureDocs { - /// Get the markdown representation of the documentation. - pub fn print(&self, f: &mut impl std::fmt::Write) -> fmt::Result { - let mut is_first = true; - let mut write_sep = |f: &mut dyn std::fmt::Write| { - if is_first { - is_first = false; - return f.write_str("\n "); - } - f.write_str(",\n ") - }; - - f.write_char('(')?; - for pos_docs in &self.pos { - write_sep(f)?; - f.write_str(&pos_docs.name)?; - if let Some(t) = &pos_docs.cano_type { - write!(f, ": {}", t.0)?; - } - } - if let Some(rest) = &self.rest { - write_sep(f)?; - f.write_str("..")?; - f.write_str(&rest.name)?; - if let Some(t) = &rest.cano_type { - write!(f, ": {}", t.0)?; - } - } - - if !self.named.is_empty() { - 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.default.clone())) - } - name_prints.sort(); - for (name, ty, val) in name_prints { - write_sep(f)?; - let val = val.as_deref().unwrap_or("any"); - let mut default = val.trim(); - if default.starts_with('{') && default.ends_with('}') && default.len() > 30 { - default = "{ .. }" - } - if default.starts_with('`') && default.ends_with('`') && default.len() > 30 { - default = "raw" - } - if default.starts_with('[') && default.ends_with(']') && default.len() > 30 { - default = "content" - } - f.write_str(&name)?; - if let Some(ty) = ty { - write!(f, ": {ty}")?; - } - if default.contains('\n') { - write!(f, " = {}", default.replace("\n", "\n "))?; - } else { - write!(f, " = {default}")?; - } - } - } - if !is_first { - f.write_str(",\n")?; - } - f.write_char(')')?; - - Ok(()) - } -} - -/// Documentation about a variable (without type information). -pub type UntypedVarDocs = VarDocsT<()>; -/// Documentation about a variable. -pub type VarDocs = VarDocsT>; - -/// Describes a primary pattern binding. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct VarDocsT { - /// Documentation for the pattern binding. - pub docs: EcoString, - /// The inferred type of the pattern binding source. - pub return_ty: T, - /// Cached documentation for the definition. - #[serde(skip)] - pub def_docs: OnceLock, -} - -impl VarDocs { - /// Get the markdown representation of the documentation. - pub fn def_docs(&self) -> &String { - self.def_docs - .get_or_init(|| plain_docs_sentence(&self.docs).into()) - } -} - -/// Documentation about a parameter (without type information). -pub type TypelessParamDocs = ParamDocsT<()>; -/// Documentation about a parameter. -pub type ParamDocs = ParamDocsT; - -/// Describes a function parameter. -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct ParamDocsT { - /// The parameter's name. - pub name: Interned, - /// Documentation for the parameter. - pub docs: EcoString, - /// Inferred type of the parameter. - pub cano_type: T, - /// The parameter's default name as value. - pub default: Option, - /// The attribute of the parameter. - #[serde(flatten)] - pub attrs: ParamAttrs, -} - -impl ParamDocs { - fn new(param: &ParamTy, ty: Option<&Ty>) -> Self { - Self { - name: param.name.as_ref().into(), - docs: param.docs.clone().unwrap_or_default(), - cano_type: format_ty(ty.or(Some(¶m.ty))), - default: param.default.clone(), - attrs: param.attrs, - } - } -} - -fn format_ty(ty: Option<&Ty>) -> TypeRepr { - let ty = ty?; - let short = ty.repr().unwrap_or_else(|| "any".into()); - let long = eco_format!("{ty:?}"); - let value = ty.value_repr().unwrap_or_else(|| "".into()); - - Some((short, long, value)) -} +use crate::LocalContext; pub(crate) fn var_docs(ctx: &mut LocalContext, pos: Span) -> Option { let source = ctx.source_by_id(pos.id()?).ok()?; diff --git a/crates/tinymist-query/src/docs/mod.rs b/crates/tinymist-query/src/docs/mod.rs index e5b5d57b..65f2393b 100644 --- a/crates/tinymist-query/src/docs/mod.rs +++ b/crates/tinymist-query/src/docs/mod.rs @@ -4,16 +4,15 @@ mod convert; mod def; mod module; mod package; -mod tidy; use tinymist_std::path::unix_slash; use typst::syntax::FileId; pub(crate) use convert::convert_docs; -pub use def::*; +pub(crate) use def::*; pub use module::*; pub use package::*; -pub(crate) use tidy::*; +pub use tinymist_analysis::docs::*; fn file_id_repr(fid: FileId) -> String { if let Some(spec) = fid.package() { diff --git a/crates/tinymist-query/src/docs/module.rs b/crates/tinymist-query/src/docs/module.rs index 2eaaed36..d42be28c 100644 --- a/crates/tinymist-query/src/docs/module.rs +++ b/crates/tinymist-query/src/docs/module.rs @@ -10,10 +10,10 @@ use typst::diag::StrResult; use typst::syntax::package::PackageSpec; use typst::syntax::FileId; +use crate::adt::interner::Interned; use crate::docs::file_id_repr; use crate::package::{get_manifest_id, PackageInfo}; use crate::syntax::{Decl, DefKind, Expr, ExprInfo}; -use crate::ty::Interned; use crate::LocalContext; use super::DefDocs; diff --git a/crates/tinymist-query/src/folding_range.rs b/crates/tinymist-query/src/folding_range.rs index 6435b080..c779ec8a 100644 --- a/crates/tinymist-query/src/folding_range.rs +++ b/crates/tinymist-query/src/folding_range.rs @@ -1,4 +1,4 @@ -use hashbrown::HashSet; +use std::collections::HashSet; use crate::{ prelude::*, diff --git a/crates/tinymist-query/src/lib.rs b/crates/tinymist-query/src/lib.rs index b02fd56b..0ad7de5c 100644 --- a/crates/tinymist-query/src/lib.rs +++ b/crates/tinymist-query/src/lib.rs @@ -46,7 +46,7 @@ pub mod docs; pub mod package; pub mod syntax; pub mod testing; -pub mod ty; +pub use tinymist_analysis::{ty, upstream}; /// The physical position in a document. pub type FramePosition = typst::layout::Position; @@ -81,15 +81,17 @@ mod semantic_tokens_delta; mod semantic_tokens_full; mod signature_help; mod symbol; -mod upstream; mod will_rename_files; mod workspace_label; use typst::syntax::Source; -use tinymist_analysis::log_debug_ct; +use tinymist_analysis::{adt::interner::Interned, log_debug_ct}; use tinymist_project::LspComputeGraph; +/// A reference to the interned string +pub(crate) type StrRef = Interned; + /// A request handler with given syntax information. pub trait SyntaxRequest { /// The response type of the request. diff --git a/crates/tinymist-query/src/references.rs b/crates/tinymist-query/src/references.rs index 3b38713f..ecd32587 100644 --- a/crates/tinymist-query/src/references.rs +++ b/crates/tinymist-query/src/references.rs @@ -1,5 +1,6 @@ use std::sync::OnceLock; +use tinymist_analysis::adt::interner::Interned; use tinymist_std::typst::TypstDocument; use typst::syntax::Span; @@ -7,7 +8,7 @@ use crate::{ analysis::{Definition, SearchCtx}, prelude::*, syntax::{get_index_info, RefExpr, SyntaxClass}, - ty::Interned, + StrRef, }; /// The [`textDocument/references`] request is sent from the client to the @@ -73,7 +74,7 @@ struct ReferencesWorker<'a> { ctx: SearchCtx<'a>, references: Vec, def: Definition, - module_path: OnceLock>, + module_path: OnceLock, } impl ReferencesWorker<'_> { @@ -148,7 +149,7 @@ impl ReferencesWorker<'_> { } // todo: references of package - fn module_path(&self) -> &Interned { + fn module_path(&self) -> &StrRef { self.module_path.get_or_init(|| { self.def .decl diff --git a/crates/tinymist-query/src/rename.rs b/crates/tinymist-query/src/rename.rs index 1ee55f57..a7352c0b 100644 --- a/crates/tinymist-query/src/rename.rs +++ b/crates/tinymist-query/src/rename.rs @@ -9,13 +9,13 @@ use typst::{ syntax::Span, }; +use crate::adt::interner::Interned; use crate::{ analysis::{get_link_exprs, LinkObject, LinkTarget}, find_references, prelude::*, prepare_renaming, syntax::{first_ancestor_expr, get_index_info, node_ancestors, Decl, RefExpr, SyntaxClass}, - ty::Interned, }; /// The [`textDocument/rename`] request is sent from the client to the server to diff --git a/crates/tinymist-query/src/syntax/docs.rs b/crates/tinymist-query/src/syntax/docs.rs index c3a2c141..3b13566c 100644 --- a/crates/tinymist-query/src/syntax/docs.rs +++ b/crates/tinymist-query/src/syntax/docs.rs @@ -7,16 +7,13 @@ use std::{ use ecow::eco_format; use typst::foundations::{IntoValue, Module, Str, Type}; +use crate::{adt::interner::Interned, StrRef}; +use crate::{adt::snapshot_map::SnapshotMap, analysis::SharedContext}; use crate::{ - adt::snapshot_map::SnapshotMap, - analysis::SharedContext, docs::{convert_docs, identify_pat_docs, identify_tidy_module_docs, UntypedDefDocs, VarDocsT}, prelude::*, syntax::{Decl, DefKind}, - ty::{ - BuiltinTy, DynTypeBounds, InsTy, Interned, PackageId, SigTy, StrRef, Ty, TypeVar, - TypeVarBounds, - }, + ty::{BuiltinTy, DynTypeBounds, InsTy, PackageId, SigTy, Ty, TypeVar, TypeVarBounds}, }; use super::DeclExpr; @@ -35,6 +32,7 @@ pub struct DocString { } impl DocString { + /// Gets the docstring as a variable doc pub fn as_var(&self) -> VarDoc { VarDoc { docs: self.docs.clone().unwrap_or_default(), diff --git a/crates/tinymist-query/src/syntax/expr.rs b/crates/tinymist-query/src/syntax/expr.rs index c505e3d8..82cfb285 100644 --- a/crates/tinymist-query/src/syntax/expr.rs +++ b/crates/tinymist-query/src/syntax/expr.rs @@ -1,9 +1,12 @@ +#![allow(missing_docs)] + use std::ops::DerefMut; use parking_lot::Mutex; use rpds::RedBlackTreeMapSync; use rustc_hash::FxHashMap; use std::ops::Deref; +use tinymist_analysis::adt::interner::Interned; use tinymist_std::hash::hash128; use typst::{ foundations::{Element, NativeElement, Type, Value}, @@ -17,7 +20,7 @@ use crate::{ analysis::{QueryStatGuard, SharedContext}, prelude::*, syntax::{find_module_level_docs, resolve_id_by_path, DefKind}, - ty::{BuiltinTy, InsTy, Interned, Ty}, + ty::{BuiltinTy, InsTy, Ty}, }; use super::{compute_docstring, def::*, DocCommentMatcher, DocString, InterpretMode}; diff --git a/crates/tinymist-query/src/syntax/mod.rs b/crates/tinymist-query/src/syntax/mod.rs index 9848ffb6..7b283b28 100644 --- a/crates/tinymist-query/src/syntax/mod.rs +++ b/crates/tinymist-query/src/syntax/mod.rs @@ -2,12 +2,8 @@ //! //! This module must hide all **AST details** from the rest of the codebase. -// todo: remove this #![allow(missing_docs)] -pub use tinymist_analysis::syntax::comment::*; -pub use tinymist_analysis::syntax::import::*; -pub use tinymist_analysis::syntax::matcher::*; pub(crate) mod lexical_hierarchy; pub use lexical_hierarchy::*; pub(crate) mod module; @@ -16,9 +12,6 @@ pub(crate) mod expr; pub use expr::*; pub(crate) mod docs; pub use docs::*; -pub(crate) mod def; -pub use def::*; -pub(crate) mod repr; -use repr::*; pub(crate) mod index; pub use index::*; +pub use tinymist_analysis::syntax::*;