on master help sign in
Introduce a command context that carries the executor and other fields.
8f0bcfeBlaž Hrastnik
helix-term/src/application.rs+25−4
@@ -476,13 +476,24 @@ impl<'a> Application<'a> {
476 match view.doc.mode() {
476 match view.doc.mode() {
477 Mode::Insert => {
477 Mode::Insert => {
478 if let Some(command) = self.keymap[&Mode::Insert].get(&keys) {
478 if let Some(command) = self.keymap[&Mode::Insert].get(&keys) {
479+ let mut cx = helix_view::commands::Context {
479− command(view, 1);
480+ view,
481+ executor: self.executor,
482+ count: 1,
483+ };
484+
485+ command(&mut cx);
480 } else if let KeyEvent {
486 } else if let KeyEvent {
481 code: KeyCode::Char(c),
487 code: KeyCode::Char(c),
482 ..
488 ..
483 } = event
489 } = event
484 {
490 {
491+ let mut cx = helix_view::commands::Context {
492+ view,
493+ executor: self.executor,
494+ count: 1,
495+ };
485− commands::insert::insert_char(view, c);
496+ commands::insert::insert_char(&mut cx, c);
486 }
497 }
487 view.ensure_cursor_in_view();
498 view.ensure_cursor_in_view();
488 }
499 }
@@ -544,7 +555,12 @@ impl<'a> Application<'a> {
544
555
545 // HAXX: special casing for command mode
556 // HAXX: special casing for command mode
546 } else if let Some(command) = self.keymap[&Mode::Normal].get(&keys) {
557 } else if let Some(command) = self.keymap[&Mode::Normal].get(&keys) {
558+ let mut cx = helix_view::commands::Context {
547− command(view, 1);
559+ view,
560+ executor: self.executor,
561+ count: 1,
562+ };
563+ command(&mut cx);
548
564
549 // TODO: simplistic ensure cursor in view for now
565 // TODO: simplistic ensure cursor in view for now
550 view.ensure_cursor_in_view();
566 view.ensure_cursor_in_view();
@@ -552,7 +568,12 @@ impl<'a> Application<'a> {
552 }
568 }
553 mode => {
569 mode => {
554 if let Some(command) = self.keymap[&mode].get(&keys) {
570 if let Some(command) = self.keymap[&mode].get(&keys) {
571+ let mut cx = helix_view::commands::Context {
555− command(view, 1);
572+ view,
573+ executor: self.executor,
574+ count: 1,
575+ };
576+ command(&mut cx);
556
577
557 // TODO: simplistic ensure cursor in view for now
578 // TODO: simplistic ensure cursor in view for now
558 view.ensure_cursor_in_view();
579 view.ensure_cursor_in_view();
helix-view/src/commands.rs+239−215
@@ -14,44 +14,54 @@ use crate::{
14 view::{View, PADDING},
14 view::{View, PADDING},
15};
15};
16
16
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+
17/// A command is a function that takes the current state and a count, and does a side-effect on the
23/// A command is a function that takes the current state and a count, and does a side-effect on the
18/// state (usually by creating and applying a transaction).
24/// state (usually by creating and applying a transaction).
19−pub type Command = fn(view: &mut View, count: usize);
25+pub type Command = fn(cx: &mut Context);
20
26
21−pub fn move_char_left(view: &mut View, count: usize) {
27+pub fn move_char_left(cx: &mut Context) {
22 let selection =
28 let selection =
23− view.doc
29+ cx.view
30+ .doc
24 .state
31 .state
25− .move_selection(Direction::Backward, Granularity::Character, count);
32+ .move_selection(Direction::Backward, Granularity::Character, cx.count);
26− view.doc.set_selection(selection);
33+ cx.view.doc.set_selection(selection);
27}
34}
28
35
29−pub fn move_char_right(view: &mut View, count: usize) {
36+pub fn move_char_right(cx: &mut Context) {
30 let selection =
37 let selection =
31− view.doc
38+ cx.view
39+ .doc
32 .state
40 .state
33− .move_selection(Direction::Forward, Granularity::Character, count);
41+ .move_selection(Direction::Forward, Granularity::Character, cx.count);
34− view.doc.set_selection(selection);
42+ cx.view.doc.set_selection(selection);
35}
43}
36
44
37−pub fn move_line_up(view: &mut View, count: usize) {
45+pub fn move_line_up(cx: &mut Context) {
38− let selection = view
46+ let selection =
47+ cx.view
39− .doc
48+ .doc
40− .state
49+ .state
41− .move_selection(Direction::Backward, Granularity::Line, count);
50+ .move_selection(Direction::Backward, Granularity::Line, cx.count);
42− view.doc.set_selection(selection);
51+ cx.view.doc.set_selection(selection);
43}
52}
44
53
45−pub fn move_line_down(view: &mut View, count: usize) {
54+pub fn move_line_down(cx: &mut Context) {
46− let selection = view
55+ let selection =
56+ cx.view
47− .doc
57+ .doc
48− .state
58+ .state
49− .move_selection(Direction::Forward, Granularity::Line, count);
59+ .move_selection(Direction::Forward, Granularity::Line, cx.count);
50− view.doc.set_selection(selection);
60+ cx.view.doc.set_selection(selection);
51}
61}
52
62
53−pub fn move_line_end(view: &mut View, _count: usize) {
63+pub fn move_line_end(cx: &mut Context) {
54− let lines = selection_lines(&view.doc.state);
64+ let lines = selection_lines(&cx.view.doc.state);
55
65
56 let positions = lines
66 let positions = lines
57 .into_iter()
67 .into_iter()
@@ -60,78 +70,78 @@ pub fn move_line_end(view: &mut View, _count: usize) {
60
70
61 // Line end is pos at the start of next line - 1
71 // Line end is pos at the start of next line - 1
62 // subtract another 1 because the line ends with \n
72 // subtract another 1 because the line ends with \n
63− view.doc.text().line_to_char(index + 1).saturating_sub(2)
73+ cx.view.doc.text().line_to_char(index + 1).saturating_sub(2)
64 })
74 })
65 .map(|pos| Range::new(pos, pos));
75 .map(|pos| Range::new(pos, pos));
66
76
67 let selection = Selection::new(positions.collect(), 0);
77 let selection = Selection::new(positions.collect(), 0);
68
78
69− view.doc.set_selection(selection);
79+ cx.view.doc.set_selection(selection);
70}
80}
71
81
72−pub fn move_line_start(view: &mut View, _count: usize) {
82+pub fn move_line_start(cx: &mut Context) {
73− let lines = selection_lines(&view.doc.state);
83+ let lines = selection_lines(&cx.view.doc.state);
74
84
75 let positions = lines
85 let positions = lines
76 .into_iter()
86 .into_iter()
77 .map(|index| {
87 .map(|index| {
78 // adjust all positions to the start of the line.
88 // adjust all positions to the start of the line.
79− view.doc.text().line_to_char(index)
89+ cx.view.doc.text().line_to_char(index)
80 })
90 })
81 .map(|pos| Range::new(pos, pos));
91 .map(|pos| Range::new(pos, pos));
82
92
83 let selection = Selection::new(positions.collect(), 0);
93 let selection = Selection::new(positions.collect(), 0);
84
94
85− view.doc.set_selection(selection);
95+ cx.view.doc.set_selection(selection);
86}
96}
87
97
88−pub fn move_next_word_start(view: &mut View, count: usize) {
98+pub fn move_next_word_start(cx: &mut Context) {
89− let pos = view.doc.state.move_pos(
99+ let pos = cx.view.doc.state.move_pos(
90− view.doc.selection().cursor(),
100+ cx.view.doc.selection().cursor(),
91 Direction::Forward,
101 Direction::Forward,
92 Granularity::Word,
102 Granularity::Word,
93− count,
103+ cx.count,
94 );
104 );
95
105
96− view.doc.set_selection(Selection::point(pos));
106+ cx.view.doc.set_selection(Selection::point(pos));
97}
107}
98
108
99−pub fn move_prev_word_start(view: &mut View, count: usize) {
109+pub fn move_prev_word_start(cx: &mut Context) {
100− let pos = view.doc.state.move_pos(
110+ let pos = cx.view.doc.state.move_pos(
101− view.doc.selection().cursor(),
111+ cx.view.doc.selection().cursor(),
102 Direction::Backward,
112 Direction::Backward,
103 Granularity::Word,
113 Granularity::Word,
104− count,
114+ cx.count,
105 );
115 );
106
116
107− view.doc.set_selection(Selection::point(pos));
117+ cx.view.doc.set_selection(Selection::point(pos));
108}
118}
109
119
110−pub fn move_next_word_end(view: &mut View, count: usize) {
120+pub fn move_next_word_end(cx: &mut Context) {
111 let pos = State::move_next_word_end(
121 let pos = State::move_next_word_end(
112− &view.doc.text().slice(..),
122+ &cx.view.doc.text().slice(..),
113− view.doc.selection().cursor(),
123+ cx.view.doc.selection().cursor(),
114− count,
124+ cx.count,
115 );
125 );
116
126
117− view.doc.set_selection(Selection::point(pos));
127+ cx.view.doc.set_selection(Selection::point(pos));
118}
128}
119
129
120−pub fn move_file_start(view: &mut View, _count: usize) {
130+pub fn move_file_start(cx: &mut Context) {
121− view.doc.set_selection(Selection::point(0));
131+ cx.view.doc.set_selection(Selection::point(0));
122
132
123− view.doc.mode = Mode::Normal;
133+ cx.view.doc.mode = Mode::Normal;
124}
134}
125
135
126−pub fn move_file_end(view: &mut View, _count: usize) {
136+pub fn move_file_end(cx: &mut Context) {
127− let text = &view.doc.text();
137+ let text = &cx.view.doc.text();
128 let last_line = text.line_to_char(text.len_lines().saturating_sub(2));
138 let last_line = text.line_to_char(text.len_lines().saturating_sub(2));
129− view.doc.set_selection(Selection::point(last_line));
139+ cx.view.doc.set_selection(Selection::point(last_line));
130
140
131− view.doc.mode = Mode::Normal;
141+ cx.view.doc.mode = Mode::Normal;
132}
142}
133
143
134−pub fn check_cursor_in_view(view: &mut View) -> bool {
144+pub fn check_cursor_in_view(view: &View) -> bool {
135 let cursor = view.doc.selection().cursor();
145 let cursor = view.doc.selection().cursor();
136 let line = view.doc.text().char_to_line(cursor);
146 let line = view.doc.text().char_to_line(cursor);
137 let document_end = view.first_line + view.size.1.saturating_sub(1) as usize;
147 let document_end = view.first_line + view.size.1.saturating_sub(1) as usize;
@@ -142,172 +152,182 @@ pub fn check_cursor_in_view(view: &mut View) -> bool {
142 true
152 true
143}
153}
144
154
145−pub fn page_up(view: &mut View, _count: usize) {
155+pub fn page_up(cx: &mut Context) {
146− if view.first_line < PADDING {
156+ if cx.view.first_line < PADDING {
147 return;
157 return;
148 }
158 }
149
159
150− 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);
151
161
152− if !check_cursor_in_view(view) {
162+ if !check_cursor_in_view(cx.view) {
153− let text = view.doc.text();
163+ let text = cx.view.doc.text();
154− let pos = text.line_to_char(view.last_line().saturating_sub(PADDING));
164+ let pos = text.line_to_char(cx.view.last_line().saturating_sub(PADDING));
155− view.doc.set_selection(Selection::point(pos));
165+ cx.view.doc.set_selection(Selection::point(pos));
156 }
166 }
157}
167}
158
168
159−pub fn page_down(view: &mut View, _count: usize) {
169+pub fn page_down(cx: &mut Context) {
160− view.first_line += view.size.1 as usize + PADDING;
170+ cx.view.first_line += cx.view.size.1 as usize + PADDING;
161
171
162− if view.first_line < view.doc.text().len_lines() {
172+ if cx.view.first_line < cx.view.doc.text().len_lines() {
163− let text = view.doc.text();
173+ let text = cx.view.doc.text();
164− let pos = text.line_to_char(view.first_line as usize);
174+ let pos = text.line_to_char(cx.view.first_line as usize);
165− view.doc.set_selection(Selection::point(pos));
175+ cx.view.doc.set_selection(Selection::point(pos));
166 }
176 }
167}
177}
168
178
169−pub fn half_page_up(view: &mut View, _count: usize) {
179+pub fn half_page_up(cx: &mut Context) {
170− if view.first_line < PADDING {
180+ if cx.view.first_line < PADDING {
171 return;
181 return;
172 }
182 }
173
183
184+ cx.view.first_line = cx
185+ .view
186+ .first_line
174− view.first_line = view.first_line.saturating_sub(view.size.1 as usize / 2);
187+ .saturating_sub(cx.view.size.1 as usize / 2);
175
188
176− if !check_cursor_in_view(view) {
189+ if !check_cursor_in_view(cx.view) {
177− let text = &view.doc.text();
190+ let text = &cx.view.doc.text();
178− let pos = text.line_to_char(view.last_line() - PADDING);
191+ let pos = text.line_to_char(cx.view.last_line() - PADDING);
179− view.doc.set_selection(Selection::point(pos));
192+ cx.view.doc.set_selection(Selection::point(pos));
180 }
193 }
181}
194}
182
195
183−pub fn half_page_down(view: &mut View, _count: usize) {
196+pub fn half_page_down(cx: &mut Context) {
184− let lines = view.doc.text().len_lines();
197+ let lines = cx.view.doc.text().len_lines();
185− if view.first_line < lines.saturating_sub(view.size.1 as usize) {
198+ if cx.view.first_line < lines.saturating_sub(cx.view.size.1 as usize) {
186− view.first_line += view.size.1 as usize / 2;
199+ cx.view.first_line += cx.view.size.1 as usize / 2;
187 }
200 }
188− if !check_cursor_in_view(view) {
201+ if !check_cursor_in_view(cx.view) {
189− let text = view.doc.text();
202+ let text = cx.view.doc.text();
190− let pos = text.line_to_char(view.first_line as usize);
203+ let pos = text.line_to_char(cx.view.first_line as usize);
191− view.doc.set_selection(Selection::point(pos));
204+ cx.view.doc.set_selection(Selection::point(pos));
192 }
205 }
193}
206}
194// avoid select by default by having a visual mode switch that makes movements into selects
207// avoid select by default by having a visual mode switch that makes movements into selects
195
208
196−pub fn extend_char_left(view: &mut View, count: usize) {
209+pub fn extend_char_left(cx: &mut Context) {
197 let selection =
210 let selection =
198− view.doc
211+ cx.view
212+ .doc
199 .state
213 .state
200− .extend_selection(Direction::Backward, Granularity::Character, count);
214+ .extend_selection(Direction::Backward, Granularity::Character, cx.count);
201− view.doc.set_selection(selection);
215+ cx.view.doc.set_selection(selection);
202}
216}
203
217
204−pub fn extend_char_right(view: &mut View, count: usize) {
218+pub fn extend_char_right(cx: &mut Context) {
205 let selection =
219 let selection =
206− view.doc
220+ cx.view
221+ .doc
207 .state
222 .state
208− .extend_selection(Direction::Forward, Granularity::Character, count);
223+ .extend_selection(Direction::Forward, Granularity::Character, cx.count);
209− view.doc.set_selection(selection);
224+ cx.view.doc.set_selection(selection);
210}
225}
211
226
212−pub fn extend_line_up(view: &mut View, count: usize) {
227+pub fn extend_line_up(cx: &mut Context) {
213− let selection = view
228+ let selection =
229+ cx.view
214− .doc
230+ .doc
215− .state
231+ .state
216− .extend_selection(Direction::Backward, Granularity::Line, count);
232+ .extend_selection(Direction::Backward, Granularity::Line, cx.count);
217− view.doc.set_selection(selection);
233+ cx.view.doc.set_selection(selection);
218}
234}
219
235
220−pub fn extend_line_down(view: &mut View, count: usize) {
236+pub fn extend_line_down(cx: &mut Context) {
221− let selection = view
237+ let selection =
238+ cx.view
222− .doc
239+ .doc
223− .state
240+ .state
224− .extend_selection(Direction::Forward, Granularity::Line, count);
241+ .extend_selection(Direction::Forward, Granularity::Line, cx.count);
225− view.doc.set_selection(selection);
242+ cx.view.doc.set_selection(selection);
226}
243}
227
244
228−pub fn split_selection_on_newline(view: &mut View, _count: usize) {
245+pub fn split_selection_on_newline(cx: &mut Context) {
229− let text = &view.doc.text().slice(..);
246+ let text = &cx.view.doc.text().slice(..);
230 // only compile the regex once
247 // only compile the regex once
231 #[allow(clippy::trivial_regex)]
248 #[allow(clippy::trivial_regex)]
232 static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\n").unwrap());
249 static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\n").unwrap());
233− let selection = selection::split_on_matches(text, view.doc.selection(), &REGEX);
250+ let selection = selection::split_on_matches(text, cx.view.doc.selection(), &REGEX);
234− view.doc.set_selection(selection);
251+ cx.view.doc.set_selection(selection);
235}
252}
236
253
237−pub fn select_line(view: &mut View, _count: usize) {
254+pub fn select_line(cx: &mut Context) {
238 // TODO: count
255 // TODO: count
239− let pos = view.doc.selection().primary();
256+ let pos = cx.view.doc.selection().primary();
240− let text = view.doc.text();
257+ let text = cx.view.doc.text();
241 let line = text.char_to_line(pos.head);
258 let line = text.char_to_line(pos.head);
242 let start = text.line_to_char(line);
259 let start = text.line_to_char(line);
243 let end = text.line_to_char(line + 1).saturating_sub(1);
260 let end = text.line_to_char(line + 1).saturating_sub(1);
244
261
245− view.doc.set_selection(Selection::single(start, end));
262+ cx.view.doc.set_selection(Selection::single(start, end));
246}
263}
247
264
248−pub fn delete_selection(view: &mut View, _count: usize) {
265+pub fn delete_selection(cx: &mut Context) {
249− let transaction = Transaction::change_by_selection(&view.doc.state, |range| {
266+ let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
250 (range.from(), range.to() + 1, None)
267 (range.from(), range.to() + 1, None)
251 });
268 });
252− view.doc.apply(&transaction);
269+ cx.view.doc.apply(&transaction);
253
270
254− append_changes_to_history(view);
271+ append_changes_to_history(cx);
255}
272}
256
273
257−pub fn change_selection(view: &mut View, count: usize) {
274+pub fn change_selection(cx: &mut Context) {
258− delete_selection(view, count);
275+ delete_selection(cx);
259− insert_mode(view, count);
276+ insert_mode(cx);
260}
277}
261
278
262−pub fn collapse_selection(view: &mut View, _count: usize) {
279+pub fn collapse_selection(cx: &mut Context) {
263− let selection = view
280+ let selection = cx
281+ .view
264 .doc
282 .doc
265 .selection()
283 .selection()
266 .transform(|range| Range::new(range.head, range.head));
284 .transform(|range| Range::new(range.head, range.head));
267
285
268− view.doc.set_selection(selection);
286+ cx.view.doc.set_selection(selection);
269}
287}
270
288
271−pub fn flip_selections(view: &mut View, _count: usize) {
289+pub fn flip_selections(cx: &mut Context) {
272− let selection = view
290+ let selection = cx
291+ .view
273 .doc
292 .doc
274 .selection()
293 .selection()
275 .transform(|range| Range::new(range.head, range.anchor));
294 .transform(|range| Range::new(range.head, range.anchor));
276
295
277− view.doc.set_selection(selection);
296+ cx.view.doc.set_selection(selection);
278}
297}
279
298
280−fn enter_insert_mode(view: &mut View) {
299+fn enter_insert_mode(cx: &mut Context) {
281− view.doc.mode = Mode::Insert;
300+ cx.view.doc.mode = Mode::Insert;
282
301
283− append_changes_to_history(view);
302+ append_changes_to_history(cx);
284}
303}
285// inserts at the start of each selection
304// inserts at the start of each selection
286−pub fn insert_mode(view: &mut View, _count: usize) {
305+pub fn insert_mode(cx: &mut Context) {
287− enter_insert_mode(view);
306+ enter_insert_mode(cx);
288
307
289− let selection = view
308+ let selection = cx
309+ .view
290 .doc
310 .doc
291 .selection()
311 .selection()
292 .transform(|range| Range::new(range.to(), range.from()));
312 .transform(|range| Range::new(range.to(), range.from()));
293− view.doc.set_selection(selection);
313+ cx.view.doc.set_selection(selection);
294}
314}
295
315
296// inserts at the end of each selection
316// inserts at the end of each selection
297−pub fn append_mode(view: &mut View, _count: usize) {
317+pub fn append_mode(cx: &mut Context) {
298− enter_insert_mode(view);
318+ enter_insert_mode(cx);
299− view.doc.restore_cursor = true;
319+ cx.view.doc.restore_cursor = true;
300
320
301 // TODO: as transaction
321 // TODO: as transaction
302− let text = &view.doc.text().slice(..);
322+ let text = &cx.view.doc.text().slice(..);
303− let selection = view.doc.selection().transform(|range| {
323+ let selection = cx.view.doc.selection().transform(|range| {
304 // TODO: to() + next char
324 // TODO: to() + next char
305 Range::new(
325 Range::new(
306 range.from(),
326 range.from(),
307 graphemes::next_grapheme_boundary(text, range.to()),
327 graphemes::next_grapheme_boundary(text, range.to()),
308 )
328 )
309 });
329 });
310− view.doc.set_selection(selection);
330+ cx.view.doc.set_selection(selection);
311}
331}
312
332
313// TODO: I, A, o and O can share a lot of the primitives.
333// TODO: I, A, o and O can share a lot of the primitives.
@@ -332,30 +352,30 @@ fn selection_lines(state: &State) -> Vec<usize> {
332}
352}
333
353
334// I inserts at the start of each line with a selection
354// I inserts at the start of each line with a selection
335−pub fn prepend_to_line(view: &mut View, count: usize) {
355+pub fn prepend_to_line(cx: &mut Context) {
336− enter_insert_mode(view);
356+ enter_insert_mode(cx);
337
357
338− move_line_start(view, count);
358+ move_line_start(cx);
339}
359}
340
360
341// A inserts at the end of each line with a selection
361// A inserts at the end of each line with a selection
342−pub fn append_to_line(view: &mut View, count: usize) {
362+pub fn append_to_line(cx: &mut Context) {
343− enter_insert_mode(view);
363+ enter_insert_mode(cx);
344
364
345− move_line_end(view, count);
365+ move_line_end(cx);
346}
366}
347
367
348// o inserts a new line after each line with a selection
368// o inserts a new line after each line with a selection
349−pub fn open_below(view: &mut View, _count: usize) {
369+pub fn open_below(cx: &mut Context) {
350− enter_insert_mode(view);
370+ enter_insert_mode(cx);
351
371
352− let lines = selection_lines(&view.doc.state);
372+ let lines = selection_lines(&cx.view.doc.state);
353
373
354 let positions: Vec<_> = lines
374 let positions: Vec<_> = lines
355 .into_iter()
375 .into_iter()
356 .map(|index| {
376 .map(|index| {
357 // adjust all positions to the end of the line/start of the next one.
377 // adjust all positions to the end of the line/start of the next one.
358− view.doc.text().line_to_char(index + 1)
378+ cx.view.doc.text().line_to_char(index + 1)
359 })
379 })
360 .collect();
380 .collect();
361
381
@@ -376,82 +396,85 @@ pub fn open_below(view: &mut View, _count: usize) {
376 0,
396 0,
377 );
397 );
378
398
379− let transaction = Transaction::change(&view.doc.state, changes).with_selection(selection);
399+ let transaction = Transaction::change(&cx.view.doc.state, changes).with_selection(selection);
380
400
381− view.doc.apply(&transaction);
401+ cx.view.doc.apply(&transaction);
382}
402}
383
403
384// O inserts a new line before each line with a selection
404// O inserts a new line before each line with a selection
385
405
386−fn append_changes_to_history(view: &mut View) {
406+fn append_changes_to_history(cx: &mut Context) {
387− if view.doc.changes.is_empty() {
407+ if cx.view.doc.changes.is_empty() {
388 return;
408 return;
389 }
409 }
390
410
391− let new_changeset = ChangeSet::new(view.doc.text());
411+ let new_changeset = ChangeSet::new(cx.view.doc.text());
392− let changes = std::mem::replace(&mut view.doc.changes, new_changeset);
412+ let changes = std::mem::replace(&mut cx.view.doc.changes, new_changeset);
393 // Instead of doing this messy merge we could always commit, and based on transaction
413 // Instead of doing this messy merge we could always commit, and based on transaction
394 // annotations either add a new layer or compose into the previous one.
414 // annotations either add a new layer or compose into the previous one.
395− let transaction = Transaction::from(changes).with_selection(view.doc.selection().clone());
415+ let transaction = Transaction::from(changes).with_selection(cx.view.doc.selection().clone());
396
416
397 // increment document version
417 // increment document version
398 // TODO: needs to happen on undo/redo too
418 // TODO: needs to happen on undo/redo too
399− view.doc.version += 1;
419+ cx.view.doc.version += 1;
400
420
401 // TODO: trigger lsp/documentDidChange with changes
421 // TODO: trigger lsp/documentDidChange with changes
402
422
403 // HAXX: we need to reconstruct the state as it was before the changes..
423 // HAXX: we need to reconstruct the state as it was before the changes..
404− let old_state = std::mem::replace(&mut view.doc.old_state, view.doc.state.clone());
424+ let old_state = std::mem::replace(&mut cx.view.doc.old_state, cx.view.doc.state.clone());
405 // TODO: take transaction by value?
425 // TODO: take transaction by value?
426+ cx.view
427+ .doc
428+ .history
406− view.doc.history.commit_revision(&transaction, &old_state);
429+ .commit_revision(&transaction, &old_state);
407
430
408 // TODO: notify LSP of changes
431 // TODO: notify LSP of changes
409}
432}
410
433
411−pub fn normal_mode(view: &mut View, _count: usize) {
434+pub fn normal_mode(cx: &mut Context) {
412− view.doc.mode = Mode::Normal;
435+ cx.view.doc.mode = Mode::Normal;
413
436
414− append_changes_to_history(view);
437+ append_changes_to_history(cx);
415
438
416 // if leaving append mode, move cursor back by 1
439 // if leaving append mode, move cursor back by 1
417− if view.doc.restore_cursor {
440+ if cx.view.doc.restore_cursor {
418− let text = &view.doc.text().slice(..);
441+ let text = &cx.view.doc.text().slice(..);
419− let selection = view.doc.selection().transform(|range| {
442+ let selection = cx.view.doc.selection().transform(|range| {
420 Range::new(
443 Range::new(
421 range.from(),
444 range.from(),
422 graphemes::prev_grapheme_boundary(text, range.to()),
445 graphemes::prev_grapheme_boundary(text, range.to()),
423 )
446 )
424 });
447 });
425− view.doc.set_selection(selection);
448+ cx.view.doc.set_selection(selection);
426
449
427− view.doc.restore_cursor = false;
450+ cx.view.doc.restore_cursor = false;
428 }
451 }
429}
452}
430
453
431−pub fn goto_mode(view: &mut View, _count: usize) {
454+pub fn goto_mode(cx: &mut Context) {
432− view.doc.mode = Mode::Goto;
455+ cx.view.doc.mode = Mode::Goto;
433}
456}
434
457
435// NOTE: Transactions in this module get appended to history when we switch back to normal mode.
458// NOTE: Transactions in this module get appended to history when we switch back to normal mode.
436pub mod insert {
459pub mod insert {
437 use super::*;
460 use super::*;
438 // TODO: insert means add text just before cursor, on exit we should be on the last letter.
461 // TODO: insert means add text just before cursor, on exit we should be on the last letter.
439− pub fn insert_char(view: &mut View, c: char) {
462+ pub fn insert_char(cx: &mut Context, c: char) {
440 let c = Tendril::from_char(c);
463 let c = Tendril::from_char(c);
441− let transaction = Transaction::insert(&view.doc.state, c);
464+ let transaction = Transaction::insert(&cx.view.doc.state, c);
442
465
443− view.doc.apply(&transaction);
466+ cx.view.doc.apply(&transaction);
444 }
467 }
445
468
446− pub fn insert_tab(view: &mut View, _count: usize) {
469+ pub fn insert_tab(cx: &mut Context) {
447− insert_char(view, '\t');
470+ insert_char(cx, '\t');
448 }
471 }
449
472
450− pub fn insert_newline(view: &mut View, _count: usize) {
473+ pub fn insert_newline(cx: &mut Context) {
451− let transaction = Transaction::change_by_selection(&view.doc.state, |range| {
474+ let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
452 let indent_level = helix_core::indent::suggested_indent_for_pos(
475 let indent_level = helix_core::indent::suggested_indent_for_pos(
453− view.doc.syntax.as_ref(),
476+ cx.view.doc.syntax.as_ref(),
454− &view.doc.state,
477+ &cx.view.doc.state,
455 range.head,
478 range.head,
456 );
479 );
457 let indent = " ".repeat(TAB_WIDTH).repeat(indent_level);
480 let indent = " ".repeat(TAB_WIDTH).repeat(indent_level);
@@ -460,32 +483,32 @@ pub mod insert {
460 text.push_str(&indent);
483 text.push_str(&indent);
461 (range.head, range.head, Some(text.into()))
484 (range.head, range.head, Some(text.into()))
462 });
485 });
463− view.doc.apply(&transaction);
486+ cx.view.doc.apply(&transaction);
464 }
487 }
465
488
466 // TODO: handle indent-aware delete
489 // TODO: handle indent-aware delete
467− pub fn delete_char_backward(view: &mut View, count: usize) {
490+ pub fn delete_char_backward(cx: &mut Context) {
468− let text = &view.doc.text().slice(..);
491+ let text = &cx.view.doc.text().slice(..);
469− let transaction = Transaction::change_by_selection(&view.doc.state, |range| {
492+ let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
470 (
493 (
471− graphemes::nth_prev_grapheme_boundary(text, range.head, count),
494+ graphemes::nth_prev_grapheme_boundary(text, range.head, cx.count),
472 range.head,
495 range.head,
473 None,
496 None,
474 )
497 )
475 });
498 });
476− view.doc.apply(&transaction);
499+ cx.view.doc.apply(&transaction);
477 }
500 }
478
501
479− pub fn delete_char_forward(view: &mut View, count: usize) {
502+ pub fn delete_char_forward(cx: &mut Context) {
480− let text = &view.doc.text().slice(..);
503+ let text = &cx.view.doc.text().slice(..);
481− let transaction = Transaction::change_by_selection(&view.doc.state, |range| {
504+ let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
482 (
505 (
483 range.head,
506 range.head,
484− graphemes::nth_next_grapheme_boundary(text, range.head, count),
507+ graphemes::nth_next_grapheme_boundary(text, range.head, cx.count),
485 None,
508 None,
486 )
509 )
487 });
510 });
488− view.doc.apply(&transaction);
511+ cx.view.doc.apply(&transaction);
489 }
512 }
490}
513}
491
514
@@ -495,31 +518,32 @@ pub fn insert_char_prompt(prompt: &mut Prompt, c: char) {
495
518
496// Undo / Redo
519// Undo / Redo
497
520
498−pub fn undo(view: &mut View, _count: usize) {
521+pub fn undo(cx: &mut Context) {
499− if let Some(revert) = view.doc.history.undo() {
522+ if let Some(revert) = cx.view.doc.history.undo() {
500− view.doc.version += 1;
523+ cx.view.doc.version += 1;
501− view.doc.apply(&revert);
524+ cx.view.doc.apply(&revert);
502 }
525 }
503
526
504 // TODO: each command could simply return a Option<transaction>, then the higher level handles storing it?
527 // TODO: each command could simply return a Option<transaction>, then the higher level handles storing it?
505}
528}
506
529
507−pub fn redo(view: &mut View, _count: usize) {
530+pub fn redo(cx: &mut Context) {
508− if let Some(transaction) = view.doc.history.redo() {
531+ if let Some(transaction) = cx.view.doc.history.redo() {
509− view.doc.version += 1;
532+ cx.view.doc.version += 1;
510− view.doc.apply(&transaction);
533+ cx.view.doc.apply(&transaction);
511 }
534 }
512}
535}
513
536
514// Yank / Paste
537// Yank / Paste
515
538
516−pub fn yank(view: &mut View, _count: usize) {
539+pub fn yank(cx: &mut Context) {
517 // TODO: should selections be made end inclusive?
540 // TODO: should selections be made end inclusive?
518− let values = view
541+ let values = cx
542+ .view
519 .doc
543 .doc
520 .state
544 .state
521 .selection()
545 .selection()
522− .fragments(&view.doc.text().slice(..))
546+ .fragments(&cx.view.doc.text().slice(..))
523 .map(|cow| cow.into_owned())
547 .map(|cow| cow.into_owned())
524 .collect();
548 .collect();
525
549
@@ -528,7 +552,7 @@ pub fn yank(view: &mut View, _count: usize) {
528 register::set(reg, values);
552 register::set(reg, values);
529}
553}
530
554
531−pub fn paste(view: &mut View, _count: usize) {
555+pub fn paste(cx: &mut Context) {
532 // TODO: allow specifying reg
556 // TODO: allow specifying reg
533 let reg = '"';
557 let reg = '"';
534 if let Some(values) = register::get(reg) {
558 if let Some(values) = register::get(reg) {
@@ -560,19 +584,19 @@ pub fn paste(view: &mut View, _count: usize) {
560 let transaction = if linewise {
584 let transaction = if linewise {
561 // paste on the next line
585 // paste on the next line
562 // TODO: can simply take a range + modifier and compute the right pos without ifs
586 // TODO: can simply take a range + modifier and compute the right pos without ifs
563− let text = view.doc.text();
587+ let text = cx.view.doc.text();
564− Transaction::change_by_selection(&view.doc.state, |range| {
588+ Transaction::change_by_selection(&cx.view.doc.state, |range| {
565 let line_end = text.line_to_char(text.char_to_line(range.head) + 1);
589 let line_end = text.line_to_char(text.char_to_line(range.head) + 1);
566 (line_end, line_end, Some(values.next().unwrap()))
590 (line_end, line_end, Some(values.next().unwrap()))
567 })
591 })
568 } else {
592 } else {
569− Transaction::change_by_selection(&view.doc.state, |range| {
593+ Transaction::change_by_selection(&cx.view.doc.state, |range| {
570 (range.head + 1, range.head + 1, Some(values.next().unwrap()))
594 (range.head + 1, range.head + 1, Some(values.next().unwrap()))
571 })
595 })
572 };
596 };
573
597
574− view.doc.apply(&transaction);
598+ cx.view.doc.apply(&transaction);
575− append_changes_to_history(view);
599+ append_changes_to_history(cx);
576 }
600 }
577}
601}
578
602
@@ -593,29 +617,29 @@ fn get_lines(view: &View) -> Vec<usize> {
593 lines
617 lines
594}
618}
595
619
596−pub fn indent(view: &mut View, _count: usize) {
620+pub fn indent(cx: &mut Context) {
597− let lines = get_lines(view);
621+ let lines = get_lines(cx.view);
598
622
599 // Indent by one level
623 // Indent by one level
600 let indent = Tendril::from(" ".repeat(TAB_WIDTH));
624 let indent = Tendril::from(" ".repeat(TAB_WIDTH));
601
625
602 let transaction = Transaction::change(
626 let transaction = Transaction::change(
603− &view.doc.state,
627+ &cx.view.doc.state,
604 lines.into_iter().map(|line| {
628 lines.into_iter().map(|line| {
605− let pos = view.doc.text().line_to_char(line);
629+ let pos = cx.view.doc.text().line_to_char(line);
606 (pos, pos, Some(indent.clone()))
630 (pos, pos, Some(indent.clone()))
607 }),
631 }),
608 );
632 );
609− view.doc.apply(&transaction);
633+ cx.view.doc.apply(&transaction);
610− append_changes_to_history(view);
634+ append_changes_to_history(cx);
611}
635}
612
636
613−pub fn unindent(view: &mut View, _count: usize) {
637+pub fn unindent(cx: &mut Context) {
614− let lines = get_lines(view);
638+ let lines = get_lines(cx.view);
615 let mut changes = Vec::with_capacity(lines.len());
639 let mut changes = Vec::with_capacity(lines.len());
616
640
617 for line_idx in lines {
641 for line_idx in lines {
618− let line = view.doc.text().line(line_idx);
642+ let line = cx.view.doc.text().line(line_idx);
619 let mut width = 0;
643 let mut width = 0;
620
644
621 for ch in line.chars() {
645 for ch in line.chars() {
@@ -631,18 +655,18 @@ pub fn unindent(view: &mut View, _count: usize) {
631 }
655 }
632
656
633 if width > 0 {
657 if width > 0 {
634− let start = view.doc.text().line_to_char(line_idx);
658+ let start = cx.view.doc.text().line_to_char(line_idx);
635 changes.push((start, start + width, None))
659 changes.push((start, start + width, None))
636 }
660 }
637 }
661 }
638
662
639− let transaction = Transaction::change(&view.doc.state, changes.into_iter());
663+ let transaction = Transaction::change(&cx.view.doc.state, changes.into_iter());
640
664
641− view.doc.apply(&transaction);
665+ cx.view.doc.apply(&transaction);
642− append_changes_to_history(view);
666+ append_changes_to_history(cx);
643}
667}
644
668
645−pub fn indent_selection(_view: &mut View, _count: usize) {
669+pub fn indent_selection(_cx: &mut Context) {
646 // loop over each line and recompute proper indentation
670 // loop over each line and recompute proper indentation
647 unimplemented!()
671 unimplemented!()
648}
672}