mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-24 13:13:43 +00:00
feat: render error as docs instead of causing failures (#786)
This commit is contained in:
parent
22621a966c
commit
d8886b2065
6 changed files with 194 additions and 72 deletions
|
@ -124,36 +124,11 @@ pub fn package_docs(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<Str
|
|||
Some((file_ids.insert_full(fid).0, 0, 0))
|
||||
});
|
||||
child.loc = span;
|
||||
// .ok_or_else(|| {
|
||||
// let err = format!("failed to convert docs in {title}").replace(
|
||||
// "-->", "—>", // avoid markdown comment
|
||||
// );
|
||||
// log::error!("{err}");
|
||||
// err
|
||||
// })
|
||||
let docs = child.parsed_docs.clone();
|
||||
// Err(e) => {
|
||||
// let err = format!("failed to convert docs: {e}").replace(
|
||||
// "-->", "—>", // avoid markdown comment
|
||||
// );
|
||||
// log::error!("{err}");
|
||||
// return Err(err);
|
||||
// }
|
||||
|
||||
let convert_err = None::<EcoString>;
|
||||
match &docs {
|
||||
Some(docs) => {
|
||||
child.parsed_docs = Some(docs.clone());
|
||||
child.docs = None;
|
||||
}
|
||||
None => {
|
||||
// let err = format!("failed to convert docs in {title}:
|
||||
// {e}").replace( "-->",
|
||||
// "—>", // avoid markdown comment
|
||||
// );
|
||||
// log::error!("{err}");
|
||||
// convert_err = Some(err);
|
||||
}
|
||||
if let Some(docs) = &child.parsed_docs {
|
||||
child.parsed_docs = Some(docs.clone());
|
||||
child.docs = None;
|
||||
}
|
||||
|
||||
let ident = if !primary.is_empty() {
|
||||
|
@ -204,6 +179,7 @@ pub fn package_docs(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<Str
|
|||
let _ = writeln!(md, "<!-- end:sig -->");
|
||||
}
|
||||
|
||||
let mut printed_docs = false;
|
||||
match (&child.parsed_docs, convert_err) {
|
||||
(_, Some(err)) => {
|
||||
let err = format!("failed to convert docs in {title}: {err}").replace(
|
||||
|
@ -212,8 +188,9 @@ pub fn package_docs(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<Str
|
|||
let _ = writeln!(md, "<!-- convert-error: {err} -->");
|
||||
errors.push(err);
|
||||
}
|
||||
(Some(docs), _) => {
|
||||
(Some(docs), _) if !child.is_external => {
|
||||
let _ = writeln!(md, "{}", remove_list_annotations(docs.docs()));
|
||||
printed_docs = true;
|
||||
if let DefDocs::Function(f) = docs {
|
||||
for param in f.pos.iter().chain(f.named.values()).chain(f.rest.as_ref())
|
||||
{
|
||||
|
@ -231,20 +208,22 @@ pub fn package_docs(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<Str
|
|||
}
|
||||
}
|
||||
}
|
||||
(None, None) => {}
|
||||
(_, None) => {}
|
||||
}
|
||||
|
||||
let plain_docs = child.docs.as_deref();
|
||||
let plain_docs = plain_docs.or(child.oneliner.as_deref());
|
||||
if !printed_docs {
|
||||
let plain_docs = child.docs.as_deref();
|
||||
let plain_docs = plain_docs.or(child.oneliner.as_deref());
|
||||
|
||||
if let Some(docs) = plain_docs {
|
||||
let contains_code = docs.contains("```");
|
||||
if contains_code {
|
||||
let _ = writeln!(md, "`````typ");
|
||||
}
|
||||
let _ = writeln!(md, "{docs}");
|
||||
if contains_code {
|
||||
let _ = writeln!(md, "`````");
|
||||
if let Some(docs) = plain_docs {
|
||||
let contains_code = docs.contains("```");
|
||||
if contains_code {
|
||||
let _ = writeln!(md, "`````typ");
|
||||
}
|
||||
let _ = writeln!(md, "{docs}");
|
||||
if contains_code {
|
||||
let _ = writeln!(md, "`````");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/// Speaker notes are a way to add additional information to your slides that is not visible to the audience. This can be useful for providing additional context or reminders to yourself.
|
||||
///
|
||||
/// == Example
|
||||
///
|
||||
/// #example(```typ
|
||||
/// #speaker-note[This is a speaker note]
|
||||
/// ```)
|
||||
#let speaker-note(mode: "typ", setting: it => it, note) = {
|
||||
touying-fn-wrapper(utils.speaker-note, mode: mode, setting: setting, note)
|
||||
}
|
||||
|
||||
#(/* ident after */ speaker-note);
|
|
@ -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_docs_error.typ
|
||||
---
|
||||
{
|
||||
"contents": "```typc\nlet speaker-note(\n note,\n mode: str = \"typ\",\n setting: (any) => any = Closure(..),\n) = none;\n```\n\n---\nSpeaker notes are a way to add additional information to your slides that is not visible to the audience. This can be useful for providing additional context or reminders to yourself.\n\n ## Example\n\n ```typ\n#speaker-note[This is a speaker note]\n\n```\n```\nRender Error\ncompiling node: error: unknown variable: speaker-note at \"/__render__.typ\":201..213\nHint: if you meant to use subtraction, try adding spaces around the minus sign: \\`speaker - note\\`\n\n```\n\n## Parameters\n\n@positional `note`\n\n@named `mode`\n\n@named `setting`",
|
||||
"range": "11:20:11:32"
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
use std::{
|
||||
collections::BTreeMap,
|
||||
ops::Deref,
|
||||
sync::{LazyLock, OnceLock},
|
||||
};
|
||||
|
||||
use ecow::eco_format;
|
||||
use typst::foundations::{IntoValue, Module, Str, Type};
|
||||
|
||||
use crate::{
|
||||
|
@ -105,11 +107,20 @@ struct DocsChecker<'a> {
|
|||
next_id: u32,
|
||||
}
|
||||
|
||||
static EMPTY_MODULE: LazyLock<Module> =
|
||||
LazyLock::new(|| Module::new("stub", typst::foundations::Scope::new()));
|
||||
impl<'a> DocsChecker<'a> {
|
||||
pub fn check_func_docs(mut self, docs: String) -> Option<DocString> {
|
||||
let converted = convert_docs(self.ctx, &docs).ok()?;
|
||||
let converted = identify_func_docs(&converted).ok()?;
|
||||
let module = self.ctx.module_by_str(docs)?;
|
||||
let converted =
|
||||
convert_docs(self.ctx, &docs).and_then(|converted| identify_func_docs(&converted));
|
||||
|
||||
let converted = match Self::fallback_docs(converted, &docs) {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Some(e),
|
||||
};
|
||||
|
||||
let module = self.ctx.module_by_str(docs);
|
||||
let module = module.as_ref().unwrap_or(EMPTY_MODULE.deref());
|
||||
|
||||
let mut params = BTreeMap::new();
|
||||
for param in converted.params.into_iter() {
|
||||
|
@ -117,14 +128,14 @@ impl<'a> DocsChecker<'a> {
|
|||
param.name.into(),
|
||||
VarDoc {
|
||||
docs: param.docs,
|
||||
ty: self.check_type_strings(&module, ¶m.types),
|
||||
ty: self.check_type_strings(module, ¶m.types),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let res_ty = converted
|
||||
.return_ty
|
||||
.and_then(|ty| self.check_type_strings(&module, &ty));
|
||||
.and_then(|ty| self.check_type_strings(module, &ty));
|
||||
|
||||
Some(DocString {
|
||||
docs: Some(converted.docs),
|
||||
|
@ -135,13 +146,19 @@ impl<'a> DocsChecker<'a> {
|
|||
}
|
||||
|
||||
pub fn check_var_docs(mut self, docs: String) -> Option<DocString> {
|
||||
let converted = convert_docs(self.ctx, &docs).ok()?;
|
||||
let converted = identify_var_docs(converted).ok()?;
|
||||
let module = self.ctx.module_by_str(docs)?;
|
||||
let converted = convert_docs(self.ctx, &docs).and_then(identify_var_docs);
|
||||
|
||||
let converted = match Self::fallback_docs(converted, &docs) {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Some(e),
|
||||
};
|
||||
|
||||
let module = self.ctx.module_by_str(docs);
|
||||
let module = module.as_ref().unwrap_or(EMPTY_MODULE.deref());
|
||||
|
||||
let res_ty = converted
|
||||
.return_ty
|
||||
.and_then(|ty| self.check_type_strings(&module, &ty.0));
|
||||
.and_then(|ty| self.check_type_strings(module, &ty.0));
|
||||
|
||||
Some(DocString {
|
||||
docs: Some(converted.docs),
|
||||
|
@ -152,8 +169,12 @@ impl<'a> DocsChecker<'a> {
|
|||
}
|
||||
|
||||
pub fn check_module_docs(self, docs: String) -> Option<DocString> {
|
||||
let converted = convert_docs(self.ctx, &docs).ok()?;
|
||||
let converted = identify_tidy_module_docs(converted).ok()?;
|
||||
let converted = convert_docs(self.ctx, &docs).and_then(identify_tidy_module_docs);
|
||||
|
||||
let converted = match Self::fallback_docs(converted, &docs) {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Some(e),
|
||||
};
|
||||
|
||||
Some(DocString {
|
||||
docs: Some(converted.docs),
|
||||
|
@ -163,6 +184,22 @@ impl<'a> DocsChecker<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
fn fallback_docs<T>(converted: Result<T, EcoString>, docs: &str) -> Result<T, DocString> {
|
||||
match converted {
|
||||
Ok(c) => Ok(c),
|
||||
Err(e) => {
|
||||
let e = e.replace("`", "\\`");
|
||||
let fallback_docs = eco_format!("```\nfailed to parse docs: {e}\n```\n\n{docs}");
|
||||
Err(DocString {
|
||||
docs: Some(fallback_docs),
|
||||
var_bounds: HashMap::new(),
|
||||
vars: BTreeMap::new(),
|
||||
res_ty: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_var(&mut self, name: StrRef) -> Ty {
|
||||
self.next_id += 1;
|
||||
let encoded = Interned::new(Decl::generated(DefId(self.next_id as u64)));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue