From 094a0aa3f9877e3b1049f262e65c8efea2b7b73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Mon, 31 Jan 2022 13:42:32 +0900 Subject: [PATCH] Render code actions as a menu, allow adding padding to popup --- helix-term/src/commands.rs | 40 ++++++++++++++++++++++------------ helix-term/src/ui/menu.rs | 15 +++++++++---- helix-term/src/ui/mod.rs | 2 +- helix-term/src/ui/popup.rs | 24 ++++++++++++++++---- helix-tui/src/widgets/block.rs | 2 +- 5 files changed, 59 insertions(+), 24 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 96acb424..6bc2b9b6 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -39,7 +39,7 @@ use movement::Movement; use crate::{ args, compositor::{self, Component, Compositor}, - ui::{self, FilePicker, Picker, Popup, Prompt, PromptEvent}, + ui::{self, FilePicker, Popup, Prompt, PromptEvent}, }; use crate::job::{self, Job, Jobs}; @@ -3463,6 +3463,15 @@ fn workspace_symbol_picker(cx: &mut Context) { ) } +impl ui::menu::Item for lsp::CodeActionOrCommand { + fn label(&self) -> &str { + match self { + lsp::CodeActionOrCommand::CodeAction(action) => action.title.as_str(), + lsp::CodeActionOrCommand::Command(command) => command.title.as_str(), + } + } +} + pub fn code_action(cx: &mut Context) { let (view, doc) = current!(cx.editor); @@ -3491,16 +3500,15 @@ pub fn code_action(cx: &mut Context) { return; } - let picker = Picker::new( - false, - actions, - |action| match action { - lsp::CodeActionOrCommand::CodeAction(action) => { - action.title.as_str().into() - } - lsp::CodeActionOrCommand::Command(command) => command.title.as_str().into(), - }, - move |editor, code_action, _action| match code_action { + let picker = ui::Menu::new(actions, move |editor, code_action, event| { + if event != PromptEvent::Validate { + return; + } + + // always present here + let code_action = code_action.unwrap(); + + match code_action { lsp::CodeActionOrCommand::Command(command) => { log::debug!("code action command: {:?}", command); execute_lsp_command(editor, command.clone()); @@ -3518,9 +3526,13 @@ pub fn code_action(cx: &mut Context) { execute_lsp_command(editor, command.clone()); } } - }, - ); - let popup = Popup::new("code-action", picker); + } + }); + let popup = + Popup::new("code-action", picker).margin(helix_view::graphics::Margin { + vertical: 1, + horizontal: 1, + }); compositor.push(Box::new(popup)) } }, diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs index 9758732c..e127e09b 100644 --- a/helix-term/src/ui/menu.rs +++ b/helix-term/src/ui/menu.rs @@ -14,11 +14,18 @@ use helix_view::{graphics::Rect, Editor}; use tui::layout::Constraint; pub trait Item { - fn sort_text(&self) -> &str; - fn filter_text(&self) -> &str; - fn label(&self) -> &str; - fn row(&self) -> Row; + + fn sort_text(&self) -> &str { + self.label() + } + fn filter_text(&self) -> &str { + self.label() + } + + fn row(&self) -> Row { + Row::new(vec![Cell::from(self.label())]) + } } pub struct Menu { diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 9ff9118f..5d650c65 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -2,7 +2,7 @@ mod completion; pub(crate) mod editor; mod info; mod markdown; -mod menu; +pub mod menu; mod picker; mod popup; mod prompt; diff --git a/helix-term/src/ui/popup.rs b/helix-term/src/ui/popup.rs index bf7510a2..4d319423 100644 --- a/helix-term/src/ui/popup.rs +++ b/helix-term/src/ui/popup.rs @@ -6,7 +6,7 @@ use crossterm::event::Event; use tui::buffer::Buffer as Surface; use helix_core::Position; -use helix_view::graphics::Rect; +use helix_view::graphics::{Margin, Rect}; // TODO: share logic with Menu, it's essentially Popup(render_fn), but render fn needs to return // a width/height hint. maybe Popup(Box) @@ -14,6 +14,7 @@ use helix_view::graphics::Rect; pub struct Popup { contents: T, position: Option, + margin: Margin, size: (u16, u16), child_size: (u16, u16), scroll: usize, @@ -25,6 +26,10 @@ impl Popup { Self { contents, position: None, + margin: Margin { + vertical: 0, + horizontal: 0, + }, size: (0, 0), child_size: (0, 0), scroll: 0, @@ -36,6 +41,11 @@ impl Popup { self.position = pos; } + pub fn margin(mut self, margin: Margin) -> Self { + self.margin = margin; + self + } + pub fn get_rel_position(&mut self, viewport: Rect, cx: &Context) -> (u16, u16) { let position = self .position @@ -126,13 +136,18 @@ impl Component for Popup { let max_width = 120.min(viewport.0); let max_height = 26.min(viewport.1.saturating_sub(2)); // add some spacing in the viewport + let inner = Rect::new(0, 0, max_width, max_height).inner(&self.margin); + let (width, height) = self .contents - .required_size((max_width, max_height)) + .required_size((inner.width, inner.height)) .expect("Component needs required_size implemented in order to be embedded in a popup"); self.child_size = (width, height); - self.size = (width.min(max_width), height.min(max_height)); + self.size = ( + (width + self.margin.horizontal * 2).min(max_width), + (height + self.margin.vertical * 2).min(max_height), + ); // re-clamp scroll offset let max_offset = self.child_size.1.saturating_sub(self.size.1); @@ -156,7 +171,8 @@ impl Component for Popup { let background = cx.editor.theme.get("ui.popup"); surface.clear_with(area, background); - self.contents.render(area, surface, cx); + let inner = area.inner(&self.margin); + self.contents.render(inner, surface, cx); } fn id(&self) -> Option<&'static str> { diff --git a/helix-tui/src/widgets/block.rs b/helix-tui/src/widgets/block.rs index 26223c3e..f084a324 100644 --- a/helix-tui/src/widgets/block.rs +++ b/helix-tui/src/widgets/block.rs @@ -134,7 +134,7 @@ impl<'a> Widget for Block<'a> { if area.area() == 0 { return; } - buf.set_style(area, self.style); + buf.clear_with(area, self.style); let symbols = BorderType::line_symbols(self.border_type); // Sides