Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-view/src/info.rs')
| -rw-r--r-- | helix-view/src/info.rs | 121 |
1 files changed, 89 insertions, 32 deletions
diff --git a/helix-view/src/info.rs b/helix-view/src/info.rs index d1e90b5a..df43937a 100644 --- a/helix-view/src/info.rs +++ b/helix-view/src/info.rs @@ -1,12 +1,12 @@ -use crate::register::Registers; -use helix_core::unicode::width::UnicodeWidthStr; -use std::{borrow::Cow, fmt::Write}; +use crate::input::KeyEvent; +use helix_core::{register::Registers, unicode::width::UnicodeWidthStr}; +use std::{collections::BTreeSet, fmt::Write}; -#[derive(Debug)] +#[derive(Debug, Clone)] /// Info box used in editor. Rendering logic will be in other crate. pub struct Info { /// Title shown at top. - pub title: Cow<'static, str>, + pub title: String, /// Text body, should contain newlines. pub text: String, /// Body width. @@ -16,55 +16,112 @@ pub struct Info { } impl Info { - pub fn new<T, K, V>(title: T, body: &[(K, V)]) -> Self - where - T: Into<Cow<'static, str>>, - K: AsRef<str>, - V: AsRef<str>, - { - let title = title.into(); + pub fn new(title: &str, body: Vec<(String, String)>) -> Self { if body.is_empty() { return Self { + title: title.to_string(), height: 1, width: title.len() as u16, text: "".to_string(), - title, }; } - let item_width = body - .iter() - .map(|(item, _)| item.as_ref().width()) - .max() - .unwrap(); + let item_width = body.iter().map(|(item, _)| item.width()).max().unwrap(); let mut text = String::new(); - for (item, desc) in body { - let _ = writeln!( - text, - "{:width$} {}", - item.as_ref(), - desc.as_ref(), - width = item_width - ); + for (item, desc) in &body { + let _ = writeln!(text, "{:width$} {}", item, desc, width = item_width); } Self { - title, + title: title.to_string(), width: text.lines().map(|l| l.width()).max().unwrap() as u16, height: body.len() as u16, text, } } - pub fn from_registers(title: impl Into<Cow<'static, str>>, registers: &Registers) -> Self { - let body: Vec<_> = registers - .iter_preview() - .map(|(ch, preview)| (ch.to_string(), preview)) + pub fn from_keymap(title: &str, body: Vec<(&str, BTreeSet<KeyEvent>)>) -> Self { + let body = body + .into_iter() + .map(|(desc, events)| { + let events = events.iter().map(ToString::to_string).collect::<Vec<_>>(); + (events.join(", "), desc.to_string()) + }) .collect(); - let mut infobox = Self::new(title, &body); + Self::new(title, body) + } + + pub fn from_registers(registers: &Registers) -> Self { + let body = registers + .inner() + .iter() + .map(|(ch, reg)| { + let content = reg + .read() + .get(0) + .and_then(|s| s.lines().next()) + .map(String::from) + .unwrap_or_default(); + (ch.to_string(), content) + }) + .collect(); + + let mut infobox = Self::new("Registers", body); infobox.width = 30; // copied content could be very long infobox } } + +// term + +use crate::{ + compositor::{self, Component, RenderContext}, + graphics::{Margin, Rect}, +}; + +#[cfg(feature = "term")] +use tui::widgets::{Block, Borders, Paragraph, Widget}; + +#[cfg(feature = "term")] +impl compositor::term::Render for Info { + fn render(&mut self, viewport: Rect, cx: &mut RenderContext<'_>) { + let text_style = cx.editor.theme.get("ui.text.info"); + let popup_style = cx.editor.theme.get("ui.popup.info"); + + // Calculate the area of the terminal to modify. Because we want to + // render at the bottom right, we use the viewport's width and height + // which evaluate to the most bottom right coordinate. + let width = self.width + 2 + 2; // +2 for border, +2 for margin + let height = self.height + 2; // +2 for border + let area = viewport.intersection(Rect::new( + viewport.width.saturating_sub(width), + viewport.height.saturating_sub(height + 2), // +2 for statusline + width, + height, + )); + cx.surface.clear_with(area, popup_style); + + let block = Block::default() + .title(self.title.as_str()) + .borders(Borders::ALL) + .border_style(popup_style); + + let margin = Margin { + vertical: 0, + horizontal: 1, + }; + let inner = block.inner(area).inner(&margin); + block.render(area, cx.surface); + + Paragraph::new(self.text.as_str()) + .style(text_style) + .render(inner, cx.surface); + } +} + +#[cfg(feature = "ui")] +impl compositor::ui::Render for Info {} + +impl Component for Info {} |