feat: render error as docs instead of causing failures (#786)

This commit is contained in:
Myriad-Dreamin 2024-11-09 21:53:55 +08:00 committed by GitHub
parent 22621a966c
commit d8886b2065
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 194 additions and 72 deletions

View file

@ -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, "`````");
}
}
}

View file

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

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

View file

@ -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, &param.types),
ty: self.check_type_strings(module, &param.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)));