diff --git a/Cargo.lock b/Cargo.lock index d1e32ad89418..a1e29d2e178d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -389,6 +389,7 @@ dependencies = [ "futures-util", "helix-core", "helix-lsp", + "helix-tui", "log", "once_cell", "serde", diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index d48a209956d5..438a19a2b522 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -15,7 +15,8 @@ use helix_core::{ use helix_view::{ document::{IndentStyle, Mode}, - input::{KeyCode, KeyEvent}, + input::KeyEvent, + keyboard::KeyCode, view::{View, PADDING}, Document, DocumentId, Editor, ViewId, }; diff --git a/helix-term/src/config.rs b/helix-term/src/config.rs index 87d9e3655d3f..3c05144a1c8c 100644 --- a/helix-term/src/config.rs +++ b/helix-term/src/config.rs @@ -23,8 +23,11 @@ pub struct LspConfig { #[test] fn parsing_keymaps_config_file() { use helix_core::hashmap; - use helix_view::document::Mode; - use helix_view::input::{KeyCode, KeyEvent, KeyModifiers}; + use helix_view::{ + document::Mode, + input::KeyEvent, + keyboard::{KeyCode, KeyModifiers}, + }; let sample_keymaps = r#" [keys.insert] diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index b4c2055f28c4..e2ed75feeb8d 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -3,8 +3,11 @@ pub use crate::commands::Command; use crate::config::Config; use anyhow::{anyhow, Error, Result}; use helix_core::hashmap; -use helix_view::document::Mode; -use helix_view::input::{KeyCode, KeyEvent, KeyModifiers}; +use helix_view::{ + document::Mode, + input::KeyEvent, + keyboard::{KeyCode, KeyModifiers} +}; use serde::Deserialize; use std::{ collections::HashMap, diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 57a3b579675f..76826d793524 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -14,8 +14,9 @@ use helix_core::{ use helix_lsp::LspProgressMap; use helix_view::{ document::Mode, - input::{KeyCode, KeyEvent, KeyModifiers} graphics::{CursorKind, Rect, Color, Modifier, Style}, + input::KeyEvent, + keyboard::{KeyCode, KeyModifiers}, Document, Editor, Theme, View }; use std::borrow::Cow; diff --git a/helix-tui/src/buffer.rs b/helix-tui/src/buffer.rs index 3d9b933a11d9..396307204fa9 100644 --- a/helix-tui/src/buffer.rs +++ b/helix-tui/src/buffer.rs @@ -87,8 +87,7 @@ impl Default for Cell { /// /// ``` /// use helix_tui::buffer::{Buffer, Cell}; -/// use helix_tui::layout::Rect; -/// use helix_tui::style::{Color, Style, Modifier}; +/// use helix_view::graphics::{Rect, Color, Style, Modifier}; /// /// let mut buf = Buffer::empty(Rect{x: 0, y: 0, width: 10, height: 5}); /// buf.get_mut(0, 2).set_symbol("x"); @@ -191,7 +190,7 @@ impl Buffer { /// /// ``` /// # use helix_tui::buffer::Buffer; - /// # use helix_tui::layout::Rect; + /// # use helix_view::graphics::Rect; /// let rect = Rect::new(200, 100, 10, 10); /// let buffer = Buffer::empty(rect); /// // Global coordinates to the top corner of this buffer's area @@ -223,7 +222,7 @@ impl Buffer { /// /// ``` /// # use helix_tui::buffer::Buffer; - /// # use helix_tui::layout::Rect; + /// # use helix_view::graphics::Rect; /// let rect = Rect::new(200, 100, 10, 10); /// let buffer = Buffer::empty(rect); /// assert_eq!(buffer.pos_of(0), (200, 100)); diff --git a/helix-tui/src/layout.rs b/helix-tui/src/layout.rs index d7088e810fa5..e6a84aa04cba 100644 --- a/helix-tui/src/layout.rs +++ b/helix-tui/src/layout.rs @@ -114,7 +114,8 @@ impl Layout { /// /// # Examples /// ``` - /// # use helix_tui::layout::{Rect, Constraint, Direction, Layout}; + /// # use helix_tui::layout::{Constraint, Direction, Layout}; + /// # use helix_view::graphics::Rect; /// let chunks = Layout::default() /// .direction(Direction::Vertical) /// .constraints([Constraint::Length(5), Constraint::Min(0)].as_ref()) diff --git a/helix-tui/src/text.rs b/helix-tui/src/text.rs index 5b2b6e91f63e..4af6b09de728 100644 --- a/helix-tui/src/text.rs +++ b/helix-tui/src/text.rs @@ -21,7 +21,7 @@ //! ```rust //! # use helix_tui::widgets::Block; //! # use helix_tui::text::{Span, Spans}; -//! # use helix_tui::style::{Color, Style}; +//! # use helix_view::graphics::{Color, Style}; //! // A simple string with no styling. //! // Converted to Spans(vec![ //! // Span { content: Cow::Borrowed("My title"), style: Style { .. } } @@ -92,7 +92,7 @@ impl<'a> Span<'a> { /// /// ```rust /// # use helix_tui::text::Span; - /// # use helix_tui::style::{Color, Modifier, Style}; + /// # use helix_view::graphics::{Color, Modifier, Style}; /// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC); /// Span::styled("My text", style); /// Span::styled(String::from("My text"), style); @@ -121,7 +121,7 @@ impl<'a> Span<'a> { /// /// ```rust /// # use helix_tui::text::{Span, StyledGrapheme}; - /// # use helix_tui::style::{Color, Modifier, Style}; + /// # use helix_view::graphics::{Color, Modifier, Style}; /// # use std::iter::Iterator; /// let style = Style::default().fg(Color::Yellow); /// let span = Span::styled("Text", style); @@ -211,7 +211,7 @@ impl<'a> Spans<'a> { /// /// ```rust /// # use helix_tui::text::{Span, Spans}; - /// # use helix_tui::style::{Color, Style}; + /// # use helix_view::graphics::{Color, Style}; /// let spans = Spans::from(vec![ /// Span::styled("My", Style::default().fg(Color::Yellow)), /// Span::raw(" text"), @@ -265,7 +265,7 @@ impl<'a> From> for String { /// /// ```rust /// # use helix_tui::text::Text; -/// # use helix_tui::style::{Color, Modifier, Style}; +/// # use helix_view::graphics::{Color, Modifier, Style}; /// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC); /// /// // An initial two lines of `Text` built from a `&str` @@ -319,7 +319,7 @@ impl<'a> Text<'a> { /// /// ```rust /// # use helix_tui::text::Text; - /// # use helix_tui::style::{Color, Modifier, Style}; + /// # use helix_view::graphics::{Color, Modifier, Style}; /// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC); /// Text::styled("The first line\nThe second line", style); /// Text::styled(String::from("The first line\nThe second line"), style); @@ -369,7 +369,7 @@ impl<'a> Text<'a> { /// /// ```rust /// # use helix_tui::text::Text; - /// # use helix_tui::style::{Color, Modifier, Style}; + /// # use helix_view::graphics::{Color, Modifier, Style}; /// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC); /// let mut raw_text = Text::raw("The first line\nThe second line"); /// let styled_text = Text::styled(String::from("The first line\nThe second line"), style); diff --git a/helix-tui/src/widgets/block.rs b/helix-tui/src/widgets/block.rs index fca92d0a001f..648c2d7eebdc 100644 --- a/helix-tui/src/widgets/block.rs +++ b/helix-tui/src/widgets/block.rs @@ -32,7 +32,7 @@ impl BorderType { /// /// ``` /// # use helix_tui::widgets::{Block, BorderType, Borders}; -/// # use helix_tui::style::{Style, Color}; +/// # use helix_view::graphics::{Style, Color}; /// Block::default() /// .title("Block") /// .borders(Borders::LEFT | Borders::RIGHT) @@ -211,7 +211,6 @@ impl<'a> Widget for Block<'a> { #[cfg(test)] mod tests { use super::*; - use crate::layout::Rect; #[test] fn inner_takes_into_account_the_borders() { diff --git a/helix-tui/src/widgets/paragraph.rs b/helix-tui/src/widgets/paragraph.rs index cf48e29255f8..cfedef79803d 100644 --- a/helix-tui/src/widgets/paragraph.rs +++ b/helix-tui/src/widgets/paragraph.rs @@ -26,8 +26,8 @@ fn get_line_offset(line_width: u16, text_area_width: u16, alignment: Alignment) /// ``` /// # use helix_tui::text::{Text, Spans, Span}; /// # use helix_tui::widgets::{Block, Borders, Paragraph, Wrap}; -/// # use helix_tui::style::{Style, Color, Modifier}; /// # use helix_tui::layout::{Alignment}; +/// # use helix_view::graphics::{Style, Color, Modifier}; /// let text = vec![ /// Spans::from(vec![ /// Span::raw("First"), diff --git a/helix-tui/src/widgets/table.rs b/helix-tui/src/widgets/table.rs index f3894bef52ec..5273ab97ff71 100644 --- a/helix-tui/src/widgets/table.rs +++ b/helix-tui/src/widgets/table.rs @@ -21,8 +21,8 @@ use helix_view::graphics::{Rect, Style}; /// It can be created from anything that can be converted to a [`Text`]. /// ```rust /// # use helix_tui::widgets::Cell; -/// # use helix_tui::style::{Style, Modifier}; /// # use helix_tui::text::{Span, Spans, Text}; +/// # use helix_view::graphics::{Style, Modifier}; /// Cell::from("simple string"); /// /// Cell::from(Span::from("span")); @@ -74,7 +74,7 @@ where /// But if you need a bit more control over individual cells, you can explicity create [`Cell`]s: /// ```rust /// # use helix_tui::widgets::{Row, Cell}; -/// # use helix_tui::style::{Style, Color}; +/// # use helix_view::graphics::{Style, Color}; /// Row::new(vec![ /// Cell::from("Cell1"), /// Cell::from("Cell2").style(Style::default().fg(Color::Yellow)), @@ -137,7 +137,7 @@ impl<'a> Row<'a> { /// ```rust /// # use helix_tui::widgets::{Block, Borders, Table, Row, Cell}; /// # use helix_tui::layout::Constraint; -/// # use helix_tui::style::{Style, Color, Modifier}; +/// # use helix_view::graphics::{Style, Color, Modifier}; /// # use helix_tui::text::{Text, Spans, Span}; /// Table::new(vec![ /// // Row can be created from simple strings. diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index 93dc29d54aff..9c3c23ff4672 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -37,3 +37,6 @@ toml = "0.5" log = "~0.4" which = "4.1" + +[dev-dependencies] +helix-tui = { path = "../helix-tui" } diff --git a/helix-view/src/graphics.rs b/helix-view/src/graphics.rs index bfc63a63d719..e14ce2b9145a 100644 --- a/helix-view/src/graphics.rs +++ b/helix-view/src/graphics.rs @@ -192,7 +192,7 @@ bitflags! { /// ## Examples /// /// ```rust - /// # use helix_tui::style::Modifier; + /// # use helix_view::graphics::Modifier; /// /// let m = Modifier::BOLD | Modifier::ITALIC; /// ``` @@ -213,7 +213,7 @@ bitflags! { /// Style let you control the main characteristics of the displayed elements. /// /// ```rust -/// # use helix_tui::style::{Color, Modifier, Style}; +/// # use helix_view::graphics::{Color, Modifier, Style}; /// Style::default() /// .fg(Color::Black) /// .bg(Color::Green) @@ -225,9 +225,8 @@ bitflags! { /// just S3. /// /// ```rust -/// # use helix_tui::style::{Color, Modifier, Style}; +/// # use helix_view::graphics::{Rect, Color, Modifier, Style}; /// # use helix_tui::buffer::Buffer; -/// # use helix_tui::layout::Rect; /// let styles = [ /// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC), /// Style::default().bg(Color::Red), @@ -252,9 +251,8 @@ bitflags! { /// reset all properties until that point use [`Style::reset`]. /// /// ``` -/// # use helix_tui::style::{Color, Modifier, Style}; +/// # use helix_view::graphics::{Rect, Color, Modifier, Style}; /// # use helix_tui::buffer::Buffer; -/// # use helix_tui::layout::Rect; /// let styles = [ /// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC), /// Style::reset().fg(Color::Yellow), @@ -309,7 +307,7 @@ impl Style { /// ## Examples /// /// ```rust - /// # use helix_tui::style::{Color, Style}; + /// # use helix_view::graphics::{Color, Style}; /// let style = Style::default().fg(Color::Blue); /// let diff = Style::default().fg(Color::Red); /// assert_eq!(style.patch(diff), Style::default().fg(Color::Red)); @@ -324,7 +322,7 @@ impl Style { /// ## Examples /// /// ```rust - /// # use helix_tui::style::{Color, Style}; + /// # use helix_view::graphics::{Color, Style}; /// let style = Style::default().bg(Color::Blue); /// let diff = Style::default().bg(Color::Red); /// assert_eq!(style.patch(diff), Style::default().bg(Color::Red)); @@ -341,7 +339,7 @@ impl Style { /// ## Examples /// /// ```rust - /// # use helix_tui::style::{Color, Modifier, Style}; + /// # use helix_view::graphics::{Color, Modifier, Style}; /// let style = Style::default().add_modifier(Modifier::BOLD); /// let diff = Style::default().add_modifier(Modifier::ITALIC); /// let patched = style.patch(diff); @@ -361,7 +359,7 @@ impl Style { /// ## Examples /// /// ```rust - /// # use helix_tui::style::{Color, Modifier, Style}; + /// # use helix_view::graphics::{Color, Modifier, Style}; /// let style = Style::default().add_modifier(Modifier::BOLD | Modifier::ITALIC); /// let diff = Style::default().remove_modifier(Modifier::ITALIC); /// let patched = style.patch(diff); @@ -379,7 +377,7 @@ impl Style { /// /// ## Examples /// ``` - /// # use helix_tui::style::{Color, Modifier, Style}; + /// # use helix_view::graphics::{Color, Modifier, Style}; /// let style_1 = Style::default().fg(Color::Yellow); /// let style_2 = Style::default().bg(Color::Red); /// let combined = style_1.patch(style_2); diff --git a/helix-view/src/input.rs b/helix-view/src/input.rs index ab417819fa42..818899262ad6 100644 --- a/helix-view/src/input.rs +++ b/helix-view/src/input.rs @@ -1,10 +1,9 @@ //! Input event handling, currently backed by crossterm. use anyhow::{anyhow, Error}; -use crossterm::event; use serde::de::{self, Deserialize, Deserializer}; use std::fmt; -pub use crossterm::event::{KeyCode, KeyModifiers}; +use crate::keyboard::{KeyCode, KeyModifiers}; /// Represents a key event. // We use a newtype here because we want to customize Deserialize and Display. @@ -132,9 +131,13 @@ impl<'de> Deserialize<'de> for KeyEvent { } } -impl From for KeyEvent { - fn from(event::KeyEvent { code, modifiers }: event::KeyEvent) -> KeyEvent { - KeyEvent { code, modifiers } +#[cfg(feature = "term")] +impl From for KeyEvent { + fn from(crossterm::event::KeyEvent { code, modifiers }: crossterm::event::KeyEvent) -> KeyEvent { + KeyEvent { + code: code.into(), + modifiers: modifiers.into() + } } } diff --git a/helix-view/src/keyboard.rs b/helix-view/src/keyboard.rs new file mode 100644 index 000000000000..e3791ed81e09 --- /dev/null +++ b/helix-view/src/keyboard.rs @@ -0,0 +1,160 @@ +use bitflags::bitflags; + +bitflags! { + /// Represents key modifiers (shift, control, alt). + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct KeyModifiers: u8 { + const SHIFT = 0b0000_0001; + const CONTROL = 0b0000_0010; + const ALT = 0b0000_0100; + const NONE = 0b0000_0000; + } +} + +#[cfg(feature = "term")] +impl From for crossterm::event::KeyModifiers { + fn from(key_modifiers: KeyModifiers) -> Self { + use crossterm::event::KeyModifiers as CKeyModifiers; + + let mut result = CKeyModifiers::NONE; + + if key_modifiers & KeyModifiers::SHIFT != KeyModifiers::NONE { + result = result & CKeyModifiers::SHIFT; + } + + if key_modifiers & KeyModifiers::CONTROL != KeyModifiers::NONE { + result = result & CKeyModifiers::CONTROL; + } + + if key_modifiers & KeyModifiers::ALT != KeyModifiers::NONE { + result = result & CKeyModifiers::ALT; + } + + result + } +} + +#[cfg(feature = "term")] +impl Into for crossterm::event::KeyModifiers { + fn into(self) -> KeyModifiers { + use crossterm::event::KeyModifiers as CKeyModifiers; + + let mut result = KeyModifiers::NONE; + + if self & CKeyModifiers::SHIFT != CKeyModifiers::NONE { + result = result & KeyModifiers::SHIFT; + } + + if self & CKeyModifiers::CONTROL != CKeyModifiers::NONE { + result = result & KeyModifiers::CONTROL; + } + + if self & CKeyModifiers::ALT != CKeyModifiers::NONE { + result = result & KeyModifiers::ALT; + } + + result + } +} + +/// Represents a key. +#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum KeyCode { + /// Backspace key. + Backspace, + /// Enter key. + Enter, + /// Left arrow key. + Left, + /// Right arrow key. + Right, + /// Up arrow key. + Up, + /// Down arrow key. + Down, + /// Home key. + Home, + /// End key. + End, + /// Page up key. + PageUp, + /// Page dow key. + PageDown, + /// Tab key. + Tab, + /// Shift + Tab key. + BackTab, + /// Delete key. + Delete, + /// Insert key. + Insert, + /// F key. + /// + /// `KeyCode::F(1)` represents F1 key, etc. + F(u8), + /// A character. + /// + /// `KeyCode::Char('c')` represents `c` character, etc. + Char(char), + /// Null. + Null, + /// Escape key. + Esc, +} + +#[cfg(feature = "term")] +impl From for crossterm::event::KeyCode { + fn from(key_code: KeyCode) -> Self { + use crossterm::event::KeyCode as CKeyCode; + + match key_code { + KeyCode::Backspace => CKeyCode::Backspace, + KeyCode::Enter => CKeyCode::Enter, + KeyCode::Left => CKeyCode::Left, + KeyCode::Right => CKeyCode::Right, + KeyCode::Up => CKeyCode::Up, + KeyCode::Down => CKeyCode::Down, + KeyCode::Home => CKeyCode::Home, + KeyCode::End => CKeyCode::End, + KeyCode::PageUp => CKeyCode::PageUp, + KeyCode::PageDown => CKeyCode::PageDown, + KeyCode::Tab => CKeyCode::Tab, + KeyCode::BackTab => CKeyCode::BackTab, + KeyCode::Delete => CKeyCode::Delete, + KeyCode::Insert => CKeyCode::Insert, + KeyCode::F(f_number) => CKeyCode::F(f_number), + KeyCode::Char(character) => CKeyCode::Char(character), + KeyCode::Null => CKeyCode::Null, + KeyCode::Esc => CKeyCode::Esc, + } + } +} + +#[cfg(feature = "term")] +impl Into for crossterm::event::KeyCode { + fn into(self) -> KeyCode { + use crossterm::event::KeyCode as CKeyCode; + + match self { + CKeyCode::Backspace => KeyCode::Backspace, + CKeyCode::Enter => KeyCode::Enter, + CKeyCode::Left => KeyCode::Left, + CKeyCode::Right => KeyCode::Right, + CKeyCode::Up => KeyCode::Up, + CKeyCode::Down => KeyCode::Down, + CKeyCode::Home => KeyCode::Home, + CKeyCode::End => KeyCode::End, + CKeyCode::PageUp => KeyCode::PageUp, + CKeyCode::PageDown => KeyCode::PageDown, + CKeyCode::Tab => KeyCode::Tab, + CKeyCode::BackTab => KeyCode::BackTab, + CKeyCode::Delete => KeyCode::Delete, + CKeyCode::Insert => KeyCode::Insert, + CKeyCode::F(f_number) => KeyCode::F(f_number), + CKeyCode::Char(character) => KeyCode::Char(character), + CKeyCode::Null => KeyCode::Null, + CKeyCode::Esc => KeyCode::Esc, + } + } +} diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index 326bd40394b0..caed2952344b 100644 --- a/helix-view/src/lib.rs +++ b/helix-view/src/lib.rs @@ -4,8 +4,9 @@ pub mod macros; pub mod clipboard; pub mod document; pub mod editor; -pub mod input; pub mod graphics; +pub mod input; +pub mod keyboard; pub mod register_selection; pub mod theme; pub mod tree;