tinymist_query/docs/
convert.rs1use std::path::Path;
2use std::sync::Arc;
3
4use ecow::{EcoString, eco_format};
5use tinymist_std::path::unix_slash;
6use tinymist_world::diag::print_diagnostics_to_string;
7use tinymist_world::vfs::WorkspaceResolver;
8use tinymist_world::{
9 DiagnosticFormat, EntryReader, EntryState, ShadowApi, SourceWorld, TaskInputs,
10};
11use typlite::{Format, TypliteFeat};
12use typst::World;
13use typst::diag::StrResult;
14use typst::foundations::Bytes;
15use typst::syntax::FileId;
16
17use crate::analysis::SharedContext;
18
19pub(crate) fn convert_docs(
20 ctx: &SharedContext,
21 content: &str,
22 source_fid: Option<FileId>,
23) -> StrResult<EcoString> {
24 let mut entry = ctx.world().entry_state();
25 let import_context = source_fid.map(|fid| {
26 let root = ctx
27 .world()
28 .vfs()
29 .file_path(fid.join("/"))
30 .ok()
31 .and_then(|e| e.to_err().ok());
32 if let Some(root) = root {
33 entry = EntryState::new_workspace(root.into());
34 }
35
36 let mut imports = Vec::new();
37 if WorkspaceResolver::is_package_file(fid)
38 && let Some(pkg) = fid.package()
39 {
40 let pkg_spec = pkg.to_string();
41 imports.push(format!("#import {pkg_spec:?}"));
42 imports.push(format!("#import {pkg_spec:?}: *"));
43 }
44 imports.push(format!(
45 "#import {:?}: *",
46 unix_slash(fid.vpath().as_rooted_path())
47 ));
48 imports.join("; ")
49 });
50 let mut feat = TypliteFeat {
51 color_theme: Some(ctx.analysis.color_theme),
52 annotate_elem: true,
53 soft_error: true,
54 remove_html: ctx.analysis.remove_html,
55 import_context,
56 ..Default::default()
57 };
58
59 let entry = entry.select_in_workspace(Path::new("__tinymist_docs__.typ"));
60
61 let mut w = ctx.world().task(TaskInputs {
62 entry: Some(entry),
63 inputs: None,
64 });
65
66 w.map_shadow_by_id(w.main(), Bytes::from_string(content.to_owned()))?;
68 w.take_db();
70 let (w, wrap_info) = feat
71 .prepare_world(&w, Format::Md)
72 .map_err(|e| eco_format!("failed to prepare world: {e}"))?;
73
74 feat.wrap_info = wrap_info;
75
76 let w = Arc::new(w);
77 let res = typlite::Typlite::new(w.clone())
78 .with_feature(feat)
79 .convert();
80 let conv = print_diag_or_error(w.as_ref(), res)?;
81
82 Ok(conv.replace("```example", "```typ"))
83}
84
85fn print_diag_or_error<T>(
86 world: &impl SourceWorld,
87 result: tinymist_std::Result<T>,
88) -> StrResult<T> {
89 match result {
90 Ok(v) => Ok(v),
91 Err(err) => {
92 if let Some(diagnostics) = err.diagnostics() {
93 return Err(print_diagnostics_to_string(
94 world,
95 diagnostics.iter(),
96 DiagnosticFormat::Human,
97 )?);
98 }
99
100 Err(eco_format!("failed to convert docs: {err}"))
101 }
102 }
103}