WIP WIPW IPW

Conrad Irwin created

t

Change summary

Cargo.lock                                    |  1 
crates/diagnostics/src/diagnostic_renderer.rs | 23 +++++++--
crates/diagnostics/src/diagnostics.rs         | 10 ++++
crates/editor/src/editor.rs                   | 18 ++++++-
crates/editor/src/hover_popover.rs            |  9 +++
crates/languages/Cargo.toml                   |  1 
crates/languages/src/vtsls.rs                 | 45 ++++++++++++++++++--
crates/markdown/src/markdown.rs               | 18 +++++++
8 files changed, 106 insertions(+), 19 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -9134,6 +9134,7 @@ dependencies = [
  "futures 0.3.31",
  "gpui",
  "http_client",
+ "indoc",
  "language",
  "log",
  "lsp",

crates/diagnostics/src/diagnostic_renderer.rs 🔗

@@ -6,7 +6,7 @@ use editor::{
     hover_popover::diagnostics_markdown_style,
 };
 use gpui::{AppContext, Entity, Focusable, WeakEntity};
-use language::{BufferId, Diagnostic, DiagnosticEntry};
+use language::{BufferId, Diagnostic, DiagnosticEntry, LanguageRegistry};
 use lsp::DiagnosticSeverity;
 use markdown::{Markdown, MarkdownElement};
 use settings::Settings;
@@ -27,6 +27,7 @@ impl DiagnosticRenderer {
         diagnostic_group: Vec<DiagnosticEntry<Point>>,
         buffer_id: BufferId,
         diagnostics_editor: Option<WeakEntity<ProjectDiagnosticsEditor>>,
+        languages: Arc<LanguageRegistry>,
         cx: &mut App,
     ) -> Vec<DiagnosticBlock> {
         let Some(primary_ix) = diagnostic_group
@@ -79,7 +80,9 @@ impl DiagnosticRenderer {
                     initial_range: primary.range.clone(),
                     severity: primary.diagnostic.severity,
                     diagnostics_editor: diagnostics_editor.clone(),
-                    markdown: cx.new(|cx| Markdown::new(markdown.into(), None, None, cx)),
+                    markdown: cx.new(|cx| {
+                        Markdown::new(markdown.into(), Some(languages.clone()), None, cx)
+                    }),
                 });
             } else if entry.range.start.row.abs_diff(primary.range.start.row) < 5 {
                 let markdown = Self::markdown(&entry.diagnostic);
@@ -88,7 +91,9 @@ impl DiagnosticRenderer {
                     initial_range: entry.range.clone(),
                     severity: entry.diagnostic.severity,
                     diagnostics_editor: diagnostics_editor.clone(),
-                    markdown: cx.new(|cx| Markdown::new(markdown.into(), None, None, cx)),
+                    markdown: cx.new(|cx| {
+                        Markdown::new(markdown.into(), Some(languages.clone()), None, cx)
+                    }),
                 });
             } else {
                 let mut markdown = Self::markdown(&entry.diagnostic);
@@ -100,7 +105,9 @@ impl DiagnosticRenderer {
                     initial_range: entry.range.clone(),
                     severity: entry.diagnostic.severity,
                     diagnostics_editor: diagnostics_editor.clone(),
-                    markdown: cx.new(|cx| Markdown::new(markdown.into(), None, None, cx)),
+                    markdown: cx.new(|cx| {
+                        Markdown::new(markdown.into(), Some(languages.clone()), None, cx)
+                    }),
                 });
             }
         }
