@@ -3,52 +3,65 @@ use helix_core::{
33 indent::TAB_WIDTH,
44 regex::Regex,
55 register, selection,
6− state::{Direction, Granularity, Mode, State},
6+ state::{Direction, Granularity, State},
77 ChangeSet, Range, Selection, Tendril, Transaction,
88};
99use once_cell::sync::Lazy;
1010
1111use crate::{
12+ document::Mode,
1213 prompt::Prompt,
1314 view::{View, PADDING},
1415};
1516
17+pub struct Context<'a, 'b> {
18+ pub count: usize,
19+ pub view: &'a mut View,
20+ pub executor: &'a smol::Executor<'b>,
21+}
22+
1623/// A command is a function that takes the current state and a count, and does a side-effect on the
1724/// state (usually by creating and applying a transaction).
18−pub type Command = fn(view: &mut View, count: usize);
25+pub type Command = fn(cx: &mut Context);
1926
20−pub fn move_char_left(view: &mut View, count: usize) {
21− // TODO: use a transaction
22− let selection = view
23− .state
24− .move_selection(Direction::Backward, Granularity::Character, count);
25− view.state.selection = selection;
27+pub fn move_char_left(cx: &mut Context) {
28+ let selection =
29+ cx.view
30+ .doc
31+ .state
32+ .move_selection(Direction::Backward, Granularity::Character, cx.count);
33+ cx.view.doc.set_selection(selection);
2634}
2735
28−pub fn move_char_right(view: &mut View, count: usize) {
29− // TODO: use a transaction
30− view.state.selection =
31− view.state
32− .move_selection(Direction::Forward, Granularity::Character, count);
36+pub fn move_char_right(cx: &mut Context) {
37+ let selection =
38+ cx.view
39+ .doc
40+ .state
41+ .move_selection(Direction::Forward, Granularity::Character, cx.count);
42+ cx.view.doc.set_selection(selection);
3343}
3444
35−pub fn move_line_up(view: &mut View, count: usize) {
36− // TODO: use a transaction
37− view.state.selection = view
38− .state
39− .move_selection(Direction::Backward, Granularity::Line, count);
45+pub fn move_line_up(cx: &mut Context) {
46+ let selection =
47+ cx.view
48+ .doc
49+ .state
50+ .move_selection(Direction::Backward, Granularity::Line, cx.count);
51+ cx.view.doc.set_selection(selection);
4052}
4153
42−pub fn move_line_down(view: &mut View, count: usize) {
43− // TODO: use a transaction
44− view.state.selection = view
45− .state
46− .move_selection(Direction::Forward, Granularity::Line, count);
54+pub fn move_line_down(cx: &mut Context) {
55+ let selection =
56+ cx.view
57+ .doc
58+ .state
59+ .move_selection(Direction::Forward, Granularity::Line, cx.count);
60+ cx.view.doc.set_selection(selection);
4761}
4862
49−pub fn move_line_end(view: &mut View, _count: usize) {
50− // TODO: use a transaction
51− let lines = selection_lines(&view.state);
63+pub fn move_line_end(cx: &mut Context) {
64+ let lines = selection_lines(&cx.view.doc.state);
5265
5366 let positions = lines
5467 .into_iter()
@@ -57,89 +70,80 @@ pub fn move_line_end(view: &mut View, _count: usize) {
5770
5871 // Line end is pos at the start of next line - 1
5972 // subtract another 1 because the line ends with \n
60− view.state.doc.line_to_char(index + 1).saturating_sub(2)
73+ cx.view.doc.text().line_to_char(index + 1).saturating_sub(2)
6174 })
6275 .map(|pos| Range::new(pos, pos));
6376
6477 let selection = Selection::new(positions.collect(), 0);
6578
66− let transaction = Transaction::new(&mut view.state).with_selection(selection);
67−
68− transaction.apply(&mut view.state);
79+ cx.view.doc.set_selection(selection);
6980}
7081
71−pub fn move_line_start(view: &mut View, _count: usize) {
72− let lines = selection_lines(&view.state);
82+pub fn move_line_start(cx: &mut Context) {
83+ let lines = selection_lines(&cx.view.doc.state);
7384
7485 let positions = lines
7586 .into_iter()
7687 .map(|index| {
7788 // adjust all positions to the start of the line.
78− view.state.doc.line_to_char(index)
89+ cx.view.doc.text().line_to_char(index)
7990 })
8091 .map(|pos| Range::new(pos, pos));
8192
8293 let selection = Selection::new(positions.collect(), 0);
8394
84− let transaction = Transaction::new(&mut view.state).with_selection(selection);
85−
86− transaction.apply(&mut view.state);
95+ cx.view.doc.set_selection(selection);
8796}
8897
89−pub fn move_next_word_start(view: &mut View, count: usize) {
90− let pos = view.state.move_pos(
91− view.state.selection.cursor(),
98+pub fn move_next_word_start(cx: &mut Context) {
99+ let pos = cx.view.doc.state.move_pos(
100+ cx.view.doc.selection().cursor(),
92101 Direction::Forward,
93102 Granularity::Word,
94− count,
103+ cx.count,
95104 );
96105
97− // TODO: use a transaction
98− view.state.selection = Selection::single(pos, pos);
106+ cx.view.doc.set_selection(Selection::point(pos));
99107}
100108
101−pub fn move_prev_word_start(view: &mut View, count: usize) {
102− let pos = view.state.move_pos(
103− view.state.selection.cursor(),
109+pub fn move_prev_word_start(cx: &mut Context) {
110+ let pos = cx.view.doc.state.move_pos(
111+ cx.view.doc.selection().cursor(),
104112 Direction::Backward,
105113 Granularity::Word,
106− count,
114+ cx.count,
107115 );
108116
109− // TODO: use a transaction
110− view.state.selection = Selection::single(pos, pos);
117+ cx.view.doc.set_selection(Selection::point(pos));
111118}
112119
113−pub fn move_next_word_end(view: &mut View, count: usize) {
120+pub fn move_next_word_end(cx: &mut Context) {
114121 let pos = State::move_next_word_end(
115− &view.state.doc().slice(..),
116− view.state.selection.cursor(),
117− count,
122+ &cx.view.doc.text().slice(..),
123+ cx.view.doc.selection().cursor(),
124+ cx.count,
118125 );
119126
120− // TODO: use a transaction
121− view.state.selection = Selection::single(pos, pos);
127+ cx.view.doc.set_selection(Selection::point(pos));
122128}
123129
124−pub fn move_file_start(view: &mut View, _count: usize) {
125− // TODO: use a transaction
126− view.state.selection = Selection::single(0, 0);
130+pub fn move_file_start(cx: &mut Context) {
131+ cx.view.doc.set_selection(Selection::point(0));
127132
128− view.state.mode = Mode::Normal;
133+ cx.view.doc.mode = Mode::Normal;
129134}
130135
131−pub fn move_file_end(view: &mut View, _count: usize) {
132− // TODO: use a transaction
133− let text = &view.state.doc;
136+pub fn move_file_end(cx: &mut Context) {
137+ let text = &cx.view.doc.text();
134138 let last_line = text.line_to_char(text.len_lines().saturating_sub(2));
135− view.state.selection = Selection::single(last_line, last_line);
139+ cx.view.doc.set_selection(Selection::point(last_line));
136140
137− view.state.mode = Mode::Normal;
141+ cx.view.doc.mode = Mode::Normal;
138142}
139143
140−pub fn check_cursor_in_view(view: &mut View) -> bool {
141− let cursor = view.state.selection().cursor();
142− let line = view.state.doc().char_to_line(cursor);
144+pub fn check_cursor_in_view(view: &View) -> bool {
145+ let cursor = view.doc.selection().cursor();
146+ let line = view.doc.text().char_to_line(cursor);
143147 let document_end = view.first_line + view.size.1.saturating_sub(1) as usize;
144148
145149 if (line > document_end.saturating_sub(PADDING)) | (line < view.first_line + PADDING) {
@@ -148,168 +152,186 @@ pub fn check_cursor_in_view(view: &mut View) -> bool {
148152 true
149153}
150154
151−pub fn page_up(view: &mut View, _count: usize) {
152− if view.first_line < PADDING {
155+pub fn page_up(cx: &mut Context) {
156+ if cx.view.first_line < PADDING {
153157 return;
154158 }
155159
156− view.first_line = view.first_line.saturating_sub(view.size.1 as usize);
160+ cx.view.first_line = cx.view.first_line.saturating_sub(cx.view.size.1 as usize);
157161
158− if !check_cursor_in_view(view) {
159− let text = view.state.doc();
160− let pos = text.line_to_char(view.last_line().saturating_sub(PADDING));
161− view.state.selection = Selection::single(pos, pos);
162+ if !check_cursor_in_view(cx.view) {
163+ let text = cx.view.doc.text();
164+ let pos = text.line_to_char(cx.view.last_line().saturating_sub(PADDING));
165+ cx.view.doc.set_selection(Selection::point(pos));
162166 }
163167}
164168
165−pub fn page_down(view: &mut View, _count: usize) {
166− view.first_line += view.size.1 as usize + PADDING;
169+pub fn page_down(cx: &mut Context) {
170+ cx.view.first_line += cx.view.size.1 as usize + PADDING;
167171
168− if view.first_line < view.state.doc().len_lines() {
169− let text = view.state.doc();
170− let pos = text.line_to_char(view.first_line as usize);
171− view.state.selection = Selection::single(pos, pos);
172+ if cx.view.first_line < cx.view.doc.text().len_lines() {
173+ let text = cx.view.doc.text();
174+ let pos = text.line_to_char(cx.view.first_line as usize);
175+ cx.view.doc.set_selection(Selection::point(pos));
172176 }
173177}
174178
175−pub fn half_page_up(view: &mut View, _count: usize) {
176− if view.first_line < PADDING {
179+pub fn half_page_up(cx: &mut Context) {
180+ if cx.view.first_line < PADDING {
177181 return;
178182 }
179183
180− view.first_line = view.first_line.saturating_sub(view.size.1 as usize / 2);
184+ cx.view.first_line = cx
185+ .view
186+ .first_line
187+ .saturating_sub(cx.view.size.1 as usize / 2);
181188
182− if !check_cursor_in_view(view) {
183− let text = &view.state.doc;
184− let pos = text.line_to_char(view.last_line() - PADDING);
185− view.state.selection = Selection::single(pos, pos);
189+ if !check_cursor_in_view(cx.view) {
190+ let text = &cx.view.doc.text();
191+ let pos = text.line_to_char(cx.view.last_line() - PADDING);
192+ cx.view.doc.set_selection(Selection::point(pos));
186193 }
187194}
188195
189−pub fn half_page_down(view: &mut View, _count: usize) {
190− let lines = view.state.doc().len_lines();
191− if view.first_line < lines.saturating_sub(view.size.1 as usize) {
192− view.first_line += view.size.1 as usize / 2;
196+pub fn half_page_down(cx: &mut Context) {
197+ let lines = cx.view.doc.text().len_lines();
198+ if cx.view.first_line < lines.saturating_sub(cx.view.size.1 as usize) {
199+ cx.view.first_line += cx.view.size.1 as usize / 2;
193200 }
194− if !check_cursor_in_view(view) {
195− let text = view.state.doc();
196− let pos = text.line_to_char(view.first_line as usize);
197− view.state.selection = Selection::single(pos, pos);
201+ if !check_cursor_in_view(cx.view) {
202+ let text = cx.view.doc.text();
203+ let pos = text.line_to_char(cx.view.first_line as usize);
204+ cx.view.doc.set_selection(Selection::point(pos));
198205 }
199206}
200207// avoid select by default by having a visual mode switch that makes movements into selects
201208
202−pub fn extend_char_left(view: &mut View, count: usize) {
203− // TODO: use a transaction
204− let selection = view
205− .state
206− .extend_selection(Direction::Backward, Granularity::Character, count);
207− view.state.selection = selection;
209+pub fn extend_char_left(cx: &mut Context) {
210+ let selection =
211+ cx.view
212+ .doc
213+ .state
214+ .extend_selection(Direction::Backward, Granularity::Character, cx.count);
215+ cx.view.doc.set_selection(selection);
208216}
209217
210−pub fn extend_char_right(view: &mut View, count: usize) {
211− // TODO: use a transaction
212− view.state.selection =
213− view.state
214− .extend_selection(Direction::Forward, Granularity::Character, count);
218+pub fn extend_char_right(cx: &mut Context) {
219+ let selection =
220+ cx.view
221+ .doc
222+ .state
223+ .extend_selection(Direction::Forward, Granularity::Character, cx.count);
224+ cx.view.doc.set_selection(selection);
215225}
216226
217−pub fn extend_line_up(view: &mut View, count: usize) {
218− // TODO: use a transaction
219− view.state.selection =
220− view.state
221− .extend_selection(Direction::Backward, Granularity::Line, count);
227+pub fn extend_line_up(cx: &mut Context) {
228+ let selection =
229+ cx.view
230+ .doc
231+ .state
232+ .extend_selection(Direction::Backward, Granularity::Line, cx.count);
233+ cx.view.doc.set_selection(selection);
222234}
223235
224−pub fn extend_line_down(view: &mut View, count: usize) {
225− // TODO: use a transaction
226− view.state.selection =
227− view.state
228− .extend_selection(Direction::Forward, Granularity::Line, count);
236+pub fn extend_line_down(cx: &mut Context) {
237+ let selection =
238+ cx.view
239+ .doc
240+ .state
241+ .extend_selection(Direction::Forward, Granularity::Line, cx.count);
242+ cx.view.doc.set_selection(selection);
229243}
230244
231−pub fn split_selection_on_newline(view: &mut View, _count: usize) {
232− let text = &view.state.doc.slice(..);
245+pub fn split_selection_on_newline(cx: &mut Context) {
246+ let text = &cx.view.doc.text().slice(..);
233247 // only compile the regex once
234248 #[allow(clippy::trivial_regex)]
235249 static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\n").unwrap());
236− // TODO: use a transaction
237− view.state.selection = selection::split_on_matches(text, view.state.selection(), ®EX)
250+ let selection = selection::split_on_matches(text, cx.view.doc.selection(), ®EX);
251+ cx.view.doc.set_selection(selection);
238252}
239253
240−pub fn select_line(view: &mut View, _count: usize) {
254+pub fn select_line(cx: &mut Context) {
241255 // TODO: count
242− let pos = view.state.selection().primary();
243− let text = view.state.doc();
256+ let pos = cx.view.doc.selection().primary();
257+ let text = cx.view.doc.text();
244258 let line = text.char_to_line(pos.head);
245259 let start = text.line_to_char(line);
246260 let end = text.line_to_char(line + 1).saturating_sub(1);
247261
248− // TODO: use a transaction
249− view.state.selection = Selection::single(start, end);
262+ cx.view.doc.set_selection(Selection::single(start, end));
250263}
251264
252−pub fn delete_selection(view: &mut View, _count: usize) {
253− let transaction =
254− Transaction::change_by_selection(&view.state, |range| (range.from(), range.to() + 1, None));
255− transaction.apply(&mut view.state);
265+pub fn delete_selection(cx: &mut Context) {
266+ let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
267+ (range.from(), range.to() + 1, None)
268+ });
269+ cx.view.doc.apply(&transaction);
256270
257− append_changes_to_history(view);
271+ append_changes_to_history(cx);
258272}
259273
260−pub fn change_selection(view: &mut View, count: usize) {
261− delete_selection(view, count);
262− insert_mode(view, count);
274+pub fn change_selection(cx: &mut Context) {
275+ delete_selection(cx);
276+ insert_mode(cx);
263277}
264278
265−pub fn collapse_selection(view: &mut View, _count: usize) {
266− view.state.selection = view
267− .state
268− .selection
269− .transform(|range| Range::new(range.head, range.head))
279+pub fn collapse_selection(cx: &mut Context) {
280+ let selection = cx
281+ .view
282+ .doc
283+ .selection()
284+ .transform(|range| Range::new(range.head, range.head));
285+
286+ cx.view.doc.set_selection(selection);
270287}
271288
272−pub fn flip_selections(view: &mut View, _count: usize) {
273− view.state.selection = view
274− .state
275− .selection
276− .transform(|range| Range::new(range.head, range.anchor))
289+pub fn flip_selections(cx: &mut Context) {
290+ let selection = cx
291+ .view
292+ .doc
293+ .selection()
294+ .transform(|range| Range::new(range.head, range.anchor));
295+
296+ cx.view.doc.set_selection(selection);
277297}
278298
279−fn enter_insert_mode(view: &mut View) {
280− view.state.mode = Mode::Insert;
299+fn enter_insert_mode(cx: &mut Context) {
300+ cx.view.doc.mode = Mode::Insert;
281301
282− append_changes_to_history(view);
302+ append_changes_to_history(cx);
283303}
284304// inserts at the start of each selection
285−pub fn insert_mode(view: &mut View, _count: usize) {
286− enter_insert_mode(view);
305+pub fn insert_mode(cx: &mut Context) {
306+ enter_insert_mode(cx);
287307
288− view.state.selection = view
289− .state
290− .selection
291− .transform(|range| Range::new(range.to(), range.from()))
308+ let selection = cx
309+ .view
310+ .doc
311+ .selection()
312+ .transform(|range| Range::new(range.to(), range.from()));
313+ cx.view.doc.set_selection(selection);
292314}
293315
294316// inserts at the end of each selection
295−pub fn append_mode(view: &mut View, _count: usize) {
296− enter_insert_mode(view);
297− view.state.restore_cursor = true;
317+pub fn append_mode(cx: &mut Context) {
318+ enter_insert_mode(cx);
319+ cx.view.doc.restore_cursor = true;
298320
299321 // TODO: as transaction
300− let text = &view.state.doc.slice(..);
301− view.state.selection = view.state.selection.transform(|range| {
322+ let text = &cx.view.doc.text().slice(..);
323+ let selection = cx.view.doc.selection().transform(|range| {
302324 // TODO: to() + next char
303325 Range::new(
304326 range.from(),
305327 graphemes::next_grapheme_boundary(text, range.to()),
306328 )
307− })
329+ });
330+ cx.view.doc.set_selection(selection);
308331}
309332
310333// TODO: I, A, o and O can share a lot of the primitives.
311−
312−pub fn command_mode(_view: &mut View, _count: usize) {
334+pub fn command_mode(_cx: &mut Context) {
313335 unimplemented!()
314336}
315337
@@ -329,30 +351,30 @@ fn selection_lines(state: &State) -> Vec<usize> {
329351}
330352
331353// I inserts at the start of each line with a selection
332−pub fn prepend_to_line(view: &mut View, count: usize) {
333− enter_insert_mode(view);
354+pub fn prepend_to_line(cx: &mut Context) {
355+ enter_insert_mode(cx);
334356
335− move_line_start(view, count);
357+ move_line_start(cx);
336358}
337359
338360// A inserts at the end of each line with a selection
339−pub fn append_to_line(view: &mut View, count: usize) {
340− enter_insert_mode(view);
361+pub fn append_to_line(cx: &mut Context) {
362+ enter_insert_mode(cx);
341363
342− move_line_end(view, count);
364+ move_line_end(cx);
343365}
344366
345367// o inserts a new line after each line with a selection
346−pub fn open_below(view: &mut View, _count: usize) {
347− enter_insert_mode(view);
368+pub fn open_below(cx: &mut Context) {
369+ enter_insert_mode(cx);
348370
349− let lines = selection_lines(&view.state);
371+ let lines = selection_lines(&cx.view.doc.state);
350372
351373 let positions: Vec<_> = lines
352374 .into_iter()
353375 .map(|index| {
354376 // adjust all positions to the end of the line/start of the next one.
355− view.state.doc.line_to_char(index + 1)
377+ cx.view.doc.text().line_to_char(index + 1)
356378 })
357379 .collect();
358380
@@ -373,111 +395,119 @@ pub fn open_below(view: &mut View, _count: usize) {
373395 0,
374396 );
375397
376− let transaction = Transaction::change(&view.state, changes).with_selection(selection);
398+ let transaction = Transaction::change(&cx.view.doc.state, changes).with_selection(selection);
377399
378− transaction.apply(&mut view.state);
400+ cx.view.doc.apply(&transaction);
379401}
380402
381403// O inserts a new line before each line with a selection
382404
383−fn append_changes_to_history(view: &mut View) {
384− if view.state.changes.is_empty() {
405+fn append_changes_to_history(cx: &mut Context) {
406+ if cx.view.doc.changes.is_empty() {
385407 return;
386408 }
387409
388− let new_changeset = ChangeSet::new(view.state.doc());
389− let changes = std::mem::replace(&mut view.state.changes, new_changeset);
410+ let new_changeset = ChangeSet::new(cx.view.doc.text());
411+ let changes = std::mem::replace(&mut cx.view.doc.changes, new_changeset);
390412 // Instead of doing this messy merge we could always commit, and based on transaction
391413 // annotations either add a new layer or compose into the previous one.
392− let transaction = Transaction::from(changes).with_selection(view.state.selection().clone());
414+ let transaction = Transaction::from(changes).with_selection(cx.view.doc.selection().clone());
393415
394− // HAXX: we need to reconstruct the state as it was before the changes..
395− let (doc, selection) = view.state.old_state.take().unwrap();
396− let mut old_state = State::new(doc);
397− old_state.selection = selection;
416+ // increment document version
417+ // TODO: needs to happen on undo/redo too
418+ cx.view.doc.version += 1;
398419
420+ // TODO: trigger lsp/documentDidChange with changes
421+
422+ // HAXX: we need to reconstruct the state as it was before the changes..
423+ let old_state = std::mem::replace(&mut cx.view.doc.old_state, cx.view.doc.state.clone());
399424 // TODO: take transaction by value?
400− view.history.commit_revision(&transaction, &old_state);
425+ cx.view
426+ .doc
427+ .history
428+ .commit_revision(&transaction, &old_state);
401429
402− // TODO: need to start the state with these vals
403− // HAXX
404− view.state.old_state = Some((view.state.doc().clone(), view.state.selection.clone()));
430+ // TODO: notify LSP of changes
405431}
406432
407−pub fn normal_mode(view: &mut View, _count: usize) {
408− view.state.mode = Mode::Normal;
433+pub fn normal_mode(cx: &mut Context) {
434+ cx.view.doc.mode = Mode::Normal;
409435
410− append_changes_to_history(view);
436+ append_changes_to_history(cx);
411437
412438 // if leaving append mode, move cursor back by 1
413− if view.state.restore_cursor {
414− let text = &view.state.doc.slice(..);
415− view.state.selection = view.state.selection.transform(|range| {
439+ if cx.view.doc.restore_cursor {
440+ let text = &cx.view.doc.text().slice(..);
441+ let selection = cx.view.doc.selection().transform(|range| {
416442 Range::new(
417443 range.from(),
418444 graphemes::prev_grapheme_boundary(text, range.to()),
419445 )
420446 });
447+ cx.view.doc.set_selection(selection);
421448
422− view.state.restore_cursor = false;
449+ cx.view.doc.restore_cursor = false;
423450 }
424451}
425452
426−pub fn goto_mode(view: &mut View, _count: usize) {
427− view.state.mode = Mode::Goto;
453+pub fn goto_mode(cx: &mut Context) {
454+ cx.view.doc.mode = Mode::Goto;
428455}
429456
430457// NOTE: Transactions in this module get appended to history when we switch back to normal mode.
431458pub mod insert {
432459 use super::*;
433460 // TODO: insert means add text just before cursor, on exit we should be on the last letter.
434− pub fn insert_char(view: &mut View, c: char) {
461+ pub fn insert_char(cx: &mut Context, c: char) {
435462 let c = Tendril::from_char(c);
436− let transaction = Transaction::insert(&view.state, c);
463+ let transaction = Transaction::insert(&cx.view.doc.state, c);
437464
438− transaction.apply(&mut view.state);
465+ cx.view.doc.apply(&transaction);
439466 }
440467
441− pub fn insert_tab(view: &mut View, _count: usize) {
442− insert_char(view, '\t');
468+ pub fn insert_tab(cx: &mut Context) {
469+ insert_char(cx, '\t');
443470 }
444471
445− pub fn insert_newline(view: &mut View, _count: usize) {
446− let transaction = Transaction::change_by_selection(&view.state, |range| {
447− let indent_level =
448− helix_core::indent::suggested_indent_for_pos(&view.state, range.head);
472+ pub fn insert_newline(cx: &mut Context) {
473+ let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
474+ let indent_level = helix_core::indent::suggested_indent_for_pos(
475+ cx.view.doc.syntax.as_ref(),
476+ &cx.view.doc.state,
477+ range.head,
478+ );
449479 let indent = " ".repeat(TAB_WIDTH).repeat(indent_level);
450480 let mut text = String::with_capacity(1 + indent.len());
451481 text.push('\n');
452482 text.push_str(&indent);
453483 (range.head, range.head, Some(text.into()))
454484 });
455− transaction.apply(&mut view.state);
485+ cx.view.doc.apply(&transaction);
456486 }
457487
458488 // TODO: handle indent-aware delete
459− pub fn delete_char_backward(view: &mut View, count: usize) {
460− let text = &view.state.doc.slice(..);
461− let transaction = Transaction::change_by_selection(&view.state, |range| {
489+ pub fn delete_char_backward(cx: &mut Context) {
490+ let text = &cx.view.doc.text().slice(..);
491+ let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
462492 (
463− graphemes::nth_prev_grapheme_boundary(text, range.head, count),
493+ graphemes::nth_prev_grapheme_boundary(text, range.head, cx.count),
464494 range.head,
465495 None,
466496 )
467497 });
468− transaction.apply(&mut view.state);
498+ cx.view.doc.apply(&transaction);
469499 }
470500
471− pub fn delete_char_forward(view: &mut View, count: usize) {
472− let text = &view.state.doc.slice(..);
473− let transaction = Transaction::change_by_selection(&view.state, |range| {
501+ pub fn delete_char_forward(cx: &mut Context) {
502+ let text = &cx.view.doc.text().slice(..);
503+ let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
474504 (
475505 range.head,
476− graphemes::nth_next_grapheme_boundary(text, range.head, count),
506+ graphemes::nth_next_grapheme_boundary(text, range.head, cx.count),
477507 None,
478508 )
479509 });
480− transaction.apply(&mut view.state);
510+ cx.view.doc.apply(&transaction);
481511 }
482512}
483513
@@ -487,24 +517,32 @@ pub fn insert_char_prompt(prompt: &mut Prompt, c: char) {
487517
488518// Undo / Redo
489519
490−pub fn undo(view: &mut View, _count: usize) {
491− view.history.undo(&mut view.state);
520+pub fn undo(cx: &mut Context) {
521+ if let Some(revert) = cx.view.doc.history.undo() {
522+ cx.view.doc.version += 1;
523+ cx.view.doc.apply(&revert);
524+ }
492525
493526 // TODO: each command could simply return a Option<transaction>, then the higher level handles storing it?
494527}
495528
496−pub fn redo(view: &mut View, _count: usize) {
497− view.history.redo(&mut view.state);
529+pub fn redo(cx: &mut Context) {
530+ if let Some(transaction) = cx.view.doc.history.redo() {
531+ cx.view.doc.version += 1;
532+ cx.view.doc.apply(&transaction);
533+ }
498534}
499535
500536// Yank / Paste
501537
502−pub fn yank(view: &mut View, _count: usize) {
538+pub fn yank(cx: &mut Context) {
503539 // TODO: should selections be made end inclusive?
504− let values = view
540+ let values = cx
541+ .view
542+ .doc
505543 .state
506544 .selection()
507− .fragments(&view.state.doc().slice(..))
545+ .fragments(&cx.view.doc.text().slice(..))
508546 .map(|cow| cow.into_owned())
509547 .collect();
510548
@@ -513,7 +551,7 @@ pub fn yank(view: &mut View, _count: usize) {
513551 register::set(reg, values);
514552}
515553
516−pub fn paste(view: &mut View, _count: usize) {
554+pub fn paste(cx: &mut Context) {
517555 // TODO: allow specifying reg
518556 let reg = '"';
519557 if let Some(values) = register::get(reg) {
@@ -545,19 +583,19 @@ pub fn paste(view: &mut View, _count: usize) {
545583 let transaction = if linewise {
546584 // paste on the next line
547585 // TODO: can simply take a range + modifier and compute the right pos without ifs
548− let text = view.state.doc();
549− Transaction::change_by_selection(&view.state, |range| {
586+ let text = cx.view.doc.text();
587+ Transaction::change_by_selection(&cx.view.doc.state, |range| {
550588 let line_end = text.line_to_char(text.char_to_line(range.head) + 1);
551589 (line_end, line_end, Some(values.next().unwrap()))
552590 })
553591 } else {
554− Transaction::change_by_selection(&view.state, |range| {
592+ Transaction::change_by_selection(&cx.view.doc.state, |range| {
555593 (range.head + 1, range.head + 1, Some(values.next().unwrap()))
556594 })
557595 };
558596
559− transaction.apply(&mut view.state);
560− append_changes_to_history(view);
597+ cx.view.doc.apply(&transaction);
598+ append_changes_to_history(cx);
561599 }
562600}
563601
@@ -565,9 +603,9 @@ fn get_lines(view: &View) -> Vec<usize> {
565603 let mut lines = Vec::new();
566604
567605 // Get all line numbers
568− for range in view.state.selection.ranges() {
569− let start = view.state.doc.char_to_line(range.from());
570− let end = view.state.doc.char_to_line(range.to());
606+ for range in view.doc.selection().ranges() {
607+ let start = view.doc.text().char_to_line(range.from());
608+ let end = view.doc.text().char_to_line(range.to());
571609
572610 for line in start..=end {
573611 lines.push(line)
@@ -578,29 +616,29 @@ fn get_lines(view: &View) -> Vec<usize> {
578616 lines
579617}
580618
581−pub fn indent(view: &mut View, _count: usize) {
582− let lines = get_lines(view);
619+pub fn indent(cx: &mut Context) {
620+ let lines = get_lines(cx.view);
583621
584622 // Indent by one level
585623 let indent = Tendril::from(" ".repeat(TAB_WIDTH));
586624
587625 let transaction = Transaction::change(
588− &view.state,
626+ &cx.view.doc.state,
589627 lines.into_iter().map(|line| {
590− let pos = view.state.doc.line_to_char(line);
628+ let pos = cx.view.doc.text().line_to_char(line);
591629 (pos, pos, Some(indent.clone()))
592630 }),
593631 );
594− transaction.apply(&mut view.state);
595− append_changes_to_history(view);
632+ cx.view.doc.apply(&transaction);
633+ append_changes_to_history(cx);
596634}
597635
598−pub fn unindent(view: &mut View, _count: usize) {
599− let lines = get_lines(view);
636+pub fn unindent(cx: &mut Context) {
637+ let lines = get_lines(cx.view);
600638 let mut changes = Vec::with_capacity(lines.len());
601639
602640 for line_idx in lines {
603− let line = view.state.doc.line(line_idx);
641+ let line = cx.view.doc.text().line(line_idx);
604642 let mut width = 0;
605643
606644 for ch in line.chars() {
@@ -616,18 +654,27 @@ pub fn unindent(view: &mut View, _count: usize) {
616654 }
617655
618656 if width > 0 {
619− let start = view.state.doc.line_to_char(line_idx);
657+ let start = cx.view.doc.text().line_to_char(line_idx);
620658 changes.push((start, start + width, None))
621659 }
622660 }
623661
624− let transaction = Transaction::change(&view.state, changes.into_iter());
662+ let transaction = Transaction::change(&cx.view.doc.state, changes.into_iter());
625663
626− transaction.apply(&mut view.state);
627− append_changes_to_history(view);
664+ cx.view.doc.apply(&transaction);
665+ append_changes_to_history(cx);
628666}
629667
630−pub fn indent_selection(_view: &mut View, _count: usize) {
668+pub fn indent_selection(_cx: &mut Context) {
631669 // loop over each line and recompute proper indentation
632670 unimplemented!()
633671}
672+
673+//
674+
675+pub fn save(cx: &mut Context) {
676+ // Spawns an async task to actually do the saving. This way we prevent blocking.
677+
678+ // TODO: handle save errors somehow?
679+ cx.executor.spawn(cx.view.doc.save()).detach();
680+}