diff options
Diffstat (limited to 'helix-term/src')
-rw-r--r-- | helix-term/src/commands.rs | 216 | ||||
-rw-r--r-- | helix-term/src/editor.rs | 68 | ||||
-rw-r--r-- | helix-term/src/keymap.rs | 134 | ||||
-rw-r--r-- | helix-term/src/macros.rs | 16 | ||||
-rw-r--r-- | helix-term/src/main.rs | 5 | ||||
-rw-r--r-- | helix-term/src/theme.rs | 98 |
6 files changed, 16 insertions, 521 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs deleted file mode 100644 index 269a7743..00000000 --- a/helix-term/src/commands.rs +++ /dev/null @@ -1,216 +0,0 @@ -use helix_core::{ - graphemes, - state::{Direction, Granularity, Mode, State}, - ChangeSet, Range, Selection, Tendril, Transaction, -}; - -use crate::editor::View; - -/// A command is a function that takes the current state and a count, and does a side-effect on the -/// state (usually by creating and applying a transaction). -pub type Command = fn(view: &mut View, count: usize); - -pub fn move_char_left(view: &mut View, count: usize) { - // TODO: use a transaction - let selection = view - .state - .move_selection(Direction::Backward, Granularity::Character, count); - view.state.selection = selection; -} - -pub fn move_char_right(view: &mut View, count: usize) { - // TODO: use a transaction - view.state.selection = - view.state - .move_selection(Direction::Forward, Granularity::Character, count); -} - -pub fn move_line_up(view: &mut View, count: usize) { - // TODO: use a transaction - view.state.selection = view - .state - .move_selection(Direction::Backward, Granularity::Line, count); -} - -pub fn move_line_down(view: &mut View, count: usize) { - // TODO: use a transaction - view.state.selection = view - .state - .move_selection(Direction::Forward, Granularity::Line, count); -} - -// avoid select by default by having a visual mode switch that makes movements into selects - -// insert mode: -// first we calculate the correct cursors/selections -// then we just append at each cursor -// lastly, if it was append mode we shift cursor by 1? - -// inserts at the start of each selection -pub fn insert_mode(view: &mut View, _count: usize) { - view.state.mode = Mode::Insert; - - view.state.selection = view - .state - .selection - .transform(|range| Range::new(range.to(), range.from())) -} - -// inserts at the end of each selection -pub fn append_mode(view: &mut View, _count: usize) { - view.state.mode = Mode::Insert; - - // TODO: as transaction - let text = &view.state.doc.slice(..); - view.state.selection = view.state.selection.transform(|range| { - // TODO: to() + next char - Range::new( - range.from(), - graphemes::next_grapheme_boundary(text, range.to()), - ) - }) -} - -// TODO: I, A, o and O can share a lot of the primitives. - -// calculate line numbers for each selection range -fn selection_lines(state: &State) -> Vec<usize> { - let mut lines = state - .selection - .ranges() - .iter() - .map(|range| state.doc.char_to_line(range.head)) - .collect::<Vec<_>>(); - - lines.sort(); - lines.dedup(); - - lines -} - -// I inserts at the start of each line with a selection -pub fn prepend_to_line(view: &mut View, _count: usize) { - view.state.mode = Mode::Insert; - - let lines = selection_lines(&view.state); - - let positions = lines - .into_iter() - .map(|index| { - // adjust all positions to the start of the line. - view.state.doc.line_to_char(index) - }) - .map(|pos| Range::new(pos, pos)); - - let selection = Selection::new(positions.collect(), 0); - - let transaction = Transaction::new(&mut view.state).with_selection(selection); - - transaction.apply(&mut view.state); - // TODO: need to store into history if successful -} - -// A inserts at the end of each line with a selection -pub fn append_to_line(view: &mut View, _count: usize) { - view.state.mode = Mode::Insert; - - let lines = selection_lines(&view.state); - - let positions = lines - .into_iter() - .map(|index| { - // adjust all positions to the end of the line. - let line = view.state.doc.line(index); - let line_start = view.state.doc.line_to_char(index); - line_start + line.len_chars() - 1 - }) - .map(|pos| Range::new(pos, pos)); - - let selection = Selection::new(positions.collect(), 0); - - let transaction = Transaction::new(&mut view.state).with_selection(selection); - - transaction.apply(&mut view.state); - // TODO: need to store into history if successful -} - -// o inserts a new line after each line with a selection -pub fn open_below(view: &mut View, _count: usize) { - view.state.mode = Mode::Insert; - - let lines = selection_lines(&view.state); - - let positions: Vec<_> = lines - .into_iter() - .map(|index| { - // adjust all positions to the end of the line. - let line = view.state.doc.line(index); - let line_start = view.state.doc.line_to_char(index); - line_start + line.len_chars() - }) - .collect(); - - let changes = positions.iter().copied().map(|index| - // generate changes - (index, index, Some(Tendril::from_char('\n')))); - - // TODO: count actually inserts "n" new lines and starts editing on all of them. - // TODO: append "count" newlines and modify cursors to those lines - - let selection = Selection::new( - positions - .iter() - .copied() - .map(|pos| Range::new(pos, pos)) - .collect(), - 0, - ); - - let transaction = Transaction::change(&view.state, changes).with_selection(selection); - - transaction.apply(&mut view.state); - // TODO: need to store into history if successful -} - -// O inserts a new line before each line with a selection - -pub fn normal_mode(view: &mut View, _count: usize) { - // TODO: if leaving append mode, move cursor back by 1 - view.state.mode = Mode::Normal; -} - -// TODO: insert means add text just before cursor, on exit we should be on the last letter. -pub fn insert_char(view: &mut View, c: char) { - let c = Tendril::from_char(c); - let transaction = Transaction::insert(&view.state, c); - - transaction.apply(&mut view.state); - // TODO: need to store into history if successful -} - -// TODO: handle indent-aware delete -pub fn delete_char_backward(view: &mut View, count: usize) { - let text = &view.state.doc.slice(..); - let transaction = Transaction::change_by_selection(&view.state, |range| { - ( - graphemes::nth_prev_grapheme_boundary(text, range.head, count), - range.head, - None, - ) - }); - transaction.apply(&mut view.state); - // TODO: need to store into history if successful -} - -pub fn delete_char_forward(view: &mut View, count: usize) { - let text = &view.state.doc.slice(..); - let transaction = Transaction::change_by_selection(&view.state, |range| { - ( - graphemes::nth_next_grapheme_boundary(text, range.head, count), - range.head, - None, - ) - }); - transaction.apply(&mut view.state); - // TODO: need to store into history if successful -} diff --git a/helix-term/src/editor.rs b/helix-term/src/editor.rs index 0542d217..e29e7ee8 100644 --- a/helix-term/src/editor.rs +++ b/helix-term/src/editor.rs @@ -1,10 +1,11 @@ -use crate::{commands, keymap, theme::Theme, Args}; +use crate::Args; use helix_core::{ state::coords_at_pos, state::Mode, syntax::{HighlightConfiguration, HighlightEvent, Highlighter}, State, }; +use helix_view::{commands, keymap, View}; use std::{ io::{self, stdout, Write}, @@ -31,19 +32,12 @@ type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>; static EX: smol::Executor = smol::Executor::new(); -pub struct View { - pub state: State, - pub first_line: u16, - pub size: (u16, u16), -} - pub struct Editor { terminal: Terminal, view: Option<View>, size: (u16, u16), surface: Surface, cache: Surface, - theme: Theme, } impl Editor { @@ -53,7 +47,6 @@ impl Editor { let mut terminal = Terminal::new(backend)?; let size = terminal::size().unwrap(); let area = Rect::new(0, 0, size.0, size.1); - let theme = Theme::default(); let mut editor = Editor { terminal, @@ -61,7 +54,6 @@ impl Editor { size, surface: Surface::empty(area), cache: Surface::empty(area), - theme, // TODO; move to state }; @@ -73,20 +65,7 @@ impl Editor { } pub fn open(&mut self, path: PathBuf) -> Result<(), Error> { - let mut state = State::load(path)?; - state - .syntax - .as_mut() - .unwrap() - .configure(self.theme.scopes()); - - let view = View { - state, - first_line: 0, - size: self.size, - }; - - self.view = Some(view); + self.view = Some(View::open(path, self.size)?); Ok(()) } @@ -102,7 +81,7 @@ impl Editor { // clear with background color self.surface - .set_style(area, self.theme.get("ui.background")); + .set_style(area, view.theme.get("ui.background").into()); let offset = 5 + 1; // 5 linenr + 1 gutter let viewport = Rect::new(offset, 0, self.size.0, self.size.1 - 1); // - 1 for statusline @@ -161,7 +140,9 @@ impl Editor { use helix_core::graphemes::{grapheme_width, RopeGraphemes}; let style = match spans.first() { - Some(span) => self.theme.get(self.theme.scopes()[span.0].as_str()), + Some(span) => { + view.theme.get(view.theme.scopes()[span.0].as_str()).into() + } None => Style::default().fg(Color::Rgb(164, 160, 232)), // lavender }; @@ -202,7 +183,7 @@ impl Editor { } let mut line = 0; - let style = self.theme.get("ui.linenr"); + let style: Style = view.theme.get("ui.linenr").into(); for i in view.first_line..(last_line as u16) { self.surface .set_stringn(0, line, format!("{:>5}", i + 1), 5, style); // lavender @@ -241,7 +222,7 @@ impl Editor { }; self.surface.set_style( Rect::new(0, self.size.1 - 1, self.size.0, 1), - self.theme.get("ui.statusline"), + view.theme.get("ui.statusline").into(), ); // TODO: unfocused one with different color let text_color = Style::default().fg(Color::Rgb(219, 191, 239)); // lilac @@ -296,7 +277,9 @@ impl Editor { self.cache = Surface::empty(area); // TODO: simplistic ensure cursor in view for now - self.ensure_cursor_in_view(); + if let Some(view) = &mut self.view { + view.ensure_cursor_in_view() + }; self.render(); } @@ -333,18 +316,19 @@ impl Editor { _ => (), // skip } // TODO: simplistic ensure cursor in view for now - self.ensure_cursor_in_view(); + view.ensure_cursor_in_view(); self.render(); } Mode::Normal => { // TODO: handle modes and sequences (`gg`) - if let Some(command) = keymap.get(&event) { + let keys = vec![event]; + if let Some(command) = keymap[&Mode::Normal].get(&keys) { // TODO: handle count other than 1 command(view, 1); // TODO: simplistic ensure cursor in view for now - self.ensure_cursor_in_view(); + view.ensure_cursor_in_view(); self.render(); } @@ -361,26 +345,6 @@ impl Editor { } } - fn ensure_cursor_in_view(&mut self) { - if let Some(view) = &mut self.view { - let cursor = view.state.selection().cursor(); - let line = view.state.doc().char_to_line(cursor) as u16; - let document_end = view.first_line + self.size.1.saturating_sub(1) - 1; - - let padding = 5u16; - - // TODO: side scroll - - if line > document_end.saturating_sub(padding) { - // scroll down - view.first_line += line - (document_end.saturating_sub(padding)); - } else if line < view.first_line + padding { - // scroll up - view.first_line = line.saturating_sub(padding); - } - } - } - pub async fn run(&mut self) -> Result<(), Error> { enable_raw_mode()?; diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs deleted file mode 100644 index d52ccca4..00000000 --- a/helix-term/src/keymap.rs +++ /dev/null @@ -1,134 +0,0 @@ -use crate::commands::{self, Command}; -use crossterm::{ - event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers}, - execute, - style::Print, -}; -use std::collections::HashMap; - -// Kakoune-inspired: -// mode = { -// normal = { -// q = record_macro -// w = (next) word -// e = end of word -// r = -// t = 'till char -// y = yank -// u = undo -// U = redo -// i = insert -// I = INSERT (start of line) -// o = open below (insert on new line below) -// O = open above (insert on new line above) -// p = paste (before cursor) -// P = PASTE (after cursor) -// ` = -// [ = select to text object start (alt = select whole object) -// ] = select to text object end -// { = extend to inner object start -// } = extend to inner object end -// a = append -// A = APPEND (end of line) -// s = split -// S = select -// d = delete() -// f = find_char() -// g = goto (gg, G, gc, gd, etc) -// -// h = move_char_left(n) -// j = move_line_down(n) -// k = move_line_up(n) -// l = move_char_right(n) -// : = command line -// ; = collapse selection to cursor -// " = use register -// ` = convert case? (to lower) (alt = swap case) -// ~ = convert to upper case -// . = repeat last command -// \ = disable hook? -// / = search -// > = indent -// < = deindent -// % = select whole buffer (in vim = jump to matching bracket) -// * = search pattern in selection -// ( = rotate main selection backward -// ) = rotate main selection forward -// - = trim selections? (alt = merge contiguous sel together) -// @ = convert tabs to spaces -// & = align cursor -// ? = extend to next given regex match (alt = to prev) -// -// z = save selections -// Z = restore selections -// x = select line -// X = extend line -// c = change selected text -// C = copy selection? -// v = view menu (viewport manipulation) -// b = select to previous word start -// B = select to previous WORD start -// -// -// -// -// -// -// = = align? -// + = -// } -// } - -type Keymap = HashMap<Key, Command>; - -pub fn default() -> Keymap { - hashmap!( - Key { - code: KeyCode::Char('h'), - modifiers: Modifiers::NONE - } => commands::move_char_left as Command, - Key { - code: KeyCode::Char('j'), - modifiers: Modifiers::NONE - } => commands::move_line_down as Command, - Key { - code: KeyCode::Char('k'), - modifiers: Modifiers::NONE - } => commands::move_line_up as Command, - Key { - code: KeyCode::Char('l'), - modifiers: Modifiers::NONE - } => commands::move_char_right as Command, - Key { - code: KeyCode::Char('i'), - modifiers: Modifiers::NONE - } => commands::insert_mode as Command, - Key { - code: KeyCode::Char('I'), - modifiers: Modifiers::SHIFT, - } => commands::prepend_to_line as Command, - Key { - code: KeyCode::Char('a'), - modifiers: Modifiers::NONE - } => commands::append_mode as Command, - Key { - code: KeyCode::Char('A'), - modifiers: Modifiers::SHIFT, - } => commands::append_to_line as Command, - Key { - code: KeyCode::Char('o'), - modifiers: Modifiers::NONE - } => commands::open_below as Command, - Key { - code: KeyCode::Esc, - modifiers: Modifiers::NONE - } => commands::normal_mode as Command, - ) - - // hashmap!( - // Key { - // code: KeyCode::Esc, - // modifiers: Modifiers::NONE - // } => commands::normal_mode as Command, - // ) -} diff --git a/helix-term/src/macros.rs b/helix-term/src/macros.rs deleted file mode 100644 index 3b22a786..00000000 --- a/helix-term/src/macros.rs +++ /dev/null @@ -1,16 +0,0 @@ -macro_rules! hashmap { - (@single $($x:tt)*) => (()); - (@count $($rest:expr),*) => (<[()]>::len(&[$(hashmap!(@single $rest)),*])); - - ($($key:expr => $value:expr,)+) => { hashmap!($($key => $value),+) }; - ($($key:expr => $value:expr),*) => { - { - let _cap = hashmap!(@count $($key),*); - let mut _map = ::std::collections::HashMap::with_capacity(_cap); - $( - let _ = _map.insert($key, $value); - )* - _map - } - }; -} diff --git a/helix-term/src/main.rs b/helix-term/src/main.rs index aca04641..b691eb65 100644 --- a/helix-term/src/main.rs +++ b/helix-term/src/main.rs @@ -1,11 +1,6 @@ #![allow(unused)] -#[macro_use] -mod macros; -mod commands; mod editor; -mod keymap; -mod theme; use editor::Editor; diff --git a/helix-term/src/theme.rs b/helix-term/src/theme.rs deleted file mode 100644 index 4b2f102e..00000000 --- a/helix-term/src/theme.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::collections::HashMap; -use tui::style::{Color, Style}; - -/// Color theme for syntax highlighting. -pub struct Theme { - scopes: Vec<String>, - mapping: HashMap<&'static str, Style>, -} - -// let highlight_names: Vec<String> = [ -// "attribute", -// "constant.builtin", -// "constant", -// "function.builtin", -// "function.macro", -// "function", -// "keyword", -// "operator", -// "property", -// "punctuation", -// "comment", -// "escape", -// "label", -// // "punctuation.bracket", -// "punctuation.delimiter", -// "string", -// "string.special", -// "tag", -// "type", -// "type.builtin", -// "constructor", -// "variable", -// "variable.builtin", -// "variable.parameter", -// "path", -// ]; - -impl Default for Theme { - fn default() -> Self { - let mapping = hashmap! { - "attribute" => Style::default().fg(Color::Rgb(219, 191, 239)), // lilac - "keyword" => Style::default().fg(Color::Rgb(236, 205, 186)), // almond - "punctuation" => Style::default().fg(Color::Rgb(164, 160, 232)), // lavender - "punctuation.delimiter" => Style::default().fg(Color::Rgb(164, 160, 232)), // lavender - "operator" => Style::default().fg(Color::Rgb(219, 191, 239)), // lilac - "property" => Style::default().fg(Color::Rgb(164, 160, 232)), // lavender - "variable.parameter" => Style::default().fg(Color::Rgb(164, 160, 232)), // lavender - // TODO distinguish type from type.builtin? - "type" => Style::default().fg(Color::Rgb(255, 255, 255)), // white - "type.builtin" => Style::default().fg(Color::Rgb(255, 255, 255)), // white - "constructor" => Style::default().fg(Color::Rgb(219, 191, 239)), // lilac - "function" => Style::default().fg(Color::Rgb(255, 255, 255)), // white - "function.macro" => Style::default().fg(Color::Rgb(219, 191, 239)), // lilac - "comment" => Style::default().fg(Color::Rgb(105, 124, 129)), // sirocco - "variable.builtin" => Style::default().fg(Color::Rgb(159, 242, 143)), // mint - "constant" => Style::default().fg(Color::Rgb(255, 255, 255)), // white - "constant.builtin" => Style::default().fg(Color::Rgb(255, 255, 255)), // white - "string" => Style::default().fg(Color::Rgb(204, 204, 204)), // silver - "escape" => Style::default().fg(Color::Rgb(239, 186, 93)), // honey - // used for lifetimes - "label" => Style::default().fg(Color::Rgb(239, 186, 93)), // honey - - // TODO: diferentiate number builtin - // TODO: diferentiate doc comment - // TODO: variable as lilac - // TODO: mod/use statements as white - // TODO: mod stuff as chamoise - // TODO: add "(scoped_identifier) @path" for std::mem:: - // - // concat (ERROR) @syntax-error and "MISSING ;" selectors for errors - - "module" => Style::default().fg(Color::Rgb(255, 0, 0)), // white - "variable" => Style::default().fg(Color::Rgb(255, 0, 0)), // white - "function.builtin" => Style::default().fg(Color::Rgb(255, 0, 0)), // white - - "ui.background" => Style::default().bg(Color::Rgb(59, 34, 76)), // midnight - "ui.linenr" => Style::default().fg(Color::Rgb(90, 89, 119)), // comet - "ui.statusline" => Style::default().bg(Color::Rgb(40, 23, 51)), // revolver - }; - - let scopes = mapping.keys().map(ToString::to_string).collect(); - - Self { mapping, scopes } - } -} - -impl Theme { - pub fn get(&self, scope: &str) -> Style { - self.mapping - .get(scope) - .copied() - .unwrap_or_else(|| Style::default().fg(Color::Rgb(0, 0, 255))) - } - - pub fn scopes(&self) -> &[String] { - &self.scopes - } -} |