feat: create query crate

This commit is contained in:
Myriad-Dreamin 2024-03-07 13:35:45 +08:00
parent 7e9bddb763
commit 9af8eb4b52
29 changed files with 1312 additions and 1341 deletions

View file

@ -0,0 +1,143 @@
use crate::prelude::*;
pub type DiagnosticsMap = HashMap<Url, Vec<LspDiagnostic>>;
pub fn convert_diagnostics<'a>(
project: &TypstSystemWorld,
errors: impl IntoIterator<Item = &'a TypstDiagnostic>,
position_encoding: PositionEncoding,
) -> DiagnosticsMap {
errors
.into_iter()
.flat_map(|error| {
convert_diagnostic(project, error, position_encoding)
.map_err(move |conversion_err| {
error!("could not convert Typst error to diagnostic: {conversion_err:?} error to convert: {error:?}");
})
})
.collect::<Vec<_>>()
.into_iter()
.into_group_map()
}
fn convert_diagnostic(
project: &TypstSystemWorld,
typst_diagnostic: &TypstDiagnostic,
position_encoding: PositionEncoding,
) -> anyhow::Result<(Url, LspDiagnostic)> {
let uri;
let lsp_range;
if let Some((id, span)) = diagnostic_span_id(typst_diagnostic) {
uri = Url::from_file_path(project.path_for_id(id)?).unwrap();
let source = project.source(id)?;
lsp_range = diagnostic_range(&source, span, position_encoding).raw_range;
} else {
uri = Url::from_file_path(project.root.clone()).unwrap();
lsp_range = LspRawRange::default();
};
let lsp_severity = diagnostic_severity(typst_diagnostic.severity);
let typst_message = &typst_diagnostic.message;
let typst_hints = &typst_diagnostic.hints;
let lsp_message = format!("{typst_message}{}", diagnostic_hints(typst_hints));
let tracepoints = diagnostic_related_information(project, typst_diagnostic, position_encoding)?;
let diagnostic = LspDiagnostic {
range: lsp_range,
severity: Some(lsp_severity),
message: lsp_message,
source: Some("typst".to_owned()),
related_information: Some(tracepoints),
..Default::default()
};
Ok((uri, diagnostic))
}
fn tracepoint_to_relatedinformation(
project: &TypstSystemWorld,
tracepoint: &Spanned<Tracepoint>,
position_encoding: PositionEncoding,
) -> anyhow::Result<Option<DiagnosticRelatedInformation>> {
if let Some(id) = tracepoint.span.id() {
let uri = Url::from_file_path(project.path_for_id(id)?).unwrap();
let source = project.source(id)?;
if let Some(typst_range) = source.range(tracepoint.span) {
let lsp_range = typst_to_lsp::range(typst_range, &source, position_encoding);
return Ok(Some(DiagnosticRelatedInformation {
location: LspLocation {
uri,
range: lsp_range.raw_range,
},
message: tracepoint.v.to_string(),
}));
}
}
Ok(None)
}
fn diagnostic_related_information(
project: &TypstSystemWorld,
typst_diagnostic: &TypstDiagnostic,
position_encoding: PositionEncoding,
) -> anyhow::Result<Vec<DiagnosticRelatedInformation>> {
let mut tracepoints = vec![];
for tracepoint in &typst_diagnostic.trace {
if let Some(info) =
tracepoint_to_relatedinformation(project, tracepoint, position_encoding)?
{
tracepoints.push(info);
}
}
Ok(tracepoints)
}
fn diagnostic_span_id(typst_diagnostic: &TypstDiagnostic) -> Option<(FileId, TypstSpan)> {
iter::once(typst_diagnostic.span)
.chain(typst_diagnostic.trace.iter().map(|trace| trace.span))
.find_map(|span| Some((span.id()?, span)))
}
fn diagnostic_range(
source: &Source,
typst_span: TypstSpan,
position_encoding: PositionEncoding,
) -> LspRange {
// Due to #241 and maybe typst/typst#2035, we sometimes fail to find the span.
// In that case, we use a default span as a better alternative to
// panicking.
//
// This may have been fixed after Typst 0.7.0, but it's still nice to avoid
// panics in case something similar reappears.
match source.find(typst_span) {
Some(node) => {
let typst_range = node.range();
typst_to_lsp::range(typst_range, source, position_encoding)
}
None => LspRange::new(
LspRawRange::new(LspPosition::new(0, 0), LspPosition::new(0, 0)),
position_encoding,
),
}
}
fn diagnostic_severity(typst_severity: TypstSeverity) -> LspSeverity {
match typst_severity {
TypstSeverity::Error => LspSeverity::ERROR,
TypstSeverity::Warning => LspSeverity::WARNING,
}
}
fn diagnostic_hints(typst_hints: &[EcoString]) -> Format<impl Iterator<Item = EcoString> + '_> {
iter::repeat(EcoString::from("\n\nHint: "))
.take(typst_hints.len())
.interleave(typst_hints.iter().cloned())
.format("")
}