mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-23 12:46:43 +00:00
feat: merge hover and docs function formatter (#683)
* feat: merge hover and docs function formatter * dev: clean up a bit * test: update snapshot
This commit is contained in:
parent
190b7dfd45
commit
957b58687e
19 changed files with 156 additions and 194 deletions
|
|
@ -22,7 +22,7 @@ use crate::analysis::{
|
|||
DefUseInfo, DefinitionLink, DocString, IdentRef, ImportInfo, PathPreference, SigTy, Signature,
|
||||
SignatureTarget, Ty, TypeScheme,
|
||||
};
|
||||
use crate::docs::DocStringKind;
|
||||
use crate::docs::{DocSignature, DocStringKind};
|
||||
use crate::prelude::*;
|
||||
use crate::syntax::{
|
||||
construct_module_dependencies, find_expr_in_import, get_deref_target, resolve_id_by_path,
|
||||
|
|
@ -481,6 +481,17 @@ impl<'w> AnalysisContext<'w> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn docs_signature(
|
||||
&mut self,
|
||||
source: &Source,
|
||||
def_ident: Option<&IdentRef>,
|
||||
runtime_fn: &Value,
|
||||
) -> Option<DocSignature> {
|
||||
let def_use = self.def_use(source.clone())?;
|
||||
let ty_chk = self.type_check(source.clone())?;
|
||||
crate::docs::docs_signature(self, Some(&(def_use, ty_chk)), def_ident, runtime_fn, None)
|
||||
}
|
||||
|
||||
pub(crate) fn compute_docstring(
|
||||
&self,
|
||||
fid: TypstFileId,
|
||||
|
|
|
|||
|
|
@ -34,6 +34,16 @@ pub struct DefinitionLink {
|
|||
pub name_range: Option<Range<usize>>,
|
||||
}
|
||||
|
||||
impl DefinitionLink {
|
||||
/// Convert the definition to an identifier reference.
|
||||
pub fn to_ident_ref(&self) -> Option<IdentRef> {
|
||||
Some(IdentRef {
|
||||
name: self.name.clone(),
|
||||
range: self.name_range.clone()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// todo: field definition
|
||||
/// Finds the definition of a symbol.
|
||||
pub fn find_definition(
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@
|
|||
use core::fmt;
|
||||
use std::{borrow::Cow, collections::HashMap, ops::Range, sync::Arc};
|
||||
|
||||
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
|
||||
use itertools::Itertools;
|
||||
use ecow::{eco_vec, EcoString, EcoVec};
|
||||
use log::trace;
|
||||
use typst::syntax::{FileId as TypstFileId, Source};
|
||||
use typst::{
|
||||
foundations::{CastInfo, Closure, Func, ParamInfo, Repr, Value},
|
||||
foundations::{Closure, Func, ParamInfo, Value},
|
||||
syntax::{
|
||||
ast::{self, AstNode},
|
||||
LinkedNode, Span, SyntaxKind,
|
||||
|
|
@ -20,6 +19,7 @@ use crate::adt::interner::Interned;
|
|||
use crate::analysis::resolve_callee;
|
||||
use crate::syntax::{get_def_target, get_deref_target, DefTarget};
|
||||
use crate::ty::SigTy;
|
||||
use crate::upstream::truncated_repr;
|
||||
use crate::AnalysisContext;
|
||||
|
||||
use super::{find_definition, DefinitionLink, LexicalKind, LexicalVarKind, Ty};
|
||||
|
|
@ -35,12 +35,8 @@ pub struct ParamSpec {
|
|||
pub docs: Cow<'static, str>,
|
||||
/// Inferred type of the parameter.
|
||||
pub(crate) base_type: Ty,
|
||||
/// The parameter's default name as type.
|
||||
pub type_repr: Option<EcoString>,
|
||||
/// The parameter's default name as value.
|
||||
pub expr: Option<EcoString>,
|
||||
/// Creates an instance of the parameter's default value.
|
||||
pub default: Option<fn() -> Value>,
|
||||
/// Is the parameter positional?
|
||||
pub positional: bool,
|
||||
/// Is the parameter named?
|
||||
|
|
@ -60,9 +56,7 @@ impl ParamSpec {
|
|||
name: p.name.into(),
|
||||
docs: Cow::Borrowed(p.docs),
|
||||
base_type: Ty::from_param_site(f, p),
|
||||
type_repr: Some(eco_format!("{}", TypeExpr(&p.input))),
|
||||
expr: None,
|
||||
default: p.default,
|
||||
expr: p.default.map(|d| truncated_repr(&d())),
|
||||
positional: p.positional,
|
||||
named: p.named,
|
||||
variadic: p.variadic,
|
||||
|
|
@ -413,9 +407,8 @@ fn analyze_closure_signature(c: Arc<LazyHash<Closure>>) -> Vec<Arc<ParamSpec>> {
|
|||
params.push(Arc::new(ParamSpec {
|
||||
name: name.as_str().into(),
|
||||
base_type: Ty::Any,
|
||||
type_repr: None,
|
||||
// type_repr: None,
|
||||
expr: None,
|
||||
default: None,
|
||||
positional: true,
|
||||
named: false,
|
||||
variadic: false,
|
||||
|
|
@ -429,9 +422,7 @@ fn analyze_closure_signature(c: Arc<LazyHash<Closure>>) -> Vec<Arc<ParamSpec>> {
|
|||
params.push(Arc::new(ParamSpec {
|
||||
name: n.name().into(),
|
||||
base_type: Ty::Any,
|
||||
type_repr: Some(expr.clone()),
|
||||
expr: Some(expr.clone()),
|
||||
default: None,
|
||||
positional: false,
|
||||
named: true,
|
||||
variadic: false,
|
||||
|
|
@ -444,9 +435,7 @@ fn analyze_closure_signature(c: Arc<LazyHash<Closure>>) -> Vec<Arc<ParamSpec>> {
|
|||
params.push(Arc::new(ParamSpec {
|
||||
name: ident.unwrap_or_default().into(),
|
||||
base_type: Ty::Any,
|
||||
type_repr: None,
|
||||
expr: None,
|
||||
default: None,
|
||||
positional: true,
|
||||
named: false,
|
||||
variadic: true,
|
||||
|
|
@ -509,23 +498,3 @@ fn unwrap_expr(mut e: ast::Expr) -> ast::Expr {
|
|||
|
||||
e
|
||||
}
|
||||
|
||||
struct TypeExpr<'a>(&'a CastInfo);
|
||||
|
||||
impl<'a> fmt::Display for TypeExpr<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self.0 {
|
||||
CastInfo::Any => "any",
|
||||
CastInfo::Value(v, _doc) => return write!(f, "{}", v.repr()),
|
||||
CastInfo::Type(v) => {
|
||||
f.write_str(v.short_name())?;
|
||||
return Ok(());
|
||||
}
|
||||
CastInfo::Union(v) => {
|
||||
let mut values = v.iter().map(|e| TypeExpr(e).to_string());
|
||||
f.write_str(&values.join(" | "))?;
|
||||
return Ok(());
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ impl Docs {
|
|||
}
|
||||
|
||||
type TypeRepr = Option<(/* short */ String, /* long */ String)>;
|
||||
type ShowTypeRepr<'a> = &'a mut dyn FnMut(Option<&Ty>) -> TypeRepr;
|
||||
|
||||
/// Describes a primary function signature.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
@ -108,6 +109,65 @@ pub struct DocSignature {
|
|||
pub ret_ty: TypeRepr,
|
||||
}
|
||||
|
||||
impl fmt::Display for DocSignature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut is_first = true;
|
||||
let mut write_sep = |f: &mut fmt::Formatter<'_>| {
|
||||
if is_first {
|
||||
is_first = false;
|
||||
return Ok(());
|
||||
}
|
||||
f.write_str(", ")
|
||||
};
|
||||
|
||||
for p in &self.pos {
|
||||
write_sep(f)?;
|
||||
f.write_str(&p.name)?;
|
||||
if let Some(t) = &p.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.expr.clone()))
|
||||
}
|
||||
name_prints.sort();
|
||||
for (k, t, v) in name_prints {
|
||||
write_sep(f)?;
|
||||
let v = v.as_deref().unwrap_or("any");
|
||||
let mut v = v.trim();
|
||||
if v.starts_with('{') && v.ends_with('}') && v.len() > 30 {
|
||||
v = "{ ... }"
|
||||
}
|
||||
if v.starts_with('`') && v.ends_with('`') && v.len() > 30 {
|
||||
v = "raw"
|
||||
}
|
||||
if v.starts_with('[') && v.ends_with(']') && v.len() > 30 {
|
||||
v = "content"
|
||||
}
|
||||
f.write_str(&k)?;
|
||||
if let Some(t) = t {
|
||||
write!(f, ": {t}")?;
|
||||
}
|
||||
write!(f, " = {v}")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a function parameter.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DocParamSpec {
|
||||
|
|
@ -117,8 +177,6 @@ pub struct DocParamSpec {
|
|||
pub docs: String,
|
||||
/// Inferred type of the parameter.
|
||||
pub cano_type: TypeRepr,
|
||||
/// The parameter's default name as type.
|
||||
pub type_repr: Option<EcoString>,
|
||||
/// The parameter's default name as value.
|
||||
pub expr: Option<EcoString>,
|
||||
/// Is the parameter positional?
|
||||
|
|
@ -438,14 +496,14 @@ fn identify_docs(kind: &str, content: &str) -> StrResult<Docs> {
|
|||
|
||||
type TypeInfo = (Arc<crate::analysis::DefUseInfo>, Arc<crate::ty::TypeScheme>);
|
||||
|
||||
fn docs_signature(
|
||||
pub(crate) fn docs_signature(
|
||||
ctx: &mut AnalysisContext,
|
||||
type_info: Option<&TypeInfo>,
|
||||
sym: &SymbolInfo,
|
||||
e: Value,
|
||||
doc_ty: &mut impl FnMut(Option<&Ty>) -> TypeRepr,
|
||||
def_ident: Option<&IdentRef>,
|
||||
runtime_fn: &Value,
|
||||
doc_ty: Option<ShowTypeRepr>,
|
||||
) -> Option<DocSignature> {
|
||||
let func = match &e {
|
||||
let func = match runtime_fn {
|
||||
Value::Func(f) => f,
|
||||
_ => return None,
|
||||
};
|
||||
|
|
@ -470,15 +528,17 @@ fn docs_signature(
|
|||
let sig = analyze_dyn_signature(ctx, func.clone());
|
||||
let type_sig = type_info.and_then(|(def_use, ty_chk)| {
|
||||
let def_fid = func.span().id()?;
|
||||
let def_ident = IdentRef {
|
||||
name: sym.head.name.clone(),
|
||||
range: sym.head.name_range.clone()?,
|
||||
};
|
||||
let (def_id, _) = def_use.get_def(def_fid, &def_ident)?;
|
||||
let (def_id, _) = def_use.get_def(def_fid, def_ident?)?;
|
||||
ty_chk.type_of_def(def_id)
|
||||
});
|
||||
let type_sig = type_sig.and_then(|type_sig| type_sig.sig_repr(true));
|
||||
|
||||
const F: fn(Option<&Ty>) -> TypeRepr = |ty: Option<&Ty>| {
|
||||
ty.and_then(|ty| ty.describe())
|
||||
.map(|short| (short, format!("{ty:?}")))
|
||||
};
|
||||
let mut binding = F;
|
||||
let doc_ty = doc_ty.unwrap_or(&mut binding);
|
||||
let pos_in = sig
|
||||
.primary()
|
||||
.pos
|
||||
|
|
@ -505,8 +565,7 @@ fn docs_signature(
|
|||
.map(|(param, ty)| DocParamSpec {
|
||||
name: param.name.as_ref().to_owned(),
|
||||
docs: param.docs.as_ref().to_owned(),
|
||||
cano_type: doc_ty(ty),
|
||||
type_repr: param.type_repr.clone(),
|
||||
cano_type: doc_ty(ty.or(Some(¶m.base_type))),
|
||||
expr: param.expr.clone(),
|
||||
positional: param.positional,
|
||||
named: param.named,
|
||||
|
|
@ -522,8 +581,7 @@ fn docs_signature(
|
|||
DocParamSpec {
|
||||
name: param.name.as_ref().to_owned(),
|
||||
docs: param.docs.as_ref().to_owned(),
|
||||
cano_type: doc_ty(ty),
|
||||
type_repr: param.type_repr.clone(),
|
||||
cano_type: doc_ty(ty.or(Some(¶m.base_type))),
|
||||
expr: param.expr.clone(),
|
||||
positional: param.positional,
|
||||
named: param.named,
|
||||
|
|
@ -537,8 +595,7 @@ fn docs_signature(
|
|||
let rest = rest_in.map(|(param, ty)| DocParamSpec {
|
||||
name: param.name.as_ref().to_owned(),
|
||||
docs: param.docs.as_ref().to_owned(),
|
||||
cano_type: doc_ty(ty),
|
||||
type_repr: param.type_repr.clone(),
|
||||
cano_type: doc_ty(ty.or(Some(¶m.base_type))),
|
||||
expr: param.expr.clone(),
|
||||
positional: param.positional,
|
||||
named: param.named,
|
||||
|
|
@ -704,8 +761,13 @@ pub fn generate_md_docs(
|
|||
sym.head.loc = span;
|
||||
|
||||
let sym_value = sym.head.value.clone();
|
||||
let signature =
|
||||
sym_value.and_then(|e| docs_signature(ctx, type_info, &sym, e, &mut doc_ty));
|
||||
let signature = sym_value.and_then(|e| {
|
||||
let def_ident = IdentRef {
|
||||
name: sym.head.name.clone(),
|
||||
range: sym.head.name_range.clone()?,
|
||||
};
|
||||
docs_signature(ctx, type_info, Some(&def_ident), &e, Some(&mut doc_ty))
|
||||
});
|
||||
sym.head.signature = signature;
|
||||
|
||||
let mut convert_err = None;
|
||||
|
|
@ -769,12 +831,7 @@ pub fn generate_md_docs(
|
|||
if let Some(sig) = &sym.head.signature {
|
||||
let _ = writeln!(md, "<!-- begin:sig -->");
|
||||
let _ = writeln!(md, "```typc");
|
||||
let _ = writeln!(
|
||||
md,
|
||||
"let {name}({params});",
|
||||
name = sym.head.name,
|
||||
params = ParamTooltip(sig)
|
||||
);
|
||||
let _ = writeln!(md, "let {name}({sig});", name = sym.head.name);
|
||||
let _ = writeln!(md, "```");
|
||||
let _ = writeln!(md, "<!-- end:sig -->");
|
||||
}
|
||||
|
|
@ -943,58 +1000,6 @@ fn oneliner(docs: &str) -> &str {
|
|||
docs.lines().next().unwrap_or_default()
|
||||
}
|
||||
|
||||
// todo: hover with `with_stack`, todo: merge with hover tooltip
|
||||
struct ParamTooltip<'a>(&'a DocSignature);
|
||||
|
||||
impl<'a> fmt::Display for ParamTooltip<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut is_first = true;
|
||||
let mut write_sep = |f: &mut fmt::Formatter<'_>| {
|
||||
if is_first {
|
||||
is_first = false;
|
||||
return Ok(());
|
||||
}
|
||||
f.write_str(", ")
|
||||
};
|
||||
|
||||
let primary_sig = self.0;
|
||||
|
||||
for p in &primary_sig.pos {
|
||||
write_sep(f)?;
|
||||
write!(f, "{}", p.name)?;
|
||||
}
|
||||
if let Some(rest) = &primary_sig.rest {
|
||||
write_sep(f)?;
|
||||
write!(f, "{}", rest.name)?;
|
||||
}
|
||||
|
||||
if !primary_sig.named.is_empty() {
|
||||
let mut name_prints = vec![];
|
||||
for v in primary_sig.named.values() {
|
||||
name_prints.push((v.name.clone(), v.type_repr.clone()))
|
||||
}
|
||||
name_prints.sort();
|
||||
for (k, v) in name_prints {
|
||||
write_sep(f)?;
|
||||
let v = v.as_deref().unwrap_or("any");
|
||||
let mut v = v.trim();
|
||||
if v.starts_with('{') && v.ends_with('}') && v.len() > 30 {
|
||||
v = "{ ... }"
|
||||
}
|
||||
if v.starts_with('`') && v.ends_with('`') && v.len() > 30 {
|
||||
v = "raw"
|
||||
}
|
||||
if v.starts_with('[') && v.ends_with(']') && v.len() > 30 {
|
||||
v = "content"
|
||||
}
|
||||
write!(f, "{k}: {v}")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_list_annotations(s: &str) -> String {
|
||||
let s = s.to_string();
|
||||
static REG: std::sync::LazyLock<regex::Regex> = std::sync::LazyLock::new(|| {
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs
|
|||
expression: CallSnapshot(result.as_deref())
|
||||
input_file: crates/tinymist-query/src/fixtures/call_info/builtin.typ
|
||||
---
|
||||
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "angle", docs: "The angle whose sine to calculate.", base_type: (Type(angle) | Type(float) | Type(integer)), type_repr: Some("int | float | angle"), expr: None, default: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "angle", docs: "The angle whose sine to calculate.", base_type: (Type(angle) | Type(float) | Type(integer)), expr: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@ source: crates/tinymist-query/src/analysis.rs
|
|||
expression: CallSnapshot(result.as_deref())
|
||||
input_file: crates/tinymist-query/src/fixtures/call_info/builtin_poly.typ
|
||||
---
|
||||
255 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "red", docs: "The red component.", base_type: (Type(integer) | Type(ratio)), 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.", base_type: (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.", base_type: (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: "red", docs: "The red component.", base_type: (Type(integer) | Type(ratio)), expr: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
255 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "green", docs: "The green component.", base_type: (Type(integer) | Type(ratio)), expr: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
255 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "blue", docs: "The blue component.", base_type: (Type(integer) | Type(ratio)), expr: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs
|
|||
expression: CallSnapshot(result.as_deref())
|
||||
input_file: crates/tinymist-query/src/fixtures/call_info/builtin_poly2.typ
|
||||
---
|
||||
"#fff" -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "red", docs: "The red component.", base_type: (Type(integer) | Type(ratio)), 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.", base_type: (Type(integer) | Type(ratio)), expr: 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: "", base_type: 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: "", base_type: 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: "", base_type: Any, expr: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "y", docs: "", base_type: Any, expr: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@ source: crates/tinymist-query/src/analysis.rs
|
|||
expression: CallSnapshot(result.as_deref())
|
||||
input_file: crates/tinymist-query/src/fixtures/call_info/user_named.typ
|
||||
---
|
||||
y: 1 -> CallParamInfo { kind: Named, is_content_block: false, param: ParamSpec { name: "y", docs: "Default value: none", base_type: Any, 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: "", base_type: 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", base_type: Any, expr: Some("none"), positional: false, named: true, variadic: false, settable: true } }
|
||||
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "x", docs: "", base_type: Any, expr: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs
|
|||
expression: CallSnapshot(result.as_deref())
|
||||
input_file: crates/tinymist-query/src/fixtures/call_info/user_named_with.typ
|
||||
---
|
||||
1 -> CallParamInfo { kind: Positional, is_content_block: false, param: ParamSpec { name: "x", docs: "", base_type: Any, 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: "", base_type: Any, expr: 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", base_type: 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", base_type: Any, expr: Some("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: "", base_type: 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: "", base_type: Any, expr: None, positional: true, named: false, variadic: false, settable: false } }
|
||||
|
|
|
|||
9
crates/tinymist-query/src/fixtures/hover/annotate_fn.typ
Normal file
9
crates/tinymist-query/src/fixtures/hover/annotate_fn.typ
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/// #let fn = `(..fn-args) => any`;
|
||||
///
|
||||
/// - fn (function, fn): The `fn`.
|
||||
/// - max-repetitions (int): The `max-repetitions`.
|
||||
/// - repetitions (int): The `repetitions`.
|
||||
/// - args (any, fn-args): The `args`.
|
||||
#let touying-fn-wrapper(fn, max-repetitions: none, repetitions: none, ..args) = none
|
||||
|
||||
#(/* ident after */ touying-fn-wrapper);
|
||||
|
|
@ -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/annotate_fn.typ
|
||||
---
|
||||
{
|
||||
"contents": "```typc\nlet touying-fn-wrapper(fn: (..: []) => any | function, ..args: arguments, max-repetitions: int | none = none, repetitions: int | none = none);\n```\n\n---\n\n\n #let fn = `(..fn-args) => any`;\n\n - fn (function, fn): The `fn`.\n - max-repetitions (int): The `max-repetitions`.\n - repetitions (int): The `repetitions`.\n - args (any, fn-args): The `args`.",
|
||||
"range": "8:20:8:38"
|
||||
}
|
||||
|
|
@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
|
|||
input_file: crates/tinymist-query/src/fixtures/hover/builtin.typ
|
||||
---
|
||||
{
|
||||
"contents": "```typc\nlet table(children, align: alignment | auto | array | function, column-gutter: auto | relative | fraction | int | array, columns: auto | relative | fraction | int | array, fill: color | gradient | pattern | none | array | function, gutter: auto | relative | fraction | int | array, inset: relative | dictionary | array | function, row-gutter: auto | relative | fraction | int | array, rows: auto | relative | fraction | int | array, stroke: length | color | gradient | pattern | dictionary | stroke | none | array | function);\n```\n\n---\n\n\nA table of items.\n\nTables are used to arrange content in cells. Cells can contain arbitrary\ncontent, including multiple paragraphs and are specified in row-major order.\nFor a hands-on explanation of all the ways you can use and customize tables\nin Typst, check out the [table guide](https://typst.app/docs/guides/table-guide/).\n\nBecause tables are just grids with different defaults for some cell\nproperties (notably `stroke` and `inset`), refer to the [grid\ndocumentation](https://typst.app/docs/reference/layout/grid/) for more information on how to size the table tracks\nand specify the cell appearance properties.\n\nIf you are unsure whether you should be using a table or a grid, consider\nwhether the content you are arranging semantically belongs together as a set\nof related data points or similar or whether you are just want to enhance\nyour presentation by arranging unrelated content in a grid. In the former\ncase, a table is the right choice, while in the latter case, a grid is more\nappropriate. Furthermore, Typst will annotate its output in the future such\nthat screenreaders will annouce content in `table` as tabular while a grid's\ncontent will be announced no different than multiple content blocks in the\ndocument flow.\n\nNote that, to override a particular cell's properties or apply show rules on\ntable cells, you can use the [`table.cell`](https://typst.app/docs/reference/model/table/#definitions-cell) element. See its\ndocumentation for more information.\n\nAlthough the `table` and the `grid` share most properties, set and show\nrules on one of them do not affect the other.\n\nTo give a table a caption and make it [referenceable](https://typst.app/docs/reference/model/ref/), put it into a\n[figure].\n\n# Example\n\nThe example below demonstrates some of the most common table options.\n```typ\n#table(\n columns: (1fr, auto, auto),\n inset: 10pt,\n align: horizon,\n table.header(\n [], [*Area*], [*Parameters*],\n ),\n image(\"cylinder.svg\"),\n $ pi h (D^2 - d^2) / 4 $,\n [\n $h$: height \\\n $D$: outer radius \\\n $d$: inner radius\n ],\n image(\"tetrahedron.svg\"),\n $ sqrt(2) / 12 a^3 $,\n [$a$: edge length]\n)\n```\n\nMuch like with grids, you can use [`table.cell`](https://typst.app/docs/reference/model/table/#definitions-cell) to customize\nthe appearance and the position of each cell.\n\n```typ\n>>> #set page(width: auto)\n>>> #set text(font: \"IBM Plex Sans\")\n>>> #let gray = rgb(\"#565565\")\n>>>\n#set table(\n stroke: none,\n gutter: 0.2em,\n fill: (x, y) =>\n if x == 0 or y == 0 { gray },\n inset: (right: 1.5em),\n)\n\n#show table.cell: it => {\n if it.x == 0 or it.y == 0 {\n set text(white)\n strong(it)\n } else if it.body == [] {\n // Replace empty cells with 'N/A'\n pad(..it.inset)[_N/A_]\n } else {\n it\n }\n}\n\n#let a = table.cell(\n fill: green.lighten(60%),\n)[A]\n#let b = table.cell(\n fill: aqua.lighten(60%),\n)[B]\n\n#table(\n columns: 4,\n [], [Exam 1], [Exam 2], [Exam 3],\n\n [John], [], a, [],\n [Mary], [], a, a,\n [Robert], b, a, b,\n)\n```\n\n---\n[Open docs](https://typst.app/docs/reference/model/table/)",
|
||||
"contents": "```typc\nlet table(..children: content, align: alignment | array | auto | function = auto, column-gutter: array | auto | integer | length = (), columns: array | auto | integer | length = (), fill: color = none, gutter: array | auto | integer | length = (), inset: inset = 5pt, row-gutter: array | auto | integer | length = (), rows: array | auto | integer | length = (), stroke: stroke = 1pt + black);\n```\n\n---\n\n\nA table of items.\n\nTables are used to arrange content in cells. Cells can contain arbitrary\ncontent, including multiple paragraphs and are specified in row-major order.\nFor a hands-on explanation of all the ways you can use and customize tables\nin Typst, check out the [table guide](https://typst.app/docs/guides/table-guide/).\n\nBecause tables are just grids with different defaults for some cell\nproperties (notably `stroke` and `inset`), refer to the [grid\ndocumentation](https://typst.app/docs/reference/layout/grid/) for more information on how to size the table tracks\nand specify the cell appearance properties.\n\nIf you are unsure whether you should be using a table or a grid, consider\nwhether the content you are arranging semantically belongs together as a set\nof related data points or similar or whether you are just want to enhance\nyour presentation by arranging unrelated content in a grid. In the former\ncase, a table is the right choice, while in the latter case, a grid is more\nappropriate. Furthermore, Typst will annotate its output in the future such\nthat screenreaders will annouce content in `table` as tabular while a grid's\ncontent will be announced no different than multiple content blocks in the\ndocument flow.\n\nNote that, to override a particular cell's properties or apply show rules on\ntable cells, you can use the [`table.cell`](https://typst.app/docs/reference/model/table/#definitions-cell) element. See its\ndocumentation for more information.\n\nAlthough the `table` and the `grid` share most properties, set and show\nrules on one of them do not affect the other.\n\nTo give a table a caption and make it [referenceable](https://typst.app/docs/reference/model/ref/), put it into a\n[figure].\n\n# Example\n\nThe example below demonstrates some of the most common table options.\n```typ\n#table(\n columns: (1fr, auto, auto),\n inset: 10pt,\n align: horizon,\n table.header(\n [], [*Area*], [*Parameters*],\n ),\n image(\"cylinder.svg\"),\n $ pi h (D^2 - d^2) / 4 $,\n [\n $h$: height \\\n $D$: outer radius \\\n $d$: inner radius\n ],\n image(\"tetrahedron.svg\"),\n $ sqrt(2) / 12 a^3 $,\n [$a$: edge length]\n)\n```\n\nMuch like with grids, you can use [`table.cell`](https://typst.app/docs/reference/model/table/#definitions-cell) to customize\nthe appearance and the position of each cell.\n\n```typ\n>>> #set page(width: auto)\n>>> #set text(font: \"IBM Plex Sans\")\n>>> #let gray = rgb(\"#565565\")\n>>>\n#set table(\n stroke: none,\n gutter: 0.2em,\n fill: (x, y) =>\n if x == 0 or y == 0 { gray },\n inset: (right: 1.5em),\n)\n\n#show table.cell: it => {\n if it.x == 0 or it.y == 0 {\n set text(white)\n strong(it)\n } else if it.body == [] {\n // Replace empty cells with 'N/A'\n pad(..it.inset)[_N/A_]\n } else {\n it\n }\n}\n\n#let a = table.cell(\n fill: green.lighten(60%),\n)[A]\n#let b = table.cell(\n fill: aqua.lighten(60%),\n)[B]\n\n#table(\n columns: 4,\n [], [Exam 1], [Exam 2], [Exam 3],\n\n [John], [], a, [],\n [Mary], [], a, a,\n [Robert], b, a, b,\n)\n```\n\n---\n[Open docs](https://typst.app/docs/reference/model/table/)",
|
||||
"range": "0:20:0:25"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
|
|||
input_file: crates/tinymist-query/src/fixtures/hover/pagebreak.typ
|
||||
---
|
||||
{
|
||||
"contents": "```typc\nlet pagebreak(to: \"even\" | \"odd\" | none, weak: bool);\n```\n\n---\n\n\nA manual page break.\n\nMust not be used inside any containers.\n\n# Example\n```typ\nThe next page contains\nmore details on compound theory.\n#pagebreak()\n\n== Compound Theory\nIn 1984, the first ...\n```\n\n---\n[Open docs](https://typst.app/docs/reference/layout/pagebreak/)",
|
||||
"contents": "```typc\nlet pagebreak(to: \"even\" | \"odd\" | none = none, weak: bool = false);\n```\n\n---\n\n\nA manual page break.\n\nMust not be used inside any containers.\n\n# Example\n```typ\nThe next page contains\nmore details on compound theory.\n#pagebreak()\n\n== Compound Theory\nIn 1984, the first ...\n```\n\n---\n[Open docs](https://typst.app/docs/reference/layout/pagebreak/)",
|
||||
"range": "0:20:0:29"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ use core::fmt;
|
|||
use typst_shim::syntax::LinkedNodeExt;
|
||||
|
||||
use crate::{
|
||||
analysis::{
|
||||
analyze_dyn_signature, find_definition, get_link_exprs_in, DefinitionLink, Signature,
|
||||
},
|
||||
analysis::{find_definition, get_link_exprs_in, DefinitionLink},
|
||||
docs::DocSignature,
|
||||
jump_from_cursor,
|
||||
prelude::*,
|
||||
syntax::{find_docs_before, get_deref_target, LexicalKind, LexicalVarKind},
|
||||
|
|
@ -252,17 +251,17 @@ fn def_tooltip(
|
|||
Some(LspHoverContents::Array(results))
|
||||
}
|
||||
LexicalKind::Var(LexicalVarKind::Function) => {
|
||||
let sig = if let Some(Value::Func(func)) = &lnk.value {
|
||||
Some(analyze_dyn_signature(ctx, func.clone()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let sig = lnk
|
||||
.value
|
||||
.as_ref()
|
||||
.and_then(|e| ctx.docs_signature(source, lnk.to_ident_ref().as_ref(), e));
|
||||
|
||||
results.push(MarkedString::LanguageString(LanguageString {
|
||||
language: "typc".to_owned(),
|
||||
value: format!(
|
||||
"let {name}({params});",
|
||||
name = lnk.name,
|
||||
params = ParamTooltip(sig)
|
||||
params = ParamTooltip(sig.as_ref())
|
||||
),
|
||||
}));
|
||||
|
||||
|
|
@ -353,64 +352,14 @@ fn render_actions(results: &mut Vec<MarkedString>, actions: Vec<CommandLink>) {
|
|||
}
|
||||
|
||||
// todo: hover with `with_stack`
|
||||
struct ParamTooltip(Option<Signature>);
|
||||
struct ParamTooltip<'a>(Option<&'a DocSignature>);
|
||||
|
||||
impl fmt::Display for ParamTooltip {
|
||||
impl fmt::Display for ParamTooltip<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Some(sig) = &self.0 else {
|
||||
let Some(sig) = self.0 else {
|
||||
return Ok(());
|
||||
};
|
||||
let mut is_first = true;
|
||||
let mut write_sep = |f: &mut fmt::Formatter<'_>| {
|
||||
if is_first {
|
||||
is_first = false;
|
||||
return Ok(());
|
||||
}
|
||||
f.write_str(", ")
|
||||
};
|
||||
|
||||
let primary_sig = match sig {
|
||||
Signature::Primary(sig) => sig,
|
||||
Signature::Partial(sig) => {
|
||||
let _ = sig.with_stack;
|
||||
|
||||
&sig.signature
|
||||
}
|
||||
};
|
||||
|
||||
for p in &primary_sig.pos {
|
||||
write_sep(f)?;
|
||||
write!(f, "{}", p.name)?;
|
||||
}
|
||||
if let Some(rest) = &primary_sig.rest {
|
||||
write_sep(f)?;
|
||||
write!(f, "{}", rest.name)?;
|
||||
}
|
||||
|
||||
if !primary_sig.named.is_empty() {
|
||||
let mut name_prints = vec![];
|
||||
for v in primary_sig.named.values() {
|
||||
name_prints.push((v.name.clone(), v.type_repr.clone()))
|
||||
}
|
||||
name_prints.sort();
|
||||
for (k, v) in name_prints {
|
||||
write_sep(f)?;
|
||||
let v = v.as_deref().unwrap_or("any");
|
||||
let mut v = v.trim();
|
||||
if v.starts_with('{') && v.ends_with('}') && v.len() > 30 {
|
||||
v = "{ ... }"
|
||||
}
|
||||
if v.starts_with('`') && v.ends_with('`') && v.len() > 30 {
|
||||
v = "raw"
|
||||
}
|
||||
if v.starts_with('[') && v.ends_with(']') && v.len() > 30 {
|
||||
v = "content"
|
||||
}
|
||||
write!(f, "{k}: {v}")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
sig.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ impl BuiltinTy {
|
|||
BuiltinTy::FlowNone => "none",
|
||||
BuiltinTy::Auto => "auto",
|
||||
|
||||
BuiltinTy::Args => "args",
|
||||
BuiltinTy::Args => "arguments",
|
||||
BuiltinTy::Color => "color",
|
||||
BuiltinTy::TextSize => "text.size",
|
||||
BuiltinTy::TextFont => "text.font",
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@ fn e2e() {
|
|||
});
|
||||
|
||||
let hash = replay_log(&tinymist_binary, &root.join("vscode"));
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:cbabb6bf71ce157c4e779aecf9d22c2a");
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:c74f86e2e0fa2ecb0323814645f39c1b");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue