aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term/src')
-rw-r--r--helix-term/src/commands.rs216
-rw-r--r--helix-term/src/editor.rs68
-rw-r--r--helix-term/src/keymap.rs134
-rw-r--r--helix-term/src/macros.rs16
-rw-r--r--helix-term/src/main.rs5
-rw-r--r--helix-term/src/theme.rs98
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
- }
-}