commands: Simplify some code, only calling cx.doc() once.

pull/8/head
Blaž Hrastnik 4 years ago
parent 7877647cf0
commit 7da6bd6a71

@ -36,6 +36,15 @@ impl<'a> Context<'a> {
pub fn doc(&mut self) -> &mut Document { pub fn doc(&mut self) -> &mut Document {
&mut self.editor.view_mut().doc &mut self.editor.view_mut().doc
} }
/// Push a new component onto the compositor.
pub fn push_layer(&mut self, component: Box<dyn crate::compositor::Component>) {
self.callback = Some(Box::new(
|compositor: &mut Compositor, editor: &mut Editor| {
compositor.push(component);
},
));
}
} }
/// 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
@ -44,42 +53,43 @@ pub type Command = fn(cx: &mut Context);
pub fn move_char_left(cx: &mut Context) { pub fn move_char_left(cx: &mut Context) {
let count = cx.count; let count = cx.count;
let selection = let doc = cx.doc();
cx.doc() let selection = doc
.state .state
.move_selection(Direction::Backward, Granularity::Character, count); .move_selection(Direction::Backward, Granularity::Character, count);
cx.doc().set_selection(selection); doc.set_selection(selection);
} }
pub fn move_char_right(cx: &mut Context) { pub fn move_char_right(cx: &mut Context) {
let count = cx.count; let count = cx.count;
let selection = let doc = cx.doc();
cx.doc() let selection = doc
.state .state
.move_selection(Direction::Forward, Granularity::Character, count); .move_selection(Direction::Forward, Granularity::Character, count);
cx.doc().set_selection(selection); doc.set_selection(selection);
} }
pub fn move_line_up(cx: &mut Context) { pub fn move_line_up(cx: &mut Context) {
let count = cx.count; let count = cx.count;
let selection = cx let doc = cx.doc();
.doc() let selection = doc
.state .state
.move_selection(Direction::Backward, Granularity::Line, count); .move_selection(Direction::Backward, Granularity::Line, count);
cx.doc().set_selection(selection); doc.set_selection(selection);
} }
pub fn move_line_down(cx: &mut Context) { pub fn move_line_down(cx: &mut Context) {
let count = cx.count; let count = cx.count;
let selection = cx let doc = cx.doc();
.doc() let selection = doc
.state .state
.move_selection(Direction::Forward, Granularity::Line, count); .move_selection(Direction::Forward, Granularity::Line, count);
cx.doc().set_selection(selection); doc.set_selection(selection);
} }
pub fn move_line_end(cx: &mut Context) { pub fn move_line_end(cx: &mut Context) {
let lines = selection_lines(&cx.doc().state); let doc = cx.doc();
let lines = selection_lines(&doc.state);
let positions = lines let positions = lines
.into_iter() .into_iter()
@ -88,29 +98,30 @@ pub fn move_line_end(cx: &mut Context) {
// Line end is pos at the start of next line - 1 // Line end is pos at the start of next line - 1
// subtract another 1 because the line ends with \n // subtract another 1 because the line ends with \n
cx.doc().text().line_to_char(index + 1).saturating_sub(2) doc.text().line_to_char(index + 1).saturating_sub(2)
}) })
.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);
cx.doc().set_selection(selection); doc.set_selection(selection);
} }
pub fn move_line_start(cx: &mut Context) { pub fn move_line_start(cx: &mut Context) {
let lines = selection_lines(&cx.doc().state); let doc = cx.doc();
let lines = selection_lines(&doc.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.
cx.doc().text().line_to_char(index) doc.text().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);
cx.doc().set_selection(selection); doc.set_selection(selection);
} }
pub fn move_next_word_start(cx: &mut Context) { pub fn move_next_word_start(cx: &mut Context) {
@ -139,17 +150,19 @@ pub fn move_next_word_end(cx: &mut Context) {
} }
pub fn move_file_start(cx: &mut Context) { pub fn move_file_start(cx: &mut Context) {
cx.doc().set_selection(Selection::point(0)); let doc = cx.doc();
doc.set_selection(Selection::point(0));
cx.doc().mode = Mode::Normal; doc.mode = Mode::Normal;
} }
pub fn move_file_end(cx: &mut Context) { pub fn move_file_end(cx: &mut Context) {
let text = &cx.doc().text(); let doc = cx.doc();
let text = doc.text();
let last_line = text.line_to_char(text.len_lines().saturating_sub(2)); let last_line = text.line_to_char(text.len_lines().saturating_sub(2));
cx.doc().set_selection(Selection::point(last_line)); doc.set_selection(Selection::point(last_line));
cx.doc().mode = Mode::Normal; doc.mode = Mode::Normal;
} }
pub fn extend_next_word_start(cx: &mut Context) { pub fn extend_next_word_start(cx: &mut Context) {
@ -161,7 +174,7 @@ pub fn extend_next_word_start(cx: &mut Context) {
range range
}); // TODO: count }); // TODO: count
cx.doc().set_selection(selection); doc.set_selection(selection);
} }
pub fn extend_prev_word_start(cx: &mut Context) { pub fn extend_prev_word_start(cx: &mut Context) {
@ -172,7 +185,7 @@ pub fn extend_prev_word_start(cx: &mut Context) {
range.head = pos; range.head = pos;
range range
}); // TODO: count }); // TODO: count
cx.doc().set_selection(selection); doc.set_selection(selection);
} }
pub fn extend_next_word_end(cx: &mut Context) { pub fn extend_next_word_end(cx: &mut Context) {
@ -184,7 +197,7 @@ pub fn extend_next_word_end(cx: &mut Context) {
range range
}); // TODO: count }); // TODO: count
cx.doc().set_selection(selection); doc.set_selection(selection);
} }
pub fn check_cursor_in_view(view: &View) -> bool { pub fn check_cursor_in_view(view: &View) -> bool {
@ -309,11 +322,8 @@ pub fn select_regex(cx: &mut Context) {
selection::select_on_matches(text, doc.selection(), &regex).expect("no matches"); selection::select_on_matches(text, doc.selection(), &regex).expect("no matches");
doc.set_selection(selection); doc.set_selection(selection);
}); });
cx.callback = Some(Box::new(
move |compositor: &mut Compositor, editor: &mut Editor| { cx.push_layer(Box::new(prompt));
compositor.push(Box::new(prompt));
},
));
} }
pub fn split_selection(cx: &mut Context) { pub fn split_selection(cx: &mut Context) {
@ -336,11 +346,7 @@ pub fn split_selection(cx: &mut Context) {
doc.set_selection(selection); doc.set_selection(selection);
}); });
cx.callback = Some(Box::new( cx.push_layer(Box::new(prompt));
move |compositor: &mut Compositor, editor: &mut Editor| {
compositor.push(Box::new(prompt));
},
));
} }
pub fn split_selection_on_newline(cx: &mut Context) { pub fn split_selection_on_newline(cx: &mut Context) {
@ -393,11 +399,7 @@ pub fn search(cx: &mut Context) {
register::set('\\', vec![regex.as_str().to_string()]); register::set('\\', vec![regex.as_str().to_string()]);
}); });
cx.callback = Some(Box::new( cx.push_layer(Box::new(prompt));
move |compositor: &mut Compositor, editor: &mut Editor| {
compositor.push(Box::new(prompt));
},
));
} }
pub fn search_next(cx: &mut Context) { pub fn search_next(cx: &mut Context) {
@ -418,74 +420,76 @@ pub fn search_next(cx: &mut Context) {
pub fn select_line(cx: &mut Context) { pub fn select_line(cx: &mut Context) {
// TODO: count // TODO: count
let pos = cx.doc().selection().primary(); let doc = cx.doc();
let text = cx.doc().text(); let pos = doc.selection().primary();
let text = doc.text();
let line = text.char_to_line(pos.head); let line = text.char_to_line(pos.head);
let start = text.line_to_char(line); let start = text.line_to_char(line);
let end = text.line_to_char(line + 1).saturating_sub(1); let end = text.line_to_char(line + 1).saturating_sub(1);
cx.doc().set_selection(Selection::single(start, end)); doc.set_selection(Selection::single(start, end));
} }
// heuristic: append changes to history after each command, unless we're in insert mode // heuristic: append changes to history after each command, unless we're in insert mode
fn _delete_selection(cx: &mut Context) { fn _delete_selection(doc: &mut Document) {
let transaction = Transaction::change_by_selection(&cx.doc().state, |range| { let transaction =
(range.from(), range.to() + 1, None) Transaction::change_by_selection(&doc.state, |range| (range.from(), range.to() + 1, None));
}); doc.apply(&transaction);
cx.doc().apply(&transaction);
} }
pub fn delete_selection(cx: &mut Context) { pub fn delete_selection(cx: &mut Context) {
_delete_selection(cx); let doc = cx.doc();
_delete_selection(doc);
append_changes_to_history(cx); append_changes_to_history(doc);
} }
pub fn change_selection(cx: &mut Context) { pub fn change_selection(cx: &mut Context) {
_delete_selection(cx); let doc = cx.doc();
_delete_selection(doc);
insert_mode(cx); insert_mode(cx);
} }
pub fn collapse_selection(cx: &mut Context) { pub fn collapse_selection(cx: &mut Context) {
let selection = cx let doc = cx.doc();
.doc() let selection = doc
.selection() .selection()
.transform(|range| Range::new(range.head, range.head)); .transform(|range| Range::new(range.head, range.head));
cx.doc().set_selection(selection); doc.set_selection(selection);
} }
pub fn flip_selections(cx: &mut Context) { pub fn flip_selections(cx: &mut Context) {
let selection = cx let doc = cx.doc();
.doc() let selection = doc
.selection() .selection()
.transform(|range| Range::new(range.head, range.anchor)); .transform(|range| Range::new(range.head, range.anchor));
cx.doc().set_selection(selection); doc.set_selection(selection);
} }
fn enter_insert_mode(cx: &mut Context) { fn enter_insert_mode(doc: &mut Document) {
cx.doc().mode = Mode::Insert; doc.mode = Mode::Insert;
// TODO: store selection for undo // TODO: store selection for undo
} }
// inserts at the start of each selection // inserts at the start of each selection
pub fn insert_mode(cx: &mut Context) { pub fn insert_mode(cx: &mut Context) {
enter_insert_mode(cx); let doc = cx.doc();
enter_insert_mode(doc);
let selection = cx let selection = doc
.doc()
.selection() .selection()
.transform(|range| Range::new(range.to(), range.from())); .transform(|range| Range::new(range.to(), range.from()));
cx.doc().set_selection(selection); doc.set_selection(selection);
} }
// inserts at the end of each selection // inserts at the end of each selection
pub fn append_mode(cx: &mut Context) { pub fn append_mode(cx: &mut Context) {
enter_insert_mode(cx);
let doc = cx.doc(); let doc = cx.doc();
enter_insert_mode(doc);
doc.restore_cursor = true; doc.restore_cursor = true;
// TODO: as transaction // TODO: as transaction
@ -503,98 +507,56 @@ pub fn append_mode(cx: &mut Context) {
// TODO: I, A, o and O can share a lot of the primitives. // TODO: I, A, o and O can share a lot of the primitives.
pub fn command_mode(cx: &mut Context) { pub fn command_mode(cx: &mut Context) {
let executor = cx.executor; let executor = cx.executor;
cx.callback = Some(Box::new( let prompt = Prompt::new(
move |compositor: &mut Compositor, editor: &mut Editor| { ":".to_owned(),
let prompt = Prompt::new( |_input: &str| {
":".to_owned(), // TODO: i need this duplicate list right now to avoid borrow checker issues
|_input: &str| { let command_list = vec![
// TODO: i need this duplicate list right now to avoid borrow checker issues "q".to_string(),
let command_list = vec![ "o".to_string(),
"q".to_string(), "w".to_string(),
"o".to_string(), // String::from("q"),
"w".to_string(), ];
// String::from("q"), command_list
// String::from("aaa"), .into_iter()
// String::from("bbb"), .filter(|command| command.contains(_input))
// String::from("ccc"), .collect()
// String::from("ddd"), }, // completion
// String::from("eee"), move |editor: &mut Editor, input: &str, event: PromptEvent| {
// String::from("averylongcommandaverylongcommandaverylongcommandaverylongcommandaverylongcommand"), if event != PromptEvent::Validate {
// String::from("q"), return;
// String::from("aaa"), }
// String::from("bbb"),
// String::from("ccc"),
// String::from("ddd"),
// String::from("eee"),
// String::from("q"),
// String::from("aaa"),
// String::from("bbb"),
// String::from("ccc"),
// String::from("ddd"),
// String::from("eee"),
// String::from("q"),
// String::from("aaa"),
// String::from("bbb"),
// String::from("ccc"),
// String::from("ddd"),
// String::from("eee"),
// String::from("q"),
// String::from("aaa"),
// String::from("bbb"),
// String::from("ccc"),
// String::from("ddd"),
// String::from("eee"),
];
command_list
.into_iter()
.filter(|command| command.contains(_input))
.collect()
}, // completion
move |editor: &mut Editor, input: &str, event: PromptEvent| {
if event != PromptEvent::Validate {
return;
}
let parts = input.split_ascii_whitespace().collect::<Vec<&str>>();
match *parts.as_slice() {
["q"] => {
editor.tree.remove(editor.view().id);
// editor.should_close = true,
}
["o", path] => {
editor.open(path.into(), executor);
}
["w"] => {
// TODO: non-blocking via save() command
smol::block_on(editor.view_mut().doc.save());
}
_ => (), let parts = input.split_ascii_whitespace().collect::<Vec<&str>>();
}
}, match *parts.as_slice() {
); ["q"] => {
compositor.push(Box::new(prompt)); editor.tree.remove(editor.view().id);
// editor.should_close = true,
}
["o", path] => {
editor.open(path.into(), executor);
}
["w"] => {
// TODO: non-blocking via save() command
smol::block_on(editor.view_mut().doc.save());
}
_ => (),
}
}, },
)); );
cx.push_layer(Box::new(prompt));
} }
pub fn file_picker(cx: &mut Context) { pub fn file_picker(cx: &mut Context) {
let picker = ui::file_picker("./", cx.executor); let picker = ui::file_picker("./", cx.executor);
cx.callback = Some(Box::new( cx.push_layer(Box::new(picker));
|compositor: &mut Compositor, editor: &mut Editor| {
compositor.push(Box::new(picker));
},
));
} }
pub fn buffer_picker(cx: &mut Context) { pub fn buffer_picker(cx: &mut Context) {
unimplemented!() unimplemented!()
// cx.callback = Some(Box::new( // let picker = ui::buffer_picker(&editor.views, editor.focus);
// |compositor: &mut Compositor, editor: &mut Editor| { // cx.push_layer(Box::new(picker));
// let picker = ui::buffer_picker(&editor.views, editor.focus);
// compositor.push(Box::new(picker));
// },
// ));
} }
// calculate line numbers for each selection range // calculate line numbers for each selection range
@ -614,23 +576,24 @@ 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(cx: &mut Context) { pub fn prepend_to_line(cx: &mut Context) {
enter_insert_mode(cx); let doc = cx.doc();
enter_insert_mode(doc);
move_line_start(cx); move_line_start(cx);
} }
// 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(cx: &mut Context) { pub fn append_to_line(cx: &mut Context) {
enter_insert_mode(cx); let doc = cx.doc();
enter_insert_mode(doc);
move_line_end(cx); move_line_end(cx);
} }
// 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(cx: &mut Context) { pub fn open_below(cx: &mut Context) {
enter_insert_mode(cx);
let doc = cx.doc(); let doc = cx.doc();
enter_insert_mode(doc);
let lines = selection_lines(&doc.state); let lines = selection_lines(&doc.state);
@ -687,40 +650,40 @@ pub fn open_below(cx: &mut Context) {
// O inserts a new line before each line with a selection // O inserts a new line before each line with a selection
fn append_changes_to_history(cx: &mut Context) { fn append_changes_to_history(doc: &mut Document) {
if cx.doc().changes.is_empty() { if doc.changes.is_empty() {
return; return;
} }
// TODO: change -> change -> undo -> change -> change fails, probably old_state needs reset // TODO: change -> change -> undo -> change -> change fails, probably old_state needs reset
let new_changeset = ChangeSet::new(cx.doc().text()); let new_changeset = ChangeSet::new(doc.text());
let changes = std::mem::replace(&mut cx.doc().changes, new_changeset); let changes = std::mem::replace(&mut doc.changes, new_changeset);
// Instead of doing this messy merge we could always commit, and based on transaction // Instead of doing this messy merge we could always commit, and based on transaction
// annotations either add a new layer or compose into the previous one. // annotations either add a new layer or compose into the previous one.
let transaction = Transaction::from(changes).with_selection(cx.doc().selection().clone()); let transaction = Transaction::from(changes).with_selection(doc.selection().clone());
// increment document version // increment document version
// TODO: needs to happen on undo/redo too // TODO: needs to happen on undo/redo too
cx.doc().version += 1; doc.version += 1;
// TODO: trigger lsp/documentDidChange with changes // TODO: trigger lsp/documentDidChange with changes
// HAXX: we need to reconstruct the state as it was before the changes.. // HAXX: we need to reconstruct the state as it was before the changes..
let old_state = cx.doc().old_state.take().expect("no old_state available"); let old_state = doc.old_state.take().expect("no old_state available");
// TODO: take transaction by value? // TODO: take transaction by value?
cx.doc().history.commit_revision(&transaction, &old_state); doc.history.commit_revision(&transaction, &old_state);
// TODO: notify LSP of changes // TODO: notify LSP of changes
} }
pub fn normal_mode(cx: &mut Context) { pub fn normal_mode(cx: &mut Context) {
cx.doc().mode = Mode::Normal; let doc = cx.doc();
append_changes_to_history(cx); doc.mode = Mode::Normal;
let doc = cx.doc(); append_changes_to_history(doc);
// if leaving append mode, move cursor back by 1 // if leaving append mode, move cursor back by 1
if doc.restore_cursor { if doc.restore_cursor {
@ -746,10 +709,11 @@ pub mod insert {
use super::*; use super::*;
// 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(cx: &mut Context, c: char) { pub fn insert_char(cx: &mut Context, c: char) {
let doc = cx.doc();
let c = Tendril::from_char(c); let c = Tendril::from_char(c);
let transaction = Transaction::insert(&cx.doc().state, c); let transaction = Transaction::insert(&doc.state, c);
cx.doc().apply(&transaction); doc.apply(&transaction);
} }
pub fn insert_tab(cx: &mut Context) { pub fn insert_tab(cx: &mut Context) {
@ -884,7 +848,7 @@ pub fn paste(cx: &mut Context) {
}; };
doc.apply(&transaction); doc.apply(&transaction);
append_changes_to_history(cx); append_changes_to_history(doc);
} }
} }
@ -919,8 +883,8 @@ pub fn indent(cx: &mut Context) {
(pos, pos, Some(indent.clone())) (pos, pos, Some(indent.clone()))
}), }),
); );
cx.doc().apply(&transaction); doc.apply(&transaction);
append_changes_to_history(cx); append_changes_to_history(doc);
} }
pub fn unindent(cx: &mut Context) { pub fn unindent(cx: &mut Context) {
@ -953,7 +917,7 @@ pub fn unindent(cx: &mut Context) {
let transaction = Transaction::change(&doc.state, changes.into_iter()); let transaction = Transaction::change(&doc.state, changes.into_iter());
doc.apply(&transaction); doc.apply(&transaction);
append_changes_to_history(cx); append_changes_to_history(doc);
} }
// //
@ -979,12 +943,11 @@ pub fn completion(cx: &mut Context) {
let pos = helix_lsp::util::pos_to_lsp_pos(doc.text().slice(..), doc.selection().cursor()); let pos = helix_lsp::util::pos_to_lsp_pos(doc.text().slice(..), doc.selection().cursor());
// TODO: handle fails // TODO: handle fails
let res = let res = smol::block_on(language_server.completion(doc.identifier(), pos)).unwrap_or_default();
smol::block_on(language_server.completion(cx.doc().identifier(), pos)).unwrap_or_default();
// TODO: if no completion, show some message or something // TODO: if no completion, show some message or something
if !res.is_empty() { if !res.is_empty() {
let snapshot = cx.doc().state.clone(); let snapshot = doc.state.clone();
let mut menu = ui::Menu::new( let mut menu = ui::Menu::new(
res, res,
|item| { |item| {
@ -1047,7 +1010,7 @@ pub fn completion(cx: &mut Context) {
let transaction = let transaction =
util::generate_transaction_from_edits(&doc.state, vec![edit]); util::generate_transaction_from_edits(&doc.state, vec![edit]);
doc.apply(&transaction); doc.apply(&transaction);
// TODO: append_changes_to_history(cx); if not in insert mode? // TODO: append_changes_to_history(doc); if not in insert mode?
} }
_ => (), _ => (),
}; };

@ -436,7 +436,7 @@ impl Component for EditorView {
// mode => write!(stdout, "\x1B[2 q"), // mode => write!(stdout, "\x1B[2 q"),
// }; // };
let view = editor.view(); let view = editor.view();
let cursor = view.doc.state.selection().cursor(); let cursor = view.doc.selection().cursor();
let mut pos = view let mut pos = view
.screen_coords_at_pos(view.doc.text().slice(..), cursor) .screen_coords_at_pos(view.doc.text().slice(..), cursor)

@ -54,8 +54,7 @@ impl Editor {
} }
let view = View::new(doc)?; let view = View::new(doc)?;
let id = self.tree.insert(view); self.tree.insert(view);
self.tree.get_mut(id).id = id;
Ok(()) Ok(())
} }

@ -99,6 +99,7 @@ impl Tree {
let mut node = Node::view(view); let mut node = Node::view(view);
node.parent = parent; node.parent = parent;
let node = self.nodes.insert(node); let node = self.nodes.insert(node);
self.get_mut(node).id = node;
let container = match &mut self.nodes[parent] { let container = match &mut self.nodes[parent] {
Node { Node {

Loading…
Cancel
Save