From 73e707f085a3b5a424787dbfb8cd235bf860eca2 Mon Sep 17 00:00:00 2001 From: ath3 Date: Sun, 16 Oct 2022 16:19:53 +0200 Subject: [PATCH] Support drawing popup border --- book/src/configuration.md | 1 + helix-term/src/ui/completion.rs | 6 ++++ helix-term/src/ui/lsp.rs | 7 ++++- helix-term/src/ui/menu.rs | 51 ++++++++++++++++++++++++--------- helix-term/src/ui/popup.rs | 14 +++++++-- helix-view/src/editor.rs | 12 ++++++++ 6 files changed, 74 insertions(+), 17 deletions(-) diff --git a/book/src/configuration.md b/book/src/configuration.md index 996c5fb6ada3..e175e7c82b26 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -56,6 +56,7 @@ on unix operating systems. | `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file. | `[]` | | `bufferline` | Renders a line at the top of the editor displaying open buffers. Can be `always`, `never` or `multiple` (only shown if more than one buffer is in use) | `never` | | `color-modes` | Whether to color the mode indicator with different colors depending on the mode itself | `false` | +| `popup-border` | Draw border around `popup`, `menu`, `all`, or `none` | `none` | ### `[editor.statusline]` Section diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 7348dcf44431..33bd475e6565 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -413,6 +413,12 @@ impl Component for Completion { // clear area let background = cx.editor.theme.get("ui.popup"); surface.clear_with(area, background); + if cx.editor.config().popup_border == helix_view::editor::PopupBorderConfig::All + || cx.editor.config().popup_border == helix_view::editor::PopupBorderConfig::Popup + { + use tui::widgets::{Block, Borders, Widget}; + Widget::render(Block::default().borders(Borders::ALL), area, surface); + } markdown_doc.render(area, surface, cx); } } diff --git a/helix-term/src/ui/lsp.rs b/helix-term/src/ui/lsp.rs index 393d24c467bc..5375205402d9 100644 --- a/helix-term/src/ui/lsp.rs +++ b/helix-term/src/ui/lsp.rs @@ -89,7 +89,12 @@ impl Component for SignatureHelp { Some(doc) => Markdown::new(doc.clone(), Arc::clone(&self.config_loader)), }; let sig_doc = sig_doc.parse(Some(&cx.editor.theme)); - let sig_doc_area = area.clip_top(sig_text_area.height + 2); + let mut sig_doc_area = area.clip_top(sig_text_area.height + 2); + if cx.editor.config().popup_border == helix_view::editor::PopupBorderConfig::All + || cx.editor.config().popup_border == helix_view::editor::PopupBorderConfig::Popup + { + sig_doc_area = sig_doc_area.clip_bottom(1); + } let sig_doc_para = Paragraph::new(sig_doc) .wrap(Wrap { trim: false }) .scroll((cx.scroll.unwrap_or_default() as u16, 0)); diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs index 1d247b1adc82..cc838a5d5666 100644 --- a/helix-term/src/ui/menu.rs +++ b/helix-term/src/ui/menu.rs @@ -291,13 +291,24 @@ impl Component for Menu { Some(self.size) } - fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { + fn render(&mut self, mut area: Rect, surface: &mut Surface, cx: &mut Context) { let theme = &cx.editor.theme; let style = theme .try_get("ui.menu") .unwrap_or_else(|| theme.get("ui.text")); let selected = theme.get("ui.menu.selected"); - surface.clear_with(area, style); + + let borders = cx.editor.config().popup_border == helix_view::editor::PopupBorderConfig::All + || cx.editor.config().popup_border == helix_view::editor::PopupBorderConfig::Menu; + if borders { + area.height += 2; + // If the menu is drawn above the current line, it would hide the line + if cx.editor.cursor().0.unwrap().row > area.y as usize && area.y >= 2 { + area.y -= 2; + } + } + + area = surface.area.intersection(area); let scroll = self.scroll; @@ -310,6 +321,14 @@ impl Component for Menu { }) .collect(); + surface.clear_with(area, style); + + if borders { + use tui::widgets::{Block, Borders, Widget}; + Widget::render(Block::default().borders(Borders::ALL), area, surface); + + area = area.clip_top(1).clip_bottom(1); + } let len = options.len(); let win_height = area.height as usize; @@ -341,15 +360,17 @@ impl Component for Menu { }, ); - if let Some(cursor) = self.cursor { - let offset_from_top = cursor - scroll; - let left = &mut surface[(area.left(), area.y + offset_from_top as u16)]; - left.set_style(selected); - let right = &mut surface[( - area.right().saturating_sub(1), - area.y + offset_from_top as u16, - )]; - right.set_style(selected); + if !borders { + if let Some(cursor) = self.cursor { + let offset_from_top = cursor - scroll; + let left = &mut surface[(area.left(), area.y + offset_from_top as u16)]; + left.set_style(selected); + let right = &mut surface[( + area.right().saturating_sub(1), + area.y + offset_from_top as u16, + )]; + right.set_style(selected); + } } let fits = len <= win_height; @@ -358,9 +379,8 @@ impl Component for Menu { for (i, _) in (scroll..(scroll + win_height).min(len)).enumerate() { let cell = &mut surface[(area.x + area.width - 1, area.y + i as u16)]; - if !fits { + if !(fits || borders) { // Draw scroll track - cell.set_symbol("▐"); // right half block cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset)); } @@ -368,6 +388,11 @@ impl Component for Menu { if !fits && is_marked { // Draw scroll thumb + if borders { + cell.set_symbol("▌"); // left half block + } else { + cell.set_symbol("▐"); // right half block + } cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset)); } } diff --git a/helix-term/src/ui/popup.rs b/helix-term/src/ui/popup.rs index 3c140da40aef..480cff3fb645 100644 --- a/helix-term/src/ui/popup.rs +++ b/helix-term/src/ui/popup.rs @@ -220,14 +220,22 @@ impl Component for Popup { let (rel_x, rel_y) = self.get_rel_position(viewport, cx); // clip to viewport - let area = viewport.intersection(Rect::new(rel_x, rel_y, self.size.0, self.size.1)); + let mut area = viewport.intersection(Rect::new(rel_x, rel_y, self.size.0, self.size.1)); // clear area let background = cx.editor.theme.get("ui.popup"); surface.clear_with(area, background); - let inner = area.inner(&self.margin); - self.contents.render(inner, surface, cx); + if cx.editor.config().popup_border == helix_view::editor::PopupBorderConfig::All + || cx.editor.config().popup_border == helix_view::editor::PopupBorderConfig::Popup + { + use tui::widgets::{Block, Borders, Widget}; + Widget::render(Block::default().borders(Borders::ALL), area, surface); + } else { + area = area.inner(&self.margin); + } + + self.contents.render(area, surface, cx); } fn id(&self) -> Option<&'static str> { diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 60b3880c6e14..ac7d005f876b 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -170,6 +170,8 @@ pub struct Config { pub indent_guides: IndentGuidesConfig, /// Whether to color modes with different colors. Defaults to `false`. pub color_modes: bool, + /// Draw border around popups. + pub popup_border: PopupBorderConfig, } #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] @@ -571,6 +573,15 @@ impl Default for IndentGuidesConfig { } } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum PopupBorderConfig { + None, + All, + Popup, + Menu, +} + impl Default for Config { fn default() -> Self { Self { @@ -605,6 +616,7 @@ impl Default for Config { bufferline: BufferLine::default(), indent_guides: IndentGuidesConfig::default(), color_modes: false, + popup_border: PopupBorderConfig::None, } } }