mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-23 04:45:21 +00:00
refactor: prepare for linting on syntaxes (#1640)
* refactor: for query * refactor: for diag * feat: lazy hash expr info * feat: hash resolves * fix: update snapshot
This commit is contained in:
parent
01afa463f4
commit
054d3aecc0
18 changed files with 261 additions and 241 deletions
|
|
@ -4,7 +4,7 @@ use tinymist_project::LspWorld;
|
|||
use tinymist_world::vfs::WorkspaceResolver;
|
||||
use typst::{diag::SourceDiagnostic, syntax::Span};
|
||||
|
||||
use crate::{prelude::*, LspWorldExt};
|
||||
use crate::{analysis::Analysis, prelude::*, LspWorldExt};
|
||||
|
||||
use regex::RegexSet;
|
||||
|
||||
|
|
@ -16,52 +16,44 @@ type TypstSeverity = typst::diag::Severity;
|
|||
|
||||
/// Converts a list of Typst diagnostics to LSP diagnostics,
|
||||
/// with potential refinements on the error messages.
|
||||
pub fn check_doc<'a>(
|
||||
pub fn convert_diagnostics<'a>(
|
||||
world: &LspWorld,
|
||||
errors: impl IntoIterator<Item = &'a TypstDiagnostic>,
|
||||
position_encoding: PositionEncoding,
|
||||
) -> DiagnosticsMap {
|
||||
CheckDocWorker::new(world, position_encoding)
|
||||
.check()
|
||||
.convert_all(errors)
|
||||
let analysis = Analysis {
|
||||
position_encoding,
|
||||
..Analysis::default()
|
||||
};
|
||||
let mut ctx = analysis.enter(world.clone());
|
||||
DiagWorker::new(&mut ctx).convert_all(errors)
|
||||
}
|
||||
|
||||
/// Context for converting Typst diagnostics to LSP diagnostics.
|
||||
pub(crate) struct CheckDocWorker<'a> {
|
||||
/// The worker for collecting diagnostics.
|
||||
pub(crate) struct DiagWorker<'a> {
|
||||
/// The world surface for Typst compiler.
|
||||
pub world: &'a LspWorld,
|
||||
/// The position encoding for the source.
|
||||
pub position_encoding: PositionEncoding,
|
||||
pub ctx: &'a mut LocalContext,
|
||||
/// Results
|
||||
pub results: DiagnosticsMap,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for CheckDocWorker<'_> {
|
||||
type Target = LspWorld;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.world
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w> CheckDocWorker<'w> {
|
||||
impl<'w> DiagWorker<'w> {
|
||||
/// Creates a new `CheckDocWorker` instance.
|
||||
pub fn new(world: &'w LspWorld, position_encoding: PositionEncoding) -> Self {
|
||||
pub fn new(ctx: &'w mut LocalContext) -> Self {
|
||||
Self {
|
||||
world,
|
||||
position_encoding,
|
||||
ctx,
|
||||
results: DiagnosticsMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs code check on the document.
|
||||
pub fn check(mut self) -> Self {
|
||||
for dep in self.world.depended_files() {
|
||||
for dep in self.ctx.world.depended_files() {
|
||||
if WorkspaceResolver::is_package_file(dep) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Ok(source) = self.world.source(dep) else {
|
||||
let Ok(source) = self.ctx.world.source(dep) else {
|
||||
continue;
|
||||
};
|
||||
let res = lint_source(&source);
|
||||
|
|
@ -89,7 +81,7 @@ impl<'w> CheckDocWorker<'w> {
|
|||
|
||||
/// Converts a list of Typst diagnostics to LSP diagnostics.
|
||||
pub fn handle(&mut self, diag: &TypstDiagnostic) {
|
||||
match convert_diagnostic(self, diag) {
|
||||
match self.convert_diagnostic(diag) {
|
||||
Ok((uri, diagnostic)) => {
|
||||
self.results.entry(uri).or_default().push(diagnostic);
|
||||
}
|
||||
|
|
@ -98,121 +90,92 @@ impl<'w> CheckDocWorker<'w> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_diagnostic(
|
||||
ctx: &CheckDocWorker,
|
||||
typst_diagnostic: &TypstDiagnostic,
|
||||
) -> anyhow::Result<(Url, Diagnostic)> {
|
||||
let typst_diagnostic = {
|
||||
let mut diag = Cow::Borrowed(typst_diagnostic);
|
||||
fn convert_diagnostic(
|
||||
&self,
|
||||
typst_diagnostic: &TypstDiagnostic,
|
||||
) -> anyhow::Result<(Url, Diagnostic)> {
|
||||
let typst_diagnostic = {
|
||||
let mut diag = Cow::Borrowed(typst_diagnostic);
|
||||
|
||||
// Extend more refiners here by adding their instances.
|
||||
let refiners: &[&dyn DiagnosticRefiner] =
|
||||
&[&DeprecationRefiner::<13> {}, &OutOfRootHintRefiner {}];
|
||||
// Extend more refiners here by adding their instances.
|
||||
let refiners: &[&dyn DiagnosticRefiner] =
|
||||
&[&DeprecationRefiner::<13> {}, &OutOfRootHintRefiner {}];
|
||||
|
||||
// NOTE: It would be nice to have caching here.
|
||||
for refiner in refiners {
|
||||
if refiner.matches(&diag) {
|
||||
diag = Cow::Owned(refiner.refine(diag.into_owned()));
|
||||
// NOTE: It would be nice to have caching here.
|
||||
for refiner in refiners {
|
||||
if refiner.matches(&diag) {
|
||||
diag = Cow::Owned(refiner.refine(diag.into_owned()));
|
||||
}
|
||||
}
|
||||
}
|
||||
diag
|
||||
};
|
||||
diag
|
||||
};
|
||||
|
||||
let (id, span) = diagnostic_span_id(ctx, &typst_diagnostic);
|
||||
let uri = ctx.uri_for_id(id)?;
|
||||
let source = ctx.source(id)?;
|
||||
let lsp_range = diagnostic_range(&source, span, ctx.position_encoding);
|
||||
let (id, span) = self.diagnostic_span_id(&typst_diagnostic);
|
||||
let uri = self.ctx.uri_for_id(id)?;
|
||||
let source = self.ctx.source_by_id(id)?;
|
||||
let lsp_range = self.diagnostic_range(&source, span);
|
||||
|
||||
let lsp_severity = diagnostic_severity(typst_diagnostic.severity);
|
||||
let lsp_message = diagnostic_message(&typst_diagnostic);
|
||||
let lsp_severity = diagnostic_severity(typst_diagnostic.severity);
|
||||
let lsp_message = diagnostic_message(&typst_diagnostic);
|
||||
|
||||
let tracepoints =
|
||||
diagnostic_related_information(ctx, &typst_diagnostic, ctx.position_encoding)?;
|
||||
let diagnostic = Diagnostic {
|
||||
range: lsp_range,
|
||||
severity: Some(lsp_severity),
|
||||
message: lsp_message,
|
||||
source: Some("typst".to_owned()),
|
||||
related_information: (!typst_diagnostic.trace.is_empty()).then(|| {
|
||||
typst_diagnostic
|
||||
.trace
|
||||
.iter()
|
||||
.flat_map(|tracepoint| self.to_related_info(tracepoint))
|
||||
.collect()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let diagnostic = Diagnostic {
|
||||
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(
|
||||
ctx: &CheckDocWorker,
|
||||
tracepoint: &Spanned<Tracepoint>,
|
||||
position_encoding: PositionEncoding,
|
||||
) -> anyhow::Result<Option<DiagnosticRelatedInformation>> {
|
||||
if let Some(id) = tracepoint.span.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 = to_lsp_range(typst_range, &source, position_encoding);
|
||||
|
||||
return Ok(Some(DiagnosticRelatedInformation {
|
||||
location: LspLocation {
|
||||
uri,
|
||||
range: lsp_range,
|
||||
},
|
||||
message: tracepoint.v.to_string(),
|
||||
}));
|
||||
}
|
||||
Ok((uri, diagnostic))
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
fn to_related_info(
|
||||
&self,
|
||||
tracepoint: &Spanned<Tracepoint>,
|
||||
) -> Option<DiagnosticRelatedInformation> {
|
||||
let id = tracepoint.span.id()?;
|
||||
// todo: expensive uri_for_id
|
||||
let uri = self.ctx.uri_for_id(id).ok()?;
|
||||
let source = self.ctx.source_by_id(id).ok()?;
|
||||
|
||||
fn diagnostic_related_information(
|
||||
project: &CheckDocWorker,
|
||||
typst_diagnostic: &TypstDiagnostic,
|
||||
position_encoding: PositionEncoding,
|
||||
) -> anyhow::Result<Vec<DiagnosticRelatedInformation>> {
|
||||
let mut tracepoints = vec![];
|
||||
let typst_range = source.range(tracepoint.span)?;
|
||||
let lsp_range = self.ctx.to_lsp_range(typst_range, &source);
|
||||
|
||||
for tracepoint in &typst_diagnostic.trace {
|
||||
if let Some(info) =
|
||||
tracepoint_to_relatedinformation(project, tracepoint, position_encoding)?
|
||||
{
|
||||
tracepoints.push(info);
|
||||
}
|
||||
Some(DiagnosticRelatedInformation {
|
||||
location: LspLocation {
|
||||
uri,
|
||||
range: lsp_range,
|
||||
},
|
||||
message: tracepoint.v.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
Ok(tracepoints)
|
||||
}
|
||||
fn diagnostic_span_id(&self, typst_diagnostic: &TypstDiagnostic) -> (TypstFileId, Span) {
|
||||
iter::once(typst_diagnostic.span)
|
||||
.chain(typst_diagnostic.trace.iter().map(|trace| trace.span))
|
||||
.find_map(|span| Some((span.id()?, span)))
|
||||
.unwrap_or_else(|| (self.ctx.world.main(), Span::detached()))
|
||||
}
|
||||
|
||||
fn diagnostic_span_id(
|
||||
ctx: &CheckDocWorker,
|
||||
typst_diagnostic: &TypstDiagnostic,
|
||||
) -> (TypstFileId, Span) {
|
||||
iter::once(typst_diagnostic.span)
|
||||
.chain(typst_diagnostic.trace.iter().map(|trace| trace.span))
|
||||
.find_map(|span| Some((span.id()?, span)))
|
||||
.unwrap_or_else(|| (ctx.main(), Span::detached()))
|
||||
}
|
||||
|
||||
fn diagnostic_range(
|
||||
source: &Source,
|
||||
typst_span: Span,
|
||||
position_encoding: PositionEncoding,
|
||||
) -> LspRange {
|
||||
// Due to nvaner/typst-lsp#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();
|
||||
to_lsp_range(typst_range, source, position_encoding)
|
||||
fn diagnostic_range(&self, source: &Source, typst_span: Span) -> LspRange {
|
||||
// Due to nvaner/typst-lsp#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) => self.ctx.to_lsp_range(node.range(), source),
|
||||
None => LspRange::new(LspPosition::new(0, 0), LspPosition::new(0, 0)),
|
||||
}
|
||||
None => LspRange::new(LspPosition::new(0, 0), LspPosition::new(0, 0)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue