Experimental new report system

This commit is contained in:
ef3d0c3e 2024-10-23 12:50:02 +02:00
parent 352b395929
commit f57173b9be
7 changed files with 209 additions and 42 deletions

View file

@ -230,7 +230,7 @@ impl RegexRule for ScriptRule {
{ {
let parse_source = Rc::new(VirtualSource::new( let parse_source = Rc::new(VirtualSource::new(
Token::new(0..source.content().len(), source.clone()), Token::new(0..source.content().len(), source.clone()),
format!("parse({})", source.name()), format!(":LUA:parse({})", source.name()),
result, result,
)) as Rc<dyn Source>; )) as Rc<dyn Source>;

View file

@ -9,7 +9,6 @@ use crate::lua::kernel::CTX;
use crate::parser::parser::ParseMode; use crate::parser::parser::ParseMode;
use crate::parser::parser::ParserState; use crate::parser::parser::ParserState;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::original_range;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
use crate::parser::state::RuleState; use crate::parser::state::RuleState;

View file

@ -8,10 +8,10 @@ use tower_lsp::lsp_types::SemanticToken;
use tower_lsp::lsp_types::SemanticTokenModifier; use tower_lsp::lsp_types::SemanticTokenModifier;
use tower_lsp::lsp_types::SemanticTokenType; use tower_lsp::lsp_types::SemanticTokenType;
use crate::parser::source::original_range;
use crate::parser::source::LineCursor; use crate::parser::source::LineCursor;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::parser::source::SourcePosition;
use crate::parser::source::VirtualSource; use crate::parser::source::VirtualSource;
pub const TOKEN_TYPE: &[SemanticTokenType] = &[ pub const TOKEN_TYPE: &[SemanticTokenType] = &[
@ -278,7 +278,6 @@ impl<'a> Semantics<'a> {
.map(|parent| parent.location()) .map(|parent| parent.location())
.unwrap_or(None) .unwrap_or(None)
{ {
//let range = location.range.start+range.start..location.range.start+range.end;
return Self::from_source_impl(location.source(), semantics, original_source); return Self::from_source_impl(location.source(), semantics, original_source);
} else if let Some(source) = source.clone().downcast_rc::<SourceFile>().ok() { } else if let Some(source) = source.clone().downcast_rc::<SourceFile>().ok() {
return Ref::filter_map( return Ref::filter_map(
@ -316,7 +315,7 @@ impl<'a> Semantics<'a> {
} }
pub fn add(&self, range: Range<usize>, token: (u32, u32)) { pub fn add(&self, range: Range<usize>, token: (u32, u32)) {
let range = original_range(self.original_source.clone(), range).1; let range = self.original_source.original_range(range).1;
let mut tokens = self.sems.tokens.borrow_mut(); let mut tokens = self.sems.tokens.borrow_mut();
let mut cursor = self.sems.cursor.borrow_mut(); let mut cursor = self.sems.cursor.borrow_mut();
let mut current = cursor.clone(); let mut current = cursor.clone();

View file

@ -7,3 +7,4 @@ pub mod util;
pub mod style; pub mod style;
pub mod layout; pub mod layout;
pub mod customstyle; pub mod customstyle;
pub mod reports;

151
src/parser/reports.rs Normal file
View file

@ -0,0 +1,151 @@
use std::{ops::Range, rc::Rc};
use super::{parser::Parser, source::{Source, SourcePosition, Token}};
#[derive(Debug)]
enum ReportKind
{
Error,
Warning,
}
impl Into<ariadne::ReportKind<'static>> for &ReportKind
{
fn into(self) -> ariadne::ReportKind<'static> {
match self
{
ReportKind::Error => ariadne::ReportKind::Error,
ReportKind::Warning => ariadne::ReportKind::Warning,
}
}
}
#[derive(Debug)]
struct ReportSpan
{
pub token: Token,
pub message: String
}
#[derive(Debug)]
struct Report
{
pub kind: ReportKind,
pub source: Rc<dyn Source>,
pub message: String,
pub spans: Vec<ReportSpan>,
}
impl Report
{
fn ariadne_format(fmt: &str, parser: &dyn Parser) -> String
{
// TODO: Colors
return fmt.to_string();
}
fn ariadne_color(kind: &ReportKind, parser: &dyn Parser) -> ariadne::Color
{
match kind
{
ReportKind::Error => parser.colors().error,
ReportKind::Warning => parser.colors().warning,
}
}
pub fn to_ariadne(self, parser: &dyn Parser) -> ariadne::Report<'static, (Rc<dyn Source>, Range<usize>)>
{
let source = self.source.original_position(0).0;
let mut start = usize::MAX;
for span in &self.spans
{
let (osource, opos) = span.token.source().original_position(span.token.start());
if &osource == &source && opos < start
{
start = opos;
}
}
if start == usize::MAX
{
start = 0;
}
let mut builder = ariadne::Report::build((&self.kind).into(), self.source, start)
.with_message(Self::ariadne_format(self.message.as_str(), parser));
for span in self.spans
{
builder = builder.with_label(
ariadne::Label::new((span.token.source(), span.token.range))
.with_message(Self::ariadne_format(span.message.as_str(), parser))
.with_color(Self::ariadne_color(&self.kind, parser))
)
}
builder.finish()
}
}
macro_rules! report_label {
($spans:expr, $psource:expr,) => {{ }};
($spans:expr, $psource:expr, span($source:expr, $range:expr, $message:expr), $(, $($tail:tt)*)?) => {{
$spans.push(ReportSpan {
token: Token::new($range, $source),
message: $message,
});
report_label!($spans, $psource, $($($tail)*)?);
}};
($spans:expr, $psource:expr, span($range:expr, $message:expr) $(, $($tail:tt)*)?) => {{
$spans.push(ReportSpan {
token: Token::new($range, $psource),
message: $message,
});
report_label!($spans, $psource, $($($tail)*)?);
}}
}
#[macro_export]
macro_rules! report_err {
($reports:expr, $source:expr, $message:expr, $($tail:tt)*) => {{
let mut spans = Vec::new();
report_label!(spans, $source.clone(), $($tail)*);
$reports.push(Report {
kind: ReportKind::Error,
source: $source,
message: $message,
spans,
});
}}
}
#[cfg(test)]
mod tests
{
use crate::parser::source::SourceFile;
use super::*;
#[test]
fn te()
{
let source = Rc::new(SourceFile::with_content(
"".to_string(),
r#"
Sit
Lorem
Ipsum
Dolor
"#
.to_string(),
None,
));
let mut reports = vec![];
//let la = report_label!(source.clone(), 5..9, "Msg".into());
report_err!(&mut reports, source.clone(), "Some message".into(),
span(5..9, "Msg".into()),
span(5..9, "Another".into()),
);
println!("Report = {reports:#?}");
}
}

View file

@ -17,6 +17,17 @@ pub trait Source: Downcast + Debug {
} }
impl_downcast!(Source); impl_downcast!(Source);
pub trait SourcePosition
{
/// Transforms a position to it's position in the oldest parent source
fn original_position(&self, pos: usize) -> (Rc<dyn Source>, usize);
/// Transforms a range to the oldest parent source
///
/// This function takes a range from a source and attempts to get the range's position in the oldest parent
fn original_range(&self, range: Range<usize>) -> (Rc<dyn Source>, Range<usize>);
}
impl core::fmt::Display for dyn Source { impl core::fmt::Display for dyn Source {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name()) write!(f, "{}", self.name())
@ -140,46 +151,55 @@ impl Source for VirtualSource {
fn content(&self) -> &String { &self.content } fn content(&self) -> &String { &self.content }
} }
/// Transforms a position to it's position in the oldest parent source impl SourcePosition for Rc<dyn Source>
pub fn original_position(source: Rc<dyn Source>, mut pos: usize) -> (Rc<dyn Source>, usize)
{ {
fn original_position(&self, mut pos: usize) -> (Rc<dyn Source>, usize) {
// Stop recursion
if self.downcast_ref::<SourceFile>().is_some()
{
return (self.clone(), pos);
}
// Apply offsets // Apply offsets
if let Some(offsets) = if let Some(offsets) =
source.downcast_ref::<VirtualSource>() self.downcast_ref::<VirtualSource>()
.and_then(|source| source.offsets.as_ref()) .and_then(|source| source.offsets.as_ref())
{ {
pos = offsets.position(pos); pos = offsets.position(pos);
} }
// Recurse to parent // Recurse to parent
if let Some(parent) = source.location() if let Some(parent) = self.location()
{ {
return original_position(parent.source.clone(), parent.range.start + pos); return parent.source().original_position(parent.range.start + pos);
} }
return (source, pos); return (self.clone(), pos);
} }
fn original_range(&self, mut range: Range<usize>) -> (Rc<dyn Source>, Range<usize>) {
// Stop recursion
if self.downcast_ref::<SourceFile>().is_some()
{
return (self.clone(), range);
}
/// Transforms a range to the oldest parent source
///
/// This function takes a range from a source and attempts to get the range's position in the oldest parent
pub fn original_range(source: Rc<dyn Source>, mut range: Range<usize>) -> (Rc<dyn Source>, Range<usize>)
{
// Apply offsets // Apply offsets
if let Some(offsets) = if let Some(offsets) =
source.downcast_ref::<VirtualSource>() self.downcast_ref::<VirtualSource>()
.and_then(|source| source.offsets.as_ref()) .and_then(|source| source.offsets.as_ref())
{ {
range = offsets.position(range.start) .. offsets.position(range.end); range = offsets.position(range.start) .. offsets.position(range.end);
} }
// Recurse to parent // Recurse to parent
if let Some(parent) = source.location() if let Some(parent) = self.location()
{ {
return original_range(parent.source.clone(), parent.range.start + range.start..parent.range.start + range.end); return parent.source.original_range(parent.range.start + range.start..parent.range.start + range.end);
} }
return (source, range); return (self.clone(), range);
}
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,5 +1,4 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::iter::Peekable;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
@ -9,12 +8,10 @@ use crate::document::document::Document;
use crate::document::document::DocumentAccessors; use crate::document::document::DocumentAccessors;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use crate::parser::source::original_range;
use super::parser::ParseMode; use super::parser::ParseMode;
use super::parser::ParserState; use super::parser::ParserState;
use super::source::Source; use super::source::Source;
use super::source::SourceFile;
use super::source::Token; use super::source::Token;
use super::source::VirtualSource; use super::source::VirtualSource;