From 49198a7961c86d9d433784647ec37ddb05ca0852 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 10 Jan 2025 16:16:52 +0200 Subject: [PATCH] Do not show copy buttons in editor's hover popovers (#22962) Follow-up of https://github.com/zed-industries/zed/pull/22866 Added a config option to the markdown renderer to omit code copying buttons, and used those for editor hover popovers. Such popovers are quite frequent in language servers' hover responses, e.g. rust-analyzer on `.clone()` hover may respond with ``` {"jsonrpc":"2.0","id":119,"result":{"contents":{"kind":"markdown","value":"\n```rust\nalloc::string::String\n```\n\n```rust\nfn clone(&self) -> Self\n```\n\n---\n\nReturns a copy of the value.\n\n# Examples\n\n```rust\nlet hello = \"Hello\"; // &str implements Clone\n\nassert_eq!(\"Hello\", hello.clone());\n```"},"range":{"start":{"line":518,"character":24},"end":{"line":518,"character":29}}}} ``` (note multiple code blocks sent) ![image](https://github.com/user-attachments/assets/4c40b15e-8f53-4b3d-a809-f1e4d35a00a7) ![image](https://github.com/user-attachments/assets/77b8e13b-b665-42d3-b633-5a0375998f06) Sounds that editor has either to use a different way to copy popover's data (so the entire text gets copied, not just its code blocks), or at least better handle hover popover's hovering to show the button. Release Notes: - N/A --- crates/editor/src/hover_popover.rs | 1 + crates/markdown/src/markdown.rs | 75 +++++++++++++++++++----------- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 439d15ec6a..7050972e59 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -590,6 +590,7 @@ async fn parse_blocks( fallback_language_name, cx, ) + .copy_code_block_buttons(false) }) .ok(); diff --git a/crates/markdown/src/markdown.rs b/crates/markdown/src/markdown.rs index 0b80c77c9c..e63265c8d4 100644 --- a/crates/markdown/src/markdown.rs +++ b/crates/markdown/src/markdown.rs @@ -61,7 +61,13 @@ pub struct Markdown { focus_handle: FocusHandle, language_registry: Option>, fallback_code_block_language: Option, + options: Options, +} + +#[derive(Debug)] +struct Options { parse_links_only: bool, + copy_code_block_buttons: bool, } actions!(markdown, [Copy]); @@ -87,7 +93,10 @@ impl Markdown { focus_handle, language_registry, fallback_code_block_language, - parse_links_only: false, + options: Options { + parse_links_only: false, + copy_code_block_buttons: true, + }, }; this.parse(cx); this @@ -113,7 +122,10 @@ impl Markdown { focus_handle, language_registry, fallback_code_block_language, - parse_links_only: true, + options: Options { + parse_links_only: true, + copy_code_block_buttons: true, + }, }; this.parse(cx); this @@ -164,7 +176,7 @@ impl Markdown { } let text = self.source.clone(); - let parse_text_only = self.parse_links_only; + let parse_text_only = self.options.parse_links_only; let parsed = cx.background_executor().spawn(async move { let text = SharedString::from(text); let events = match parse_text_only { @@ -195,6 +207,11 @@ impl Markdown { .log_err() })); } + + pub fn copy_code_block_buttons(mut self, should_copy: bool) -> Self { + self.options.copy_code_block_buttons = should_copy; + self + } } impl Render for Markdown { @@ -667,31 +684,35 @@ impl Element for MarkdownElement { } MarkdownTagEnd::CodeBlock => { builder.trim_trailing_newline(); - builder.flush_text(); - builder.modify_current_div(|el| { - let id = - ElementId::NamedInteger("copy-markdown-code".into(), range.end); - let copy_button = div().absolute().top_1().right_1().w_5().child( - IconButton::new(id, IconName::Copy) - .icon_color(Color::Muted) - .shape(ui::IconButtonShape::Square) - .tooltip(|cx| Tooltip::text("Copy Code Block", cx)) - .on_click({ - let code = without_fences( - parsed_markdown.source()[range.clone()].trim(), - ) - .to_string(); - move |_, cx| { - cx.write_to_clipboard(ClipboardItem::new_string( - code.clone(), - )) - } - }), - ); + if self.markdown.read(cx).options.copy_code_block_buttons { + builder.flush_text(); + builder.modify_current_div(|el| { + let id = + ElementId::NamedInteger("copy-markdown-code".into(), range.end); + let copy_button = div().absolute().top_1().right_1().w_5().child( + IconButton::new(id, IconName::Copy) + .icon_color(Color::Muted) + .shape(ui::IconButtonShape::Square) + .tooltip(|cx| Tooltip::text("Copy Code Block", cx)) + .on_click({ + let code = without_fences( + parsed_markdown.source()[range.clone()].trim(), + ) + .to_string(); + + move |_, cx| { + cx.write_to_clipboard(ClipboardItem::new_string( + code.clone(), + )) + } + }), + ); + + el.child(copy_button) + }); + } - el.child(copy_button) - }); builder.pop_div(); builder.pop_code_block(); if self.style.code_block.text.is_some() { @@ -1033,7 +1054,7 @@ impl MarkdownElementBuilder { } } - pub fn flush_text(&mut self) { + fn flush_text(&mut self) { let line = mem::take(&mut self.pending_line); if line.text.is_empty() { return;