Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(linter): add hyperlinks to diagnostic messages #5318

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions crates/oxc_diagnostics/src/graphic_reporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,12 +271,24 @@ impl GraphicalReportHandler {
opts = opts.word_splitter(word_splitter);
}

let title = if let Some(code) = diagnostic.code() {
format!("{code}: {diagnostic}")
} else {
diagnostic.to_string()
let title = match (self.links, diagnostic.url(), diagnostic.code()) {
Boshen marked this conversation as resolved.
Show resolved Hide resolved
(LinkStyle::Link, Some(url), Some(code)) => {
// magic unicode escape sequences to make the terminal print a hyperlink
const CTL: &str = "\u{1b}]8;;";
const END: &str = "\u{1b}]8;;\u{1b}\\";
let code = code.style(severity_style);
let message = diagnostic.to_string();
let title = message.style(severity_style);
format!("{CTL}{url}\u{1b}\\{code}{END}: {title}",)
}
(_, _, Some(code)) => {
let title = format!("{code}: {}", diagnostic);
format!("{}", title.style(severity_style))
}
_ => {
format!("{}", diagnostic.to_string().style(severity_style))
}
};
let title = format!("{}", title.style(severity_style));
let title = textwrap::fill(&title, opts);
writeln!(f, "{}", title)?;

Expand Down
11 changes: 11 additions & 0 deletions crates/oxc_diagnostics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub struct OxcDiagnosticInner {
pub help: Option<Cow<'static, str>>,
pub severity: Severity,
pub code: OxcCode,
pub url: Option<Cow<'static, str>>,
}

impl fmt::Display for OxcDiagnostic {
Expand Down Expand Up @@ -101,6 +102,9 @@ impl Diagnostic for OxcDiagnostic {
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
self.code.is_some().then(|| Box::new(&self.code) as Box<dyn Display>)
}
fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
self.url.as_ref().map(Box::new).map(|c| c as Box<dyn Display>)
}
}

impl OxcDiagnostic {
Expand All @@ -112,6 +116,7 @@ impl OxcDiagnostic {
help: None,
severity: Severity::Error,
code: OxcCode::default(),
url: None,
}),
}
}
Expand All @@ -124,6 +129,7 @@ impl OxcDiagnostic {
help: None,
severity: Severity::Warning,
code: OxcCode::default(),
url: None,
}),
}
}
Expand Down Expand Up @@ -205,6 +211,11 @@ impl OxcDiagnostic {
self
}

pub fn with_url<S: Into<Cow<'static, str>>>(mut self, url: S) -> Self {
self.inner.url = Some(url.into());
self
}

pub fn with_source_code<T: SourceCode + Send + Sync + 'static>(self, code: T) -> Error {
Error::from(self).with_source_code(code)
}
Expand Down
30 changes: 21 additions & 9 deletions crates/oxc_linter/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub struct LintContext<'a> {
eslint_config: Arc<OxlintConfig>,

// states
current_plugin_name: &'static str,
current_plugin_prefix: &'static str,
current_rule_name: &'static str,
#[cfg(debug_assertions)]
Expand All @@ -60,6 +61,7 @@ pub struct LintContext<'a> {
}

impl<'a> LintContext<'a> {
const WEBSITE_BASE_URL: &'static str = "https://oxc.rs/docs/guide/usage/linter/rules";
/// # Panics
/// If `semantic.cfg()` is `None`.
pub fn new(file_path: Box<Path>, semantic: Rc<Semantic<'a>>) -> Self {
Expand All @@ -81,6 +83,7 @@ impl<'a> LintContext<'a> {
fix: FixKind::None,
file_path: file_path.into(),
eslint_config: Arc::new(OxlintConfig::default()),
current_plugin_name: "eslint",
current_plugin_prefix: "eslint",
current_rule_name: "",
#[cfg(debug_assertions)]
Expand All @@ -102,6 +105,7 @@ impl<'a> LintContext<'a> {
}

pub fn with_plugin_name(mut self, plugin: &'static str) -> Self {
self.current_plugin_name = plugin;
self.current_plugin_prefix = plugin_name_to_prefix(plugin);
self
}
Expand Down Expand Up @@ -209,16 +213,24 @@ impl<'a> LintContext<'a> {
self.diagnostics.borrow().iter().cloned().collect::<Vec<_>>()
}

fn add_diagnostic(&self, message: Message<'a>) {
if !self.disable_directives.contains(self.current_rule_name, message.span()) {
let mut message = message;
message.error =
message.error.with_error_code(self.current_plugin_prefix, self.current_rule_name);
if message.error.severity != self.severity {
message.error = message.error.with_severity(self.severity);
}
self.diagnostics.borrow_mut().push(message);
fn add_diagnostic(&self, mut message: Message<'a>) {
if self.disable_directives.contains(self.current_rule_name, message.span()) {
return;
}
message.error = message
.error
.with_error_code(self.current_plugin_prefix, self.current_rule_name)
.with_url(format!(
"{}/{}/{}.html",
Self::WEBSITE_BASE_URL,
self.current_plugin_name,
self.current_rule_name
));
if message.error.severity != self.severity {
message.error = message.error.with_severity(self.severity);
}

self.diagnostics.borrow_mut().push(message);
}

/// Report a lint rule violation.
Expand Down
4 changes: 3 additions & 1 deletion crates/oxc_linter/src/tester.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,9 @@ impl Tester {
}
.to_string_lossy();

let handler = GraphicalReportHandler::new().with_theme(GraphicalTheme::unicode_nocolor());
let handler = GraphicalReportHandler::new()
.with_links(false)
.with_theme(GraphicalTheme::unicode_nocolor());
for diagnostic in result {
let diagnostic = diagnostic.error.with_source_code(NamedSource::new(
diagnostic_path.clone(),
Expand Down