From c5b2973739901f8cd4bc26f3cfc8232249eb7968 Mon Sep 17 00:00:00 2001 From: Kirawi Date: Fri, 2 Jul 2021 10:54:50 -0400 Subject: `:reload` (#374) * reloading functionality * fn with_newline_eof() * fmt * wip * wip * wip * wip * moved to core, added simd feature for encoding_rs * wip * rm * .gitignore * wip * local wip * wip * wip * no features * wip * nit * remove simd * doc * clippy * clippy * address comments * add indentation & line ending change--- helix-view/src/document.rs | 61 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 8 deletions(-) (limited to 'helix-view') diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 0f1f3a8f..f85ded11 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -307,6 +307,19 @@ pub async fn to_writer<'a, W: tokio::io::AsyncWriteExt + Unpin + ?Sized>( Ok(()) } +/// Inserts the final line ending into `rope` if it's missing. [Why?](https://stackoverflow.com/questions/729692/why-should-text-files-end-with-a-newline) +pub fn with_line_ending(rope: &mut Rope) -> LineEnding { + // search for line endings + let line_ending = auto_detect_line_ending(rope).unwrap_or(DEFAULT_LINE_ENDING); + + // add missing newline at the end of file + if rope.len_bytes() == 0 || !char_is_line_ending(rope.char(rope.len_chars() - 1)) { + rope.insert(rope.len_chars(), line_ending.as_str()); + } + + line_ending +} + /// Like std::mem::replace() except it allows the replacement value to be mapped from the /// original value. fn take_with(mut_ref: &mut T, closure: F) @@ -449,14 +462,7 @@ impl Document { let mut file = std::fs::File::open(&path).context(format!("unable to open {:?}", path))?; let (mut rope, encoding) = from_reader(&mut file, encoding)?; - - // search for line endings - let line_ending = auto_detect_line_ending(&rope).unwrap_or(DEFAULT_LINE_ENDING); - - // add missing newline at the end of file - if rope.len_bytes() == 0 || !char_is_line_ending(rope.char(rope.len_chars() - 1)) { - rope.insert(rope.len_chars(), line_ending.as_str()); - } + let line_ending = with_line_ending(&mut rope); let mut doc = Self::from(rope, Some(encoding)); @@ -586,6 +592,45 @@ impl Document { } } + /// Reload the document from its path. + pub fn reload(&mut self, view_id: ViewId) -> Result<(), Error> { + let encoding = &self.encoding; + let path = self.path().filter(|path| path.exists()); + + // If there is no path or the path no longer exists. + if path.is_none() { + return Err(anyhow!("can't find file to reload from")); + } + + let mut file = std::fs::File::open(path.unwrap())?; + let (mut rope, ..) = from_reader(&mut file, Some(encoding))?; + let line_ending = with_line_ending(&mut rope); + + let transaction = helix_core::diff::compare_ropes(self.text(), &rope); + self.apply(&transaction, view_id); + self.append_changes_to_history(view_id); + + // Detect indentation style and set line ending. + self.detect_indent_style(); + self.line_ending = line_ending; + + Ok(()) + } + + /// Sets the [`Document`]'s encoding with the encoding correspondent to `label`. + pub fn set_encoding(&mut self, label: &str) -> Result<(), Error> { + match encoding_rs::Encoding::for_label(label.as_bytes()) { + Some(encoding) => self.encoding = encoding, + None => return Err(anyhow::anyhow!("unknown encoding")), + } + Ok(()) + } + + /// Returns the [`Document`]'s current encoding. + pub fn encoding(&self) -> &'static encoding_rs::Encoding { + self.encoding + } + fn detect_indent_style(&mut self) { // Build a histogram of the indentation *increases* between // subsequent lines, ignoring lines that are all whitespace. -- cgit v1.2.3-70-g09d2 From 8985c58fd328cde512df0ac7bc577bd8a8c949a0 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Sat, 19 Jun 2021 23:54:37 +0800 Subject: Add infobox --- Cargo.lock | 1 + helix-term/src/commands.rs | 105 +++++++++++++++++++++------- helix-term/src/keymap.rs | 16 ++--- helix-term/src/ui/editor.rs | 7 +- helix-term/src/ui/info.rs | 24 +++++++ helix-term/src/ui/mod.rs | 1 + helix-view/Cargo.toml | 1 + helix-view/src/editor.rs | 3 + helix-view/src/info.rs | 51 ++++++++++++++ helix-view/src/input.rs | 164 ++++++++++++++++++++++++++++++++------------ helix-view/src/lib.rs | 1 + 11 files changed, 296 insertions(+), 78 deletions(-) create mode 100644 helix-term/src/ui/info.rs create mode 100644 helix-view/src/info.rs (limited to 'helix-view') diff --git a/Cargo.lock b/Cargo.lock index a377e2f4..59eb894e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -419,6 +419,7 @@ dependencies = [ "slotmap", "tokio", "toml", + "unicode-width", "url", "which", ] diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index d198b803..106c58d1 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -16,6 +16,7 @@ use helix_core::{ use helix_view::{ document::{IndentStyle, Mode}, editor::Action, + info::Info, input::KeyEvent, keyboard::KeyCode, view::{View, PADDING}, @@ -33,6 +34,7 @@ use movement::Movement; use crate::{ compositor::{self, Component, Compositor}, + key, ui::{self, Picker, Popup, Prompt, PromptEvent}, }; @@ -3400,33 +3402,88 @@ fn select_register(cx: &mut Context) { }) } -fn space_mode(cx: &mut Context) { - cx.on_next_key(move |cx, event| { - if let KeyEvent { - code: KeyCode::Char(ch), - .. - } = event - { - // TODO: temporarily show SPC in the mode list - match ch { - 'f' => file_picker(cx), - 'b' => buffer_picker(cx), - 's' => symbol_picker(cx), - 'w' => window_mode(cx), - 'y' => yank_joined_to_clipboard(cx), - 'Y' => yank_main_selection_to_clipboard(cx), - 'p' => paste_clipboard_after(cx), - 'P' => paste_clipboard_before(cx), - 'R' => replace_selections_with_clipboard(cx), - // ' ' => toggle_alternate_buffer(cx), - // TODO: temporary since space mode took its old key - ' ' => keep_primary_selection(cx), - _ => (), - } +macro_rules! mode_info { + // TODO: how to use one expr for both pat and expr? + // TODO: how to use replaced function name as str at compile time? + // TODO: extend to support multiple keys, but first solve the other two + {$name:literal, $cx:expr, $($key:expr => $func:expr; $funcs:literal),+,} => { + mode_info! { + $name, $cx, + $($key; $key => $func; $funcs,)+ } - }) + }; + {$name:literal, $cx:expr, $($key:expr; $keyp:pat => $func:expr; $funcs:literal),+,} => { + $cx.editor.autoinfo = Some(Info::key( + $name, + vec![ + $( + (vec![$key], $funcs), + )+ + ], + )); + $cx.on_next_key(move |cx, event| { + match event { + $( + $keyp => $func(cx), + )+ + _ => {} + } + }) + } } +fn space_mode(cx: &mut Context) { + mode_info! { + "space mode", cx, + key!('f'); key!('f') => file_picker; "file picker", + key!('b'); key!('b') => buffer_picker; "buffer picker", + key!('s'); key!('s') => symbol_picker; "symbol picker", + key!('w'); key!('w') => window_mode; "window mode", + key!('y'); key!('y') => yank_joined_to_clipboard; "yank joined to clipboard", + key!('Y'); key!('Y') => yank_main_selection_to_clipboard; "yank main selection to clipboard", + key!('p'); key!('p') => paste_clipboard_after; "paste clipboard after", + key!('P'); key!('P') => paste_clipboard_before; "paste clipboard before", + key!('R'); key!('R') => replace_selections_with_clipboard; "replace selections with clipboard", + key!(' '); key!(' ') => keep_primary_selection; "keep primary selection", + } +} + +// TODO: generated, delete it later +// fn space_mode(cx: &mut Context) { +// cx.editor.autoinfo = Some(Info::key( +// "space", +// vec![ +// (vec![key!('f')], "file picker"), +// (vec![key!('b')], "buffer picker"), +// (vec![key!('s')], "symbol picker"), +// (vec![key!('w')], "window mode"), +// (vec![key!('y')], "yank joined to clipboard"), +// (vec![key!('Y')], "yank main selection to clipboard"), +// (vec![key!('p')], "paste clipboard after"), +// (vec![key!('P')], "paste clipboard before"), +// (vec![key!('R')], "replace selections with clipboard"), +// (vec![key!(' ')], "keep primary selection"), +// ], +// )); +// cx.on_next_key(move |cx, event| { +// match event { +// key!('f') => file_picker(cx), +// key!('b') => buffer_picker(cx), +// key!('s') => symbol_picker(cx), +// key!('w') => window_mode(cx), +// key!('y') => yank_joined_to_clipboard(cx), +// key!('Y') => yank_main_selection_to_clipboard(cx), +// key!('p') => paste_clipboard_after(cx), +// key!('P') => paste_clipboard_before(cx), +// key!('R') => replace_selections_with_clipboard(cx), +// // key!(' ') => toggle_alternate_buffer(cx), +// // TODO: temporary since space mode took its old key +// key!(' ') => keep_primary_selection(cx), +// _ => {} +// } +// }) +// } + fn view_mode(cx: &mut Context) { cx.on_next_key(move |cx, event| { if let KeyEvent { diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 53588a2b..53143c71 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -105,14 +105,14 @@ use std::{ macro_rules! key { ($key:ident) => { KeyEvent { - code: KeyCode::$key, - modifiers: KeyModifiers::NONE, + code: helix_view::keyboard::KeyCode::$key, + modifiers: helix_view::keyboard::KeyModifiers::NONE, } }; ($($ch:tt)*) => { KeyEvent { - code: KeyCode::Char($($ch)*), - modifiers: KeyModifiers::NONE, + code: helix_view::keyboard::KeyCode::Char($($ch)*), + modifiers: helix_view::keyboard::KeyModifiers::NONE, } }; } @@ -120,8 +120,8 @@ macro_rules! key { macro_rules! ctrl { ($($ch:tt)*) => { KeyEvent { - code: KeyCode::Char($($ch)*), - modifiers: KeyModifiers::CONTROL, + code: helix_view::keyboard::KeyCode::Char($($ch)*), + modifiers: helix_view::keyboard::KeyModifiers::CONTROL, } }; } @@ -129,8 +129,8 @@ macro_rules! ctrl { macro_rules! alt { ($($ch:tt)*) => { KeyEvent { - code: KeyCode::Char($($ch)*), - modifiers: KeyModifiers::ALT, + code: helix_view::keyboard::KeyCode::Char($($ch)*), + modifiers: helix_view::keyboard::KeyModifiers::ALT, } }; } diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 14c34493..8b7c92de 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -717,6 +717,10 @@ impl Component for EditorView { self.render_view(doc, view, area, surface, &cx.editor.theme, is_focused); } + if let Some(info) = std::mem::take(&mut cx.editor.autoinfo) { + info.render(area, surface, cx); + } + // render status msg if let Some((status_msg, severity)) = &cx.editor.status_msg { use helix_view::editor::Severity; @@ -735,8 +739,7 @@ impl Component for EditorView { } if let Some(completion) = &self.completion { - completion.render(area, surface, cx) - // render completion here + completion.render(area, surface, cx); } } diff --git a/helix-term/src/ui/info.rs b/helix-term/src/ui/info.rs new file mode 100644 index 00000000..085a2d9b --- /dev/null +++ b/helix-term/src/ui/info.rs @@ -0,0 +1,24 @@ +use crate::compositor::{Component, Context}; +use helix_view::graphics::{Margin, Rect, Style}; +use helix_view::info::Info; +use tui::buffer::Buffer as Surface; +use tui::widgets::{Block, Borders, Widget}; + +impl Component for Info { + fn render(&self, viewport: Rect, surface: &mut Surface, cx: &mut Context) { + let block = Block::default().title(self.title).borders(Borders::ALL); + let Info { width, height, .. } = self; + let (w, h) = (*width + 2, *height + 2); + // -2 to subtract command line + statusline. a bit of a hack, because of splits. + let area = Rect::new(viewport.width - w, viewport.height - h - 2, w, h); + let margin = Margin { + vertical: 1, + horizontal: 1, + }; + let Rect { x, y, .. } = area.inner(&margin); + for (y, line) in (y..).zip(self.text.lines()) { + surface.set_string(x, y, line, Style::default()); + } + block.render(area, surface); + } +} diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 7111c968..288d3d2e 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -1,5 +1,6 @@ mod completion; mod editor; +mod info; mod markdown; mod menu; mod picker; diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index cb2032de..b6816d71 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -31,6 +31,7 @@ slotmap = "1" encoding_rs = "0.8" chardetng = "0.1" +unicode-width = "0.1" serde = { version = "1.0", features = ["derive"] } toml = "0.5" diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index a16cc50f..b006a124 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1,6 +1,7 @@ use crate::{ clipboard::{get_clipboard_provider, ClipboardProvider}, graphics::{CursorKind, Rect}, + info::Info, theme::{self, Theme}, tree::Tree, Document, DocumentId, RegisterSelection, View, ViewId, @@ -32,6 +33,7 @@ pub struct Editor { pub syn_loader: Arc, pub theme_loader: Arc, + pub autoinfo: Option, pub status_msg: Option<(String, Severity)>, } @@ -64,6 +66,7 @@ impl Editor { theme_loader: themes, registers: Registers::default(), clipboard_provider: get_clipboard_provider(), + autoinfo: None, status_msg: None, } } diff --git a/helix-view/src/info.rs b/helix-view/src/info.rs new file mode 100644 index 00000000..eef8d3a1 --- /dev/null +++ b/helix-view/src/info.rs @@ -0,0 +1,51 @@ +use crate::input::KeyEvent; +use std::fmt::Write; +use unicode_width::UnicodeWidthStr; + +#[derive(Debug)] +/// Info box used in editor. Rendering logic will be in other crate. +pub struct Info { + /// Title kept as static str for now. + pub title: &'static str, + /// Text body, should contains newline. + pub text: String, + /// Body width. + pub width: u16, + /// Body height. + pub height: u16, +} + +impl Info { + pub fn key(title: &'static str, body: Vec<(Vec, &'static str)>) -> Info { + let keymaps_width: u16 = body + .iter() + .map(|r| r.0.iter().map(|e| e.width() as u16 + 2).sum::() - 2) + .max() + .unwrap(); + let mut text = String::new(); + let mut width = 0; + let height = body.len() as u16; + for (mut keyevents, desc) in body { + let keyevent = keyevents.remove(0); + let mut left = keymaps_width - keyevent.width() as u16; + write!(text, "{}", keyevent).ok(); + for keyevent in keyevents { + write!(text, ", {}", keyevent).ok(); + left -= 2 + keyevent.width() as u16; + } + for _ in 0..left { + text.push(' '); + } + if keymaps_width + 2 + (desc.width() as u16) > width { + width = keymaps_width + 2 + desc.width() as u16; + } + writeln!(text, " {}", &desc).ok(); + } + Info { + title, + text, + width, + height, + } + } +} diff --git a/helix-view/src/input.rs b/helix-view/src/input.rs index 5f61ce14..6e8292e9 100644 --- a/helix-view/src/input.rs +++ b/helix-view/src/input.rs @@ -13,6 +13,32 @@ pub struct KeyEvent { pub modifiers: KeyModifiers, } +pub(crate) mod keys { + pub(crate) const BACKSPACE: &str = "backspace"; + pub(crate) const ENTER: &str = "ret"; + pub(crate) const LEFT: &str = "left"; + pub(crate) const RIGHT: &str = "right"; + pub(crate) const UP: &str = "up"; + pub(crate) const DOWN: &str = "down"; + pub(crate) const HOME: &str = "home"; + pub(crate) const END: &str = "end"; + pub(crate) const PAGEUP: &str = "pageup"; + pub(crate) const PAGEDOWN: &str = "pagedown"; + pub(crate) const TAB: &str = "tab"; + pub(crate) const BACKTAB: &str = "backtab"; + pub(crate) const DELETE: &str = "del"; + pub(crate) const INSERT: &str = "ins"; + pub(crate) const NULL: &str = "null"; + pub(crate) const ESC: &str = "esc"; + pub(crate) const SPACE: &str = "space"; + pub(crate) const LESS_THAN: &str = "lt"; + pub(crate) const GREATER_THAN: &str = "gt"; + pub(crate) const PLUS: &str = "plus"; + pub(crate) const MINUS: &str = "minus"; + pub(crate) const SEMICOLON: &str = "semicolon"; + pub(crate) const PERCENT: &str = "percent"; +} + impl fmt::Display for KeyEvent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!( @@ -34,28 +60,29 @@ impl fmt::Display for KeyEvent { }, ))?; match self.code { - KeyCode::Backspace => f.write_str("backspace")?, - KeyCode::Enter => f.write_str("ret")?, - KeyCode::Left => f.write_str("left")?, - KeyCode::Right => f.write_str("right")?, - KeyCode::Up => f.write_str("up")?, - KeyCode::Down => f.write_str("down")?, - KeyCode::Home => f.write_str("home")?, - KeyCode::End => f.write_str("end")?, - KeyCode::PageUp => f.write_str("pageup")?, - KeyCode::PageDown => f.write_str("pagedown")?, - KeyCode::Tab => f.write_str("tab")?, - KeyCode::BackTab => f.write_str("backtab")?, - KeyCode::Delete => f.write_str("del")?, - KeyCode::Insert => f.write_str("ins")?, - KeyCode::Null => f.write_str("null")?, - KeyCode::Esc => f.write_str("esc")?, - KeyCode::Char('<') => f.write_str("lt")?, - KeyCode::Char('>') => f.write_str("gt")?, - KeyCode::Char('+') => f.write_str("plus")?, - KeyCode::Char('-') => f.write_str("minus")?, - KeyCode::Char(';') => f.write_str("semicolon")?, - KeyCode::Char('%') => f.write_str("percent")?, + KeyCode::Backspace => f.write_str(keys::BACKSPACE)?, + KeyCode::Enter => f.write_str(keys::ENTER)?, + KeyCode::Left => f.write_str(keys::LEFT)?, + KeyCode::Right => f.write_str(keys::RIGHT)?, + KeyCode::Up => f.write_str(keys::UP)?, + KeyCode::Down => f.write_str(keys::DOWN)?, + KeyCode::Home => f.write_str(keys::HOME)?, + KeyCode::End => f.write_str(keys::END)?, + KeyCode::PageUp => f.write_str(keys::PAGEUP)?, + KeyCode::PageDown => f.write_str(keys::PAGEDOWN)?, + KeyCode::Tab => f.write_str(keys::TAB)?, + KeyCode::BackTab => f.write_str(keys::BACKTAB)?, + KeyCode::Delete => f.write_str(keys::DELETE)?, + KeyCode::Insert => f.write_str(keys::INSERT)?, + KeyCode::Null => f.write_str(keys::NULL)?, + KeyCode::Esc => f.write_str(keys::ESC)?, + KeyCode::Char(' ') => f.write_str(keys::SPACE)?, + KeyCode::Char('<') => f.write_str(keys::LESS_THAN)?, + KeyCode::Char('>') => f.write_str(keys::GREATER_THAN)?, + KeyCode::Char('+') => f.write_str(keys::PLUS)?, + KeyCode::Char('-') => f.write_str(keys::MINUS)?, + KeyCode::Char(';') => f.write_str(keys::SEMICOLON)?, + KeyCode::Char('%') => f.write_str(keys::PERCENT)?, KeyCode::F(i) => f.write_fmt(format_args!("F{}", i))?, KeyCode::Char(c) => f.write_fmt(format_args!("{}", c))?, }; @@ -63,34 +90,83 @@ impl fmt::Display for KeyEvent { } } +impl unicode_width::UnicodeWidthStr for KeyEvent { + fn width(&self) -> usize { + use unicode_width::UnicodeWidthChar; + let mut width = match self.code { + KeyCode::Backspace => keys::BACKSPACE.len(), + KeyCode::Enter => keys::ENTER.len(), + KeyCode::Left => keys::LEFT.len(), + KeyCode::Right => keys::RIGHT.len(), + KeyCode::Up => keys::UP.len(), + KeyCode::Down => keys::DOWN.len(), + KeyCode::Home => keys::HOME.len(), + KeyCode::End => keys::END.len(), + KeyCode::PageUp => keys::PAGEUP.len(), + KeyCode::PageDown => keys::PAGEDOWN.len(), + KeyCode::Tab => keys::TAB.len(), + KeyCode::BackTab => keys::BACKTAB.len(), + KeyCode::Delete => keys::DELETE.len(), + KeyCode::Insert => keys::INSERT.len(), + KeyCode::Null => keys::NULL.len(), + KeyCode::Esc => keys::ESC.len(), + KeyCode::Char(' ') => keys::SPACE.len(), + KeyCode::Char('<') => keys::LESS_THAN.len(), + KeyCode::Char('>') => keys::GREATER_THAN.len(), + KeyCode::Char('+') => keys::PLUS.len(), + KeyCode::Char('-') => keys::MINUS.len(), + KeyCode::Char(';') => keys::SEMICOLON.len(), + KeyCode::Char('%') => keys::PERCENT.len(), + KeyCode::F(1..=9) => 2, + KeyCode::F(_) => 3, + KeyCode::Char(c) => c.width().unwrap_or(0), + }; + if self.modifiers.contains(KeyModifiers::SHIFT) { + width += 2; + } + if self.modifiers.contains(KeyModifiers::ALT) { + width += 2; + } + if self.modifiers.contains(KeyModifiers::CONTROL) { + width += 2; + } + width + } + + fn width_cjk(&self) -> usize { + self.width() + } +} + impl std::str::FromStr for KeyEvent { type Err = Error; fn from_str(s: &str) -> Result { let mut tokens: Vec<_> = s.split('-').collect(); let code = match tokens.pop().ok_or_else(|| anyhow!("Missing key code"))? { - "backspace" => KeyCode::Backspace, - "space" => KeyCode::Char(' '), - "ret" => KeyCode::Enter, - "lt" => KeyCode::Char('<'), - "gt" => KeyCode::Char('>'), - "plus" => KeyCode::Char('+'), - "minus" => KeyCode::Char('-'), - "semicolon" => KeyCode::Char(';'), - "percent" => KeyCode::Char('%'), - "left" => KeyCode::Left, - "right" => KeyCode::Right, - "up" => KeyCode::Down, - "home" => KeyCode::Home, - "end" => KeyCode::End, - "pageup" => KeyCode::PageUp, - "pagedown" => KeyCode::PageDown, - "tab" => KeyCode::Tab, - "backtab" => KeyCode::BackTab, - "del" => KeyCode::Delete, - "ins" => KeyCode::Insert, - "null" => KeyCode::Null, - "esc" => KeyCode::Esc, + keys::BACKSPACE => KeyCode::Backspace, + keys::ENTER => KeyCode::Enter, + keys::LEFT => KeyCode::Left, + keys::RIGHT => KeyCode::Right, + keys::UP => KeyCode::Up, + keys::DOWN => KeyCode::Down, + keys::HOME => KeyCode::Home, + keys::END => KeyCode::End, + keys::PAGEUP => KeyCode::PageUp, + keys::PAGEDOWN => KeyCode::PageDown, + keys::TAB => KeyCode::Tab, + keys::BACKTAB => KeyCode::BackTab, + keys::DELETE => KeyCode::Delete, + keys::INSERT => KeyCode::Insert, + keys::NULL => KeyCode::Null, + keys::ESC => KeyCode::Esc, + keys::SPACE => KeyCode::Char(' '), + keys::LESS_THAN => KeyCode::Char('<'), + keys::GREATER_THAN => KeyCode::Char('>'), + keys::PLUS => KeyCode::Char('+'), + keys::MINUS => KeyCode::Char('-'), + keys::SEMICOLON => KeyCode::Char(';'), + keys::PERCENT => KeyCode::Char('%'), single if single.len() == 1 => KeyCode::Char(single.chars().next().unwrap()), function if function.len() > 1 && function.starts_with('F') => { let function: String = function.chars().skip(1).collect(); diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index caed2952..9bcc0b7d 100644 --- a/helix-view/src/lib.rs +++ b/helix-view/src/lib.rs @@ -5,6 +5,7 @@ pub mod clipboard; pub mod document; pub mod editor; pub mod graphics; +pub mod info; pub mod input; pub mod keyboard; pub mod register_selection; -- cgit v1.2.3-70-g09d2 From 9effe71b7d2133f18545d182cef384ea3fd1c0ff Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Wed, 30 Jun 2021 00:17:16 +0800 Subject: Apply suggestions from blaz for infobox --- Cargo.lock | 2 -- helix-term/src/ui/info.rs | 6 +----- helix-tui/Cargo.toml | 1 - helix-tui/src/backend/test.rs | 2 +- helix-tui/src/buffer.rs | 2 +- helix-tui/src/text.rs | 2 +- helix-tui/src/widgets/paragraph.rs | 2 +- helix-tui/src/widgets/reflow.rs | 2 +- helix-tui/src/widgets/table.rs | 2 +- helix-view/Cargo.toml | 1 - helix-view/src/info.rs | 2 +- helix-view/src/input.rs | 5 +++-- 12 files changed, 11 insertions(+), 18 deletions(-) (limited to 'helix-view') diff --git a/Cargo.lock b/Cargo.lock index 59eb894e..2cd202f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -396,7 +396,6 @@ dependencies = [ "helix-view", "serde", "unicode-segmentation", - "unicode-width", ] [[package]] @@ -419,7 +418,6 @@ dependencies = [ "slotmap", "tokio", "toml", - "unicode-width", "url", "which", ] diff --git a/helix-term/src/ui/info.rs b/helix-term/src/ui/info.rs index 085a2d9b..c5709356 100644 --- a/helix-term/src/ui/info.rs +++ b/helix-term/src/ui/info.rs @@ -11,11 +11,7 @@ impl Component for Info { let (w, h) = (*width + 2, *height + 2); // -2 to subtract command line + statusline. a bit of a hack, because of splits. let area = Rect::new(viewport.width - w, viewport.height - h - 2, w, h); - let margin = Margin { - vertical: 1, - horizontal: 1, - }; - let Rect { x, y, .. } = area.inner(&margin); + let Rect { x, y, .. } = block.inner(area); for (y, line) in (y..).zip(self.text.lines()) { surface.set_string(x, y, line, Style::default()); } diff --git a/helix-tui/Cargo.toml b/helix-tui/Cargo.toml index dde2eafe..7f98144c 100644 --- a/helix-tui/Cargo.toml +++ b/helix-tui/Cargo.toml @@ -19,7 +19,6 @@ default = ["crossterm"] bitflags = "1.0" cassowary = "0.3" unicode-segmentation = "1.2" -unicode-width = "0.1" crossterm = { version = "0.20", optional = true } serde = { version = "1", "optional" = true, features = ["derive"]} helix-view = { version = "0.3", path = "../helix-view", features = ["term"] } diff --git a/helix-tui/src/backend/test.rs b/helix-tui/src/backend/test.rs index a03bcd8e..3f56b49c 100644 --- a/helix-tui/src/backend/test.rs +++ b/helix-tui/src/backend/test.rs @@ -2,9 +2,9 @@ use crate::{ backend::Backend, buffer::{Buffer, Cell}, }; +use helix_core::unicode::width::UnicodeWidthStr; use helix_view::graphics::{CursorKind, Rect}; use std::{fmt::Write, io}; -use unicode_width::UnicodeWidthStr; /// A backend used for the integration tests. #[derive(Debug)] diff --git a/helix-tui/src/buffer.rs b/helix-tui/src/buffer.rs index 3a7ad144..377e3e39 100644 --- a/helix-tui/src/buffer.rs +++ b/helix-tui/src/buffer.rs @@ -1,7 +1,7 @@ use crate::text::{Span, Spans}; +use helix_core::unicode::width::UnicodeWidthStr; use std::cmp::min; use unicode_segmentation::UnicodeSegmentation; -use unicode_width::UnicodeWidthStr; use helix_view::graphics::{Color, Modifier, Rect, Style}; diff --git a/helix-tui/src/text.rs b/helix-tui/src/text.rs index 4af6b09d..b8e52479 100644 --- a/helix-tui/src/text.rs +++ b/helix-tui/src/text.rs @@ -47,10 +47,10 @@ //! ]); //! ``` use helix_core::line_ending::str_is_line_ending; +use helix_core::unicode::width::UnicodeWidthStr; use helix_view::graphics::Style; use std::borrow::Cow; use unicode_segmentation::UnicodeSegmentation; -use unicode_width::UnicodeWidthStr; /// A grapheme associated to a style. #[derive(Debug, Clone, PartialEq)] diff --git a/helix-tui/src/widgets/paragraph.rs b/helix-tui/src/widgets/paragraph.rs index bdfb5b9a..fee35d25 100644 --- a/helix-tui/src/widgets/paragraph.rs +++ b/helix-tui/src/widgets/paragraph.rs @@ -7,9 +7,9 @@ use crate::{ Block, Widget, }, }; +use helix_core::unicode::width::UnicodeWidthStr; use helix_view::graphics::{Rect, Style}; use std::iter; -use unicode_width::UnicodeWidthStr; fn get_line_offset(line_width: u16, text_area_width: u16, alignment: Alignment) -> u16 { match alignment { diff --git a/helix-tui/src/widgets/reflow.rs b/helix-tui/src/widgets/reflow.rs index ae561a4f..21847783 100644 --- a/helix-tui/src/widgets/reflow.rs +++ b/helix-tui/src/widgets/reflow.rs @@ -1,7 +1,7 @@ use crate::text::StyledGrapheme; use helix_core::line_ending::str_is_line_ending; +use helix_core::unicode::width::UnicodeWidthStr; use unicode_segmentation::UnicodeSegmentation; -use unicode_width::UnicodeWidthStr; const NBSP: &str = "\u{00a0}"; diff --git a/helix-tui/src/widgets/table.rs b/helix-tui/src/widgets/table.rs index ee5147b7..1ee4286a 100644 --- a/helix-tui/src/widgets/table.rs +++ b/helix-tui/src/widgets/table.rs @@ -9,9 +9,9 @@ use cassowary::{ WeightedRelation::*, {Expression, Solver}, }; +use helix_core::unicode::width::UnicodeWidthStr; use helix_view::graphics::{Rect, Style}; use std::collections::HashMap; -use unicode_width::UnicodeWidthStr; /// A [`Cell`] contains the [`Text`] to be displayed in a [`Row`] of a [`Table`]. /// diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index b6816d71..cb2032de 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -31,7 +31,6 @@ slotmap = "1" encoding_rs = "0.8" chardetng = "0.1" -unicode-width = "0.1" serde = { version = "1.0", features = ["derive"] } toml = "0.5" diff --git a/helix-view/src/info.rs b/helix-view/src/info.rs index eef8d3a1..0eaab783 100644 --- a/helix-view/src/info.rs +++ b/helix-view/src/info.rs @@ -1,6 +1,6 @@ use crate::input::KeyEvent; +use helix_core::unicode::width::UnicodeWidthStr; use std::fmt::Write; -use unicode_width::UnicodeWidthStr; #[derive(Debug)] /// Info box used in editor. Rendering logic will be in other crate. diff --git a/helix-view/src/input.rs b/helix-view/src/input.rs index 6e8292e9..2847bb69 100644 --- a/helix-view/src/input.rs +++ b/helix-view/src/input.rs @@ -1,5 +1,6 @@ //! Input event handling, currently backed by crossterm. use anyhow::{anyhow, Error}; +use helix_core::unicode::width::UnicodeWidthStr; use serde::de::{self, Deserialize, Deserializer}; use std::fmt; @@ -90,9 +91,9 @@ impl fmt::Display for KeyEvent { } } -impl unicode_width::UnicodeWidthStr for KeyEvent { +impl UnicodeWidthStr for KeyEvent { fn width(&self) -> usize { - use unicode_width::UnicodeWidthChar; + use helix_core::unicode::width::UnicodeWidthChar; let mut width = match self.code { KeyCode::Backspace => keys::BACKSPACE.len(), KeyCode::Enter => keys::ENTER.len(), -- cgit v1.2.3-70-g09d2 From 5977b07e197cc6ef9051dd34a28b9fe28e01e966 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Fri, 2 Jul 2021 09:46:28 +0800 Subject: Reduce calculation and improve pattern in infobox - switch to use static OnceCell to calculate Info once - pass Vec<(&[KeyEvent], &str)> rather than Vec<(Vec, &str)> - expr -> tt to allow using | as separator, make it more like match --- helix-term/src/commands.rs | 21 ++++++++++----------- helix-term/src/keymap.rs | 7 ++----- helix-term/src/ui/info.rs | 2 +- helix-view/src/editor.rs | 2 +- helix-view/src/info.rs | 8 ++++---- 5 files changed, 18 insertions(+), 22 deletions(-) (limited to 'helix-view') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index b6f3c11f..351ec1fb 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -34,7 +34,6 @@ use movement::Movement; use crate::{ compositor::{self, Component, Compositor}, - key, ui::{self, Picker, Popup, Prompt, PromptEvent}, }; @@ -48,7 +47,7 @@ use std::{ path::{Path, PathBuf}, }; -use once_cell::sync::Lazy; +use once_cell::sync::{Lazy, OnceCell}; use serde::de::{self, Deserialize, Deserializer}; pub struct Context<'a> { @@ -3414,13 +3413,11 @@ fn select_register(cx: &mut Context) { } macro_rules! mode_info { - // TODO: how to use one expr for both pat and expr? - // TODO: how to use replaced function name as str at compile time? - // TODO: extend to support multiple keys, but first solve the other two + // TODO: reuse $mode for $stat (@join $first:expr $(,$rest:expr)*) => { concat!($first, $(", ", $rest),*) }; - {$mode:ident, $name:literal, $(#[doc = $desc:literal] $($key:expr),+ => $func:expr),+,} => { + {$mode:ident, $stat:ident, $name:literal, $(#[doc = $desc:literal] $($key:tt)|+ => $func:expr),+,} => { #[doc = $name] #[doc = ""] #[doc = ""] @@ -3439,12 +3436,14 @@ macro_rules! mode_info { )+ #[doc = "
keydesc
"] pub fn $mode(cx: &mut Context) { - cx.editor.autoinfo = Some(Info::key( + static $stat: OnceCell = OnceCell::new(); + cx.editor.autoinfo = Some($stat.get_or_init(|| Info::key( $name, - vec![$((vec![$($key.parse().unwrap()),+], $desc)),+], - )); + vec![$((&[$($key.parse().unwrap()),+], $desc)),+], + ))); use helix_core::hashmap; - let mut map = hashmap! { + // TODO: try and convert this to match later + let map = hashmap! { $($($key.parse::().unwrap() => $func as for<'r, 's> fn(&'r mut Context<'s>)),+),* }; cx.on_next_key_mode(map); @@ -3453,7 +3452,7 @@ macro_rules! mode_info { } mode_info! { - space_mode, "space mode", + space_mode, SPACE_MODE, "space mode", /// file picker "f" => file_picker, /// buffer picker diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index ef4a2138..3cd540ea 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -1,11 +1,7 @@ pub use crate::commands::Command; use crate::config::Config; use helix_core::hashmap; -use helix_view::{ - document::Mode, - input::KeyEvent, - keyboard::{KeyCode, KeyModifiers}, -}; +use helix_view::{document::Mode, input::KeyEvent}; use serde::Deserialize; use std::{ collections::HashMap, @@ -352,6 +348,7 @@ pub fn merge_keys(mut config: Config) -> Config { #[test] fn merge_partial_keys() { + use helix_view::keyboard::{KeyCode, KeyModifiers}; let config = Config { keys: Keymaps(hashmap! { Mode::Normal => hashmap! { diff --git a/helix-term/src/ui/info.rs b/helix-term/src/ui/info.rs index 87c2c213..c6f8db43 100644 --- a/helix-term/src/ui/info.rs +++ b/helix-term/src/ui/info.rs @@ -1,5 +1,5 @@ use crate::compositor::{Component, Context}; -use helix_view::graphics::{Margin, Rect, Style}; +use helix_view::graphics::Rect; use helix_view::info::Info; use tui::buffer::Buffer as Surface; use tui::widgets::{Block, Borders, Widget}; diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index b006a124..4f01cce4 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -33,7 +33,7 @@ pub struct Editor { pub syn_loader: Arc, pub theme_loader: Arc, - pub autoinfo: Option, + pub autoinfo: Option<&'static Info>, pub status_msg: Option<(String, Severity)>, } diff --git a/helix-view/src/info.rs b/helix-view/src/info.rs index 0eaab783..92c10351 100644 --- a/helix-view/src/info.rs +++ b/helix-view/src/info.rs @@ -16,7 +16,7 @@ pub struct Info { } impl Info { - pub fn key(title: &'static str, body: Vec<(Vec, &'static str)>) -> Info { + pub fn key(title: &'static str, body: Vec<(&[KeyEvent], &'static str)>) -> Info { let keymaps_width: u16 = body .iter() .map(|r| r.0.iter().map(|e| e.width() as u16 + 2).sum::() - 2) @@ -25,11 +25,11 @@ impl Info { let mut text = String::new(); let mut width = 0; let height = body.len() as u16; - for (mut keyevents, desc) in body { - let keyevent = keyevents.remove(0); + for (keyevents, desc) in body { + let keyevent = keyevents[0]; let mut left = keymaps_width - keyevent.width() as u16; write!(text, "{}", keyevent).ok(); - for keyevent in keyevents { + for keyevent in &keyevents[1..] { write!(text, ", {}", keyevent).ok(); left -= 2 + keyevent.width() as u16; } -- cgit v1.2.3-70-g09d2 From 916362d3a97ddc1b4a630f7d7ba5ae5dc405c21a Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Sun, 4 Jul 2021 00:12:02 +0800 Subject: Info box add horizontal padding --- helix-view/src/info.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'helix-view') diff --git a/helix-view/src/info.rs b/helix-view/src/info.rs index 92c10351..f3df50fe 100644 --- a/helix-view/src/info.rs +++ b/helix-view/src/info.rs @@ -17,6 +17,7 @@ pub struct Info { impl Info { pub fn key(title: &'static str, body: Vec<(&[KeyEvent], &'static str)>) -> Info { + let (lpad, mpad, rpad) = (1, 2, 1); let keymaps_width: u16 = body .iter() .map(|r| r.0.iter().map(|e| e.width() as u16 + 2).sum::() - 2) @@ -28,18 +29,23 @@ impl Info { for (keyevents, desc) in body { let keyevent = keyevents[0]; let mut left = keymaps_width - keyevent.width() as u16; + for _ in 0..lpad { + text.push(' '); + } write!(text, "{}", keyevent).ok(); for keyevent in &keyevents[1..] { write!(text, ", {}", keyevent).ok(); left -= 2 + keyevent.width() as u16; } - for _ in 0..left { + for _ in 0..left + mpad { text.push(' '); } - if keymaps_width + 2 + (desc.width() as u16) > width { - width = keymaps_width + 2 + desc.width() as u16; + let desc = desc.trim(); + let w = lpad + keymaps_width + mpad + (desc.width() as u16) + rpad; + if w > width { + width = w; } - writeln!(text, " {}", &desc).ok(); + writeln!(text, "{}", desc).ok(); } Info { title, -- cgit v1.2.3-70-g09d2 From b72c6204e530987d825acb8b27667085b1c95158 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Mon, 5 Jul 2021 10:17:26 +0900 Subject: fix: When calculating relative path, expand tilde last --- helix-view/src/document.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'helix-view') diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index f85ded11..fdb6f8c3 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1049,14 +1049,12 @@ impl Document { let cwdir = std::env::current_dir().expect("couldn't determine current directory"); self.path.as_ref().map(|path| { - let path = fold_home_dir(path); - if path.is_relative() { - path + let path = if path.is_relative() { + path.as_path() } else { - path.strip_prefix(cwdir) - .map(|p| p.to_path_buf()) - .unwrap_or(path) - } + path.strip_prefix(cwdir).unwrap_or(path.as_path()) + }; + fold_home_dir(path) }) } -- cgit v1.2.3-70-g09d2 From 48481db8ca3a19c704825adb72a667c4266e9370 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Mon, 5 Jul 2021 10:26:51 +0900 Subject: fix: Make path absolute before normalizing :open ../file.txt failed before because .. would be stripped --- helix-view/src/document.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'helix-view') diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index fdb6f8c3..2ab1602e 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -70,7 +70,6 @@ pub enum IndentStyle { } pub struct Document { - // rope + selection pub(crate) id: DocumentId, text: Rope, pub(crate) selections: HashMap, @@ -408,12 +407,13 @@ pub fn normalize_path(path: &Path) -> PathBuf { /// This function is used instead of `std::fs::canonicalize` because we don't want to verify /// here if the path exists, just normalize it's components. pub fn canonicalize_path(path: &Path) -> std::io::Result { - let normalized = normalize_path(path); - if normalized.is_absolute() { - Ok(normalized) + let path = if path.is_relative() { + std::env::current_dir().map(|current_dir| current_dir.join(path))? } else { - std::env::current_dir().map(|current_dir| current_dir.join(normalized)) - } + path.to_path_buf() + }; + + Ok(normalize_path(&path)) } use helix_lsp::lsp; -- cgit v1.2.3-70-g09d2 From fc34efea124c23285770f9bd2e23dbcb905a6965 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Mon, 5 Jul 2021 10:34:48 +0900 Subject: appease clippy --- helix-view/src/document.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'helix-view') diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 2ab1602e..a2bd1c41 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1049,10 +1049,9 @@ impl Document { let cwdir = std::env::current_dir().expect("couldn't determine current directory"); self.path.as_ref().map(|path| { - let path = if path.is_relative() { - path.as_path() - } else { - path.strip_prefix(cwdir).unwrap_or(path.as_path()) + let mut path = path.as_path(); + if path.is_absolute() { + path = path.strip_prefix(cwdir).unwrap_or(path) }; fold_home_dir(path) }) -- cgit v1.2.3-70-g09d2