Merge pull request #2415 from zed-industries/diagnostic-hovers-source

Julia created

Show source of diagnostic on hover

Change summary

crates/editor/src/hover_popover.rs     | 14 ++++++++++++--
crates/language/src/buffer.rs          |  2 ++
crates/language/src/proto.rs           |  2 ++
crates/project/src/project.rs          |  2 ++
crates/project/src/project_tests.rs    |  6 ++++++
crates/rpc/proto/zed.proto             | 17 +++++++++--------
crates/rpc/src/rpc.rs                  |  2 +-
crates/theme/src/theme.rs              |  1 +
crates/zed/src/languages.rs            | 23 +++++++++++++----------
crates/zed/src/languages/typescript.rs |  4 ----
styles/src/styleTree/hoverPopover.ts   |  5 +++--
11 files changed, 51 insertions(+), 27 deletions(-)

Detailed changes

crates/editor/src/hover_popover.rs 🔗

@@ -378,6 +378,17 @@ impl DiagnosticPopover {
 
         let mut text_style = style.hover_popover.prose.clone();
         text_style.font_size = style.text.font_size;
+        let diagnostic_source_style = style.hover_popover.diagnostic_source_highlight.clone();
+
+        let text = match &self.local_diagnostic.diagnostic.source {
+            Some(source) => Text::new(
+                format!("{source}: {}", self.local_diagnostic.diagnostic.message),
+                text_style,
+            )
+            .with_highlights(vec![(0..source.len(), diagnostic_source_style)]),
+
+            None => Text::new(self.local_diagnostic.diagnostic.message.clone(), text_style),
+        };
 
         let container_style = match self.local_diagnostic.diagnostic.severity {
             DiagnosticSeverity::HINT => style.hover_popover.info_container,
@@ -390,8 +401,7 @@ impl DiagnosticPopover {
         let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
 
         MouseEventHandler::<DiagnosticPopover, _>::new(0, cx, |_, _| {
-            Text::new(self.local_diagnostic.diagnostic.message.clone(), text_style)
-                .with_soft_wrap(true)
+            text.with_soft_wrap(true)
                 .contained()
                 .with_style(container_style)
         })

crates/language/src/buffer.rs 🔗

@@ -138,6 +138,7 @@ pub struct GroupId {
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct Diagnostic {
+    pub source: Option<String>,
     pub code: Option<String>,
     pub severity: DiagnosticSeverity,
     pub message: String,
@@ -2881,6 +2882,7 @@ impl operation_queue::Operation for Operation {
 impl Default for Diagnostic {
     fn default() -> Self {
         Self {
+            source: Default::default(),
             code: None,
             severity: DiagnosticSeverity::ERROR,
             message: Default::default(),

crates/language/src/proto.rs 🔗

@@ -173,6 +173,7 @@ pub fn serialize_diagnostics<'a>(
     diagnostics
         .into_iter()
         .map(|entry| proto::Diagnostic {
+            source: entry.diagnostic.source.clone(),
             start: Some(serialize_anchor(&entry.range.start)),
             end: Some(serialize_anchor(&entry.range.end)),
             message: entry.diagnostic.message.clone(),
@@ -359,6 +360,7 @@ pub fn deserialize_diagnostics(
             Some(DiagnosticEntry {
                 range: deserialize_anchor(diagnostic.start?)?..deserialize_anchor(diagnostic.end?)?,
                 diagnostic: Diagnostic {
+                    source: diagnostic.source,
                     severity: match proto::diagnostic::Severity::from_i32(diagnostic.severity)? {
                         proto::diagnostic::Severity::Error => DiagnosticSeverity::ERROR,
                         proto::diagnostic::Severity::Warning => DiagnosticSeverity::WARNING,

crates/project/src/project.rs 🔗

@@ -2954,6 +2954,7 @@ impl Project {
                 diagnostics.push(DiagnosticEntry {
                     range,
                     diagnostic: Diagnostic {
+                        source: diagnostic.source.clone(),
                         code: code.clone(),
                         severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR),
                         message: diagnostic.message.clone(),
@@ -2971,6 +2972,7 @@ impl Project {
                             diagnostics.push(DiagnosticEntry {
                                 range,
                                 diagnostic: Diagnostic {
+                                    source: diagnostic.source.clone(),
                                     code: code.clone(),
                                     severity: DiagnosticSeverity::INFORMATION,
                                     message: info.message.clone(),

crates/project/src/project_tests.rs 🔗

@@ -1190,6 +1190,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
                 DiagnosticEntry {
                     range: Point::new(3, 9)..Point::new(3, 11),
                     diagnostic: Diagnostic {
+                        source: Some("disk".into()),
                         severity: DiagnosticSeverity::ERROR,
                         message: "undefined variable 'BB'".to_string(),
                         is_disk_based: true,
@@ -1201,6 +1202,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
                 DiagnosticEntry {
                     range: Point::new(4, 9)..Point::new(4, 12),
                     diagnostic: Diagnostic {
+                        source: Some("disk".into()),
                         severity: DiagnosticSeverity::ERROR,
                         message: "undefined variable 'CCC'".to_string(),
                         is_disk_based: true,
@@ -1266,6 +1268,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
                 DiagnosticEntry {
                     range: Point::new(2, 9)..Point::new(2, 12),
                     diagnostic: Diagnostic {
+                        source: Some("disk".into()),
                         severity: DiagnosticSeverity::WARNING,
                         message: "unreachable statement".to_string(),
                         is_disk_based: true,
@@ -1277,6 +1280,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
                 DiagnosticEntry {
                     range: Point::new(2, 9)..Point::new(2, 10),
                     diagnostic: Diagnostic {
+                        source: Some("disk".into()),
                         severity: DiagnosticSeverity::ERROR,
                         message: "undefined variable 'A'".to_string(),
                         is_disk_based: true,
@@ -1356,6 +1360,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
                 DiagnosticEntry {
                     range: Point::new(2, 21)..Point::new(2, 22),
                     diagnostic: Diagnostic {
+                        source: Some("disk".into()),
                         severity: DiagnosticSeverity::WARNING,
                         message: "undefined variable 'A'".to_string(),
                         is_disk_based: true,
@@ -1367,6 +1372,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
                 DiagnosticEntry {
                     range: Point::new(3, 9)..Point::new(3, 14),
                     diagnostic: Diagnostic {
+                        source: Some("disk".into()),
                         severity: DiagnosticSeverity::ERROR,
                         message: "undefined variable 'BB'".to_string(),
                         is_disk_based: true,

crates/rpc/proto/zed.proto 🔗

@@ -1049,14 +1049,15 @@ enum Bias {
 message Diagnostic {
     Anchor start = 1;
     Anchor end = 2;
-    Severity severity = 3;
-    string message = 4;
-    optional string code = 5;
-    uint64 group_id = 6;
-    bool is_primary = 7;
-    bool is_valid = 8;
-    bool is_disk_based = 9;
-    bool is_unnecessary = 10;
+    optional string source = 3;
+    Severity severity = 4;
+    string message = 5;
+    optional string code = 6;
+    uint64 group_id = 7;
+    bool is_primary = 8;
+    bool is_valid = 9;
+    bool is_disk_based = 10;
+    bool is_unnecessary = 11;
 
     enum Severity {
         None = 0;

crates/rpc/src/rpc.rs 🔗

@@ -6,4 +6,4 @@ pub use conn::Connection;
 pub use peer::*;
 mod macros;
 
-pub const PROTOCOL_VERSION: u32 = 52;
+pub const PROTOCOL_VERSION: u32 = 53;

crates/theme/src/theme.rs 🔗

@@ -887,6 +887,7 @@ pub struct HoverPopover {
     pub error_container: ContainerStyle,
     pub block_style: ContainerStyle,
     pub prose: TextStyle,
+    pub diagnostic_source_highlight: HighlightStyle,
     pub highlight: Color,
 }
 

crates/zed/src/languages.rs 🔗

@@ -89,23 +89,26 @@ pub fn init(
         (
             "tsx",
             tree_sitter_typescript::language_tsx(),
-            vec![adapter_arc(typescript::TypeScriptLspAdapter::new(
-                node_runtime.clone(),
-            ))],
+            vec![
+                adapter_arc(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
+                adapter_arc(typescript::EsLintLspAdapter::new(node_runtime.clone())),
+            ],
         ),
         (
             "typescript",
             tree_sitter_typescript::language_typescript(),
-            vec![adapter_arc(typescript::TypeScriptLspAdapter::new(
-                node_runtime.clone(),
-            ))],
+            vec![
+                adapter_arc(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
+                adapter_arc(typescript::EsLintLspAdapter::new(node_runtime.clone())),
+            ],
         ),
         (
             "javascript",
             tree_sitter_typescript::language_tsx(),
-            vec![adapter_arc(typescript::TypeScriptLspAdapter::new(
-                node_runtime.clone(),
-            ))],
+            vec![
+                adapter_arc(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
+                adapter_arc(typescript::EsLintLspAdapter::new(node_runtime.clone())),
+            ],
         ),
         (
             "html",
@@ -132,7 +135,7 @@ pub fn init(
         (
             "yaml",
             tree_sitter_yaml::language(),
-            vec![adapter_arc(yaml::YamlLspAdapter::new(node_runtime.clone()))],
+            vec![adapter_arc(yaml::YamlLspAdapter::new(node_runtime))],
         ),
     ];
 

crates/zed/src/languages/typescript.rs 🔗

@@ -206,10 +206,6 @@ impl LspAdapter for EsLintLspAdapter {
                         "shortenToSingleLine": false
                       },
                       "nodePath": null,
-                      "workspaceFolder": {
-                        "name": "testing_ts",
-                        "uri": "file:///Users/julia/Stuff/testing_ts"
-                      },
                       "codeAction": {
                         "disableRuleComment": {
                           "enable": true,

styles/src/styleTree/hoverPopover.ts 🔗

@@ -1,5 +1,5 @@
 import { ColorScheme } from "../themes/common/colorScheme"
-import { background, border, text } from "./components"
+import { background, border, foreground, text } from "./components"
 
 export default function HoverPopover(colorScheme: ColorScheme) {
     let layer = colorScheme.middle
@@ -36,10 +36,11 @@ export default function HoverPopover(colorScheme: ColorScheme) {
             background: background(layer, "negative"),
             border: border(layer, "negative"),
         },
-        block_style: {
+        blockStyle: {
             padding: { top: 4 },
         },
         prose: text(layer, "sans", { size: "sm" }),
+        diagnosticSourceHighlight: { underline: true, color: foreground(layer, "accent") },
         highlight: colorScheme.ramps.neutral(0.5).alpha(0.2).hex(), // TODO: blend was used here. Replace with something better
     }
 }