From c79799c0e418c0c37e4076d653d64e6adaa3378b Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 23 Oct 2021 22:14:56 +0530 Subject: add support for errfmt friendly output --- bin/src/main.rs | 2 +- bin/src/traits.rs | 135 ++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 96 insertions(+), 41 deletions(-) diff --git a/bin/src/main.rs b/bin/src/main.rs index 4cd525a..6f0343e 100644 --- a/bin/src/main.rs +++ b/bin/src/main.rs @@ -49,7 +49,7 @@ fn _main() -> Result<(), StatixErr> { let mut stderr = io::stderr(); lint_results.for_each(|r| { - stderr.write(&r, &vfs).unwrap(); + stderr.write(&r, &vfs, lint_config.format).unwrap(); }); errors.for_each(|e| { eprintln!("{}", e); diff --git a/bin/src/traits.rs b/bin/src/traits.rs index c3427df..5cf7208 100644 --- a/bin/src/traits.rs +++ b/bin/src/traits.rs @@ -3,7 +3,7 @@ use std::{ str, }; -use crate::lint::LintResult; +use crate::{config::OutFormat, lint::LintResult}; use ariadne::{ CharSet, Color, Config as CliConfig, Fmt, Label, LabelAttach, Report as CliReport, @@ -13,55 +13,110 @@ use rnix::TextRange; use vfs::ReadOnlyVfs; pub trait WriteDiagnostic { - fn write(&mut self, report: &LintResult, vfs: &ReadOnlyVfs) -> io::Result<()>; + fn write( + &mut self, + report: &LintResult, + vfs: &ReadOnlyVfs, + format: OutFormat, + ) -> io::Result<()>; } impl WriteDiagnostic for T where T: Write, { - fn write(&mut self, lint_result: &LintResult, vfs: &ReadOnlyVfs) -> io::Result<()> { - let file_id = lint_result.file_id; - let src = str::from_utf8(vfs.get(file_id)).unwrap(); - let path = vfs.file_path(file_id); - let range = |at: TextRange| at.start().into()..at.end().into(); - let src_id = path.to_str().unwrap_or(""); - for report in lint_result.reports.iter() { - let offset = report - .diagnostics - .iter() - .map(|d| d.at.start().into()) - .min() - .unwrap_or(0usize); - report - .diagnostics - .iter() - .fold( - CliReport::build(CliReportKind::Warning, src_id, offset) - .with_config( - CliConfig::default() - .with_cross_gap(true) - .with_multiline_arrows(false) - .with_label_attach(LabelAttach::Middle) - .with_char_set(CharSet::Unicode), - ) - .with_message(report.note) - .with_code(report.code), - |cli_report, diagnostic| { - cli_report.with_label( - Label::new((src_id, range(diagnostic.at))) - .with_message(&colorize(&diagnostic.message)) - .with_color(Color::Magenta), - ) - }, - ) - .finish() - .write((src_id, Source::from(src)), &mut *self)?; + fn write( + &mut self, + lint_result: &LintResult, + vfs: &ReadOnlyVfs, + format: OutFormat, + ) -> io::Result<()> { + match format { + OutFormat::StdErr => write_stderr(self, lint_result, vfs), + OutFormat::Errfmt => write_errfmt(self, lint_result, vfs), + _ => Ok(()), } - Ok(()) } } +fn write_stderr( + writer: &mut T, + lint_result: &LintResult, + vfs: &ReadOnlyVfs, +) -> io::Result<()> { + let file_id = lint_result.file_id; + let src = str::from_utf8(vfs.get(file_id)).unwrap(); + let path = vfs.file_path(file_id); + let range = |at: TextRange| at.start().into()..at.end().into(); + let src_id = path.to_str().unwrap_or(""); + for report in lint_result.reports.iter() { + let offset = report + .diagnostics + .iter() + .map(|d| d.at.start().into()) + .min() + .unwrap_or(0usize); + report + .diagnostics + .iter() + .fold( + CliReport::build(CliReportKind::Warning, src_id, offset) + .with_config( + CliConfig::default() + .with_cross_gap(true) + .with_multiline_arrows(false) + .with_label_attach(LabelAttach::Middle) + .with_char_set(CharSet::Unicode), + ) + .with_message(report.note) + .with_code(report.code), + |cli_report, diagnostic| { + cli_report.with_label( + Label::new((src_id, range(diagnostic.at))) + .with_message(&colorize(&diagnostic.message)) + .with_color(Color::Magenta), + ) + }, + ) + .finish() + .write((src_id, Source::from(src)), &mut *writer)?; + } + Ok(()) +} + +fn write_errfmt(writer: &mut T, lint_result: &LintResult, vfs: &ReadOnlyVfs) -> io::Result<()> { + let file_id = lint_result.file_id; + let src = str::from_utf8(vfs.get(file_id)).unwrap(); + let path = vfs.file_path(file_id); + for report in lint_result.reports.iter() { + for diagnostic in report.diagnostics.iter() { + let line = line(diagnostic.at, &src); + let col = column(diagnostic.at, &src); + writeln!( + writer, + "{filename}>{linenumber}:{columnnumber}:{errortype}:{errornumber}:{errormessage}", + filename = path.to_str().unwrap_or(""), + linenumber = line, + columnnumber = col, + errortype = "W", + errornumber = report.code, + errormessage = diagnostic.message + )?; + } + } + Ok(()) +} + +fn line(at: TextRange, src: &str) -> usize { + let at = at.start().into(); + src[..at].chars().filter(|&c| c == '\n').count() + 1 +} + +fn column(at: TextRange, src: &str) -> usize { + let at = at.start().into(); + src[..at].rfind('\n').map(|c| at - c).unwrap_or(at + 1) +} + // everything within backticks is colorized, backticks are removed fn colorize(message: &str) -> String { message -- cgit v1.2.3