Command needs access to view information for certain changes.
This commit is contained in:
parent
1303ffd94a
commit
48330ddb5f
7 changed files with 132 additions and 107 deletions
|
@ -1,5 +1,4 @@
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
pub mod commands;
|
|
||||||
pub mod graphemes;
|
pub mod graphemes;
|
||||||
mod position;
|
mod position;
|
||||||
mod selection;
|
mod selection;
|
||||||
|
@ -11,7 +10,7 @@ pub use ropey::{Rope, RopeSlice};
|
||||||
pub use tendril::StrTendril as Tendril;
|
pub use tendril::StrTendril as Tendril;
|
||||||
|
|
||||||
pub use position::Position;
|
pub use position::Position;
|
||||||
pub use selection::Range as SelectionRange;
|
pub use selection::Range;
|
||||||
pub use selection::Selection;
|
pub use selection::Selection;
|
||||||
pub use syntax::Syntax;
|
pub use syntax::Syntax;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary, RopeGraphemes};
|
use crate::graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary, RopeGraphemes};
|
||||||
use crate::{Position, Rope, RopeSlice, Selection, SelectionRange, Syntax};
|
use crate::{Position, Range, Rope, RopeSlice, Selection, Syntax};
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -12,11 +12,12 @@ pub enum Mode {
|
||||||
|
|
||||||
/// A state represents the current editor state of a single buffer.
|
/// A state represents the current editor state of a single buffer.
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
// TODO: fields should be private but we need to refactor commands.rs first
|
||||||
/// Path to file on disk.
|
/// Path to file on disk.
|
||||||
pub(crate) path: Option<PathBuf>,
|
pub path: Option<PathBuf>,
|
||||||
pub(crate) doc: Rope,
|
pub doc: Rope,
|
||||||
pub(crate) selection: Selection,
|
pub selection: Selection,
|
||||||
pub(crate) mode: Mode,
|
pub mode: Mode,
|
||||||
|
|
||||||
//
|
//
|
||||||
pub syntax: Option<Syntax>,
|
pub syntax: Option<Syntax>,
|
||||||
|
@ -189,7 +190,7 @@ impl State {
|
||||||
// } else {
|
// } else {
|
||||||
let pos = self.move_pos(range.head, dir, granularity, count);
|
let pos = self.move_pos(range.head, dir, granularity, count);
|
||||||
// };
|
// };
|
||||||
SelectionRange::new(pos, pos)
|
Range::new(pos, pos)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +202,7 @@ impl State {
|
||||||
) -> Selection {
|
) -> Selection {
|
||||||
self.selection.transform(|range| {
|
self.selection.transform(|range| {
|
||||||
let pos = self.move_pos(range.head, dir, granularity, count);
|
let pos = self.move_pos(range.head, dir, granularity, count);
|
||||||
SelectionRange::new(range.anchor, pos)
|
Range::new(range.anchor, pos)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{Rope, Selection, SelectionRange, State, Tendril};
|
use crate::{Range, Rope, Selection, State, Tendril};
|
||||||
|
|
||||||
/// (from, to, replacement)
|
/// (from, to, replacement)
|
||||||
pub type Change = (usize, usize, Option<Tendril>);
|
pub type Change = (usize, usize, Option<Tendril>);
|
||||||
|
@ -387,7 +387,7 @@ impl Transaction {
|
||||||
/// Generate a transaction with a change per selection range.
|
/// Generate a transaction with a change per selection range.
|
||||||
pub fn change_by_selection<F>(state: &State, f: F) -> Self
|
pub fn change_by_selection<F>(state: &State, f: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(&SelectionRange) -> Change,
|
F: Fn(&Range) -> Change,
|
||||||
{
|
{
|
||||||
Self::change(state, state.selection.ranges().iter().map(f))
|
Self::change(state, state.selection.ranges().iter().map(f))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,42 @@
|
||||||
use crate::graphemes;
|
use helix_core::{
|
||||||
use crate::selection::{Range, Selection};
|
graphemes,
|
||||||
use crate::state::{Direction, Granularity, Mode, State};
|
state::{Direction, Granularity, Mode, State},
|
||||||
use crate::transaction::{ChangeSet, Transaction};
|
ChangeSet, Range, Selection, Tendril, Transaction,
|
||||||
use crate::Tendril;
|
};
|
||||||
|
|
||||||
|
use crate::editor::View;
|
||||||
|
|
||||||
/// A command is a function that takes the current state and a count, and does a side-effect on the
|
/// 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).
|
/// state (usually by creating and applying a transaction).
|
||||||
pub type Command = fn(state: &mut State, count: usize);
|
pub type Command = fn(view: &mut View, count: usize);
|
||||||
|
|
||||||
pub fn move_char_left(state: &mut State, count: usize) {
|
pub fn move_char_left(view: &mut View, count: usize) {
|
||||||
// TODO: use a transaction
|
// TODO: use a transaction
|
||||||
let selection = state.move_selection(Direction::Backward, Granularity::Character, count);
|
let selection = view
|
||||||
state.selection = selection;
|
.state
|
||||||
|
.move_selection(Direction::Backward, Granularity::Character, count);
|
||||||
|
view.state.selection = selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_char_right(state: &mut State, count: usize) {
|
pub fn move_char_right(view: &mut View, count: usize) {
|
||||||
// TODO: use a transaction
|
// TODO: use a transaction
|
||||||
state.selection = state.move_selection(Direction::Forward, Granularity::Character, count);
|
view.state.selection =
|
||||||
|
view.state
|
||||||
|
.move_selection(Direction::Forward, Granularity::Character, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_line_up(state: &mut State, count: usize) {
|
pub fn move_line_up(view: &mut View, count: usize) {
|
||||||
// TODO: use a transaction
|
// TODO: use a transaction
|
||||||
state.selection = state.move_selection(Direction::Backward, Granularity::Line, count);
|
view.state.selection = view
|
||||||
|
.state
|
||||||
|
.move_selection(Direction::Backward, Granularity::Line, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_line_down(state: &mut State, count: usize) {
|
pub fn move_line_down(view: &mut View, count: usize) {
|
||||||
// TODO: use a transaction
|
// TODO: use a transaction
|
||||||
state.selection = state.move_selection(Direction::Forward, Granularity::Line, count);
|
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
|
// avoid select by default by having a visual mode switch that makes movements into selects
|
||||||
|
@ -37,21 +47,22 @@ pub fn move_line_down(state: &mut State, count: usize) {
|
||||||
// lastly, if it was append mode we shift cursor by 1?
|
// lastly, if it was append mode we shift cursor by 1?
|
||||||
|
|
||||||
// inserts at the start of each selection
|
// inserts at the start of each selection
|
||||||
pub fn insert_mode(state: &mut State, _count: usize) {
|
pub fn insert_mode(view: &mut View, _count: usize) {
|
||||||
state.mode = Mode::Insert;
|
view.state.mode = Mode::Insert;
|
||||||
|
|
||||||
state.selection = state
|
view.state.selection = view
|
||||||
|
.state
|
||||||
.selection
|
.selection
|
||||||
.transform(|range| Range::new(range.to(), range.from()))
|
.transform(|range| Range::new(range.to(), range.from()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// inserts at the end of each selection
|
// inserts at the end of each selection
|
||||||
pub fn append_mode(state: &mut State, _count: usize) {
|
pub fn append_mode(view: &mut View, _count: usize) {
|
||||||
state.mode = Mode::Insert;
|
view.state.mode = Mode::Insert;
|
||||||
|
|
||||||
// TODO: as transaction
|
// TODO: as transaction
|
||||||
let text = &state.doc.slice(..);
|
let text = &view.state.doc.slice(..);
|
||||||
state.selection = state.selection.transform(|range| {
|
view.state.selection = view.state.selection.transform(|range| {
|
||||||
// TODO: to() + next char
|
// TODO: to() + next char
|
||||||
Range::new(
|
Range::new(
|
||||||
range.from(),
|
range.from(),
|
||||||
|
@ -78,63 +89,63 @@ fn selection_lines(state: &State) -> Vec<usize> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// I inserts at the start of each line with a selection
|
// I inserts at the start of each line with a selection
|
||||||
pub fn prepend_to_line(state: &mut State, _count: usize) {
|
pub fn prepend_to_line(view: &mut View, _count: usize) {
|
||||||
state.mode = Mode::Insert;
|
view.state.mode = Mode::Insert;
|
||||||
|
|
||||||
let lines = selection_lines(state);
|
let lines = selection_lines(&view.state);
|
||||||
|
|
||||||
let positions = lines
|
let positions = lines
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
// adjust all positions to the start of the line.
|
// adjust all positions to the start of the line.
|
||||||
state.doc.line_to_char(index)
|
view.state.doc.line_to_char(index)
|
||||||
})
|
})
|
||||||
.map(|pos| Range::new(pos, pos));
|
.map(|pos| Range::new(pos, pos));
|
||||||
|
|
||||||
let selection = Selection::new(positions.collect(), 0);
|
let selection = Selection::new(positions.collect(), 0);
|
||||||
|
|
||||||
let transaction = Transaction::new(state).with_selection(selection);
|
let transaction = Transaction::new(&mut view.state).with_selection(selection);
|
||||||
|
|
||||||
transaction.apply(state);
|
transaction.apply(&mut view.state);
|
||||||
// TODO: need to store into history if successful
|
// TODO: need to store into history if successful
|
||||||
}
|
}
|
||||||
|
|
||||||
// A inserts at the end of each line with a selection
|
// A inserts at the end of each line with a selection
|
||||||
pub fn append_to_line(state: &mut State, _count: usize) {
|
pub fn append_to_line(view: &mut View, _count: usize) {
|
||||||
state.mode = Mode::Insert;
|
view.state.mode = Mode::Insert;
|
||||||
|
|
||||||
let lines = selection_lines(state);
|
let lines = selection_lines(&view.state);
|
||||||
|
|
||||||
let positions = lines
|
let positions = lines
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
// adjust all positions to the end of the line.
|
// adjust all positions to the end of the line.
|
||||||
let line = state.doc.line(index);
|
let line = view.state.doc.line(index);
|
||||||
let line_start = state.doc.line_to_char(index);
|
let line_start = view.state.doc.line_to_char(index);
|
||||||
line_start + line.len_chars() - 1
|
line_start + line.len_chars() - 1
|
||||||
})
|
})
|
||||||
.map(|pos| Range::new(pos, pos));
|
.map(|pos| Range::new(pos, pos));
|
||||||
|
|
||||||
let selection = Selection::new(positions.collect(), 0);
|
let selection = Selection::new(positions.collect(), 0);
|
||||||
|
|
||||||
let transaction = Transaction::new(state).with_selection(selection);
|
let transaction = Transaction::new(&mut view.state).with_selection(selection);
|
||||||
|
|
||||||
transaction.apply(state);
|
transaction.apply(&mut view.state);
|
||||||
// TODO: need to store into history if successful
|
// TODO: need to store into history if successful
|
||||||
}
|
}
|
||||||
|
|
||||||
// o inserts a new line after each line with a selection
|
// o inserts a new line after each line with a selection
|
||||||
pub fn open_below(state: &mut State, _count: usize) {
|
pub fn open_below(view: &mut View, _count: usize) {
|
||||||
state.mode = Mode::Insert;
|
view.state.mode = Mode::Insert;
|
||||||
|
|
||||||
let lines = selection_lines(state);
|
let lines = selection_lines(&view.state);
|
||||||
|
|
||||||
let positions: Vec<_> = lines
|
let positions: Vec<_> = lines
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
// adjust all positions to the end of the line.
|
// adjust all positions to the end of the line.
|
||||||
let line = state.doc.line(index);
|
let line = view.state.doc.line(index);
|
||||||
let line_start = state.doc.line_to_char(index);
|
let line_start = view.state.doc.line_to_char(index);
|
||||||
line_start + line.len_chars()
|
line_start + line.len_chars()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -155,51 +166,51 @@ pub fn open_below(state: &mut State, _count: usize) {
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
let transaction = Transaction::change(state, changes).with_selection(selection);
|
let transaction = Transaction::change(&view.state, changes).with_selection(selection);
|
||||||
|
|
||||||
transaction.apply(state);
|
transaction.apply(&mut view.state);
|
||||||
// TODO: need to store into history if successful
|
// TODO: need to store into history if successful
|
||||||
}
|
}
|
||||||
|
|
||||||
// O inserts a new line before each line with a selection
|
// O inserts a new line before each line with a selection
|
||||||
|
|
||||||
pub fn normal_mode(state: &mut State, _count: usize) {
|
pub fn normal_mode(view: &mut View, _count: usize) {
|
||||||
// TODO: if leaving append mode, move cursor back by 1
|
// TODO: if leaving append mode, move cursor back by 1
|
||||||
state.mode = Mode::Normal;
|
view.state.mode = Mode::Normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: insert means add text just before cursor, on exit we should be on the last letter.
|
// TODO: insert means add text just before cursor, on exit we should be on the last letter.
|
||||||
pub fn insert_char(state: &mut State, c: char) {
|
pub fn insert_char(view: &mut View, c: char) {
|
||||||
let c = Tendril::from_char(c);
|
let c = Tendril::from_char(c);
|
||||||
let transaction = Transaction::insert(&state, c);
|
let transaction = Transaction::insert(&view.state, c);
|
||||||
|
|
||||||
transaction.apply(state);
|
transaction.apply(&mut view.state);
|
||||||
// TODO: need to store into history if successful
|
// TODO: need to store into history if successful
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle indent-aware delete
|
// TODO: handle indent-aware delete
|
||||||
pub fn delete_char_backward(state: &mut State, count: usize) {
|
pub fn delete_char_backward(view: &mut View, count: usize) {
|
||||||
let text = &state.doc.slice(..);
|
let text = &view.state.doc.slice(..);
|
||||||
let transaction = Transaction::change_by_selection(state, |range| {
|
let transaction = Transaction::change_by_selection(&view.state, |range| {
|
||||||
(
|
(
|
||||||
graphemes::nth_prev_grapheme_boundary(text, range.head, count),
|
graphemes::nth_prev_grapheme_boundary(text, range.head, count),
|
||||||
range.head,
|
range.head,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
transaction.apply(state);
|
transaction.apply(&mut view.state);
|
||||||
// TODO: need to store into history if successful
|
// TODO: need to store into history if successful
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_char_forward(state: &mut State, count: usize) {
|
pub fn delete_char_forward(view: &mut View, count: usize) {
|
||||||
let text = &state.doc.slice(..);
|
let text = &view.state.doc.slice(..);
|
||||||
let transaction = Transaction::change_by_selection(state, |range| {
|
let transaction = Transaction::change_by_selection(&view.state, |range| {
|
||||||
(
|
(
|
||||||
graphemes::nth_next_grapheme_boundary(text, range.head, count),
|
graphemes::nth_next_grapheme_boundary(text, range.head, count),
|
||||||
range.head,
|
range.head,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
transaction.apply(state);
|
transaction.apply(&mut view.state);
|
||||||
// TODO: need to store into history if successful
|
// TODO: need to store into history if successful
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{keymap, theme::Theme, Args};
|
use crate::{commands, keymap, theme::Theme, Args};
|
||||||
use helix_core::{
|
use helix_core::{
|
||||||
state::coords_at_pos,
|
state::coords_at_pos,
|
||||||
state::Mode,
|
state::Mode,
|
||||||
|
@ -31,10 +31,15 @@ type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
|
||||||
|
|
||||||
static EX: smol::Executor = smol::Executor::new();
|
static EX: smol::Executor = smol::Executor::new();
|
||||||
|
|
||||||
|
pub struct View {
|
||||||
|
pub state: State,
|
||||||
|
pub first_line: u16,
|
||||||
|
pub size: (u16, u16),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Editor {
|
pub struct Editor {
|
||||||
terminal: Terminal,
|
terminal: Terminal,
|
||||||
state: Option<State>,
|
view: Option<View>,
|
||||||
first_line: u16,
|
|
||||||
size: (u16, u16),
|
size: (u16, u16),
|
||||||
surface: Surface,
|
surface: Surface,
|
||||||
cache: Surface,
|
cache: Surface,
|
||||||
|
@ -52,8 +57,7 @@ impl Editor {
|
||||||
|
|
||||||
let mut editor = Editor {
|
let mut editor = Editor {
|
||||||
terminal,
|
terminal,
|
||||||
state: None,
|
view: None,
|
||||||
first_line: 0,
|
|
||||||
size,
|
size,
|
||||||
surface: Surface::empty(area),
|
surface: Surface::empty(area),
|
||||||
cache: Surface::empty(area),
|
cache: Surface::empty(area),
|
||||||
|
@ -75,7 +79,14 @@ impl Editor {
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.configure(self.theme.scopes());
|
.configure(self.theme.scopes());
|
||||||
self.state = Some(state);
|
|
||||||
|
let view = View {
|
||||||
|
state,
|
||||||
|
first_line: 0,
|
||||||
|
size: self.size,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.view = Some(view);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,8 +94,8 @@ impl Editor {
|
||||||
use tui::backend::Backend;
|
use tui::backend::Backend;
|
||||||
use tui::style::Color;
|
use tui::style::Color;
|
||||||
// TODO: ideally not mut but highlights require it because of cursor cache
|
// TODO: ideally not mut but highlights require it because of cursor cache
|
||||||
match &mut self.state {
|
match &mut self.view {
|
||||||
Some(state) => {
|
Some(view) => {
|
||||||
let area = Rect::new(0, 0, self.size.0, self.size.1);
|
let area = Rect::new(0, 0, self.size.0, self.size.1);
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
self.surface.reset(); // reset is faster than allocating new empty surface
|
self.surface.reset(); // reset is faster than allocating new empty surface
|
||||||
|
@ -97,18 +108,18 @@ impl Editor {
|
||||||
let viewport = Rect::new(offset, 0, self.size.0, self.size.1 - 1); // - 1 for statusline
|
let viewport = Rect::new(offset, 0, self.size.0, self.size.1 - 1); // - 1 for statusline
|
||||||
|
|
||||||
// TODO: inefficient, should feed chunks.iter() to tree_sitter.parse_with(|offset, pos|)
|
// TODO: inefficient, should feed chunks.iter() to tree_sitter.parse_with(|offset, pos|)
|
||||||
let source_code = state.doc().to_string();
|
let source_code = view.state.doc().to_string();
|
||||||
|
|
||||||
let last_line = std::cmp::min(
|
let last_line = std::cmp::min(
|
||||||
(self.first_line + viewport.height - 1) as usize,
|
(view.first_line + viewport.height - 1) as usize,
|
||||||
state.doc().len_lines() - 1,
|
view.state.doc().len_lines() - 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
let range = {
|
let range = {
|
||||||
// calculate viewport byte ranges
|
// calculate viewport byte ranges
|
||||||
let start = state.doc().line_to_byte(self.first_line.into());
|
let start = view.state.doc().line_to_byte(view.first_line.into());
|
||||||
let end = state.doc().line_to_byte(last_line)
|
let end = view.state.doc().line_to_byte(last_line)
|
||||||
+ state.doc().line(last_line).len_bytes();
|
+ view.state.doc().line(last_line).len_bytes();
|
||||||
|
|
||||||
start..end
|
start..end
|
||||||
};
|
};
|
||||||
|
@ -117,7 +128,8 @@ impl Editor {
|
||||||
|
|
||||||
// TODO: cache highlight results
|
// TODO: cache highlight results
|
||||||
// TODO: only recalculate when state.doc is actually modified
|
// TODO: only recalculate when state.doc is actually modified
|
||||||
let highlights: Vec<_> = state
|
let highlights: Vec<_> = view
|
||||||
|
.state
|
||||||
.syntax
|
.syntax
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -141,10 +153,10 @@ impl Editor {
|
||||||
HighlightEvent::Source { start, end } => {
|
HighlightEvent::Source { start, end } => {
|
||||||
// TODO: filter out spans out of viewport for now..
|
// TODO: filter out spans out of viewport for now..
|
||||||
|
|
||||||
let start = state.doc().byte_to_char(start);
|
let start = view.state.doc().byte_to_char(start);
|
||||||
let end = state.doc().byte_to_char(end);
|
let end = view.state.doc().byte_to_char(end);
|
||||||
|
|
||||||
let text = state.doc().slice(start..end);
|
let text = view.state.doc().slice(start..end);
|
||||||
|
|
||||||
use helix_core::graphemes::{grapheme_width, RopeGraphemes};
|
use helix_core::graphemes::{grapheme_width, RopeGraphemes};
|
||||||
|
|
||||||
|
@ -191,7 +203,7 @@ impl Editor {
|
||||||
|
|
||||||
let mut line = 0;
|
let mut line = 0;
|
||||||
let style = self.theme.get("ui.linenr");
|
let style = self.theme.get("ui.linenr");
|
||||||
for i in self.first_line..(last_line as u16) {
|
for i in view.first_line..(last_line as u16) {
|
||||||
self.surface
|
self.surface
|
||||||
.set_stringn(0, line, format!("{:>5}", i + 1), 5, style); // lavender
|
.set_stringn(0, line, format!("{:>5}", i + 1), 5, style); // lavender
|
||||||
line += 1;
|
line += 1;
|
||||||
|
@ -223,7 +235,7 @@ impl Editor {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// statusline
|
// statusline
|
||||||
let mode = match state.mode() {
|
let mode = match view.state.mode() {
|
||||||
Mode::Insert => "INS",
|
Mode::Insert => "INS",
|
||||||
Mode::Normal => "NOR",
|
Mode::Normal => "NOR",
|
||||||
};
|
};
|
||||||
|
@ -235,7 +247,7 @@ impl Editor {
|
||||||
let text_color = Style::default().fg(Color::Rgb(219, 191, 239)); // lilac
|
let text_color = Style::default().fg(Color::Rgb(219, 191, 239)); // lilac
|
||||||
self.surface
|
self.surface
|
||||||
.set_string(1, self.size.1 - 1, mode, text_color);
|
.set_string(1, self.size.1 - 1, mode, text_color);
|
||||||
if let Some(path) = state.path() {
|
if let Some(path) = view.state.path() {
|
||||||
self.surface
|
self.surface
|
||||||
.set_string(6, self.size.1 - 1, path.to_string_lossy(), text_color);
|
.set_string(6, self.size.1 - 1, path.to_string_lossy(), text_color);
|
||||||
}
|
}
|
||||||
|
@ -247,19 +259,19 @@ impl Editor {
|
||||||
std::mem::swap(&mut self.surface, &mut self.cache);
|
std::mem::swap(&mut self.surface, &mut self.cache);
|
||||||
|
|
||||||
// set cursor shape
|
// set cursor shape
|
||||||
match state.mode() {
|
match view.state.mode() {
|
||||||
Mode::Insert => write!(stdout, "\x1B[6 q"),
|
Mode::Insert => write!(stdout, "\x1B[6 q"),
|
||||||
Mode::Normal => write!(stdout, "\x1B[2 q"),
|
Mode::Normal => write!(stdout, "\x1B[2 q"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// render the cursor
|
// render the cursor
|
||||||
let pos = state.selection().cursor();
|
let pos = view.state.selection().cursor();
|
||||||
let coords = coords_at_pos(&state.doc().slice(..), pos);
|
let coords = coords_at_pos(&view.state.doc().slice(..), pos);
|
||||||
execute!(
|
execute!(
|
||||||
stdout,
|
stdout,
|
||||||
cursor::MoveTo(
|
cursor::MoveTo(
|
||||||
coords.col as u16 + viewport.x,
|
coords.col as u16 + viewport.x,
|
||||||
coords.row as u16 - self.first_line + viewport.y,
|
coords.row as u16 - view.first_line + viewport.y,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -295,29 +307,29 @@ impl Editor {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Some(Ok(Event::Key(event))) => {
|
Some(Ok(Event::Key(event))) => {
|
||||||
if let Some(state) = &mut self.state {
|
if let Some(view) = &mut self.view {
|
||||||
match state.mode() {
|
match view.state.mode() {
|
||||||
Mode::Insert => {
|
Mode::Insert => {
|
||||||
match event {
|
match event {
|
||||||
KeyEvent {
|
KeyEvent {
|
||||||
code: KeyCode::Esc, ..
|
code: KeyCode::Esc, ..
|
||||||
} => helix_core::commands::normal_mode(state, 1),
|
} => commands::normal_mode(view, 1),
|
||||||
KeyEvent {
|
KeyEvent {
|
||||||
code: KeyCode::Backspace,
|
code: KeyCode::Backspace,
|
||||||
..
|
..
|
||||||
} => helix_core::commands::delete_char_backward(state, 1),
|
} => commands::delete_char_backward(view, 1),
|
||||||
KeyEvent {
|
KeyEvent {
|
||||||
code: KeyCode::Delete,
|
code: KeyCode::Delete,
|
||||||
..
|
..
|
||||||
} => helix_core::commands::delete_char_forward(state, 1),
|
} => commands::delete_char_forward(view, 1),
|
||||||
KeyEvent {
|
KeyEvent {
|
||||||
code: KeyCode::Char(c),
|
code: KeyCode::Char(c),
|
||||||
..
|
..
|
||||||
} => helix_core::commands::insert_char(state, c),
|
} => commands::insert_char(view, c),
|
||||||
KeyEvent {
|
KeyEvent {
|
||||||
code: KeyCode::Enter,
|
code: KeyCode::Enter,
|
||||||
..
|
..
|
||||||
} => helix_core::commands::insert_char(state, '\n'),
|
} => commands::insert_char(view, '\n'),
|
||||||
_ => (), // skip
|
_ => (), // skip
|
||||||
}
|
}
|
||||||
// TODO: simplistic ensure cursor in view for now
|
// TODO: simplistic ensure cursor in view for now
|
||||||
|
@ -329,7 +341,7 @@ impl Editor {
|
||||||
// TODO: handle modes and sequences (`gg`)
|
// TODO: handle modes and sequences (`gg`)
|
||||||
if let Some(command) = keymap.get(&event) {
|
if let Some(command) = keymap.get(&event) {
|
||||||
// TODO: handle count other than 1
|
// TODO: handle count other than 1
|
||||||
command(state, 1);
|
command(view, 1);
|
||||||
|
|
||||||
// TODO: simplistic ensure cursor in view for now
|
// TODO: simplistic ensure cursor in view for now
|
||||||
self.ensure_cursor_in_view();
|
self.ensure_cursor_in_view();
|
||||||
|
@ -350,10 +362,10 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_cursor_in_view(&mut self) {
|
fn ensure_cursor_in_view(&mut self) {
|
||||||
if let Some(state) = &mut self.state {
|
if let Some(view) = &mut self.view {
|
||||||
let cursor = state.selection().cursor();
|
let cursor = view.state.selection().cursor();
|
||||||
let line = state.doc().char_to_line(cursor) as u16;
|
let line = view.state.doc().char_to_line(cursor) as u16;
|
||||||
let document_end = self.first_line + self.size.1.saturating_sub(1) - 1;
|
let document_end = view.first_line + self.size.1.saturating_sub(1) - 1;
|
||||||
|
|
||||||
let padding = 5u16;
|
let padding = 5u16;
|
||||||
|
|
||||||
|
@ -361,10 +373,10 @@ impl Editor {
|
||||||
|
|
||||||
if line > document_end.saturating_sub(padding) {
|
if line > document_end.saturating_sub(padding) {
|
||||||
// scroll down
|
// scroll down
|
||||||
self.first_line += line - (document_end.saturating_sub(padding));
|
view.first_line += line - (document_end.saturating_sub(padding));
|
||||||
} else if line < self.first_line + padding {
|
} else if line < view.first_line + padding {
|
||||||
// scroll up
|
// scroll up
|
||||||
self.first_line = line.saturating_sub(padding);
|
view.first_line = line.saturating_sub(padding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
use crate::commands::{self, Command};
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers},
|
event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers},
|
||||||
execute,
|
execute,
|
||||||
style::Print,
|
style::Print,
|
||||||
};
|
};
|
||||||
use helix_core::commands::{self, Command};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// Kakoune-inspired:
|
// Kakoune-inspired:
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
|
mod commands;
|
||||||
mod editor;
|
mod editor;
|
||||||
mod keymap;
|
mod keymap;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
Loading…
Add table
Reference in a new issue