editor: Add copy button for project diagnostics messages (#49671)

ozacod , ozacod , and Danilo Leal created

| Before | After |
|---|---|
| <img width="734" height="178" alt="before"
src="https://github.com/user-attachments/assets/1cea5854-e1b5-45a5-92ee-ed66bde06cc6"
/> | <img width="834" height="186" alt="after"
src="https://github.com/user-attachments/assets/bddb9280-89b3-4800-b9c9-08e456bc9ed8"
/> |

Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [x] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)

Release Notes:

- Added copy button for project diagnostics messages

---------

Co-authored-by: ozacod <ozacod@users.noreply.github.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>

Change summary

crates/diagnostics/src/diagnostic_renderer.rs | 67 +++++++++++++-------
1 file changed, 42 insertions(+), 25 deletions(-)

Detailed changes

crates/diagnostics/src/diagnostic_renderer.rs 🔗

@@ -12,10 +12,7 @@ use markdown::{Markdown, MarkdownElement};
 use settings::Settings;
 use text::{AnchorRangeExt, Point};
 use theme::ThemeSettings;
-use ui::{
-    ActiveTheme, AnyElement, App, Context, IntoElement, ParentElement, SharedString, Styled,
-    Window, div,
-};
+use ui::{CopyButton, prelude::*};
 use util::maybe;
 
 use crate::toolbar_controls::DiagnosticsToolbarEditor;
@@ -81,6 +78,7 @@ impl DiagnosticRenderer {
                     initial_range: primary.range.clone(),
                     severity: primary.diagnostic.severity,
                     diagnostics_editor: diagnostics_editor.clone(),
+                    copy_message: primary.diagnostic.message.clone().into(),
                     markdown: cx.new(|cx| {
                         Markdown::new(markdown.into(), language_registry.clone(), None, cx)
                     }),
@@ -95,6 +93,7 @@ impl DiagnosticRenderer {
                     initial_range: entry.range.clone(),
                     severity: entry.diagnostic.severity,
                     diagnostics_editor: diagnostics_editor.clone(),
+                    copy_message: entry.diagnostic.message.clone().into(),
                     markdown: cx.new(|cx| {
                         Markdown::new(markdown.into(), language_registry.clone(), None, cx)
                     }),
@@ -191,6 +190,7 @@ pub(crate) struct DiagnosticBlock {
     pub(crate) severity: DiagnosticSeverity,
     pub(crate) markdown: Entity<Markdown>,
     pub(crate) diagnostics_editor: Option<Arc<dyn DiagnosticsToolbarEditor>>,
+    pub(crate) copy_message: SharedString,
 }
 
 impl DiagnosticBlock {
@@ -214,32 +214,49 @@ impl DiagnosticBlock {
         let line_height = editor_line_height;
         let diagnostics_editor = self.diagnostics_editor.clone();
 
-        div()
+        let copy_button_id = format!(
+            "copy-diagnostic-{}-{}-{}-{}",
+            self.initial_range.start.row,
+            self.initial_range.start.column,
+            self.initial_range.end.row,
+            self.initial_range.end.column
+        );
+
+        h_flex()
+            .max_w(max_width)
+            .pl_1p5()
+            .pr_0p5()
+            .items_start()
+            .gap_1()
             .border_l_2()
-            .px_2()
             .line_height(line_height)
             .bg(background_color)
             .border_color(border_color)
-            .max_w(max_width)
             .child(
-                MarkdownElement::new(
-                    self.markdown.clone(),
-                    diagnostics_markdown_style(bcx.window, cx),
-                )
-                .code_block_renderer(markdown::CodeBlockRenderer::Default {
-                    copy_button: false,
-                    copy_button_on_hover: false,
-                    border: false,
-                })
-                .on_url_click({
-                    move |link, window, cx| {
-                        editor
-                            .update(cx, |editor, cx| {
-                                Self::open_link(editor, &diagnostics_editor, link, window, cx)
-                            })
-                            .ok();
-                    }
-                }),
+                div().flex_1().min_w_0().child(
+                    MarkdownElement::new(
+                        self.markdown.clone(),
+                        diagnostics_markdown_style(bcx.window, cx),
+                    )
+                    .code_block_renderer(markdown::CodeBlockRenderer::Default {
+                        copy_button: false,
+                        copy_button_on_hover: false,
+                        border: false,
+                    })
+                    .on_url_click({
+                        move |link, window, cx| {
+                            editor
+                                .update(cx, |editor, cx| {
+                                    Self::open_link(editor, &diagnostics_editor, link, window, cx)
+                                })
+                                .ok();
+                        }
+                    }),
+                ),
+            )
+            .child(
+                CopyButton::new(copy_button_id, self.copy_message.clone())
+                    .tooltip_label("Copy Diagnostic"),
             )
             .into_any_element()
     }