Skip to content
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
25 changes: 25 additions & 0 deletions crates/ruff_db/src/diagnostic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1382,6 +1382,18 @@ impl Display for SubDiagnosticSeverity {
}
}

/// Controls whether colored diagnostic output includes hyperlinks.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum HyperlinkMode {
/// Detect hyperlink support from the environment.
#[default]
Auto,
/// Always emit hyperlinks.
Always,
/// Never emit hyperlinks.
Never,
}

/// Configuration for rendering diagnostics.
#[derive(Clone, Debug)]
pub struct DisplayDiagnosticConfig {
Expand All @@ -1395,6 +1407,10 @@ pub struct DisplayDiagnosticConfig {
///
/// Disabled by default.
color: bool,
/// Whether to emit hyperlinks in colored diagnostic output.
///
/// By default, hyperlink support is detected from the environment.
hyperlinks: HyperlinkMode,
/// Whether to anonymize line numbers in full diagnostic output.
///
/// Disabled by default.
Expand Down Expand Up @@ -1438,6 +1454,7 @@ impl DisplayDiagnosticConfig {
program,
format: DiagnosticFormat::default(),
color: false,
hyperlinks: HyperlinkMode::Auto,
anonymized_line_numbers: false,
context: 2,
merge_window: 2,
Expand All @@ -1460,6 +1477,14 @@ impl DisplayDiagnosticConfig {
DisplayDiagnosticConfig { color: yes, ..self }
}

/// Configures hyperlink rendering for colored diagnostic output.
pub fn hyperlinks(self, mode: HyperlinkMode) -> DisplayDiagnosticConfig {
DisplayDiagnosticConfig {
hyperlinks: mode,
..self
}
}

/// Whether to anonymize line numbers in full diagnostic output.
pub fn anonymized_line_numbers(self, yes: bool) -> DisplayDiagnosticConfig {
DisplayDiagnosticConfig {
Expand Down
2 changes: 1 addition & 1 deletion crates/ruff_db/src/diagnostic/render/concise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl<'a> ConciseRenderer<'a> {
diagnostics: &[Diagnostic],
) -> std::fmt::Result {
let stylesheet = if self.config.color {
DiagnosticStylesheet::styled()
DiagnosticStylesheet::styled().hyperlinks(self.config.hyperlinks)
} else {
DiagnosticStylesheet::plain()
};
Expand Down
2 changes: 1 addition & 1 deletion crates/ruff_db/src/diagnostic/render/full.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl<'a> FullRenderer<'a> {
diagnostics: &[Diagnostic],
) -> std::fmt::Result {
let stylesheet = if self.config.color {
DiagnosticStylesheet::styled()
DiagnosticStylesheet::styled().hyperlinks(self.config.hyperlinks)
} else {
DiagnosticStylesheet::plain()
};
Expand Down
11 changes: 11 additions & 0 deletions crates/ruff_db/src/diagnostic/stylesheet.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use anstyle::{AnsiColor, Effects, Style};
use std::fmt::Formatter;

use crate::diagnostic::HyperlinkMode;

pub(super) const fn fmt_styled<'a, T>(
content: T,
style: anstyle::Style,
Expand Down Expand Up @@ -118,6 +120,15 @@ impl DiagnosticStylesheet {
}
}

pub(super) fn hyperlinks(mut self, mode: HyperlinkMode) -> Self {
match mode {
HyperlinkMode::Auto => {}
HyperlinkMode::Always => self.hyperlink = true,
HyperlinkMode::Never => self.hyperlink = false,
}
self
}

pub fn plain() -> Self {
Self {
error: Style::new(),
Expand Down
2 changes: 1 addition & 1 deletion crates/ty_server/src/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ impl ResolvedClientCapabilities {
if client_capabilities
.experimental
.as_ref()
// Protocol: crates/ty_server/README.md#full-diagnostic-output
// Protocol: https://docs.astral.sh/ty/features/language-server/#full-diagnostic-output
.and_then(|experimental| experimental.get("fullDiagnosticOutput"))
.and_then(serde_json::Value::as_bool)
.unwrap_or_default()
Expand Down
13 changes: 11 additions & 2 deletions crates/ty_server/src/server/api/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use ruff_text_size::Ranged;
use rustc_hash::{FxHashMap, FxHashSet};
use ty_ide::{Hint, hints};

use ruff_db::diagnostic::{Annotation, DisplayDiagnosticConfig, Severity, SubDiagnostic};
use ruff_db::diagnostic::{
Annotation, DisplayDiagnosticConfig, HyperlinkMode, Severity, SubDiagnostic,
};
use ruff_db::files::{File, FileRange};
use ruff_db::source::source_text;
use ruff_db::system::SystemPathBuf;
Expand Down Expand Up @@ -668,7 +670,14 @@ impl DiagnosticData {
rendered: diagnostic
.display(
&(db as &dyn ruff_db::Db),
&DisplayDiagnosticConfig::new("ty").color(false),
&DisplayDiagnosticConfig::new("ty")
.color(true)
// The styled renderer can enable OSC-8 hyperlinks based on the process
// environment, even though this output is sent over LSP rather than to a
// terminal. The ANSI parser used by ty-vscode does not strip OSC-8
// sequences, so their escape codes would appear in the virtual diagnostic
// document.
.hyperlinks(HyperlinkMode::Never),
)
.to_string(),
diagnostic_id: diagnostic.id().to_string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ PublishDiagnosticsParams {
data: Some(
Object {
"diagnostic_id": String("invalid-return-type"),
"rendered": String("error[invalid-return-type]: Return type does not match returned value\n --> src/foo.py:1:14\n |\n1 | def foo() -> str:\n | --- Expected `str` because of return type\n2 | return 42\n | ^^ expected `str`, found `Literal[42]`\n |\n\n"),
"rendered": String("\u{1b}[1m\u{1b}[91merror[invalid-return-type]\u{1b}[0m: \u{1b}[1mReturn type does not match returned value\u{1b}[0m\n \u{1b}[1m\u{1b}[94m-->\u{1b}[0m src/foo.py:1:14\n \u{1b}[1m\u{1b}[94m|\u{1b}[0m\n\u{1b}[1m\u{1b}[94m1 |\u{1b}[0m def foo() -> str:\n \u{1b}[1m\u{1b}[94m|\u{1b}[0m \u{1b}[1m\u{1b}[33m---\u{1b}[0m \u{1b}[1m\u{1b}[33mExpected `str` because of return type\u{1b}[0m\n\u{1b}[1m\u{1b}[94m2 |\u{1b}[0m return 42\n \u{1b}[1m\u{1b}[94m|\u{1b}[0m \u{1b}[1m\u{1b}[91m^^\u{1b}[0m \u{1b}[1m\u{1b}[91mexpected `str`, found `Literal[42]`\u{1b}[0m\n \u{1b}[1m\u{1b}[94m|\u{1b}[0m\n\n"),
},
),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ RelatedFullDocumentDiagnosticReport(
data: Some(
Object {
"diagnostic_id": String("invalid-return-type"),
"rendered": String("error[invalid-return-type]: Return type does not match returned value\n --> src/foo.py:1:14\n |\n1 | def foo() -> str:\n | --- Expected `str` because of return type\n2 | return 42 # after!\n | ^^ expected `str`, found `Literal[42]`\n |\n\n"),
"rendered": String("\u{1b}[1m\u{1b}[91merror[invalid-return-type]\u{1b}[0m: \u{1b}[1mReturn type does not match returned value\u{1b}[0m\n \u{1b}[1m\u{1b}[94m-->\u{1b}[0m src/foo.py:1:14\n \u{1b}[1m\u{1b}[94m|\u{1b}[0m\n\u{1b}[1m\u{1b}[94m1 |\u{1b}[0m def foo() -> str:\n \u{1b}[1m\u{1b}[94m|\u{1b}[0m \u{1b}[1m\u{1b}[33m---\u{1b}[0m \u{1b}[1m\u{1b}[33mExpected `str` because of return type\u{1b}[0m\n\u{1b}[1m\u{1b}[94m2 |\u{1b}[0m return 42 # after!\n \u{1b}[1m\u{1b}[94m|\u{1b}[0m \u{1b}[1m\u{1b}[91m^^\u{1b}[0m \u{1b}[1m\u{1b}[91mexpected `str`, found `Literal[42]`\u{1b}[0m\n \u{1b}[1m\u{1b}[94m|\u{1b}[0m\n\n"),
},
),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ RelatedFullDocumentDiagnosticReport(
data: Some(
Object {
"diagnostic_id": String("invalid-return-type"),
"rendered": String("error[invalid-return-type]: Return type does not match returned value\n --> src/foo.py:1:14\n |\n1 | def foo() -> str:\n | --- Expected `str` because of return type\n2 | return 42 # before\n | ^^ expected `str`, found `Literal[42]`\n |\n\n"),
"rendered": String("\u{1b}[1m\u{1b}[91merror[invalid-return-type]\u{1b}[0m: \u{1b}[1mReturn type does not match returned value\u{1b}[0m\n \u{1b}[1m\u{1b}[94m-->\u{1b}[0m src/foo.py:1:14\n \u{1b}[1m\u{1b}[94m|\u{1b}[0m\n\u{1b}[1m\u{1b}[94m1 |\u{1b}[0m def foo() -> str:\n \u{1b}[1m\u{1b}[94m|\u{1b}[0m \u{1b}[1m\u{1b}[33m---\u{1b}[0m \u{1b}[1m\u{1b}[33mExpected `str` because of return type\u{1b}[0m\n\u{1b}[1m\u{1b}[94m2 |\u{1b}[0m return 42 # before\n \u{1b}[1m\u{1b}[94m|\u{1b}[0m \u{1b}[1m\u{1b}[91m^^\u{1b}[0m \u{1b}[1m\u{1b}[91mexpected `str`, found `Literal[42]`\u{1b}[0m\n \u{1b}[1m\u{1b}[94m|\u{1b}[0m\n\n"),
},
),
},
Expand Down
Loading