diff --git a/crates/tinymist-query/src/analysis.rs b/crates/tinymist-query/src/analysis.rs index 6b78c6de..e38c1af3 100644 --- a/crates/tinymist-query/src/analysis.rs +++ b/crates/tinymist-query/src/analysis.rs @@ -26,8 +26,14 @@ mod prelude; mod global; pub use global::*; +use ecow::eco_format; +use lsp_types::Url; +use reflexo_typst::TypstFileId; +use typst::diag::FileError; use typst::foundations::{Func, Value}; +use crate::path_to_url; + pub(crate) trait ToFunc { fn to_func(&self) -> Option; } @@ -42,6 +48,22 @@ impl ToFunc for Value { } } +/// Extension trait for `typst::World`. +pub trait LspWorldExt { + /// Resolve the uri for a file id. + fn uri_for_id(&self, id: TypstFileId) -> Result; +} + +impl LspWorldExt for tinymist_world::LspWorld { + /// Resolve the uri for a file id. + fn uri_for_id(&self, id: TypstFileId) -> Result { + self.path_for_id(id).and_then(|e| { + path_to_url(&e) + .map_err(|e| FileError::Other(Some(eco_format!("convert to url: {e:?}")))) + }) + } +} + #[cfg(test)] mod matcher_tests { diff --git a/crates/tinymist-query/src/analysis/global.rs b/crates/tinymist-query/src/analysis/global.rs index 44a15bfe..cbc27e04 100644 --- a/crates/tinymist-query/src/analysis/global.rs +++ b/crates/tinymist-query/src/analysis/global.rs @@ -36,7 +36,7 @@ use crate::syntax::{ }; use crate::upstream::{tooltip_, Tooltip}; use crate::{ - lsp_to_typst, path_to_url, typst_to_lsp, ColorTheme, LspPosition, LspRange, PositionEncoding, + lsp_to_typst, typst_to_lsp, ColorTheme, LspPosition, LspRange, LspWorldExt, PositionEncoding, SemanticTokenContext, TypstRange, VersionedDocument, }; @@ -452,10 +452,7 @@ impl SharedContext { /// Resolve the uri for a file id. pub fn uri_for_id(&self, id: TypstFileId) -> Result { - self.path_for_id(id).and_then(|e| { - path_to_url(&e) - .map_err(|e| FileError::Other(Some(eco_format!("convert to url: {e:?}")))) - }) + self.world.uri_for_id(id) } /// Get file's id by its path diff --git a/crates/tinymist-query/src/diagnostics.rs b/crates/tinymist-query/src/diagnostics.rs index b2e96d42..f71a3b34 100644 --- a/crates/tinymist-query/src/diagnostics.rs +++ b/crates/tinymist-query/src/diagnostics.rs @@ -1,19 +1,42 @@ use reflexo_typst::EntryReader; +use tinymist_world::LspWorld; -use crate::prelude::*; +use crate::{prelude::*, LspWorldExt}; /// Stores diagnostics for files. pub type DiagnosticsMap = HashMap>; +/// Context for converting Typst diagnostics to LSP diagnostics. +struct LocalDiagContext<'a> { + /// The world surface for Typst compiler. + pub world: &'a LspWorld, + /// The position encoding for the source. + pub position_encoding: PositionEncoding, +} + +impl std::ops::Deref for LocalDiagContext<'_> { + type Target = LspWorld; + + fn deref(&self) -> &Self::Target { + self.world + } +} + /// Converts a list of Typst diagnostics to LSP diagnostics. pub fn convert_diagnostics<'a>( - ctx: &LocalContext, + world: &LspWorld, errors: impl IntoIterator, + position_encoding: PositionEncoding, ) -> DiagnosticsMap { + let ctx = LocalDiagContext { + world, + position_encoding, + }; + errors .into_iter() .flat_map(|error| { - convert_diagnostic(ctx, error) + convert_diagnostic(&ctx, error) .map_err(move |conversion_err| { log::error!("could not convert Typst error to diagnostic: {conversion_err:?} error to convert: {error:?}"); }) @@ -24,18 +47,17 @@ pub fn convert_diagnostics<'a>( } fn convert_diagnostic( - ctx: &LocalContext, + ctx: &LocalDiagContext, typst_diagnostic: &TypstDiagnostic, ) -> anyhow::Result<(Url, LspDiagnostic)> { let uri; let lsp_range; if let Some((id, span)) = diagnostic_span_id(typst_diagnostic) { uri = ctx.uri_for_id(id)?; - let source = ctx.world().source(id)?; - lsp_range = diagnostic_range(&source, span, ctx.position_encoding()); + let source = ctx.source(id)?; + lsp_range = diagnostic_range(&source, span, ctx.position_encoding); } else { let root = ctx - .world .workspace_root() .ok_or_else(|| anyhow::anyhow!("no workspace root"))?; uri = path_to_url(&root)?; @@ -48,8 +70,7 @@ fn convert_diagnostic( let typst_hints = &typst_diagnostic.hints; let lsp_message = format!("{typst_message}{}", diagnostic_hints(typst_hints)); - let tracepoints = - diagnostic_related_information(ctx, typst_diagnostic, ctx.position_encoding())?; + let tracepoints = diagnostic_related_information(ctx, typst_diagnostic, ctx.position_encoding)?; let diagnostic = LspDiagnostic { range: lsp_range, @@ -64,13 +85,13 @@ fn convert_diagnostic( } fn tracepoint_to_relatedinformation( - project: &LocalContext, + ctx: &LocalDiagContext, tracepoint: &Spanned, position_encoding: PositionEncoding, ) -> anyhow::Result> { if let Some(id) = tracepoint.span.id() { - let uri = project.uri_for_id(id)?; - let source = project.world().source(id)?; + let uri = ctx.uri_for_id(id)?; + let source = ctx.source(id)?; if let Some(typst_range) = source.range(tracepoint.span) { let lsp_range = typst_to_lsp::range(typst_range, &source, position_encoding); @@ -89,7 +110,7 @@ fn tracepoint_to_relatedinformation( } fn diagnostic_related_information( - project: &LocalContext, + project: &LocalDiagContext, typst_diagnostic: &TypstDiagnostic, position_encoding: PositionEncoding, ) -> anyhow::Result> { diff --git a/crates/tinymist-query/src/lib.rs b/crates/tinymist-query/src/lib.rs index de3d7743..b2a24741 100644 --- a/crates/tinymist-query/src/lib.rs +++ b/crates/tinymist-query/src/lib.rs @@ -15,7 +15,7 @@ pub mod syntax; pub mod ty; mod upstream; -pub use analysis::{LocalContext, LocalContextGuard}; +pub use analysis::{LocalContext, LocalContextGuard, LspWorldExt}; pub use upstream::with_vm; mod diagnostics; diff --git a/crates/tinymist/src/actor/typ_client.rs b/crates/tinymist/src/actor/typ_client.rs index d95bad4f..59d610b2 100644 --- a/crates/tinymist/src/actor/typ_client.rs +++ b/crates/tinymist/src/actor/typ_client.rs @@ -130,24 +130,18 @@ impl CompileHandler { let revision = world.revision().get(); trace!("notify diagnostics({revision}): {errors:#?} {warnings:#?}"); - let diagnostics = self.run_analysis(world, |ctx| { - tinymist_query::convert_diagnostics(ctx, errors.iter().chain(warnings.iter())) - }); + let diagnostics = tinymist_query::convert_diagnostics( + world, + errors.iter().chain(warnings.iter()), + self.analysis.position_encoding, + ); - match diagnostics { - Ok(diagnostics) => { - let entry = world.entry_state(); - // todo: better way to remove diagnostics - // todo: check all errors in this file - let detached = entry.is_inactive(); - let valid = !detached; - self.push_diagnostics(revision, valid.then_some(diagnostics)); - } - Err(err) => { - error!("TypstActor: failed to convert diagnostics: {:#}", err); - self.push_diagnostics(revision, None); - } - } + let entry = world.entry_state(); + // todo: better way to remove diagnostics + // todo: check all errors in this file + let detached = entry.is_inactive(); + let valid = !detached; + self.push_diagnostics(revision, valid.then_some(diagnostics)); } pub fn run_stateful( diff --git a/crates/tinymist/src/task/user_action.rs b/crates/tinymist/src/task/user_action.rs index eb9da4fe..48bc7cc0 100644 --- a/crates/tinymist/src/task/user_action.rs +++ b/crates/tinymist/src/task/user_action.rs @@ -176,11 +176,9 @@ async fn trace_main( let timings = writer.into_inner().unwrap(); - let diagnostics = state.primary().handle.run_analysis(w, |ctx| { - tinymist_query::convert_diagnostics(ctx, diags.iter()) - }); - - let diagnostics = diagnostics.unwrap_or_default(); + let handle = &state.primary().handle; + let diagnostics = + tinymist_query::convert_diagnostics(w, diags.iter(), handle.analysis.position_encoding); let rpc_kind = rpc_kind.as_str();