on master help sign in
Render code actions as a menu, allow adding padding to popup
094a0aaBlaž Hrastnik
helix-term/src/commands.rs+26−14
@@ -39,7 +39,7 @@ use movement::Movement;
39use crate::{
39use crate::{
40 args,
40 args,
41 compositor::{self, Component, Compositor},
41 compositor::{self, Component, Compositor},
42− ui::{self, FilePicker, Picker, Popup, Prompt, PromptEvent},
42+ ui::{self, FilePicker, Popup, Prompt, PromptEvent},
43};
43};
44
44
45use crate::job::{self, Job, Jobs};
45use crate::job::{self, Job, Jobs};
@@ -3463,6 +3463,15 @@ fn workspace_symbol_picker(cx: &mut Context) {
3463 )
3463 )
3464}
3464}
3465
3465
3466+impl ui::menu::Item for lsp::CodeActionOrCommand {
3467+ fn label(&self) -> &str {
3468+ match self {
3469+ lsp::CodeActionOrCommand::CodeAction(action) => action.title.as_str(),
3470+ lsp::CodeActionOrCommand::Command(command) => command.title.as_str(),
3471+ }
3472+ }
3473+}
3474+
3466pub fn code_action(cx: &mut Context) {
3475pub fn code_action(cx: &mut Context) {
3467 let (view, doc) = current!(cx.editor);
3476 let (view, doc) = current!(cx.editor);
3468
3477
@@ -3491,16 +3500,15 @@ pub fn code_action(cx: &mut Context) {
3491 return;
3500 return;
3492 }
3501 }
3493
3502
3494− let picker = Picker::new(
3503+ let picker = ui::Menu::new(actions, move |editor, code_action, event| {
3495− false,
3504+ if event != PromptEvent::Validate {
3496− actions,
3505+ return;
3497− |action| match action {
3506+ }
3498− lsp::CodeActionOrCommand::CodeAction(action) => {
3507+
3499− action.title.as_str().into()
3508+ // always present here
3500− }
3509+ let code_action = code_action.unwrap();
3501− lsp::CodeActionOrCommand::Command(command) => command.title.as_str().into(),
3510+
3502− },
3503− move |editor, code_action, _action| match code_action {
3511+ match code_action {
3504 lsp::CodeActionOrCommand::Command(command) => {
3512 lsp::CodeActionOrCommand::Command(command) => {
3505 log::debug!("code action command: {:?}", command);
3513 log::debug!("code action command: {:?}", command);
3506 execute_lsp_command(editor, command.clone());
3514 execute_lsp_command(editor, command.clone());
@@ -3518,9 +3526,13 @@ pub fn code_action(cx: &mut Context) {
3518 execute_lsp_command(editor, command.clone());
3526 execute_lsp_command(editor, command.clone());
3519 }
3527 }
3520 }
3528 }
3521− },
3529+ }
3522− );
3530+ });
3531+ let popup =
3523− let popup = Popup::new("code-action", picker);
3532+ Popup::new("code-action", picker).margin(helix_view::graphics::Margin {
3533+ vertical: 1,
3534+ horizontal: 1,
3535+ });
3524 compositor.push(Box::new(popup))
3536 compositor.push(Box::new(popup))
3525 }
3537 }
3526 },
3538 },
helix-term/src/ui/menu.rs+11−4
@@ -14,11 +14,18 @@ use helix_view::{graphics::Rect, Editor};
14use tui::layout::Constraint;
14use tui::layout::Constraint;
15
15
16pub trait Item {
16pub trait Item {
17− fn sort_text(&self) -> &str;
18− fn filter_text(&self) -> &str;
19−
20 fn label(&self) -> &str;
17 fn label(&self) -> &str;
18+
19+ fn sort_text(&self) -> &str {
20+ self.label()
21+ }
22+ fn filter_text(&self) -> &str {
23+ self.label()
24+ }
25+
21− fn row(&self) -> Row;
26+ fn row(&self) -> Row {
27+ Row::new(vec![Cell::from(self.label())])
28+ }
22}
29}
23
30
24pub struct Menu<T: Item> {
31pub struct Menu<T: Item> {
helix-term/src/ui/mod.rs+1−1
@@ -2,7 +2,7 @@ mod completion;
2pub(crate) mod editor;
2pub(crate) mod editor;
3mod info;
3mod info;
4mod markdown;
4mod markdown;
5−mod menu;
5+pub mod menu;
6mod picker;
6mod picker;
7mod popup;
7mod popup;
8mod prompt;
8mod prompt;
helix-term/src/ui/popup.rs+20−4
@@ -6,7 +6,7 @@ use crossterm::event::Event;
6use tui::buffer::Buffer as Surface;
6use tui::buffer::Buffer as Surface;
7
7
8use helix_core::Position;
8use helix_core::Position;
9−use helix_view::graphics::Rect;
9+use helix_view::graphics::{Margin, Rect};
10
10
11// TODO: share logic with Menu, it's essentially Popup(render_fn), but render fn needs to return
11// TODO: share logic with Menu, it's essentially Popup(render_fn), but render fn needs to return
12// a width/height hint. maybe Popup(Box<Component>)
12// a width/height hint. maybe Popup(Box<Component>)
@@ -14,6 +14,7 @@ use helix_view::graphics::Rect;
14pub struct Popup<T: Component> {
14pub struct Popup<T: Component> {
15 contents: T,
15 contents: T,
16 position: Option<Position>,
16 position: Option<Position>,
17+ margin: Margin,
17 size: (u16, u16),
18 size: (u16, u16),
18 child_size: (u16, u16),
19 child_size: (u16, u16),
19 scroll: usize,
20 scroll: usize,
@@ -25,6 +26,10 @@ impl<T: Component> Popup<T> {
25 Self {
26 Self {
26 contents,
27 contents,
27 position: None,
28 position: None,
29+ margin: Margin {
30+ vertical: 0,
31+ horizontal: 0,
32+ },
28 size: (0, 0),
33 size: (0, 0),
29 child_size: (0, 0),
34 child_size: (0, 0),
30 scroll: 0,
35 scroll: 0,
@@ -36,6 +41,11 @@ impl<T: Component> Popup<T> {
36 self.position = pos;
41 self.position = pos;
37 }
42 }
38
43
44+ pub fn margin(mut self, margin: Margin) -> Self {
45+ self.margin = margin;
46+ self
47+ }
48+
39 pub fn get_rel_position(&mut self, viewport: Rect, cx: &Context) -> (u16, u16) {
49 pub fn get_rel_position(&mut self, viewport: Rect, cx: &Context) -> (u16, u16) {
40 let position = self
50 let position = self
41 .position
51 .position
@@ -126,13 +136,18 @@ impl<T: Component> Component for Popup<T> {
126 let max_width = 120.min(viewport.0);
136 let max_width = 120.min(viewport.0);
127 let max_height = 26.min(viewport.1.saturating_sub(2)); // add some spacing in the viewport
137 let max_height = 26.min(viewport.1.saturating_sub(2)); // add some spacing in the viewport
128
138
139+ let inner = Rect::new(0, 0, max_width, max_height).inner(&self.margin);
140+
129 let (width, height) = self
141 let (width, height) = self
130 .contents
142 .contents
131− .required_size((max_width, max_height))
143+ .required_size((inner.width, inner.height))
132 .expect("Component needs required_size implemented in order to be embedded in a popup");
144 .expect("Component needs required_size implemented in order to be embedded in a popup");
133
145
134 self.child_size = (width, height);
146 self.child_size = (width, height);
147+ self.size = (
148+ (width + self.margin.horizontal * 2).min(max_width),
135− self.size = (width.min(max_width), height.min(max_height));
149+ (height + self.margin.vertical * 2).min(max_height),
150+ );
136
151
137 // re-clamp scroll offset
152 // re-clamp scroll offset
138 let max_offset = self.child_size.1.saturating_sub(self.size.1);
153 let max_offset = self.child_size.1.saturating_sub(self.size.1);
@@ -156,7 +171,8 @@ impl<T: Component> Component for Popup<T> {
156 let background = cx.editor.theme.get("ui.popup");
171 let background = cx.editor.theme.get("ui.popup");
157 surface.clear_with(area, background);
172 surface.clear_with(area, background);
158
173
174+ let inner = area.inner(&self.margin);
159− self.contents.render(area, surface, cx);
175+ self.contents.render(inner, surface, cx);
160 }
176 }
161
177
162 fn id(&self) -> Option<&'static str> {
178 fn id(&self) -> Option<&'static str> {
helix-tui/src/widgets/block.rs+1−1
@@ -134,7 +134,7 @@ impl<'a> Widget for Block<'a> {
134 if area.area() == 0 {
134 if area.area() == 0 {
135 return;
135 return;
136 }
136 }
137− buf.set_style(area, self.style);
137+ buf.clear_with(area, self.style);
138 let symbols = BorderType::line_symbols(self.border_type);
138 let symbols = BorderType::line_symbols(self.border_type);
139
139
140 // Sides
140 // Sides