@@ -127,9 +134,11 @@ impl editor::DiagnosticRenderer for DiagnosticRenderer {
         buffer_id: BufferId,
         snapshot: EditorSnapshot,
         editor: WeakEntity<Editor>,
+        languages: Arc<LanguageRegistry>,
         cx: &mut App,
     ) -> Vec<BlockProperties<Anchor>> {
-        let blocks = Self::diagnostic_blocks_for_group(diagnostic_group, buffer_id, None, cx);
+        let blocks =
+            Self::diagnostic_blocks_for_group(diagnostic_group, buffer_id, None, languages, cx);
         blocks
             .into_iter()
             .map(|block| {
@@ -155,9 +164,11 @@ impl editor::DiagnosticRenderer for DiagnosticRenderer {
         diagnostic_group: Vec<DiagnosticEntry<Point>>,
         range: Range<Point>,
         buffer_id: BufferId,
+        languages: Arc<LanguageRegistry>,
         cx: &mut App,
     ) -> Option<Entity<Markdown>> {
-        let blocks = Self::diagnostic_blocks_for_group(diagnostic_group, buffer_id, None, cx);
+        let blocks =
+            Self::diagnostic_blocks_for_group(diagnostic_group, buffer_id, None, languages, cx);
         blocks.into_iter().find_map(|block| {
             if block.initial_range == range {
                 Some(block.markdown)

crates/diagnostics/src/diagnostics.rs 🔗

@@ -508,6 +508,15 @@ impl ProjectDiagnosticsEditor {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
+        let languages = self
+            .editor
+            .read(cx)
+            .project
+            .as_ref()
+            .unwrap()
+            .read(cx)
+            .languages()
+            .clone();
         let was_empty = self.multibuffer.read(cx).is_empty();
         let buffer_snapshot = buffer.read(cx).snapshot();
         let buffer_id = buffer_snapshot.remote_id();
@@ -559,6 +568,7 @@ impl ProjectDiagnosticsEditor {
                         group,
                         buffer_snapshot.remote_id(),
                         Some(this.clone()),
+                        languages.clone(),
                         cx,
                     )
                 })?;

crates/editor/src/editor.rs 🔗

@@ -111,8 +111,9 @@ use itertools::Itertools;
 use language::{
     AutoindentMode, BracketMatch, BracketPair, Buffer, Capability, CharKind, CodeLabel,
     CursorShape, DiagnosticEntry, DiffOptions, DocumentationConfig, EditPredictionsMode,
-    EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
-    Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery,
+    EditPreview, HighlightedText, IndentKind, IndentSize, Language, LanguageRegistry,
+    OffsetRangeExt, Point, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
+    WordsQuery,
     language_settings::{
         self, InlayHintSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
         all_language_settings, language_settings,
@@ -402,6 +403,7 @@ pub trait DiagnosticRenderer {
         buffer_id: BufferId,
         snapshot: EditorSnapshot,
         editor: WeakEntity<Editor>,
+        languages: Arc<LanguageRegistry>,
         cx: &mut App,
     ) -> Vec<BlockProperties<Anchor>>;
 
@@ -410,6 +412,7 @@ pub trait DiagnosticRenderer {
         diagnostic_group: Vec<DiagnosticEntry<Point>>,
         range: Range<Point>,
         buffer_id: BufferId,
+        languages: Arc<LanguageRegistry>,
         cx: &mut App,
     ) -> Option<Entity<markdown::Markdown>>;
 
@@ -16574,13 +16577,20 @@ impl Editor {
         let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
             return;
         };
+        let languages = self.project.as_ref().unwrap().read(cx).languages().clone();
 
         let diagnostic_group = buffer
             .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
             .collect::<Vec<_>>();
 
-        let blocks =
-            renderer.render_group(diagnostic_group, buffer_id, snapshot, cx.weak_entity(), cx);
+        let blocks = renderer.render_group(
+            diagnostic_group,
+            buffer_id,
+            snapshot,
+            cx.weak_entity(),
+            languages,
+            cx,
+        );
 
         let blocks = self.display_map.update(cx, |display_map, cx| {
             display_map.insert_blocks(blocks, cx).into_iter().collect()

crates/editor/src/hover_popover.rs 🔗

@@ -275,6 +275,13 @@ fn show_hover(
             return None;
         }
     }
+    let languages = editor
+        .project
+        .as_ref()
+        .unwrap()
+        .read(cx)
+        .languages()
+        .clone();
 
     let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay;
     let all_diagnostics_active = editor.active_diagnostics == ActiveDiagnostic::All;
@@ -340,7 +347,7 @@ fn show_hover(
                     renderer
                         .as_ref()
                         .and_then(|renderer| {
-                            renderer.render_hover(group, point_range, buffer_id, cx)
+                            renderer.render_hover(group, point_range, buffer_id, languages, cx)
                         })
                         .context("no rendered diagnostic")
                 })??;

crates/languages/Cargo.toml 🔗

@@ -44,6 +44,7 @@ dap.workspace = true
 futures.workspace = true
 gpui.workspace = true
 http_client.workspace = true
+indoc.workspace = true
 language.workspace = true
 log.workspace = true
 lsp.workspace = true

crates/languages/src/vtsls.rs 🔗

@@ -283,26 +283,35 @@ impl LspAdapter for VtslsLspAdapter {
 
     fn diagnostic_message_to_markdown(&self, message: &str) -> Option<String> {
         use regex::{Captures, Regex};
+        dbg!(&message);
 
         // Helper functions for formatting
         let format_type_block = |prefix: &str, content: &str| -> String {
             if prefix.is_empty() {
                 if content.len() > 50 || content.contains('\n') || content.contains('`') {
-                    format!("\n```typescript\n{}\n```\n", content)
+                    format!("\n```typescript\ntype a ={}\n```\n", dbg!(content))
                 } else {
-                    format!("`{}`", content)
+                    format!("`{}`", dbg!(content))
                 }
             } else {
-                format!("{} `{}`", prefix, content)
+                if content.len() > 50 || content.contains('\n') || content.contains('`') {
+                    format!(
+                        "{}\n```typescript\ntype a ={}\n```\n",
+                        prefix,
+                        dbg!(content)
+                    )
+                } else {
+                    format!("{} `{}`", prefix, dbg!(content))
+                }
             }
         };
 
         let format_typescript_block =
-            |content: &str| -> String { format!("\n\n```typescript\n{}\n```\n", content) };
+            |content: &str| -> String { format!("\n\n```typescript\n{}\n```\n", dbg!(content)) };
 
-        let format_simple_type_block = |content: &str| -> String { format!("`{}`", content) };
+        let format_simple_type_block = |content: &str| -> String { format!("`{}`", dbg!(content)) };
 
-        let unstyle_code_block = |content: &str| -> String { format!("`{}`", content) };
+        let unstyle_code_block = |content: &str| -> String { format!("`{}`", dbg!(content)) };
 
         let mut result = message.to_string();
 
@@ -396,9 +405,11 @@ impl LspAdapter for VtslsLspAdapter {
             .to_string();
 
         // Format types
+        dbg!(&result);
         let re = Regex::new(r#"(?i)(type|type alias|interface|module|file|file name|class|method's|subtype of constraint) ['"](.*?)['"]"#).unwrap();
         result = re
             .replace_all(&result, |caps: &Captures| {
+                dbg!(&caps);
                 format_type_block(&caps[1], &caps[2])
             })
             .to_string();
@@ -469,3 +480,25 @@ async fn get_cached_ts_server_binary(
     .await
     .log_err()
 }
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use indoc::indoc;
+
+    #[test]
+    fn test_diagnostic_message_to_markdown() {
+        let message = "Property 'user' is missing in type '{ person: { username: string; email: string; }; }' but required in type '{ user: { name: string; email: `${string}@${string}.${string}`; age: number; }; }'.";
+        let expected = indoc! { "
+            Property `user` is missing in type `{ person: { username: string; email: string; }; }` but required in type
+
+            ```typescript
+            { user: { name: string; email: `${string}@${string}.${string}`; age: number; }; }
+            ```
+        "};
+        let result = VtslsLspAdapter::new(NodeRuntime::unavailable())
+            .diagnostic_message_to_markdown(message)
+            .unwrap();
+        pretty_assertions::assert_eq!(result, expected.to_string());
+    }
+}

crates/markdown/src/markdown.rs 🔗

@@ -1534,12 +1534,26 @@ impl MarkdownElementBuilder {
             rendered_index: self.pending_line.text.len(),
             source_index: source_range.start,
         });
-        self.pending_line.text.push_str(text);
+        if text.starts_with("type a =") {
+            self.pending_line.text.push_str(&text["type a =".len()..]);
+        } else {
+            self.pending_line.text.push_str(text);
+        }
         self.current_source_index = source_range.end;
 
         if let Some(Some(language)) = self.code_block_stack.last() {
+            dbg!(&language);
             let mut offset = 0;
-            for (range, highlight_id) in language.highlight_text(&Rope::from(text), 0..text.len()) {
+            for (mut range, highlight_id) in
+                language.highlight_text(&Rope::from(text), 0..text.len())
+            {
+                if text.starts_with("type a =") {
+                    if range.start < "type a =".len() || range.end < "type a =".len() {
+                        continue;
+                    }
+                    range.start -= "type a =".len();
+                    range.end -= "type a =".len();
+                };
                 if range.start > offset {
                     self.pending_line
                         .runs