tinymist_analysis/ty/
describe.rsuse ecow::{eco_format, EcoString};
use tinymist_std::hash::hash128;
use tinymist_world::vfs::WorkspaceResolver;
use typst::foundations::Repr;
use super::{is_plain_value, term_value};
use crate::{ty::prelude::*, upstream::truncated_repr_};
impl Ty {
pub fn repr(&self) -> Option<EcoString> {
let mut worker = TypeDescriber {
repr: true,
..Default::default()
};
worker.describe_root(self)
}
pub fn value_repr(&self) -> Option<EcoString> {
let mut worker = TypeDescriber {
repr: true,
value: true,
..Default::default()
};
worker.describe_root(self)
}
pub fn describe(&self) -> Option<EcoString> {
let mut worker = TypeDescriber::default();
worker.describe_root(self)
}
}
#[derive(Default)]
struct TypeDescriber {
repr: bool,
value: bool,
described: HashMap<u128, EcoString>,
results: HashSet<EcoString>,
functions: Vec<Interned<SigTy>>,
}
impl TypeDescriber {
fn describe_root(&mut self, ty: &Ty) -> Option<EcoString> {
let _ = TypeDescriber::describe_iter;
if let Some(t) = self.described.get(&hash128(ty)) {
return Some(t.clone());
}
let res = self.describe(ty);
if !res.is_empty() {
return Some(res);
}
self.described.insert(hash128(ty), "$self".into());
let mut results = std::mem::take(&mut self.results)
.into_iter()
.collect::<Vec<_>>();
let functions = std::mem::take(&mut self.functions);
if !functions.is_empty() {
let func = functions[0].clone();
let mut res = EcoString::new();
res.push('(');
let mut not_first = false;
for ty in func.positional_params() {
if not_first {
res.push_str(", ");
} else {
not_first = true;
}
res.push_str(self.describe_root(ty).as_deref().unwrap_or("any"));
}
for (name, ty) in func.named_params() {
if not_first {
res.push_str(", ");
} else {
not_first = true;
}
res.push_str(name);
res.push_str(": ");
res.push_str(self.describe_root(ty).as_deref().unwrap_or("any"));
}
if let Some(spread_right) = func.rest_param() {
if not_first {
res.push_str(", ");
}
res.push_str("..: ");
res.push_str(self.describe_root(spread_right).as_deref().unwrap_or("any"));
}
res.push_str(") => ");
res.push_str(
func.body
.as_ref()
.and_then(|ret| self.describe_root(ret))
.as_deref()
.unwrap_or("any"),
);
results.push(res);
}
if results.is_empty() {
self.described.insert(hash128(ty), "any".into());
return None;
}
results.sort();
results.dedup();
let res: EcoString = results.join(" | ").into();
self.described.insert(hash128(ty), res.clone());
Some(res)
}
fn describe_iter(&mut self, ty: &[Ty]) {
for ty in ty.iter() {
let desc = self.describe(ty);
if !desc.is_empty() {
self.results.insert(desc);
}
}
}
fn describe(&mut self, ty: &Ty) -> EcoString {
match ty {
Ty::Var(..) => {}
Ty::Union(types) => {
self.describe_iter(types);
}
Ty::Let(bounds) => {
self.describe_iter(&bounds.lbs);
self.describe_iter(&bounds.ubs);
}
Ty::Func(func) => {
self.functions.push(func.clone());
}
Ty::Dict(..) => {
return "dictionary".into();
}
Ty::Tuple(..) => {
return "array".into();
}
Ty::Array(..) => {
return "array".into();
}
Ty::With(w) => {
return self.describe(&w.sig);
}
Ty::Builtin(BuiltinTy::Content(Some(elem))) => {
return elem.name().into();
}
Ty::Builtin(BuiltinTy::Content(None) | BuiltinTy::Space) => {
return "content".into();
}
Ty::Any | Ty::Builtin(BuiltinTy::Clause | BuiltinTy::Undef | BuiltinTy::Infer) => {}
Ty::Builtin(BuiltinTy::FlowNone | BuiltinTy::None) => {
return "none".into();
}
Ty::Builtin(BuiltinTy::Auto) => {
return "auto".into();
}
Ty::Boolean(..) if self.repr => {
return "bool".into();
}
Ty::Boolean(None) => {
return "bool".into();
}
Ty::Boolean(Some(b)) => {
return eco_format!("{b}");
}
Ty::Builtin(b) => {
return b.describe();
}
Ty::Value(v) if matches!(v.val, Value::Module(..)) => {
let Value::Module(m) = &v.val else {
return "module".into();
};
return match (m.name(), m.file_id()) {
(Some(name), ..) => eco_format!("module({name:?})"),
(None, file_id) => {
eco_format!("module({:?})", WorkspaceResolver::display(file_id))
}
};
}
Ty::Value(v) if !is_plain_value(&v.val) => return self.describe(&term_value(&v.val)),
Ty::Value(v) if self.value => return truncated_repr_::<181>(&v.val),
Ty::Value(v) if self.repr => return v.val.ty().short_name().into(),
Ty::Value(v) => return v.val.repr(),
Ty::Param(..) => {
return "param".into();
}
Ty::Args(..) => {
return "arguments".into();
}
Ty::Pattern(..) => {
return "pattern".into();
}
Ty::Select(..) | Ty::Unary(..) | Ty::Binary(..) | Ty::If(..) => return "any".into(),
}
EcoString::new()
}
}