@@ -18,57 +18,66 @@ use helix_view::{
21−pub struct Context<'a, 'b> {
21+pub struct Context<'a> {
22 pub count: usize,
22 pub count: usize,
23− pub view: &'a mut View,
23+ pub editor: &'a mut Editor,
24− pub executor: &'a smol::Executor<'b>,
24+ pub executor: &'static smol::Executor<'static>,
25− pub language_servers: &'a helix_lsp::Registry,
27 pub callback: Option<crate::compositor::Callback>,
26 pub callback: Option<crate::compositor::Callback>,
29+impl<'a> Context<'a> {
30+ pub fn view(&mut self) -> &mut View {
31+ self.editor.view_mut()
30/// A command is a function that takes the current state and a count, and does a side-effect on the
35/// A command is a function that takes the current state and a count, and does a side-effect on the
31/// state (usually by creating and applying a transaction).
36/// state (usually by creating and applying a transaction).
32pub type Command = fn(cx: &mut Context);
37pub type Command = fn(cx: &mut Context);
34pub fn move_char_left(cx: &mut Context) {
39pub fn move_char_left(cx: &mut Context) {
40+ let count = cx.count;
35 let selection =
41 let selection =
39− .move_selection(Direction::Backward, Granularity::Character, cx.count);
45+ .move_selection(Direction::Backward, Granularity::Character, count);
40− cx.view.doc.set_selection(selection);
46+ cx.view().doc.set_selection(selection);
43pub fn move_char_right(cx: &mut Context) {
49pub fn move_char_right(cx: &mut Context) {
50+ let count = cx.count;
44 let selection =
51 let selection =
48− .move_selection(Direction::Forward, Granularity::Character, cx.count);
55+ .move_selection(Direction::Forward, Granularity::Character, count);
49− cx.view.doc.set_selection(selection);
56+ cx.view().doc.set_selection(selection);
52pub fn move_line_up(cx: &mut Context) {
59pub fn move_line_up(cx: &mut Context) {
60+ let count = cx.count;
53 let selection =
61 let selection =
57− .move_selection(Direction::Backward, Granularity::Line, cx.count);
65+ .move_selection(Direction::Backward, Granularity::Line, count);
58− cx.view.doc.set_selection(selection);
66+ cx.view().doc.set_selection(selection);
61pub fn move_line_down(cx: &mut Context) {
69pub fn move_line_down(cx: &mut Context) {
70+ let count = cx.count;
62 let selection =
71 let selection =
66− .move_selection(Direction::Forward, Granularity::Line, cx.count);
75+ .move_selection(Direction::Forward, Granularity::Line, count);
67− cx.view.doc.set_selection(selection);
76+ cx.view().doc.set_selection(selection);
70pub fn move_line_end(cx: &mut Context) {
79pub fn move_line_end(cx: &mut Context) {
71− let lines = selection_lines(&cx.view.doc.state);
80+ let lines = selection_lines(&cx.view().doc.state);
73 let positions = lines
82 let positions = lines
74 .into_iter()
83 .into_iter()
@@ -77,75 +86,85 @@ pub fn move_line_end(cx: &mut Context) {
78 // Line end is pos at the start of next line - 1
87 // Line end is pos at the start of next line - 1
79 // subtract another 1 because the line ends with \n
88 // subtract another 1 because the line ends with \n
80− cx.view.doc.text().line_to_char(index + 1).saturating_sub(2)
92+ .line_to_char(index + 1)
82 .map(|pos| Range::new(pos, pos));
95 .map(|pos| Range::new(pos, pos));
84 let selection = Selection::new(positions.collect(), 0);
97 let selection = Selection::new(positions.collect(), 0);
86− cx.view.doc.set_selection(selection);
99+ cx.view().doc.set_selection(selection);
89pub fn move_line_start(cx: &mut Context) {
102pub fn move_line_start(cx: &mut Context) {
90− let lines = selection_lines(&cx.view.doc.state);
103+ let lines = selection_lines(&cx.view().doc.state);
92 let positions = lines
105 let positions = lines
93 .into_iter()
106 .into_iter()
94 .map(|index| {
107 .map(|index| {
95 // adjust all positions to the start of the line.
108 // adjust all positions to the start of the line.
96− cx.view.doc.text().line_to_char(index)
109+ cx.view().doc.text().line_to_char(index)
98 .map(|pos| Range::new(pos, pos));
111 .map(|pos| Range::new(pos, pos));
100 let selection = Selection::new(positions.collect(), 0);
113 let selection = Selection::new(positions.collect(), 0);
102− cx.view.doc.set_selection(selection);
115+ cx.view().doc.set_selection(selection);
105pub fn move_next_word_start(cx: &mut Context) {
118pub fn move_next_word_start(cx: &mut Context) {
119+ let count = cx.count;
120+ let view = cx.view();
106− let pos = cx.view.doc.state.move_pos(
121+ let pos = view.doc.state.move_pos(
107− cx.view.doc.selection().cursor(),
122+ view.doc.selection().cursor(),
108 Direction::Forward,
123 Direction::Forward,
109 Granularity::Word,
124 Granularity::Word,
110− cx.count,
125+ count,
113− cx.view.doc.set_selection(Selection::point(pos));
128+ view.doc.set_selection(Selection::point(pos));
116pub fn move_prev_word_start(cx: &mut Context) {
131pub fn move_prev_word_start(cx: &mut Context) {
132+ let count = cx.count;
133+ let view = cx.view();
117− let pos = cx.view.doc.state.move_pos(
134+ let pos = view.doc.state.move_pos(
118− cx.view.doc.selection().cursor(),
135+ view.doc.selection().cursor(),
119 Direction::Backward,
136 Direction::Backward,
120 Granularity::Word,
137 Granularity::Word,
121− cx.count,
138+ count,
124− cx.view.doc.set_selection(Selection::point(pos));
141+ view.doc.set_selection(Selection::point(pos));
127pub fn move_next_word_end(cx: &mut Context) {
144pub fn move_next_word_end(cx: &mut Context) {
145+ let count = cx.count;
146+ let view = cx.view();
128 let pos = State::move_next_word_end(
147 let pos = State::move_next_word_end(
129− &cx.view.doc.text().slice(..),
148+ &view.doc.text().slice(..),
130− cx.view.doc.selection().cursor(),
149+ view.doc.selection().cursor(),
131− cx.count,
150+ count,
134− cx.view.doc.set_selection(Selection::point(pos));
153+ view.doc.set_selection(Selection::point(pos));
137pub fn move_file_start(cx: &mut Context) {
156pub fn move_file_start(cx: &mut Context) {
138− cx.view.doc.set_selection(Selection::point(0));
157+ cx.view().doc.set_selection(Selection::point(0));
140− cx.view.doc.mode = Mode::Normal;
159+ cx.view().doc.mode = Mode::Normal;
143pub fn move_file_end(cx: &mut Context) {
162pub fn move_file_end(cx: &mut Context) {
144− let text = &cx.view.doc.text();
163+ let text = &cx.view().doc.text();
145 let last_line = text.line_to_char(text.len_lines().saturating_sub(2));
164 let last_line = text.line_to_char(text.len_lines().saturating_sub(2));
146− cx.view.doc.set_selection(Selection::point(last_line));
165+ cx.view().doc.set_selection(Selection::point(last_line));
148− cx.view.doc.mode = Mode::Normal;
167+ cx.view().doc.mode = Mode::Normal;
151pub fn check_cursor_in_view(view: &View) -> bool {
170pub fn check_cursor_in_view(view: &View) -> bool {
@@ -160,93 +179,98 @@ pub fn check_cursor_in_view(view: &View) -> bool {
162pub fn page_up(cx: &mut Context) {
181pub fn page_up(cx: &mut Context) {
182+ let view = cx.view();
163− if cx.view.first_line < PADDING {
183+ if view.first_line < PADDING {
167− cx.view.first_line = cx.view.first_line.saturating_sub(cx.view.size.1 as usize);
187+ view.first_line = view.first_line.saturating_sub(view.size.1 as usize);
169− if !check_cursor_in_view(cx.view) {
189+ if !check_cursor_in_view(view) {
170− let text = cx.view.doc.text();
190+ let text = view.doc.text();
171− let pos = text.line_to_char(cx.view.last_line().saturating_sub(PADDING));
191+ let pos = text.line_to_char(view.last_line().saturating_sub(PADDING));
172− cx.view.doc.set_selection(Selection::point(pos));
192+ view.doc.set_selection(Selection::point(pos));
176pub fn page_down(cx: &mut Context) {
196pub fn page_down(cx: &mut Context) {
197+ let view = cx.view();
177− cx.view.first_line += cx.view.size.1 as usize + PADDING;
198+ view.first_line += view.size.1 as usize + PADDING;
179− if cx.view.first_line < cx.view.doc.text().len_lines() {
200+ if view.first_line < view.doc.text().len_lines() {
180− let text = cx.view.doc.text();
201+ let text = view.doc.text();
181− let pos = text.line_to_char(cx.view.first_line as usize);
202+ let pos = text.line_to_char(view.first_line as usize);
182− cx.view.doc.set_selection(Selection::point(pos));
203+ view.doc.set_selection(Selection::point(pos));
186pub fn half_page_up(cx: &mut Context) {
207pub fn half_page_up(cx: &mut Context) {
208+ let view = cx.view();
187− if cx.view.first_line < PADDING {
209+ if view.first_line < PADDING {
191− cx.view.first_line = cx
194− .saturating_sub(cx.view.size.1 as usize / 2);
213+ view.first_line = view.first_line.saturating_sub(view.size.1 as usize / 2);
196− if !check_cursor_in_view(cx.view) {
215+ if !check_cursor_in_view(view) {
197− let text = &cx.view.doc.text();
216+ let text = &view.doc.text();
198− let pos = text.line_to_char(cx.view.last_line() - PADDING);
217+ let pos = text.line_to_char(view.last_line() - PADDING);
199− cx.view.doc.set_selection(Selection::point(pos));
218+ view.doc.set_selection(Selection::point(pos));
203pub fn half_page_down(cx: &mut Context) {
222pub fn half_page_down(cx: &mut Context) {
223+ let view = cx.view();
204− let lines = cx.view.doc.text().len_lines();
224+ let lines = view.doc.text().len_lines();
205− if cx.view.first_line < lines.saturating_sub(cx.view.size.1 as usize) {
225+ if view.first_line < lines.saturating_sub(view.size.1 as usize) {
206− cx.view.first_line += cx.view.size.1 as usize / 2;
226+ view.first_line += view.size.1 as usize / 2;
208− if !check_cursor_in_view(cx.view) {
228+ if !check_cursor_in_view(view) {
209− let text = cx.view.doc.text();
229+ let text = view.doc.text();
210− let pos = text.line_to_char(cx.view.first_line as usize);
230+ let pos = text.line_to_char(view.first_line as usize);
211− cx.view.doc.set_selection(Selection::point(pos));
231+ view.doc.set_selection(Selection::point(pos));
214// avoid select by default by having a visual mode switch that makes movements into selects
234// avoid select by default by having a visual mode switch that makes movements into selects
216pub fn extend_char_left(cx: &mut Context) {
236pub fn extend_char_left(cx: &mut Context) {
237+ let count = cx.count;
238+ let view = cx.view();
217 let selection =
239 let selection =
218− cx.view
240+ view.doc
221− .extend_selection(Direction::Backward, Granularity::Character, cx.count);
242+ .extend_selection(Direction::Backward, Granularity::Character, count);
222− cx.view.doc.set_selection(selection);
243+ view.doc.set_selection(selection);
225pub fn extend_char_right(cx: &mut Context) {
246pub fn extend_char_right(cx: &mut Context) {
247+ let count = cx.count;
248+ let view = cx.view();
226 let selection =
249 let selection =
227− cx.view
250+ view.doc
230− .extend_selection(Direction::Forward, Granularity::Character, cx.count);
252+ .extend_selection(Direction::Forward, Granularity::Character, count);
231− cx.view.doc.set_selection(selection);
253+ view.doc.set_selection(selection);
234pub fn extend_line_up(cx: &mut Context) {
256pub fn extend_line_up(cx: &mut Context) {
235− let selection =
257+ let count = cx.count;
236− cx.view
258+ let view = cx.view();
259+ let selection = view
239− .extend_selection(Direction::Backward, Granularity::Line, cx.count);
262+ .extend_selection(Direction::Backward, Granularity::Line, count);
240− cx.view.doc.set_selection(selection);
263+ view.doc.set_selection(selection);
243pub fn extend_line_down(cx: &mut Context) {
266pub fn extend_line_down(cx: &mut Context) {
244− let selection =
267+ let count = cx.count;
245− cx.view
268+ let view = cx.view();
269+ let selection = view
248− .extend_selection(Direction::Forward, Granularity::Line, cx.count);
272+ .extend_selection(Direction::Forward, Granularity::Line, count);
249− cx.view.doc.set_selection(selection);
273+ view.doc.set_selection(selection);
252pub fn split_selection(cx: &mut Context) {
276pub fn split_selection(cx: &mut Context) {
@@ -263,7 +287,7 @@ pub fn split_selection(cx: &mut Context) {
263 // # update state
287 // # update state
266− let snapshot = cx.view.doc.state.clone();
290+ let snapshot = cx.view().doc.state.clone();
268 let prompt = Prompt::new(
292 let prompt = Prompt::new(
269 "split:".to_string(),
293 "split:".to_string(),
@@ -272,7 +296,7 @@ pub fn split_selection(cx: &mut Context) {
272 match event {
296 match event {
273 PromptEvent::Abort => {
297 PromptEvent::Abort => {
274 // revert state
298 // revert state
275− let view = editor.view_mut().unwrap();
299+ let view = editor.view_mut();
276 view.doc.state = snapshot.clone();
300 view.doc.state = snapshot.clone();
278 PromptEvent::Validate => {
302 PromptEvent::Validate => {
@@ -281,7 +305,7 @@ pub fn split_selection(cx: &mut Context) {
281 PromptEvent::Update => {
305 PromptEvent::Update => {
282 match Regex::new(input) {
306 match Regex::new(input) {
283 Ok(regex) => {
307 Ok(regex) => {
284− let view = editor.view_mut().unwrap();
308+ let view = editor.view_mut();
286 // revert state to what it was before the last update
310 // revert state to what it was before the last update
287 view.doc.state = snapshot.clone();
311 view.doc.state = snapshot.clone();
@@ -306,32 +330,33 @@ pub fn split_selection(cx: &mut Context) {
308pub fn split_selection_on_newline(cx: &mut Context) {
332pub fn split_selection_on_newline(cx: &mut Context) {
333+ let view = cx.view();
309− let text = &cx.view.doc.text().slice(..);
334+ let text = &view.doc.text().slice(..);
310 // only compile the regex once
335 // only compile the regex once
311 #[allow(clippy::trivial_regex)]
336 #[allow(clippy::trivial_regex)]
312 static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\n").unwrap());
337 static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\n").unwrap());
313− let selection = selection::split_on_matches(text, cx.view.doc.selection(), ®EX);
338+ let selection = selection::split_on_matches(text, view.doc.selection(), ®EX);
314− cx.view.doc.set_selection(selection);
339+ view.doc.set_selection(selection);
317pub fn select_line(cx: &mut Context) {
342pub fn select_line(cx: &mut Context) {
318 // TODO: count
343 // TODO: count
319− let pos = cx.view.doc.selection().primary();
344+ let pos = cx.view().doc.selection().primary();
320− let text = cx.view.doc.text();
345+ let text = cx.view().doc.text();
321 let line = text.char_to_line(pos.head);
346 let line = text.char_to_line(pos.head);
322 let start = text.line_to_char(line);
347 let start = text.line_to_char(line);
323 let end = text.line_to_char(line + 1).saturating_sub(1);
348 let end = text.line_to_char(line + 1).saturating_sub(1);
325− cx.view.doc.set_selection(Selection::single(start, end));
350+ cx.view().doc.set_selection(Selection::single(start, end));
328// heuristic: append changes to history after each command, unless we're in insert mode
353// heuristic: append changes to history after each command, unless we're in insert mode
330fn _delete_selection(cx: &mut Context) {
355fn _delete_selection(cx: &mut Context) {
331− let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
356+ let transaction = Transaction::change_by_selection(&cx.view().doc.state, |range| {
332 (range.from(), range.to() + 1, None)
357 (range.from(), range.to() + 1, None)
334− cx.view.doc.apply(&transaction);
359+ cx.view().doc.apply(&transaction);
337pub fn delete_selection(cx: &mut Context) {
362pub fn delete_selection(cx: &mut Context) {
@@ -347,26 +372,26 @@ pub fn change_selection(cx: &mut Context) {
348pub fn collapse_selection(cx: &mut Context) {
373pub fn collapse_selection(cx: &mut Context) {
349 let selection = cx
374 let selection = cx
352 .selection()
377 .selection()
353 .transform(|range| Range::new(range.head, range.head));
378 .transform(|range| Range::new(range.head, range.head));
355− cx.view.doc.set_selection(selection);
380+ cx.view().doc.set_selection(selection);
358pub fn flip_selections(cx: &mut Context) {
383pub fn flip_selections(cx: &mut Context) {
359 let selection = cx
384 let selection = cx
362 .selection()
387 .selection()
363 .transform(|range| Range::new(range.head, range.anchor));
388 .transform(|range| Range::new(range.head, range.anchor));
365− cx.view.doc.set_selection(selection);
390+ cx.view().doc.set_selection(selection);
368fn enter_insert_mode(cx: &mut Context) {
393fn enter_insert_mode(cx: &mut Context) {
369− cx.view.doc.mode = Mode::Insert;
394+ cx.view().doc.mode = Mode::Insert;
371 // TODO: store selection for undo
396 // TODO: store selection for undo
@@ -376,34 +401,36 @@ pub fn insert_mode(cx: &mut Context) {
376 enter_insert_mode(cx);
401 enter_insert_mode(cx);
378 let selection = cx
403 let selection = cx
381 .selection()
406 .selection()
382 .transform(|range| Range::new(range.to(), range.from()));
407 .transform(|range| Range::new(range.to(), range.from()));
383− cx.view.doc.set_selection(selection);
408+ cx.view().doc.set_selection(selection);
386// inserts at the end of each selection
411// inserts at the end of each selection
387pub fn append_mode(cx: &mut Context) {
412pub fn append_mode(cx: &mut Context) {
388 enter_insert_mode(cx);
413 enter_insert_mode(cx);
414+ let view = cx.view();
389− cx.view.doc.restore_cursor = true;
415+ view.doc.restore_cursor = true;
391 // TODO: as transaction
417 // TODO: as transaction
392− let text = &cx.view.doc.text().slice(..);
418+ let text = &view.doc.text().slice(..);
393− let selection = cx.view.doc.selection().transform(|range| {
419+ let selection = view.doc.selection().transform(|range| {
394 // TODO: to() + next char
420 // TODO: to() + next char
395 Range::new(
421 Range::new(
396 range.from(),
422 range.from(),
397 graphemes::next_grapheme_boundary(text, range.to()),
423 graphemes::next_grapheme_boundary(text, range.to()),
400− cx.view.doc.set_selection(selection);
426+ view.doc.set_selection(selection);
403// TODO: I, A, o and O can share a lot of the primitives.
429// TODO: I, A, o and O can share a lot of the primitives.
404pub fn command_mode(cx: &mut Context) {
430pub fn command_mode(cx: &mut Context) {
431+ let executor = cx.executor;
405 cx.callback = Some(Box::new(
432 cx.callback = Some(Box::new(
406− |compositor: &mut Compositor, editor: &mut Editor| {
433+ move |compositor: &mut Compositor, editor: &mut Editor| {
407 let prompt = Prompt::new(
434 let prompt = Prompt::new(
408 ":".to_owned(),
435 ":".to_owned(),
409 |_input: &str| {
436 |_input: &str| {
@@ -446,7 +473,7 @@ pub fn command_mode(cx: &mut Context) {
446 .filter(|command| command.contains(_input))
473 .filter(|command| command.contains(_input))
447 .collect()
474 .collect()
448 }, // completion
475 }, // completion
449− |editor: &mut Editor, input: &str, event: PromptEvent| {
476+ move |editor: &mut Editor, input: &str, event: PromptEvent| {
450 if event != PromptEvent::Validate {
477 if event != PromptEvent::Validate {
@@ -456,9 +483,8 @@ pub fn command_mode(cx: &mut Context) {
456 match *parts.as_slice() {
483 match *parts.as_slice() {
457 ["q"] => editor.should_close = true,
484 ["q"] => editor.should_close = true,
458 ["o", path] => {
485 ["o", path] => {
459− // TODO: make view()/view_mut() always contain a view.
460− let size = editor.view().unwrap().size;
486+ let size = editor.view().size;
461− editor.open(path.into(), size);
487+ editor.open(path.into(), size, executor);
@@ -469,9 +495,9 @@ pub fn command_mode(cx: &mut Context) {
471pub fn file_picker(cx: &mut Context) {
497pub fn file_picker(cx: &mut Context) {
498+ let picker = ui::file_picker("./", cx.executor);
472 cx.callback = Some(Box::new(
499 cx.callback = Some(Box::new(
473 |compositor: &mut Compositor, editor: &mut Editor| {
500 |compositor: &mut Compositor, editor: &mut Editor| {
474− let picker = ui::file_picker("./");
475 compositor.push(Box::new(picker));
501 compositor.push(Box::new(picker));
@@ -519,21 +545,23 @@ pub fn append_to_line(cx: &mut Context) {
519pub fn open_below(cx: &mut Context) {
545pub fn open_below(cx: &mut Context) {
520 enter_insert_mode(cx);
546 enter_insert_mode(cx);
548+ let view = cx.view();
522− let lines = selection_lines(&cx.view.doc.state);
550+ let lines = selection_lines(&view.doc.state);
524 let positions: Vec<_> = lines
552 let positions: Vec<_> = lines
525 .into_iter()
553 .into_iter()
526 .map(|index| {
554 .map(|index| {
527 // adjust all positions to the end of the line/start of the next one.
555 // adjust all positions to the end of the line/start of the next one.
528− cx.view.doc.text().line_to_char(index + 1)
556+ view.doc.text().line_to_char(index + 1)
530 .collect();
558 .collect();
532 let changes = positions.iter().copied().map(|index| {
560 let changes = positions.iter().copied().map(|index| {
533 // TODO: share logic with insert_newline for indentation
561 // TODO: share logic with insert_newline for indentation
534 let indent_level = helix_core::indent::suggested_indent_for_pos(
562 let indent_level = helix_core::indent::suggested_indent_for_pos(
535− cx.view.doc.syntax.as_ref(),
563+ view.doc.syntax.as_ref(),
536− &cx.view.doc.state,
564+ &view.doc.state,
@@ -567,42 +595,42 @@ pub fn open_below(cx: &mut Context) {
570− let transaction = Transaction::change(&cx.view.doc.state, changes).with_selection(selection);
598+ let transaction = Transaction::change(&view.doc.state, changes).with_selection(selection);
572− cx.view.doc.apply(&transaction);
600+ view.doc.apply(&transaction);
575// O inserts a new line before each line with a selection
603// O inserts a new line before each line with a selection
577fn append_changes_to_history(cx: &mut Context) {
605fn append_changes_to_history(cx: &mut Context) {
578− if cx.view.doc.changes.is_empty() {
606+ if cx.view().doc.changes.is_empty() {
582 // TODO: change -> change -> undo -> change -> change fails, probably old_state needs reset
610 // TODO: change -> change -> undo -> change -> change fails, probably old_state needs reset
584− let new_changeset = ChangeSet::new(cx.view.doc.text());
612+ let new_changeset = ChangeSet::new(cx.view().doc.text());
585− let changes = std::mem::replace(&mut cx.view.doc.changes, new_changeset);
613+ let changes = std::mem::replace(&mut cx.view().doc.changes, new_changeset);
586 // Instead of doing this messy merge we could always commit, and based on transaction
614 // Instead of doing this messy merge we could always commit, and based on transaction
587 // annotations either add a new layer or compose into the previous one.
615 // annotations either add a new layer or compose into the previous one.
588− let transaction = Transaction::from(changes).with_selection(cx.view.doc.selection().clone());
616+ let transaction = Transaction::from(changes).with_selection(cx.view().doc.selection().clone());
590 // increment document version
618 // increment document version
591 // TODO: needs to happen on undo/redo too
619 // TODO: needs to happen on undo/redo too
592− cx.view.doc.version += 1;
620+ cx.view().doc.version += 1;
594 // TODO: trigger lsp/documentDidChange with changes
622 // TODO: trigger lsp/documentDidChange with changes
596 // HAXX: we need to reconstruct the state as it was before the changes..
624 // HAXX: we need to reconstruct the state as it was before the changes..
597 let old_state = cx
625 let old_state = cx
600 .old_state
628 .old_state
602 .expect("no old_state available");
630 .expect("no old_state available");
604 // TODO: take transaction by value?
632 // TODO: take transaction by value?
605− cx.view
633+ cx.view()
608 .commit_revision(&transaction, &old_state);
636 .commit_revision(&transaction, &old_state);
@@ -611,27 +639,29 @@ fn append_changes_to_history(cx: &mut Context) {
613pub fn normal_mode(cx: &mut Context) {
641pub fn normal_mode(cx: &mut Context) {
614− cx.view.doc.mode = Mode::Normal;
642+ cx.view().doc.mode = Mode::Normal;
616 append_changes_to_history(cx);
644 append_changes_to_history(cx);
646+ let view = cx.view();
618 // if leaving append mode, move cursor back by 1
648 // if leaving append mode, move cursor back by 1
619− if cx.view.doc.restore_cursor {
649+ if view.doc.restore_cursor {
620− let text = &cx.view.doc.text().slice(..);
650+ let text = &view.doc.text().slice(..);
621− let selection = cx.view.doc.selection().transform(|range| {
651+ let selection = view.doc.selection().transform(|range| {
622 Range::new(
652 Range::new(
623 range.from(),
653 range.from(),
624 graphemes::prev_grapheme_boundary(text, range.to()),
654 graphemes::prev_grapheme_boundary(text, range.to()),
627− cx.view.doc.set_selection(selection);
657+ view.doc.set_selection(selection);
629− cx.view.doc.restore_cursor = false;
659+ view.doc.restore_cursor = false;
633pub fn goto_mode(cx: &mut Context) {
663pub fn goto_mode(cx: &mut Context) {
634− cx.view.doc.mode = Mode::Goto;
664+ cx.view().doc.mode = Mode::Goto;
637// NOTE: Transactions in this module get appended to history when we switch back to normal mode.
667// NOTE: Transactions in this module get appended to history when we switch back to normal mode.
@@ -640,9 +670,9 @@ pub mod insert {
640 // TODO: insert means add text just before cursor, on exit we should be on the last letter.
670 // TODO: insert means add text just before cursor, on exit we should be on the last letter.
641 pub fn insert_char(cx: &mut Context, c: char) {
671 pub fn insert_char(cx: &mut Context, c: char) {
642 let c = Tendril::from_char(c);
672 let c = Tendril::from_char(c);
643− let transaction = Transaction::insert(&cx.view.doc.state, c);
673+ let transaction = Transaction::insert(&cx.view().doc.state, c);
645− cx.view.doc.apply(&transaction);
675+ cx.view().doc.apply(&transaction);
648 pub fn insert_tab(cx: &mut Context) {
678 pub fn insert_tab(cx: &mut Context) {
@@ -650,10 +680,11 @@ pub mod insert {
652 pub fn insert_newline(cx: &mut Context) {
682 pub fn insert_newline(cx: &mut Context) {
683+ let view = cx.view();
653− let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
684+ let transaction = Transaction::change_by_selection(&view.doc.state, |range| {
654 let indent_level = helix_core::indent::suggested_indent_for_pos(
685 let indent_level = helix_core::indent::suggested_indent_for_pos(
655− cx.view.doc.syntax.as_ref(),
686+ view.doc.syntax.as_ref(),
656− &cx.view.doc.state,
687+ &view.doc.state,
657 range.head,
688 range.head,
@@ -663,32 +694,36 @@ pub mod insert {
663 text.push_str(&indent);
694 text.push_str(&indent);
664 (range.head, range.head, Some(text.into()))
695 (range.head, range.head, Some(text.into()))
666− cx.view.doc.apply(&transaction);
697+ view.doc.apply(&transaction);
669 // TODO: handle indent-aware delete
700 // TODO: handle indent-aware delete
670 pub fn delete_char_backward(cx: &mut Context) {
701 pub fn delete_char_backward(cx: &mut Context) {
702+ let count = cx.count;
703+ let view = cx.view();
671− let text = &cx.view.doc.text().slice(..);
704+ let text = &view.doc.text().slice(..);
672− let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
705+ let transaction = Transaction::change_by_selection(&view.doc.state, |range| {
674− graphemes::nth_prev_grapheme_boundary(text, range.head, cx.count),
707+ graphemes::nth_prev_grapheme_boundary(text, range.head, count),
675 range.head,
708 range.head,
679− cx.view.doc.apply(&transaction);
712+ view.doc.apply(&transaction);
682 pub fn delete_char_forward(cx: &mut Context) {
715 pub fn delete_char_forward(cx: &mut Context) {
716+ let count = cx.count;
717+ let view = cx.view();
683− let text = &cx.view.doc.text().slice(..);
718+ let text = &view.doc.text().slice(..);
684− let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
719+ let transaction = Transaction::change_by_selection(&view.doc.state, |range| {
686 range.head,
721 range.head,
687− graphemes::nth_next_grapheme_boundary(text, range.head, cx.count),
722+ graphemes::nth_next_grapheme_boundary(text, range.head, count),
691− cx.view.doc.apply(&transaction);
726+ view.doc.apply(&transaction);
@@ -702,23 +737,23 @@ pub fn insert_char_prompt(prompt: &mut Prompt, c: char) {
702// storing it?
737// storing it?
704pub fn undo(cx: &mut Context) {
739pub fn undo(cx: &mut Context) {
705− cx.view.doc.undo();
740+ cx.view().doc.undo();
708pub fn redo(cx: &mut Context) {
743pub fn redo(cx: &mut Context) {
709− cx.view.doc.redo();
744+ cx.view().doc.redo();
712// Yank / Paste
747// Yank / Paste
714pub fn yank(cx: &mut Context) {
749pub fn yank(cx: &mut Context) {
715 // TODO: should selections be made end inclusive?
750 // TODO: should selections be made end inclusive?
716− let values = cx
751+ let view = cx.view();
717− .view
752+ let values = view
720 .selection()
755 .selection()
721− .fragments(&cx.view.doc.text().slice(..))
756+ .fragments(&view.doc.text().slice(..))
722 .map(|cow| cow.into_owned())
757 .map(|cow| cow.into_owned())
723 .collect();
758 .collect();
@@ -756,21 +791,23 @@ pub fn paste(cx: &mut Context) {
757 let mut values = values.into_iter().map(Tendril::from).chain(repeat);
792 let mut values = values.into_iter().map(Tendril::from).chain(repeat);
794+ let view = cx.view();
759 let transaction = if linewise {
796 let transaction = if linewise {
760 // paste on the next line
797 // paste on the next line
761 // TODO: can simply take a range + modifier and compute the right pos without ifs
798 // TODO: can simply take a range + modifier and compute the right pos without ifs
762− let text = cx.view.doc.text();
799+ let text = view.doc.text();
763− Transaction::change_by_selection(&cx.view.doc.state, |range| {
800+ Transaction::change_by_selection(&view.doc.state, |range| {
764 let line_end = text.line_to_char(text.char_to_line(range.head) + 1);
801 let line_end = text.line_to_char(text.char_to_line(range.head) + 1);
765 (line_end, line_end, Some(values.next().unwrap()))
802 (line_end, line_end, Some(values.next().unwrap()))
768− Transaction::change_by_selection(&cx.view.doc.state, |range| {
805+ Transaction::change_by_selection(&view.doc.state, |range| {
769 (range.head + 1, range.head + 1, Some(values.next().unwrap()))
806 (range.head + 1, range.head + 1, Some(values.next().unwrap()))
773− cx.view.doc.apply(&transaction);
810+ view.doc.apply(&transaction);
774 append_changes_to_history(cx);
811 append_changes_to_history(cx);
@@ -793,28 +830,30 @@ fn get_lines(view: &View) -> Vec<usize> {
795pub fn indent(cx: &mut Context) {
832pub fn indent(cx: &mut Context) {
796− let lines = get_lines(cx.view);
833+ let lines = get_lines(cx.view());
798 // Indent by one level
835 // Indent by one level
799 let indent = Tendril::from(" ".repeat(TAB_WIDTH));
836 let indent = Tendril::from(" ".repeat(TAB_WIDTH));
838+ let view = cx.view();
801 let transaction = Transaction::change(
840 let transaction = Transaction::change(
802− &cx.view.doc.state,
841+ &view.doc.state,
803 lines.into_iter().map(|line| {
842 lines.into_iter().map(|line| {
804− let pos = cx.view.doc.text().line_to_char(line);
843+ let pos = view.doc.text().line_to_char(line);
805 (pos, pos, Some(indent.clone()))
844 (pos, pos, Some(indent.clone()))
808− cx.view.doc.apply(&transaction);
847+ cx.view().doc.apply(&transaction);
809 append_changes_to_history(cx);
848 append_changes_to_history(cx);
812pub fn unindent(cx: &mut Context) {
851pub fn unindent(cx: &mut Context) {
813− let lines = get_lines(cx.view);
852+ let lines = get_lines(cx.view());
814 let mut changes = Vec::with_capacity(lines.len());
853 let mut changes = Vec::with_capacity(lines.len());
816 for line_idx in lines {
855 for line_idx in lines {
817− let line = cx.view.doc.text().line(line_idx);
856+ let line = cx.view().doc.text().line(line_idx);
818 let mut width = 0;
857 let mut width = 0;
820 for ch in line.chars() {
859 for ch in line.chars() {
@@ -830,14 +869,14 @@ pub fn unindent(cx: &mut Context) {
832 if width > 0 {
871 if width > 0 {
833− let start = cx.view.doc.text().line_to_char(line_idx);
872+ let start = cx.view().doc.text().line_to_char(line_idx);
834 changes.push((start, start + width, None))
873 changes.push((start, start + width, None))
838− let transaction = Transaction::change(&cx.view.doc.state, changes.into_iter());
877+ let transaction = Transaction::change(&cx.view().doc.state, changes.into_iter());
840− cx.view.doc.apply(&transaction);
879+ cx.view().doc.apply(&transaction);
841 append_changes_to_history(cx);
880 append_changes_to_history(cx);
@@ -852,21 +891,25 @@ pub fn save(cx: &mut Context) {
852 // Spawns an async task to actually do the saving. This way we prevent blocking.
891 // Spawns an async task to actually do the saving. This way we prevent blocking.
854 // TODO: handle save errors somehow?
893 // TODO: handle save errors somehow?
855− cx.executor.spawn(cx.view.doc.save()).detach();
894+ cx.executor.spawn(cx.view().doc.save()).detach();
858pub fn completion(cx: &mut Context) {
897pub fn completion(cx: &mut Context) {
859− let language_server = cx.language_servers.get("rust", &cx.executor).unwrap();
898+ let language_server = cx
901+ .get("source.rust", &cx.executor)
860 use log::info;
903 use log::info;
905+ let view = cx.view();
862 // TODO: blocking here is not ideal
907 // TODO: blocking here is not ideal
863− let pos = helix_lsp::util::pos_to_lsp_pos(
909+ helix_lsp::util::pos_to_lsp_pos(&view.doc.text().slice(..), view.doc.selection().cursor());
864− &cx.view.doc.text().slice(..),
865− cx.view.doc.selection().cursor(),
868 // TODO: handle fails
911 // TODO: handle fails
869− let res = smol::block_on(language_server.completion(cx.view.doc.identifier(), pos))
912+ let res = smol::block_on(language_server.completion(cx.view().doc.identifier(), pos))
870 .unwrap_or_default();
913 .unwrap_or_default();
872 // TODO: if no completion, show some message or something
915 // TODO: if no completion, show some message or something
@@ -903,7 +946,7 @@ pub fn completion(cx: &mut Context) {
906− let view = editor.view_mut().unwrap();
949+ let view = editor.view_mut();
907 let transaction =
950 let transaction =
908 util::generate_transaction_from_edits(&view.doc.state, vec![edit]);
951 util::generate_transaction_from_edits(&view.doc.state, vec![edit]);
909 view.doc.apply(&transaction);
952 view.doc.apply(&transaction);