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:
Myriad-Dreamin 2024-04-11 23:28:32 +08:00 committed by GitHub
parent 0c4a898c97
commit 71ad5aaaed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 1929 additions and 81 deletions

View file

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

View file

@ -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>()?;

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
#(/* position after */ calc.sin(1))

View file

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

View file

@ -3,6 +3,6 @@ source: crates/tinymist-query/src/analysis.rs
expression: CallSnapshot(result.as_deref())
input_file: crates/tinymist-query/src/fixtures/call_info/builtin_poly.typ
---
255 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "red", docs: "The red component.", 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 } }

View file

@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs
expression: CallSnapshot(result.as_deref())
input_file: crates/tinymist-query/src/fixtures/call_info/builtin_poly2.typ
---
"#fff" -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "red", docs: "The red component.", 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 } }

View file

@ -3,5 +3,5 @@ source: crates/tinymist-query/src/analysis.rs
expression: CallSnapshot(result.as_deref())
input_file: crates/tinymist-query/src/fixtures/call_info/user.typ
---
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "x", docs: "", 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 } }

View file

@ -3,5 +3,5 @@ source: crates/tinymist-query/src/analysis.rs
expression: CallSnapshot(result.as_deref())
input_file: crates/tinymist-query/src/fixtures/call_info/user_named.typ
---
y: 1 -> CallParamInfo { kind: Named, is_content_block: false, param: ParamSpec { name: "y", docs: "Default value: none", 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 } }

View file

@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs
expression: CallSnapshot(result.as_deref())
input_file: crates/tinymist-query/src/fixtures/call_info/user_named_with.typ
---
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "x", docs: "", 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 } }

View file

@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs
expression: CallSnapshot(result.as_deref())
input_file: crates/tinymist-query/src/fixtures/call_info/user_named_with2.typ
---
y: 1 -> CallParamInfo { kind: Named, is_content_block: false, param: ParamSpec { name: "y", docs: "Default value: none", 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 } }

View file

@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs
expression: CallSnapshot(result.as_deref())
input_file: crates/tinymist-query/src/fixtures/call_info/user_with.typ
---
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "y", docs: "", 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 } }

View file

@ -0,0 +1,2 @@
#(sys.version /* ident */ );

View file

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

View file

@ -1,2 +1,2 @@
#let x = 1;
#(/* position after */ x);
#(/* position after */ x);

View file

@ -0,0 +1 @@
#let f() = 1;

View file

@ -0,0 +1 @@
#let f(x) = 1 + 1;

View file

@ -0,0 +1,6 @@
#image("test.jpg");
#read("test.jpg");
#json("test.json");
#yaml("test.yaml");
#xml("test.xml");
#toml("test.toml");

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,2 @@
#let f(x) = x;
#let g = f.with(1);

View file

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

View file

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