mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-24 05:05:00 +00:00
dev: introduce basic type checker (#183)
* dev: draft type checker v1 * dev: use check_in_mode * dev: refactor entries * dev: change name * update test * dev: clean up playground * dev: clean tests * dev: update snapshot * fix: bad refactor * dev: update snapshot
This commit is contained in:
parent
0c4a898c97
commit
71ad5aaaed
29 changed files with 1929 additions and 81 deletions
|
@ -12,6 +12,8 @@ pub mod linked_def;
|
|||
pub use linked_def::*;
|
||||
pub mod signature;
|
||||
pub use signature::*;
|
||||
pub mod r#type;
|
||||
pub(crate) use r#type::*;
|
||||
pub mod track_values;
|
||||
pub use track_values::*;
|
||||
mod prelude;
|
||||
|
@ -19,6 +21,73 @@ mod prelude;
|
|||
mod global;
|
||||
pub use global::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod type_check_tests {
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use typst::syntax::Source;
|
||||
|
||||
use crate::analysis::type_check;
|
||||
use crate::tests::*;
|
||||
|
||||
use super::TypeCheckInfo;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
snapshot_testing("type_check", &|ctx, path| {
|
||||
let source = ctx.source_by_path(&path).unwrap();
|
||||
|
||||
let result = type_check(ctx, source.clone());
|
||||
let result = result
|
||||
.as_deref()
|
||||
.map(|e| format!("{:#?}", TypeCheckSnapshot(&source, e)));
|
||||
let result = result.as_deref().unwrap_or("<nil>");
|
||||
|
||||
assert_snapshot!(result);
|
||||
});
|
||||
}
|
||||
|
||||
struct TypeCheckSnapshot<'a>(&'a Source, &'a TypeCheckInfo);
|
||||
|
||||
impl fmt::Debug for TypeCheckSnapshot<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let source = self.0;
|
||||
let info = self.1;
|
||||
let mut vars = info
|
||||
.vars
|
||||
.iter()
|
||||
.map(|e| (e.1.name(), e.1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
vars.sort_by(|x, y| x.0.cmp(&y.0));
|
||||
|
||||
for (name, var) in vars {
|
||||
writeln!(f, "{:?} = {:?}", name, info.simplify(var.get_ref()))?;
|
||||
}
|
||||
|
||||
writeln!(f, "---")?;
|
||||
let mut mapping = info
|
||||
.mapping
|
||||
.iter()
|
||||
.map(|e| (source.range(*e.0).unwrap_or_default(), e.1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
mapping.sort_by(|x, y| {
|
||||
x.0.start
|
||||
.cmp(&y.0.start)
|
||||
.then_with(|| x.0.end.cmp(&y.0.end))
|
||||
});
|
||||
|
||||
for (range, value) in mapping {
|
||||
writeln!(f, "{range:?} -> {value:?}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod module_tests {
|
||||
use reflexo::path::unix_slash;
|
||||
|
@ -283,7 +352,7 @@ mod signature_tests {
|
|||
write!(f, "with ")?;
|
||||
for arg in &w.items {
|
||||
if let Some(name) = &arg.name {
|
||||
write!(f, "{}: ", name)?;
|
||||
write!(f, "{name}: ")?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
|
@ -291,7 +360,7 @@ mod signature_tests {
|
|||
arg.value.as_ref().map(|v| v.repr()).unwrap_or_default()
|
||||
)?;
|
||||
}
|
||||
writeln!(f, "")?;
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
|
||||
&sig.signature
|
||||
|
|
|
@ -43,10 +43,10 @@ pub struct CallInfo {
|
|||
|
||||
// todo: cache call
|
||||
/// Analyzes a function call.
|
||||
pub fn analyze_call<'a>(
|
||||
pub fn analyze_call(
|
||||
ctx: &mut AnalysisContext,
|
||||
source: Source,
|
||||
node: LinkedNode<'a>,
|
||||
node: LinkedNode,
|
||||
) -> Option<Arc<CallInfo>> {
|
||||
trace!("func call found: {:?}", node);
|
||||
let f = node.cast::<ast::FuncCall>()?;
|
||||
|
|
|
@ -11,6 +11,7 @@ use once_cell::sync::OnceCell;
|
|||
use parking_lot::RwLock;
|
||||
use reflexo::hash::hash128;
|
||||
use reflexo::{cow_mut::CowMut, debug_loc::DataSource, ImmutPath};
|
||||
use typst::eval::Eval;
|
||||
use typst::foundations;
|
||||
use typst::{
|
||||
diag::{eco_format, FileError, FileResult, PackageError},
|
||||
|
@ -20,7 +21,7 @@ use typst::{
|
|||
use typst::{foundations::Value, syntax::ast, text::Font};
|
||||
use typst::{layout::Position, syntax::FileId as TypstFileId};
|
||||
|
||||
use super::{DefUseInfo, ImportInfo, Signature, SignatureTarget};
|
||||
use super::{DefUseInfo, ImportInfo, Signature, SignatureTarget, TypeCheckInfo};
|
||||
use crate::{
|
||||
lsp_to_typst,
|
||||
syntax::{
|
||||
|
@ -35,6 +36,7 @@ use crate::{
|
|||
#[derive(Default)]
|
||||
pub struct ModuleAnalysisCache {
|
||||
source: OnceCell<FileResult<Source>>,
|
||||
top_level_eval: OnceCell<Option<Arc<TypeCheckInfo>>>,
|
||||
def_use: OnceCell<Option<Arc<DefUseInfo>>>,
|
||||
}
|
||||
|
||||
|
@ -58,6 +60,19 @@ impl ModuleAnalysisCache {
|
|||
) -> Option<Arc<DefUseInfo>> {
|
||||
self.def_use.get_or_init(f).clone()
|
||||
}
|
||||
|
||||
/// Try to get the top-level evaluation information of a file.
|
||||
pub(crate) fn type_check(&self) -> Option<Arc<TypeCheckInfo>> {
|
||||
self.top_level_eval.get().cloned().flatten()
|
||||
}
|
||||
|
||||
/// Compute the top-level evaluation information of a file.
|
||||
pub(crate) fn compute_type_check(
|
||||
&self,
|
||||
f: impl FnOnce() -> Option<Arc<TypeCheckInfo>>,
|
||||
) -> Option<Arc<TypeCheckInfo>> {
|
||||
self.top_level_eval.get_or_init(f).clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// The analysis data holds globally.
|
||||
|
@ -195,9 +210,10 @@ impl<Inputs, Output> ComputingNode<Inputs, Output> {
|
|||
#[allow(clippy::type_complexity)]
|
||||
pub struct ModuleAnalysisGlobalCache {
|
||||
def_use_lexical_hierarchy: ComputingNode<Source, EcoVec<LexicalHierarchy>>,
|
||||
import: Arc<ComputingNode<EcoVec<LexicalHierarchy>, Arc<ImportInfo>>>,
|
||||
type_check: Arc<ComputingNode<Source, Arc<TypeCheckInfo>>>,
|
||||
def_use: Arc<ComputingNode<(EcoVec<LexicalHierarchy>, Arc<ImportInfo>), Arc<DefUseInfo>>>,
|
||||
|
||||
import: Arc<ComputingNode<EcoVec<LexicalHierarchy>, Arc<ImportInfo>>>,
|
||||
signature_source: Option<Source>,
|
||||
signatures: HashMap<usize, Signature>,
|
||||
}
|
||||
|
@ -206,6 +222,7 @@ impl Default for ModuleAnalysisGlobalCache {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
def_use_lexical_hierarchy: ComputingNode::new("def_use_lexical_hierarchy"),
|
||||
type_check: Arc::new(ComputingNode::new("type_check")),
|
||||
import: Arc::new(ComputingNode::new("import")),
|
||||
def_use: Arc::new(ComputingNode::new("def_use")),
|
||||
|
||||
|
@ -462,6 +479,34 @@ impl<'w> AnalysisContext<'w> {
|
|||
typst_to_lsp::range(position, src, self.analysis.position_encoding)
|
||||
}
|
||||
|
||||
/// Get the type check information of a source file.
|
||||
pub(crate) fn type_check(&mut self, source: Source) -> Option<Arc<TypeCheckInfo>> {
|
||||
let fid = source.id();
|
||||
|
||||
if let Some(res) = self.caches.modules.entry(fid).or_default().type_check() {
|
||||
return Some(res);
|
||||
}
|
||||
|
||||
let cache = self.at_module(fid);
|
||||
|
||||
let tl = cache.type_check.clone();
|
||||
let res = tl
|
||||
.compute(source, |_before, after| {
|
||||
let next = crate::analysis::type_check(self, after);
|
||||
next.or_else(|| tl.output.read().clone())
|
||||
})
|
||||
.ok()
|
||||
.flatten();
|
||||
|
||||
self.caches
|
||||
.modules
|
||||
.entry(fid)
|
||||
.or_default()
|
||||
.compute_type_check(|| res.clone());
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Get the def-use information of a source file.
|
||||
pub fn def_use(&mut self, source: Source) -> Option<Arc<DefUseInfo>> {
|
||||
let fid = source.id();
|
||||
|
@ -512,6 +557,35 @@ impl<'w> AnalysisContext<'w> {
|
|||
self.analysis.caches.modules.entry(fid).or_default()
|
||||
}
|
||||
|
||||
pub(crate) fn with_vm<T>(&self, f: impl FnOnce(&mut typst::eval::Vm) -> T) -> T {
|
||||
use comemo::Track;
|
||||
use typst::engine::*;
|
||||
use typst::eval::*;
|
||||
use typst::foundations::*;
|
||||
use typst::introspection::*;
|
||||
|
||||
let mut locator = Locator::default();
|
||||
let introspector = Introspector::default();
|
||||
let mut tracer = Tracer::new();
|
||||
let engine = Engine {
|
||||
world: self.world().track(),
|
||||
route: Route::default(),
|
||||
introspector: introspector.track(),
|
||||
locator: &mut locator,
|
||||
tracer: tracer.track_mut(),
|
||||
};
|
||||
|
||||
let context = Context::none();
|
||||
let mut vm = Vm::new(
|
||||
engine,
|
||||
context.track(),
|
||||
Scopes::new(Some(self.world().library())),
|
||||
Span::detached(),
|
||||
);
|
||||
|
||||
f(&mut vm)
|
||||
}
|
||||
|
||||
pub(crate) fn mini_eval(&self, rr: ast::Expr<'_>) -> Option<Value> {
|
||||
Some(match rr {
|
||||
ast::Expr::None(_) => Value::None,
|
||||
|
@ -521,34 +595,7 @@ impl<'w> AnalysisContext<'w> {
|
|||
ast::Expr::Float(v) => Value::Float(v.get()),
|
||||
ast::Expr::Numeric(v) => Value::numeric(v.get()),
|
||||
ast::Expr::Str(v) => Value::Str(v.get().into()),
|
||||
e => {
|
||||
use comemo::Track;
|
||||
use typst::engine::*;
|
||||
use typst::eval::*;
|
||||
use typst::foundations::*;
|
||||
use typst::introspection::*;
|
||||
|
||||
let mut locator = Locator::default();
|
||||
let introspector = Introspector::default();
|
||||
let mut tracer = Tracer::new();
|
||||
let engine = Engine {
|
||||
world: self.world().track(),
|
||||
route: Route::default(),
|
||||
introspector: introspector.track(),
|
||||
locator: &mut locator,
|
||||
tracer: tracer.track_mut(),
|
||||
};
|
||||
|
||||
let context = Context::none();
|
||||
let mut vm = Vm::new(
|
||||
engine,
|
||||
context.track(),
|
||||
Scopes::new(Some(self.world().library())),
|
||||
Span::detached(),
|
||||
);
|
||||
|
||||
return e.eval(&mut vm).ok();
|
||||
}
|
||||
e => return self.with_vm(|vm| e.eval(vm).ok()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ pub fn find_definition(
|
|||
}
|
||||
|
||||
let Some((def_fid, def)) = def_info else {
|
||||
return resolve_global(ctx, use_site.clone()).and_then(move |f| {
|
||||
return resolve_global_value(ctx, use_site.clone(), false).and_then(move |f| {
|
||||
value_to_def(
|
||||
ctx,
|
||||
f,
|
||||
|
@ -209,7 +209,7 @@ pub fn resolve_callee(ctx: &mut AnalysisContext, callee: LinkedNode) -> Option<F
|
|||
}
|
||||
})
|
||||
.or_else(|| {
|
||||
resolve_global(ctx, callee).and_then(|v| match v {
|
||||
resolve_global_value(ctx, callee, false).and_then(|v| match v {
|
||||
Value::Func(f) => Some(f),
|
||||
_ => None,
|
||||
})
|
||||
|
@ -217,12 +217,21 @@ pub fn resolve_callee(ctx: &mut AnalysisContext, callee: LinkedNode) -> Option<F
|
|||
}
|
||||
|
||||
// todo: math scope
|
||||
fn resolve_global(ctx: &AnalysisContext, callee: LinkedNode) -> Option<Value> {
|
||||
pub(crate) fn resolve_global_value(
|
||||
ctx: &AnalysisContext,
|
||||
callee: LinkedNode,
|
||||
is_math: bool,
|
||||
) -> Option<Value> {
|
||||
let lib = ctx.world().library();
|
||||
let scope = if is_math {
|
||||
lib.math.scope()
|
||||
} else {
|
||||
lib.global.scope()
|
||||
};
|
||||
let v = match callee.cast::<ast::Expr>()? {
|
||||
ast::Expr::Ident(ident) => lib.global.scope().get(&ident)?,
|
||||
ast::Expr::Ident(ident) => scope.get(&ident)?,
|
||||
ast::Expr::FieldAccess(access) => match access.target() {
|
||||
ast::Expr::Ident(target) => match lib.global.scope().get(&target)? {
|
||||
ast::Expr::Ident(target) => match scope.get(&target)? {
|
||||
Value::Module(module) => module.field(&access.field()).ok()?,
|
||||
Value::Func(func) => func.field(&access.field()).ok()?,
|
||||
_ => return None,
|
||||
|
|
|
@ -15,8 +15,14 @@ use typst::{
|
|||
util::LazyHash,
|
||||
};
|
||||
|
||||
use crate::analysis::resolve_callee;
|
||||
use crate::syntax::{get_def_target, get_deref_target, DefTarget};
|
||||
use crate::AnalysisContext;
|
||||
|
||||
use super::{
|
||||
find_definition, DefinitionLink, FlowType, FlowVar, LexicalKind, LexicalVarKind, TypeCheckInfo,
|
||||
};
|
||||
|
||||
// pub fn analyze_signature
|
||||
|
||||
/// Describes a function parameter.
|
||||
|
@ -28,6 +34,8 @@ pub struct ParamSpec {
|
|||
pub docs: Cow<'static, str>,
|
||||
/// Describe what values this parameter accepts.
|
||||
pub input: CastInfo,
|
||||
/// Inferred type of the parameter.
|
||||
pub(crate) infer_type: Option<FlowType>,
|
||||
/// The parameter's default name as type.
|
||||
pub type_repr: Option<EcoString>,
|
||||
/// The parameter's default name as value.
|
||||
|
@ -48,18 +56,19 @@ pub struct ParamSpec {
|
|||
}
|
||||
|
||||
impl ParamSpec {
|
||||
fn from_static(s: &ParamInfo) -> Arc<Self> {
|
||||
fn from_static(f: &Func, p: &ParamInfo) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
name: Cow::Borrowed(s.name),
|
||||
docs: Cow::Borrowed(s.docs),
|
||||
input: s.input.clone(),
|
||||
type_repr: Some(eco_format!("{}", TypeExpr(&s.input))),
|
||||
name: Cow::Borrowed(p.name),
|
||||
docs: Cow::Borrowed(p.docs),
|
||||
input: p.input.clone(),
|
||||
infer_type: FlowType::from_param_site(f, p, &p.input),
|
||||
type_repr: Some(eco_format!("{}", TypeExpr(&p.input))),
|
||||
expr: None,
|
||||
default: s.default,
|
||||
positional: s.positional,
|
||||
named: s.named,
|
||||
variadic: s.variadic,
|
||||
settable: s.settable,
|
||||
default: p.default,
|
||||
positional: p.positional,
|
||||
named: p.named,
|
||||
variadic: p.variadic,
|
||||
settable: p.settable,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -102,6 +111,8 @@ pub struct PrimarySignature {
|
|||
pub has_fill_or_size_or_stroke: bool,
|
||||
/// The rest parameter.
|
||||
pub rest: Option<Arc<ParamSpec>>,
|
||||
/// The return type.
|
||||
pub(crate) ret_ty: Option<FlowType>,
|
||||
_broken: bool,
|
||||
}
|
||||
|
||||
|
@ -149,11 +160,11 @@ pub enum SignatureTarget<'a> {
|
|||
Runtime(Func),
|
||||
}
|
||||
|
||||
pub(crate) fn analyze_signature(ctx: &mut AnalysisContext, func: Func) -> Signature {
|
||||
pub(crate) fn analyze_dyn_signature(ctx: &mut AnalysisContext, func: Func) -> Signature {
|
||||
ctx.analysis
|
||||
.caches
|
||||
.compute_signature(None, SignatureTarget::Runtime(func.clone()), || {
|
||||
Signature::Primary(analyze_dyn_signature(func))
|
||||
Signature::Primary(analyze_dyn_signature_inner(func))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -162,19 +173,32 @@ pub(crate) fn analyze_signature_v2(
|
|||
source: Source,
|
||||
callee_node: SignatureTarget,
|
||||
) -> Option<Signature> {
|
||||
if let Some(sig) = ctx.analysis.caches.signature(Some(source), &callee_node) {
|
||||
if let Some(sig) = ctx
|
||||
.analysis
|
||||
.caches
|
||||
.signature(Some(source.clone()), &callee_node)
|
||||
{
|
||||
return Some(sig);
|
||||
}
|
||||
|
||||
let func = match callee_node {
|
||||
SignatureTarget::Syntax(node) => {
|
||||
let values = crate::analysis::analyze_expr(ctx.world(), &node);
|
||||
let func = values.into_iter().find_map(|v| match v.0 {
|
||||
Value::Func(f) => Some(f),
|
||||
_ => None,
|
||||
})?;
|
||||
log::debug!("got function {func:?}");
|
||||
let _ = resolve_callee_v2;
|
||||
|
||||
// let res = resolve_callee_v2(ctx, node)?;
|
||||
|
||||
// let func = match res {
|
||||
// TryResolveCalleeResult::Syntax(lnk) => {
|
||||
// println!("Syntax {:?}", lnk.name);
|
||||
|
||||
// return analyze_static_signature(ctx, source, lnk);
|
||||
// }
|
||||
// TryResolveCalleeResult::Runtime(func) => func,
|
||||
// };
|
||||
|
||||
let func = resolve_callee(ctx, node)?;
|
||||
|
||||
log::debug!("got function {func:?}");
|
||||
func
|
||||
}
|
||||
SignatureTarget::Runtime(func) => func,
|
||||
|
@ -203,7 +227,7 @@ pub(crate) fn analyze_signature_v2(
|
|||
.analysis
|
||||
.caches
|
||||
.compute_signature(None, SignatureTarget::Runtime(func.clone()), || {
|
||||
Signature::Primary(analyze_dyn_signature(func))
|
||||
Signature::Primary(analyze_dyn_signature_inner(func))
|
||||
})
|
||||
.primary()
|
||||
.clone();
|
||||
|
@ -219,14 +243,94 @@ pub(crate) fn analyze_signature_v2(
|
|||
})))
|
||||
}
|
||||
|
||||
pub(crate) fn analyze_dyn_signature(func: Func) -> Arc<PrimarySignature> {
|
||||
// fn analyze_static_signature(
|
||||
// ctx: &mut AnalysisContext<'_>,
|
||||
// source: Source,
|
||||
// lnk: DefinitionLink,
|
||||
// ) -> Option<Signature> {
|
||||
// let def_at = lnk.def_at?;
|
||||
// let def_source = if def_at.0 == source.id() {
|
||||
// source.clone()
|
||||
// } else {
|
||||
// ctx.source_by_id(def_at.0).ok()?
|
||||
// };
|
||||
|
||||
// let root = LinkedNode::new(def_source.root());
|
||||
// let def_node = root.leaf_at(def_at.1.start + 1)?;
|
||||
// let def_node = get_def_target(def_node)?;
|
||||
// let def_node = match def_node {
|
||||
// DefTarget::Let(node) => node,
|
||||
// DefTarget::Import(_) => return None,
|
||||
// };
|
||||
|
||||
// println!("def_node {def_node:?}");
|
||||
|
||||
// None
|
||||
// }
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum TryResolveCalleeResult {
|
||||
Syntax(DefinitionLink),
|
||||
Runtime(Func),
|
||||
}
|
||||
|
||||
/// Resolve a callee expression to a function but prefer to keep static.
|
||||
fn resolve_callee_v2(
|
||||
ctx: &mut AnalysisContext,
|
||||
callee: LinkedNode,
|
||||
) -> Option<TryResolveCalleeResult> {
|
||||
let source = ctx.source_by_id(callee.span().id()?).ok()?;
|
||||
let node = source.find(callee.span())?;
|
||||
let cursor = node.offset();
|
||||
let deref_target = get_deref_target(node, cursor)?;
|
||||
let def = find_definition(ctx, source.clone(), deref_target)?;
|
||||
if let LexicalKind::Var(LexicalVarKind::Function) = def.kind {
|
||||
if let Some(Value::Func(f)) = def.value {
|
||||
return Some(TryResolveCalleeResult::Runtime(f));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(def_at) = &def.def_at {
|
||||
let def_source = if def_at.0 == source.id() {
|
||||
source.clone()
|
||||
} else {
|
||||
ctx.source_by_id(def_at.0).ok()?
|
||||
};
|
||||
|
||||
let _t = ctx.type_check(source)?;
|
||||
let _ = FlowVar::name;
|
||||
let _ = FlowVar::id;
|
||||
let _ = TypeCheckInfo::simplify;
|
||||
|
||||
let root = LinkedNode::new(def_source.root());
|
||||
let def_node = root.leaf_at(def_at.1.start + 1)?;
|
||||
let def_node = get_def_target(def_node)?;
|
||||
let _def_node = match def_node {
|
||||
DefTarget::Let(node) => node,
|
||||
DefTarget::Import(_) => return None,
|
||||
};
|
||||
}
|
||||
|
||||
Some(TryResolveCalleeResult::Syntax(def))
|
||||
}
|
||||
|
||||
fn analyze_dyn_signature_inner(func: Func) -> Arc<PrimarySignature> {
|
||||
use typst::foundations::func::Repr;
|
||||
let params = match func.inner() {
|
||||
let (params, ret_ty) = match func.inner() {
|
||||
Repr::With(..) => unreachable!(),
|
||||
Repr::Closure(c) => analyze_closure_signature(c.clone()),
|
||||
Repr::Closure(c) => (analyze_closure_signature(c.clone()), None),
|
||||
Repr::Element(..) | Repr::Native(..) => {
|
||||
let ret_ty = func
|
||||
.returns()
|
||||
.and_then(|r| FlowType::from_return_site(&func, r));
|
||||
let params = func.params().unwrap();
|
||||
params.iter().map(ParamSpec::from_static).collect()
|
||||
(
|
||||
params
|
||||
.iter()
|
||||
.map(|p| ParamSpec::from_static(&func, p))
|
||||
.collect(),
|
||||
ret_ty,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -272,6 +376,7 @@ pub(crate) fn analyze_dyn_signature(func: Func) -> Arc<PrimarySignature> {
|
|||
pos,
|
||||
named,
|
||||
rest,
|
||||
ret_ty,
|
||||
has_fill_or_size_or_stroke: has_fill || has_stroke || has_size,
|
||||
_broken: broken,
|
||||
})
|
||||
|
@ -294,6 +399,7 @@ fn analyze_closure_signature(c: Arc<LazyHash<Closure>>) -> Vec<Arc<ParamSpec>> {
|
|||
params.push(Arc::new(ParamSpec {
|
||||
name: Cow::Borrowed("_"),
|
||||
input: CastInfo::Any,
|
||||
infer_type: None,
|
||||
type_repr: None,
|
||||
expr: None,
|
||||
default: None,
|
||||
|
@ -315,6 +421,7 @@ fn analyze_closure_signature(c: Arc<LazyHash<Closure>>) -> Vec<Arc<ParamSpec>> {
|
|||
params.push(Arc::new(ParamSpec {
|
||||
name: Cow::Owned(name.to_owned()),
|
||||
input: CastInfo::Any,
|
||||
infer_type: None,
|
||||
type_repr: None,
|
||||
expr: None,
|
||||
default: None,
|
||||
|
@ -331,6 +438,7 @@ fn analyze_closure_signature(c: Arc<LazyHash<Closure>>) -> Vec<Arc<ParamSpec>> {
|
|||
params.push(Arc::new(ParamSpec {
|
||||
name: Cow::Owned(n.name().as_str().to_owned()),
|
||||
input: CastInfo::Any,
|
||||
infer_type: None,
|
||||
type_repr: Some(expr.clone()),
|
||||
expr: Some(expr.clone()),
|
||||
default: None,
|
||||
|
@ -346,6 +454,7 @@ fn analyze_closure_signature(c: Arc<LazyHash<Closure>>) -> Vec<Arc<ParamSpec>> {
|
|||
params.push(Arc::new(ParamSpec {
|
||||
name: Cow::Owned(ident.unwrap_or_default().to_owned()),
|
||||
input: CastInfo::Any,
|
||||
infer_type: None,
|
||||
type_repr: None,
|
||||
expr: None,
|
||||
default: None,
|
||||
|
|
1537
crates/tinymist-query/src/analysis/type.rs
Normal file
1537
crates/tinymist-query/src/analysis/type.rs
Normal file
File diff suppressed because it is too large
Load diff
1
crates/tinymist-query/src/fixtures/call_info/builtin.typ
Normal file
1
crates/tinymist-query/src/fixtures/call_info/builtin.typ
Normal file
|
@ -0,0 +1 @@
|
|||
#(/* position after */ calc.sin(1))
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
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.", input: Union([Type(Type(integer)), Type(Type(float)), Type(Type(angle))]), infer_type: Some((Type(integer) | Type(float) | Type(angle))), type_repr: Some("int | float | angle"), expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
|
@ -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.", input: Union([Type(Type(integer)), Type(Type(ratio))]), type_repr: Some("int | ratio"), expr: None, default: 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.", input: Union([Type(Type(integer)), Type(Type(ratio))]), type_repr: Some("int | ratio"), expr: None, default: 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.", input: Union([Type(Type(integer)), Type(Type(ratio))]), type_repr: Some("int | ratio"), expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
255 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "red", docs: "The red component.", input: Union([Type(Type(integer)), Type(Type(ratio))]), infer_type: Some((Type(integer) | Type(ratio))), type_repr: Some("int | ratio"), expr: None, default: 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.", input: Union([Type(Type(integer)), Type(Type(ratio))]), infer_type: Some((Type(integer) | Type(ratio))), type_repr: Some("int | ratio"), expr: None, default: 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.", input: Union([Type(Type(integer)), Type(Type(ratio))]), infer_type: Some((Type(integer) | Type(ratio))), type_repr: Some("int | ratio"), expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
|
|
|
@ -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.", input: Union([Type(Type(integer)), Type(Type(ratio))]), type_repr: Some("int | ratio"), expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
"#fff" -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "red", docs: "The red component.", input: Union([Type(Type(integer)), Type(Type(ratio))]), infer_type: Some((Type(integer) | Type(ratio))), type_repr: Some("int | ratio"), expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
|
|
|
@ -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: "", input: Any, type_repr: None, expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "y", docs: "", input: Any, type_repr: None, expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "x", docs: "", input: Any, infer_type: None, type_repr: None, expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "y", docs: "", input: Any, infer_type: None, type_repr: None, expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
|
|
|
@ -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", input: Any, type_repr: Some("none"), expr: Some("none"), default: None, positional: false, named: true, variadic: false, settable: true } }
|
||||
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "x", docs: "", input: Any, type_repr: None, expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
y: 1 -> CallParamInfo { kind: Named, is_content_block: false, param: ParamSpec { name: "y", docs: "Default value: none", input: Any, infer_type: None, type_repr: Some("none"), expr: Some("none"), default: None, positional: false, named: true, variadic: false, settable: true } }
|
||||
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "x", docs: "", input: Any, infer_type: None, type_repr: None, expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
|
|
|
@ -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: "", input: Any, type_repr: None, expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "x", docs: "", input: Any, infer_type: None, type_repr: None, expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
|
|
|
@ -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", input: Any, type_repr: Some("none"), expr: Some("none"), default: None, positional: false, named: true, variadic: false, settable: true } }
|
||||
y: 1 -> CallParamInfo { kind: Named, is_content_block: false, param: ParamSpec { name: "y", docs: "Default value: none", input: Any, infer_type: None, type_repr: Some("none"), expr: Some("none"), default: None, positional: false, named: true, variadic: false, settable: true } }
|
||||
|
|
|
@ -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: "", input: Any, type_repr: None, expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "y", docs: "", input: Any, infer_type: None, type_repr: None, expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
#(sys.version /* ident */ );
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/hover.rs
|
||||
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
|
||||
input_file: crates/tinymist-query/src/fixtures/hover/builtin_var3.typ
|
||||
---
|
||||
{
|
||||
"contents": "```typc\n// Values\n<module sys>\n```\n---\n```typc\nlet sys;\n```",
|
||||
"range": "0:2:0:5"
|
||||
}
|
|
@ -1,2 +1,2 @@
|
|||
#let x = 1;
|
||||
#(/* position after */ x);
|
||||
#(/* position after */ x);
|
1
crates/tinymist-query/src/fixtures/type_check/base.typ
Normal file
1
crates/tinymist-query/src/fixtures/type_check/base.typ
Normal file
|
@ -0,0 +1 @@
|
|||
#let f() = 1;
|
|
@ -0,0 +1 @@
|
|||
#let f(x) = 1 + 1;
|
6
crates/tinymist-query/src/fixtures/type_check/infer.typ
Normal file
6
crates/tinymist-query/src/fixtures/type_check/infer.typ
Normal file
|
@ -0,0 +1,6 @@
|
|||
#image("test.jpg");
|
||||
#read("test.jpg");
|
||||
#json("test.json");
|
||||
#yaml("test.yaml");
|
||||
#xml("test.xml");
|
||||
#toml("test.toml");
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/type_check/base.typ
|
||||
---
|
||||
"f" = () -> 1
|
||||
---
|
||||
5..6 -> @f
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/type_check/constants.typ
|
||||
---
|
||||
"f" = (Any) -> 2
|
||||
"x" = Any
|
||||
---
|
||||
5..6 -> @f
|
||||
7..8 -> @x
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/type_check/infer.typ
|
||||
---
|
||||
---
|
||||
1..18 -> Element(image)
|
||||
7..17 -> Path(Image)
|
||||
21..37 -> (Type(string) | Type(bytes))
|
||||
26..36 -> Path(None)
|
||||
40..57 -> Any
|
||||
45..56 -> Path(Json)
|
||||
60..77 -> Any
|
||||
65..76 -> Path(Yaml)
|
||||
80..95 -> Any
|
||||
84..94 -> Path(Xml)
|
||||
98..115 -> Any
|
||||
103..114 -> Path(Toml)
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/type_check/with.typ
|
||||
---
|
||||
"f" = (Any) -> Any
|
||||
"g" = ((Any) -> Any).with(..[&(1)])
|
||||
"x" = ⪰ Any | 1
|
||||
---
|
||||
5..6 -> @f
|
||||
7..8 -> @x
|
||||
20..21 -> @g
|
||||
24..33 -> ((@x) -> @x).with(..[&(1)])
|
2
crates/tinymist-query/src/fixtures/type_check/with.typ
Normal file
2
crates/tinymist-query/src/fixtures/type_check/with.typ
Normal file
|
@ -0,0 +1,2 @@
|
|||
#let f(x) = x;
|
||||
#let g = f.with(1);
|
|
@ -1,7 +1,7 @@
|
|||
use core::fmt;
|
||||
|
||||
use crate::{
|
||||
analysis::{analyze_signature, find_definition, DefinitionLink, Signature},
|
||||
analysis::{analyze_dyn_signature, find_definition, DefinitionLink, Signature},
|
||||
jump_from_cursor,
|
||||
prelude::*,
|
||||
syntax::{find_document_before, get_deref_target, LexicalKind, LexicalVarKind},
|
||||
|
@ -137,7 +137,7 @@ fn def_tooltip(
|
|||
| LexicalKind::Heading(..) => None,
|
||||
LexicalKind::Var(LexicalVarKind::Function) => {
|
||||
let sig = if let Some(Value::Func(func)) = &lnk.value {
|
||||
Some(analyze_signature(ctx, func.clone()))
|
||||
Some(analyze_dyn_signature(ctx, func.clone()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@ use typst::foundations::Value;
|
|||
use typst::syntax::ast::AstNode;
|
||||
use typst::syntax::{ast, SyntaxKind};
|
||||
|
||||
use crate::analysis::{analyze_import, analyze_signature, resolve_callee};
|
||||
use crate::analysis::{analyze_dyn_signature, analyze_import, resolve_callee};
|
||||
use crate::upstream::plain_docs_sentence;
|
||||
|
||||
impl<'a, 'w> CompletionContext<'a, 'w> {
|
||||
|
@ -179,7 +179,7 @@ pub fn param_completions<'a>(
|
|||
func = f.0.clone();
|
||||
}
|
||||
|
||||
let signature = analyze_signature(ctx.ctx, func.clone());
|
||||
let signature = analyze_dyn_signature(ctx.ctx, func.clone());
|
||||
|
||||
// Exclude named arguments which are already present.
|
||||
let exclude: Vec<_> = args
|
||||
|
@ -249,7 +249,7 @@ pub fn named_param_value_completions<'a>(
|
|||
func = f.0.clone();
|
||||
}
|
||||
|
||||
let signature = analyze_signature(ctx.ctx, func.clone());
|
||||
let signature = analyze_dyn_signature(ctx.ctx, func.clone());
|
||||
|
||||
let primary_sig = signature.primary();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue