diff --git a/crates/ruff_db/src/diagnostic/mod.rs b/crates/ruff_db/src/diagnostic/mod.rs index 4dd9b52e76..3b99831865 100644 --- a/crates/ruff_db/src/diagnostic/mod.rs +++ b/crates/ruff_db/src/diagnostic/mod.rs @@ -1237,7 +1237,14 @@ pub enum DiagnosticFormat { /// [Azure Pipelines]: https://learn.microsoft.com/en-us/azure/devops/pipelines/scripts/logging-commands?view=azure-devops&tabs=bash#logissue-log-an-error-or-warning Azure, /// Print diagnostics in JSON format. + /// + /// Unlike `json-lines`, this prints all of the diagnostics as a JSON array. Json, + /// Print diagnostics in JSON format, one per line. + /// + /// This will print each diagnostic as a separate JSON object on its own line. See the `json` + /// format for an array of all diagnostics. + JsonLines, } impl DiagnosticFormat { diff --git a/crates/ruff_db/src/diagnostic/render.rs b/crates/ruff_db/src/diagnostic/render.rs index fe0f127d0c..02fb2f68e6 100644 --- a/crates/ruff_db/src/diagnostic/render.rs +++ b/crates/ruff_db/src/diagnostic/render.rs @@ -63,52 +63,6 @@ impl<'a> DisplayDiagnostic<'a> { } } -/// A type that implements `std::fmt::Display` for rendering a collection of diagnostics. -/// -/// It intended for collections of diagnostics that need to be serialized together, as is the case -/// for JSON, for example. -/// -/// See [`DisplayDiagnostic`] for rendering individual `Diagnostic`s and details about the lifetime -/// constraints. -pub struct DisplayDiagnostics<'a> { - config: &'a DisplayDiagnosticConfig, - resolver: &'a dyn FileResolver, - diagnostics: &'a [Diagnostic], -} - -impl<'a> DisplayDiagnostics<'a> { - pub fn new( - resolver: &'a dyn FileResolver, - config: &'a DisplayDiagnosticConfig, - diagnostics: &'a [Diagnostic], - ) -> DisplayDiagnostics<'a> { - DisplayDiagnostics { - config, - resolver, - diagnostics, - } - } -} - -impl std::fmt::Display for DisplayDiagnostics<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self.config.format { - DiagnosticFormat::Concise | DiagnosticFormat::Azure | DiagnosticFormat::Full => { - for diag in self.diagnostics { - write!(f, "{}", diag.display(self.resolver, self.config))?; - } - } - DiagnosticFormat::Json => write!( - f, - "{:#}", - diagnostics_to_json_value(self.diagnostics, self.resolver) - )?, - } - - Ok(()) - } -} - impl std::fmt::Display for DisplayDiagnostic<'_> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let stylesheet = if self.config.color { @@ -187,11 +141,15 @@ impl std::fmt::Display for DisplayDiagnostic<'_> { body = self.diag.body(), )?; } - DiagnosticFormat::Json => { + DiagnosticFormat::JsonLines => { if let Some(value) = message_to_json_value(self.diag, self.resolver) { writeln!(f, "{value}")?; } } + DiagnosticFormat::Json => { + let value = diagnostics_to_json_value(std::iter::once(self.diag), self.resolver); + writeln!(f, "{value}")?; + } DiagnosticFormat::Full => { let mut renderer = self.annotate_renderer.clone(); renderer = renderer @@ -217,6 +175,55 @@ impl std::fmt::Display for DisplayDiagnostic<'_> { } } +/// A type that implements `std::fmt::Display` for rendering a collection of diagnostics. +/// +/// It intended for collections of diagnostics that need to be serialized together, as is the case +/// for JSON, for example. +/// +/// See [`DisplayDiagnostic`] for rendering individual `Diagnostic`s and details about the lifetime +/// constraints. +pub struct DisplayDiagnostics<'a> { + config: &'a DisplayDiagnosticConfig, + resolver: &'a dyn FileResolver, + diagnostics: &'a [Diagnostic], +} + +impl<'a> DisplayDiagnostics<'a> { + pub fn new( + resolver: &'a dyn FileResolver, + config: &'a DisplayDiagnosticConfig, + diagnostics: &'a [Diagnostic], + ) -> DisplayDiagnostics<'a> { + DisplayDiagnostics { + config, + resolver, + diagnostics, + } + } +} + +impl std::fmt::Display for DisplayDiagnostics<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self.config.format { + DiagnosticFormat::Concise + | DiagnosticFormat::Azure + | DiagnosticFormat::Full + | DiagnosticFormat::JsonLines => { + for diag in self.diagnostics { + write!(f, "{}", diag.display(self.resolver, self.config))?; + } + } + DiagnosticFormat::Json => write!( + f, + "{:#}", + diagnostics_to_json_value(self.diagnostics, self.resolver) + )?, + } + + Ok(()) + } +} + /// A sequence of resolved diagnostics. /// /// Resolving a diagnostic refers to the process of restructuring its internal diff --git a/crates/ruff_linter/src/message/json_lines.rs b/crates/ruff_linter/src/message/json_lines.rs index c34cc5ee34..f7de98a50a 100644 --- a/crates/ruff_linter/src/message/json_lines.rs +++ b/crates/ruff_linter/src/message/json_lines.rs @@ -14,7 +14,7 @@ impl Emitter for JsonLinesEmitter { diagnostics: &[Diagnostic], context: &EmitterContext, ) -> anyhow::Result<()> { - let config = DisplayDiagnosticConfig::default().format(DiagnosticFormat::Json); + let config = DisplayDiagnosticConfig::default().format(DiagnosticFormat::JsonLines); for diagnostic in diagnostics { write!(writer, "{}", diagnostic.display(context, &config))?; } diff --git a/ty.schema.json b/ty.schema.json index fd8f68cbcc..a63b646a83 100644 --- a/ty.schema.json +++ b/ty.schema.json @@ -83,11 +83,18 @@ ] }, { - "description": "Print diagnostics in JSON format.", + "description": "Print diagnostics in JSON format.\n\nUnlike `json-lines`, this prints all of the diagnostics as a JSON array.", "type": "string", "enum": [ "json" ] + }, + { + "description": "Print diagnostics in JSON format, one per line.\n\nThis will print each diagnostic as a separate JSON object on its own line. See the `json` format for an array of all diagnostics.", + "type": "string", + "enum": [ + "json-lines" + ] } ] },