on master help sign in
Configurable keys 2 (Mapping keys to commands) (#268)
f7e00cfPabloMansanet
book/src/SUMMARY.md+1
@@ -4,4 +4,5 @@
4- [Usage](./usage.md)
4- [Usage](./usage.md)
5- [Configuration](./configuration.md)
5- [Configuration](./configuration.md)
6 - [Keymap](./keymap.md)
6 - [Keymap](./keymap.md)
7+ - [Key Remapping](./remapping.md)
7 - [Hooks](./hooks.md)
8 - [Hooks](./hooks.md)
book/src/remapping.mdnew file+48
@@ -0,0 +1,48 @@
1+# Key Remapping
2+
3+One-way key remapping is temporarily supported via a simple TOML configuration
4+file. (More powerful solutions such as rebinding via commands will be
5+available in the feature).
6+
7+To remap keys, write a `config.toml` file in your `helix` configuration
8+directory (default `~/.config/helix` in Linux systems) with a structure like
9+this:
10+
11+```toml
12+# At most one section each of 'keys.normal', 'keys.insert' and 'keys.select'
13+[keys.normal]
14+a = "move_char_left" # Maps the 'a' key to the move_char_left command
15+w = "move_line_up" # Maps the 'w' key move_line_up
16+C-S-esc = "select_line" # Maps Control-Shift-Escape to select_line
17+
18+[keys.insert]
19+A-x = "normal_mode" # Maps Alt-X to enter normal mode
20+```
21+
22+Control, Shift and Alt modifiers are encoded respectively with the prefixes
23+`C-`, `S-` and `A-`. Special keys are encoded as follows:
24+
25+* Backspace => "backspace"
26+* Space => "space"
27+* Return/Enter => "ret"
28+* < => "lt"
29+* > => "gt"
30+* + => "plus"
31+* - => "minus"
32+* ; => "semicolon"
33+* % => "percent"
34+* Left => "left"
35+* Right => "right"
36+* Up => "up"
37+* Home => "home"
38+* End => "end"
39+* Page Up => "pageup"
40+* Page Down => "pagedown"
41+* Tab => "tab"
42+* Back Tab => "backtab"
43+* Delete => "del"
44+* Insert => "ins"
45+* Null => "null"
46+* Escape => "esc"
47+
48+Commands can be found in the source code at `../../helix-term/src/commands.rs`
helix-term/src/application.rs+4−3
@@ -1,7 +1,7 @@
1use helix_lsp::lsp;
1use helix_lsp::lsp;
2use helix_view::{document::Mode, Document, Editor, Theme, View};
2use helix_view::{document::Mode, Document, Editor, Theme, View};
3
3
4−use crate::{args::Args, compositor::Compositor, ui};
4+use crate::{args::Args, compositor::Compositor, config::Config, keymap::Keymaps, ui};
5
5
6use log::{error, info};
6use log::{error, info};
7
7
@@ -40,13 +40,14 @@ pub struct Application {
40}
40}
41
41
42impl Application {
42impl Application {
43− pub fn new(mut args: Args) -> Result<Self, Error> {
43+ pub fn new(mut args: Args, config: Config) -> Result<Self, Error> {
44 use helix_view::editor::Action;
44 use helix_view::editor::Action;
45 let mut compositor = Compositor::new()?;
45 let mut compositor = Compositor::new()?;
46 let size = compositor.size();
46 let size = compositor.size();
47 let mut editor = Editor::new(size);
47 let mut editor = Editor::new(size);
48
48
49− compositor.push(Box::new(ui::EditorView::new()));
49+ let mut editor_view = Box::new(ui::EditorView::new(config.keymaps));
50+ compositor.push(editor_view);
50
51
51 if !args.files.is_empty() {
52 if !args.files.is_empty() {
52 let first = &args.files[0]; // we know it's not empty
53 let first = &args.files[0]; // we know it's not empty
helix-term/src/commands.rs+294−131
@@ -15,11 +15,13 @@ use helix_view::{
15 Document, DocumentId, Editor, ViewId,
15 Document, DocumentId, Editor, ViewId,
16};
16};
17
17
18+use anyhow::anyhow;
18use helix_lsp::{
19use helix_lsp::{
19 lsp,
20 lsp,
20 util::{lsp_pos_to_pos, lsp_range_to_range, pos_to_lsp_pos, range_to_lsp_range},
21 util::{lsp_pos_to_pos, lsp_range_to_range, pos_to_lsp_pos, range_to_lsp_range},
21 OffsetEncoding,
22 OffsetEncoding,
22};
23};
24+use insert::*;
23use movement::Movement;
25use movement::Movement;
24
26
25use crate::{
27use crate::{
@@ -29,7 +31,7 @@ use crate::{
29
31
30use crate::application::{LspCallbackWrapper, LspCallbacks};
32use crate::application::{LspCallbackWrapper, LspCallbacks};
31use futures_util::FutureExt;
33use futures_util::FutureExt;
32−use std::future::Future;
34+use std::{fmt, future::Future, path::Display, str::FromStr};
33
35
34use std::{
36use std::{
35 borrow::Cow,
37 borrow::Cow,
@@ -133,11 +135,145 @@ fn align_view(doc: &Document, view: &mut View, align: Align) {
133 view.first_line = line.saturating_sub(relative);
135 view.first_line = line.saturating_sub(relative);
134}
136}
135
137
136−/// A command is a function that takes the current state and a count, and does a side-effect on the
138+/// A command is composed of a static name, and a function that takes the current state plus a count,
137−/// state (usually by creating and applying a transaction).
139+/// and does a side-effect on the state (usually by creating and applying a transaction).
140+#[derive(Copy, Clone)]
138−pub type Command = fn(cx: &mut Context);
141+pub struct Command(&'static str, fn(cx: &mut Context));
142+
143+macro_rules! commands {
144+ ( $($name:ident),* ) => {
145+ $(
146+ #[allow(non_upper_case_globals)]
147+ pub const $name: Self = Self(stringify!($name), $name);
148+ )*
149+
150+ pub const COMMAND_LIST: &'static [Self] = &[
151+ $( Self::$name, )*
152+ ];
153+ }
154+}
139
155
156+impl Command {
157+ pub fn execute(&self, cx: &mut Context) {
158+ (self.1)(cx);
159+ }
160+
161+ pub fn name(&self) -> &'static str {
162+ self.0
163+ }
164+
165+ commands!(
166+ move_char_left,
167+ move_char_right,
168+ move_line_up,
169+ move_line_down,
170+ move_line_end,
171+ move_line_start,
172+ move_first_nonwhitespace,
173+ move_next_word_start,
174+ move_prev_word_start,
175+ move_next_word_end,
176+ move_file_start,
177+ move_file_end,
178+ extend_next_word_start,
179+ extend_prev_word_start,
180+ extend_next_word_end,
181+ find_till_char,
182+ find_next_char,
183+ extend_till_char,
184+ extend_next_char,
185+ till_prev_char,
186+ find_prev_char,
187+ extend_till_prev_char,
188+ extend_prev_char,
189+ extend_first_nonwhitespace,
190+ replace,
191+ page_up,
192+ page_down,
193+ half_page_up,
194+ half_page_down,
195+ extend_char_left,
196+ extend_char_right,
197+ extend_line_up,
198+ extend_line_down,
199+ extend_line_end,
200+ extend_line_start,
201+ select_all,
202+ select_regex,
203+ split_selection,
204+ split_selection_on_newline,
205+ search,
206+ search_next,
207+ extend_search_next,
208+ search_selection,
209+ select_line,
210+ extend_line,
211+ delete_selection,
212+ change_selection,
213+ collapse_selection,
214+ flip_selections,
215+ insert_mode,
216+ append_mode,
217+ command_mode,
218+ file_picker,
219+ buffer_picker,
220+ symbol_picker,
221+ prepend_to_line,
222+ append_to_line,
223+ open_below,
224+ open_above,
225+ normal_mode,
226+ goto_mode,
227+ select_mode,
228+ exit_select_mode,
229+ goto_definition,
230+ goto_type_definition,
231+ goto_implementation,
232+ goto_reference,
233+ goto_first_diag,
234+ goto_last_diag,
235+ goto_next_diag,
236+ goto_prev_diag,
237+ signature_help,
238+ insert_tab,
239+ insert_newline,
240+ delete_char_backward,
241+ delete_char_forward,
242+ delete_word_backward,
243+ undo,
244+ redo,
245+ yank,
246+ replace_with_yanked,
247+ paste_after,
248+ paste_before,
249+ indent,
250+ unindent,
251+ format_selections,
252+ join_selections,
253+ keep_selections,
254+ keep_primary_selection,
255+ save,
256+ completion,
257+ hover,
258+ toggle_comments,
259+ expand_selection,
260+ match_brackets,
261+ jump_forward,
262+ jump_backward,
263+ window_mode,
264+ rotate_view,
265+ hsplit,
266+ vsplit,
267+ wclose,
268+ select_register,
269+ space_mode,
270+ view_mode,
271+ left_bracket_mode,
272+ right_bracket_mode
273+ );
274+}
275+
140−pub fn move_char_left(cx: &mut Context) {
276+fn move_char_left(cx: &mut Context) {
141 let count = cx.count();
277 let count = cx.count();
142 let (view, doc) = cx.current();
278 let (view, doc) = cx.current();
143 let text = doc.text().slice(..);
279 let text = doc.text().slice(..);
@@ -147,7 +283,7 @@ pub fn move_char_left(cx: &mut Context) {
147 doc.set_selection(view.id, selection);
283 doc.set_selection(view.id, selection);
148}
284}
149
285
150−pub fn move_char_right(cx: &mut Context) {
286+fn move_char_right(cx: &mut Context) {
151 let count = cx.count();
287 let count = cx.count();
152 let (view, doc) = cx.current();
288 let (view, doc) = cx.current();
153 let text = doc.text().slice(..);
289 let text = doc.text().slice(..);
@@ -157,7 +293,7 @@ pub fn move_char_right(cx: &mut Context) {
157 doc.set_selection(view.id, selection);
293 doc.set_selection(view.id, selection);
158}
294}
159
295
160−pub fn move_line_up(cx: &mut Context) {
296+fn move_line_up(cx: &mut Context) {
161 let count = cx.count();
297 let count = cx.count();
162 let (view, doc) = cx.current();
298 let (view, doc) = cx.current();
163 let text = doc.text().slice(..);
299 let text = doc.text().slice(..);
@@ -167,7 +303,7 @@ pub fn move_line_up(cx: &mut Context) {
167 doc.set_selection(view.id, selection);
303 doc.set_selection(view.id, selection);
168}
304}
169
305
170−pub fn move_line_down(cx: &mut Context) {
306+fn move_line_down(cx: &mut Context) {
171 let count = cx.count();
307 let count = cx.count();
172 let (view, doc) = cx.current();
308 let (view, doc) = cx.current();
173 let text = doc.text().slice(..);
309 let text = doc.text().slice(..);
@@ -177,7 +313,7 @@ pub fn move_line_down(cx: &mut Context) {
177 doc.set_selection(view.id, selection);
313 doc.set_selection(view.id, selection);
178}
314}
179
315
180−pub fn move_line_end(cx: &mut Context) {
316+fn move_line_end(cx: &mut Context) {
181 let (view, doc) = cx.current();
317 let (view, doc) = cx.current();
182
318
183 let selection = doc.selection(view.id).transform(|range| {
319 let selection = doc.selection(view.id).transform(|range| {
@@ -193,7 +329,7 @@ pub fn move_line_end(cx: &mut Context) {
193 doc.set_selection(view.id, selection);
329 doc.set_selection(view.id, selection);
194}
330}
195
331
196−pub fn move_line_start(cx: &mut Context) {
332+fn move_line_start(cx: &mut Context) {
197 let (view, doc) = cx.current();
333 let (view, doc) = cx.current();
198
334
199 let selection = doc.selection(view.id).transform(|range| {
335 let selection = doc.selection(view.id).transform(|range| {
@@ -208,7 +344,7 @@ pub fn move_line_start(cx: &mut Context) {
208 doc.set_selection(view.id, selection);
344 doc.set_selection(view.id, selection);
209}
345}
210
346
211−pub fn move_first_nonwhitespace(cx: &mut Context) {
347+fn move_first_nonwhitespace(cx: &mut Context) {
212 let (view, doc) = cx.current();
348 let (view, doc) = cx.current();
213
349
214 let selection = doc.selection(view.id).transform(|range| {
350 let selection = doc.selection(view.id).transform(|range| {
@@ -230,7 +366,7 @@ pub fn move_first_nonwhitespace(cx: &mut Context) {
230// Range::new(if Move { pos } if Extend { range.anchor }, pos)
366// Range::new(if Move { pos } if Extend { range.anchor }, pos)
231// since these all really do the same thing
367// since these all really do the same thing
232
368
233−pub fn move_next_word_start(cx: &mut Context) {
369+fn move_next_word_start(cx: &mut Context) {
234 let count = cx.count();
370 let count = cx.count();
235 let (view, doc) = cx.current();
371 let (view, doc) = cx.current();
236 let text = doc.text().slice(..);
372 let text = doc.text().slice(..);
@@ -242,7 +378,7 @@ pub fn move_next_word_start(cx: &mut Context) {
242 doc.set_selection(view.id, selection);
378 doc.set_selection(view.id, selection);
243}
379}
244
380
245−pub fn move_prev_word_start(cx: &mut Context) {
381+fn move_prev_word_start(cx: &mut Context) {
246 let count = cx.count();
382 let count = cx.count();
247 let (view, doc) = cx.current();
383 let (view, doc) = cx.current();
248 let text = doc.text().slice(..);
384 let text = doc.text().slice(..);
@@ -254,7 +390,7 @@ pub fn move_prev_word_start(cx: &mut Context) {
254 doc.set_selection(view.id, selection);
390 doc.set_selection(view.id, selection);
255}
391}
256
392
257−pub fn move_next_word_end(cx: &mut Context) {
393+fn move_next_word_end(cx: &mut Context) {
258 let count = cx.count();
394 let count = cx.count();
259 let (view, doc) = cx.current();
395 let (view, doc) = cx.current();
260 let text = doc.text().slice(..);
396 let text = doc.text().slice(..);
@@ -266,13 +402,13 @@ pub fn move_next_word_end(cx: &mut Context) {
266 doc.set_selection(view.id, selection);
402 doc.set_selection(view.id, selection);
267}
403}
268
404
269−pub fn move_file_start(cx: &mut Context) {
405+fn move_file_start(cx: &mut Context) {
270 push_jump(cx.editor);
406 push_jump(cx.editor);
271 let (view, doc) = cx.current();
407 let (view, doc) = cx.current();
272 doc.set_selection(view.id, Selection::point(0));
408 doc.set_selection(view.id, Selection::point(0));
273}
409}
274
410
275−pub fn move_file_end(cx: &mut Context) {
411+fn move_file_end(cx: &mut Context) {
276 push_jump(cx.editor);
412 push_jump(cx.editor);
277 let (view, doc) = cx.current();
413 let (view, doc) = cx.current();
278 let text = doc.text();
414 let text = doc.text();
@@ -280,7 +416,7 @@ pub fn move_file_end(cx: &mut Context) {
280 doc.set_selection(view.id, Selection::point(last_line));
416 doc.set_selection(view.id, Selection::point(last_line));
281}
417}
282
418
283−pub fn extend_next_word_start(cx: &mut Context) {
419+fn extend_next_word_start(cx: &mut Context) {
284 let count = cx.count();
420 let count = cx.count();
285 let (view, doc) = cx.current();
421 let (view, doc) = cx.current();
286 let text = doc.text().slice(..);
422 let text = doc.text().slice(..);
@@ -294,7 +430,7 @@ pub fn extend_next_word_start(cx: &mut Context) {
294 doc.set_selection(view.id, selection);
430 doc.set_selection(view.id, selection);
295}
431}
296
432
297−pub fn extend_prev_word_start(cx: &mut Context) {
433+fn extend_prev_word_start(cx: &mut Context) {
298 let count = cx.count();
434 let count = cx.count();
299 let (view, doc) = cx.current();
435 let (view, doc) = cx.current();
300 let text = doc.text().slice(..);
436 let text = doc.text().slice(..);
@@ -307,7 +443,7 @@ pub fn extend_prev_word_start(cx: &mut Context) {
307 doc.set_selection(view.id, selection);
443 doc.set_selection(view.id, selection);
308}
444}
309
445
310−pub fn extend_next_word_end(cx: &mut Context) {
446+fn extend_next_word_end(cx: &mut Context) {
311 let count = cx.count();
447 let count = cx.count();
312 let (view, doc) = cx.current();
448 let (view, doc) = cx.current();
313 let text = doc.text().slice(..);
449 let text = doc.text().slice(..);
@@ -365,7 +501,7 @@ where
365 })
501 })
366}
502}
367
503
368−pub fn find_till_char(cx: &mut Context) {
504+fn find_till_char(cx: &mut Context) {
369 find_char_impl(
505 find_char_impl(
370 cx,
506 cx,
371 search::find_nth_next,
507 search::find_nth_next,
@@ -374,7 +510,7 @@ pub fn find_till_char(cx: &mut Context) {
374 )
510 )
375}
511}
376
512
377−pub fn find_next_char(cx: &mut Context) {
513+fn find_next_char(cx: &mut Context) {
378 find_char_impl(
514 find_char_impl(
379 cx,
515 cx,
380 search::find_nth_next,
516 search::find_nth_next,
@@ -383,7 +519,7 @@ pub fn find_next_char(cx: &mut Context) {
383 )
519 )
384}
520}
385
521
386−pub fn extend_till_char(cx: &mut Context) {
522+fn extend_till_char(cx: &mut Context) {
387 find_char_impl(
523 find_char_impl(
388 cx,
524 cx,
389 search::find_nth_next,
525 search::find_nth_next,
@@ -392,7 +528,7 @@ pub fn extend_till_char(cx: &mut Context) {
392 )
528 )
393}
529}
394
530
395−pub fn extend_next_char(cx: &mut Context) {
531+fn extend_next_char(cx: &mut Context) {
396 find_char_impl(
532 find_char_impl(
397 cx,
533 cx,
398 search::find_nth_next,
534 search::find_nth_next,
@@ -401,7 +537,7 @@ pub fn extend_next_char(cx: &mut Context) {
401 )
537 )
402}
538}
403
539
404−pub fn till_prev_char(cx: &mut Context) {
540+fn till_prev_char(cx: &mut Context) {
405 find_char_impl(
541 find_char_impl(
406 cx,
542 cx,
407 search::find_nth_prev,
543 search::find_nth_prev,
@@ -410,7 +546,7 @@ pub fn till_prev_char(cx: &mut Context) {
410 )
546 )
411}
547}
412
548
413−pub fn find_prev_char(cx: &mut Context) {
549+fn find_prev_char(cx: &mut Context) {
414 find_char_impl(
550 find_char_impl(
415 cx,
551 cx,
416 search::find_nth_prev,
552 search::find_nth_prev,
@@ -419,7 +555,7 @@ pub fn find_prev_char(cx: &mut Context) {
419 )
555 )
420}
556}
421
557
422−pub fn extend_till_prev_char(cx: &mut Context) {
558+fn extend_till_prev_char(cx: &mut Context) {
423 find_char_impl(
559 find_char_impl(
424 cx,
560 cx,
425 search::find_nth_prev,
561 search::find_nth_prev,
@@ -428,7 +564,7 @@ pub fn extend_till_prev_char(cx: &mut Context) {
428 )
564 )
429}
565}
430
566
431−pub fn extend_prev_char(cx: &mut Context) {
567+fn extend_prev_char(cx: &mut Context) {
432 find_char_impl(
568 find_char_impl(
433 cx,
569 cx,
434 search::find_nth_prev,
570 search::find_nth_prev,
@@ -437,7 +573,7 @@ pub fn extend_prev_char(cx: &mut Context) {
437 )
573 )
438}
574}
439
575
440−pub fn extend_first_nonwhitespace(cx: &mut Context) {
576+fn extend_first_nonwhitespace(cx: &mut Context) {
441 let (view, doc) = cx.current();
577 let (view, doc) = cx.current();
442
578
443 let selection = doc.selection(view.id).transform(|range| {
579 let selection = doc.selection(view.id).transform(|range| {
@@ -455,7 +591,7 @@ pub fn extend_first_nonwhitespace(cx: &mut Context) {
455 doc.set_selection(view.id, selection);
591 doc.set_selection(view.id, selection);
456}
592}
457
593
458−pub fn replace(cx: &mut Context) {
594+fn replace(cx: &mut Context) {
459 // need to wait for next key
595 // need to wait for next key
460 cx.on_next_key(move |cx, event| {
596 cx.on_next_key(move |cx, event| {
461 let ch = match event {
597 let ch = match event {
@@ -535,31 +671,31 @@ fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
535 doc.set_selection(view.id, Selection::point(pos));
671 doc.set_selection(view.id, Selection::point(pos));
536}
672}
537
673
538−pub fn page_up(cx: &mut Context) {
674+fn page_up(cx: &mut Context) {
539 let view = cx.view();
675 let view = cx.view();
540 let offset = view.area.height as usize;
676 let offset = view.area.height as usize;
541 scroll(cx, offset, Direction::Backward);
677 scroll(cx, offset, Direction::Backward);
542}
678}
543
679
544−pub fn page_down(cx: &mut Context) {
680+fn page_down(cx: &mut Context) {
545 let view = cx.view();
681 let view = cx.view();
546 let offset = view.area.height as usize;
682 let offset = view.area.height as usize;
547 scroll(cx, offset, Direction::Forward);
683 scroll(cx, offset, Direction::Forward);
548}
684}
549
685
550−pub fn half_page_up(cx: &mut Context) {
686+fn half_page_up(cx: &mut Context) {
551 let view = cx.view();
687 let view = cx.view();
552 let offset = view.area.height as usize / 2;
688 let offset = view.area.height as usize / 2;
553 scroll(cx, offset, Direction::Backward);
689 scroll(cx, offset, Direction::Backward);
554}
690}
555
691
556−pub fn half_page_down(cx: &mut Context) {
692+fn half_page_down(cx: &mut Context) {
557 let view = cx.view();
693 let view = cx.view();
558 let offset = view.area.height as usize / 2;
694 let offset = view.area.height as usize / 2;
559 scroll(cx, offset, Direction::Forward);
695 scroll(cx, offset, Direction::Forward);
560}
696}
561
697
562−pub fn extend_char_left(cx: &mut Context) {
698+fn extend_char_left(cx: &mut Context) {
563 let count = cx.count();
699 let count = cx.count();
564 let (view, doc) = cx.current();
700 let (view, doc) = cx.current();
565 let text = doc.text().slice(..);
701 let text = doc.text().slice(..);
@@ -569,7 +705,7 @@ pub fn extend_char_left(cx: &mut Context) {
569 doc.set_selection(view.id, selection);
705 doc.set_selection(view.id, selection);
570}
706}
571
707
572−pub fn extend_char_right(cx: &mut Context) {
708+fn extend_char_right(cx: &mut Context) {
573 let count = cx.count();
709 let count = cx.count();
574 let (view, doc) = cx.current();
710 let (view, doc) = cx.current();
575 let text = doc.text().slice(..);
711 let text = doc.text().slice(..);
@@ -579,7 +715,7 @@ pub fn extend_char_right(cx: &mut Context) {
579 doc.set_selection(view.id, selection);
715 doc.set_selection(view.id, selection);
580}
716}
581
717
582−pub fn extend_line_up(cx: &mut Context) {
718+fn extend_line_up(cx: &mut Context) {
583 let count = cx.count();
719 let count = cx.count();
584 let (view, doc) = cx.current();
720 let (view, doc) = cx.current();
585 let text = doc.text().slice(..);
721 let text = doc.text().slice(..);
@@ -589,7 +725,7 @@ pub fn extend_line_up(cx: &mut Context) {
589 doc.set_selection(view.id, selection);
725 doc.set_selection(view.id, selection);
590}
726}
591
727
592−pub fn extend_line_down(cx: &mut Context) {
728+fn extend_line_down(cx: &mut Context) {
593 let count = cx.count();
729 let count = cx.count();
594 let (view, doc) = cx.current();
730 let (view, doc) = cx.current();
595 let text = doc.text().slice(..);
731 let text = doc.text().slice(..);
@@ -599,7 +735,7 @@ pub fn extend_line_down(cx: &mut Context) {
599 doc.set_selection(view.id, selection);
735 doc.set_selection(view.id, selection);
600}
736}
601
737
602−pub fn extend_line_end(cx: &mut Context) {
738+fn extend_line_end(cx: &mut Context) {
603 let (view, doc) = cx.current();
739 let (view, doc) = cx.current();
604
740
605 let selection = doc.selection(view.id).transform(|range| {
741 let selection = doc.selection(view.id).transform(|range| {
@@ -615,7 +751,7 @@ pub fn extend_line_end(cx: &mut Context) {
615 doc.set_selection(view.id, selection);
751 doc.set_selection(view.id, selection);
616}
752}
617
753
618−pub fn extend_line_start(cx: &mut Context) {
754+fn extend_line_start(cx: &mut Context) {
619 let (view, doc) = cx.current();
755 let (view, doc) = cx.current();
620
756
621 let selection = doc.selection(view.id).transform(|range| {
757 let selection = doc.selection(view.id).transform(|range| {
@@ -630,14 +766,14 @@ pub fn extend_line_start(cx: &mut Context) {
630 doc.set_selection(view.id, selection);
766 doc.set_selection(view.id, selection);
631}
767}
632
768
633−pub fn select_all(cx: &mut Context) {
769+fn select_all(cx: &mut Context) {
634 let (view, doc) = cx.current();
770 let (view, doc) = cx.current();
635
771
636 let end = doc.text().len_chars().saturating_sub(1);
772 let end = doc.text().len_chars().saturating_sub(1);
637 doc.set_selection(view.id, Selection::single(0, end))
773 doc.set_selection(view.id, Selection::single(0, end))
638}
774}
639
775
640−pub fn select_regex(cx: &mut Context) {
776+fn select_regex(cx: &mut Context) {
641 let prompt = ui::regex_prompt(cx, "select:".to_string(), move |view, doc, _, regex| {
777 let prompt = ui::regex_prompt(cx, "select:".to_string(), move |view, doc, _, regex| {
642 let text = doc.text().slice(..);
778 let text = doc.text().slice(..);
643 if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), &regex)
779 if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), &regex)
@@ -649,7 +785,7 @@ pub fn select_regex(cx: &mut Context) {
649 cx.push_layer(Box::new(prompt));
785 cx.push_layer(Box::new(prompt));
650}
786}
651
787
652−pub fn split_selection(cx: &mut Context) {
788+fn split_selection(cx: &mut Context) {
653 let prompt = ui::regex_prompt(cx, "split:".to_string(), move |view, doc, _, regex| {
789 let prompt = ui::regex_prompt(cx, "split:".to_string(), move |view, doc, _, regex| {
654 let text = doc.text().slice(..);
790 let text = doc.text().slice(..);
655 let selection = selection::split_on_matches(text, doc.selection(view.id), &regex);
791 let selection = selection::split_on_matches(text, doc.selection(view.id), &regex);
@@ -659,7 +795,7 @@ pub fn split_selection(cx: &mut Context) {
659 cx.push_layer(Box::new(prompt));
795 cx.push_layer(Box::new(prompt));
660}
796}
661
797
662−pub fn split_selection_on_newline(cx: &mut Context) {
798+fn split_selection_on_newline(cx: &mut Context) {
663 let (view, doc) = cx.current();
799 let (view, doc) = cx.current();
664 let text = doc.text().slice(..);
800 let text = doc.text().slice(..);
665 // only compile the regex once
801 // only compile the regex once
@@ -711,7 +847,7 @@ fn search_impl(doc: &mut Document, view: &mut View, contents: &str, regex: &Rege
711}
847}
712
848
713// TODO: use one function for search vs extend
849// TODO: use one function for search vs extend
714−pub fn search(cx: &mut Context) {
850+fn search(cx: &mut Context) {
715 let (view, doc) = cx.current();
851 let (view, doc) = cx.current();
716
852
717 // TODO: could probably share with select_on_matches?
853 // TODO: could probably share with select_on_matches?
@@ -733,9 +869,8 @@ pub fn search(cx: &mut Context) {
733
869
734 cx.push_layer(Box::new(prompt));
870 cx.push_layer(Box::new(prompt));
735}
871}
736−// can't search next for ""compose"" for some reason
737
872
738−pub fn search_next_impl(cx: &mut Context, extend: bool) {
873+fn search_next_impl(cx: &mut Context, extend: bool) {
739 let (view, doc, registers) = cx.current_with_registers();
874 let (view, doc, registers) = cx.current_with_registers();
740 if let Some(query) = registers.read('\\') {
875 if let Some(query) = registers.read('\\') {
741 let query = query.first().unwrap();
876 let query = query.first().unwrap();
@@ -745,15 +880,15 @@ pub fn search_next_impl(cx: &mut Context, extend: bool) {
745 }
880 }
746}
881}
747
882
748−pub fn search_next(cx: &mut Context) {
883+fn search_next(cx: &mut Context) {
749 search_next_impl(cx, false);
884 search_next_impl(cx, false);
750}
885}
751
886
752−pub fn extend_search_next(cx: &mut Context) {
887+fn extend_search_next(cx: &mut Context) {
753 search_next_impl(cx, true);
888 search_next_impl(cx, true);
754}
889}
755
890
756−pub fn search_selection(cx: &mut Context) {
891+fn search_selection(cx: &mut Context) {
757 let (view, doc) = cx.current();
892 let (view, doc) = cx.current();
758 let contents = doc.text().slice(..);
893 let contents = doc.text().slice(..);
759 let query = doc.selection(view.id).primary().fragment(contents);
894 let query = doc.selection(view.id).primary().fragment(contents);
@@ -768,7 +903,7 @@ pub fn search_selection(cx: &mut Context) {
768
903
769//
904//
770
905
771−pub fn select_line(cx: &mut Context) {
906+fn select_line(cx: &mut Context) {
772 let count = cx.count();
907 let count = cx.count();
773 let (view, doc) = cx.current();
908 let (view, doc) = cx.current();
774
909
@@ -783,7 +918,7 @@ pub fn select_line(cx: &mut Context) {
783
918
784 doc.set_selection(view.id, Selection::single(start, end));
919 doc.set_selection(view.id, Selection::single(start, end));
785}
920}
786−pub fn extend_line(cx: &mut Context) {
921+fn extend_line(cx: &mut Context) {
787 let count = cx.count();
922 let count = cx.count();
788 let (view, doc) = cx.current();
923 let (view, doc) = cx.current();
789
924
@@ -825,7 +960,7 @@ fn delete_selection_impl(reg: &mut Register, doc: &mut Document, view_id: ViewId
825 doc.apply(&transaction, view_id);
960 doc.apply(&transaction, view_id);
826}
961}
827
962
828−pub fn delete_selection(cx: &mut Context) {
963+fn delete_selection(cx: &mut Context) {
829 let reg_name = cx.selected_register.name();
964 let reg_name = cx.selected_register.name();
830 let (view, doc, registers) = cx.current_with_registers();
965 let (view, doc, registers) = cx.current_with_registers();
831 let reg = registers.get_or_insert(reg_name);
966 let reg = registers.get_or_insert(reg_name);
@@ -837,7 +972,7 @@ pub fn delete_selection(cx: &mut Context) {
837 exit_select_mode(cx);
972 exit_select_mode(cx);
838}
973}
839
974
840−pub fn change_selection(cx: &mut Context) {
975+fn change_selection(cx: &mut Context) {
841 let reg_name = cx.selected_register.name();
976 let reg_name = cx.selected_register.name();
842 let (view, doc, registers) = cx.current_with_registers();
977 let (view, doc, registers) = cx.current_with_registers();
843 let reg = registers.get_or_insert(reg_name);
978 let reg = registers.get_or_insert(reg_name);
@@ -845,7 +980,7 @@ pub fn change_selection(cx: &mut Context) {
845 enter_insert_mode(doc);
980 enter_insert_mode(doc);
846}
981}
847
982
848−pub fn collapse_selection(cx: &mut Context) {
983+fn collapse_selection(cx: &mut Context) {
849 let (view, doc) = cx.current();
984 let (view, doc) = cx.current();
850 let selection = doc
985 let selection = doc
851 .selection(view.id)
986 .selection(view.id)
@@ -854,7 +989,7 @@ pub fn collapse_selection(cx: &mut Context) {
854 doc.set_selection(view.id, selection);
989 doc.set_selection(view.id, selection);
855}
990}
856
991
857−pub fn flip_selections(cx: &mut Context) {
992+fn flip_selections(cx: &mut Context) {
858 let (view, doc) = cx.current();
993 let (view, doc) = cx.current();
859 let selection = doc
994 let selection = doc
860 .selection(view.id)
995 .selection(view.id)
@@ -868,7 +1003,7 @@ fn enter_insert_mode(doc: &mut Document) {
868}
1003}
869
1004
870// inserts at the start of each selection
1005// inserts at the start of each selection
871−pub fn insert_mode(cx: &mut Context) {
1006+fn insert_mode(cx: &mut Context) {
872 let (view, doc) = cx.current();
1007 let (view, doc) = cx.current();
873 enter_insert_mode(doc);
1008 enter_insert_mode(doc);
874
1009
@@ -879,7 +1014,7 @@ pub fn insert_mode(cx: &mut Context) {
879}
1014}
880
1015
881// inserts at the end of each selection
1016// inserts at the end of each selection
882−pub fn append_mode(cx: &mut Context) {
1017+fn append_mode(cx: &mut Context) {
883 let (view, doc) = cx.current();
1018 let (view, doc) = cx.current();
884 enter_insert_mode(doc);
1019 enter_insert_mode(doc);
885 doc.restore_cursor = true;
1020 doc.restore_cursor = true;
@@ -913,7 +1048,7 @@ mod cmd {
913 use ui::completers::{self, Completer};
1048 use ui::completers::{self, Completer};
914
1049
915 #[derive(Clone)]
1050 #[derive(Clone)]
916− pub struct Command {
1051+ pub struct TypableCommand {
917 pub name: &'static str,
1052 pub name: &'static str,
918 pub alias: Option<&'static str>,
1053 pub alias: Option<&'static str>,
919 pub doc: &'static str,
1054 pub doc: &'static str,
@@ -1152,113 +1287,113 @@ mod cmd {
1152 quit_all_impl(editor, args, event, true)
1287 quit_all_impl(editor, args, event, true)
1153 }
1288 }
1154
1289
1155− pub const COMMAND_LIST: &[Command] = &[
1290+ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
1156− Command {
1291+ TypableCommand {
1157 name: "quit",
1292 name: "quit",
1158 alias: Some("q"),
1293 alias: Some("q"),
1159 doc: "Close the current view.",
1294 doc: "Close the current view.",
1160 fun: quit,
1295 fun: quit,
1161 completer: None,
1296 completer: None,
1162 },
1297 },
1163− Command {
1298+ TypableCommand {
1164 name: "quit!",
1299 name: "quit!",
1165 alias: Some("q!"),
1300 alias: Some("q!"),
1166 doc: "Close the current view.",
1301 doc: "Close the current view.",
1167 fun: force_quit,
1302 fun: force_quit,
1168 completer: None,
1303 completer: None,
1169 },
1304 },
1170− Command {
1305+ TypableCommand {
1171 name: "open",
1306 name: "open",
1172 alias: Some("o"),
1307 alias: Some("o"),
1173 doc: "Open a file from disk into the current view.",
1308 doc: "Open a file from disk into the current view.",
1174 fun: open,
1309 fun: open,
1175 completer: Some(completers::filename),
1310 completer: Some(completers::filename),
1176 },
1311 },
1177− Command {
1312+ TypableCommand {
1178 name: "write",
1313 name: "write",
1179 alias: Some("w"),
1314 alias: Some("w"),
1180 doc: "Write changes to disk. Accepts an optional path (:write some/path.txt)",
1315 doc: "Write changes to disk. Accepts an optional path (:write some/path.txt)",
1181 fun: write,
1316 fun: write,
1182 completer: Some(completers::filename),
1317 completer: Some(completers::filename),
1183 },
1318 },
1184− Command {
1319+ TypableCommand {
1185 name: "new",
1320 name: "new",
1186 alias: Some("n"),
1321 alias: Some("n"),
1187 doc: "Create a new scratch buffer.",
1322 doc: "Create a new scratch buffer.",
1188 fun: new_file,
1323 fun: new_file,
1189 completer: Some(completers::filename),
1324 completer: Some(completers::filename),
1190 },
1325 },
1191− Command {
1326+ TypableCommand {
1192 name: "format",
1327 name: "format",
1193 alias: Some("fmt"),
1328 alias: Some("fmt"),
1194 doc: "Format the file using a formatter.",
1329 doc: "Format the file using a formatter.",
1195 fun: format,
1330 fun: format,
1196 completer: None,
1331 completer: None,
1197 },
1332 },
1198− Command {
1333+ TypableCommand {
1199 name: "indent-style",
1334 name: "indent-style",
1200 alias: None,
1335 alias: None,
1201 doc: "Set the indentation style for editing. ('t' for tabs or 1-8 for number of spaces.)",
1336 doc: "Set the indentation style for editing. ('t' for tabs or 1-8 for number of spaces.)",
1202 fun: set_indent_style,
1337 fun: set_indent_style,
1203 completer: None,
1338 completer: None,
1204 },
1339 },
1205− Command {
1340+ TypableCommand {
1206 name: "earlier",
1341 name: "earlier",
1207 alias: Some("ear"),
1342 alias: Some("ear"),
1208 doc: "Jump back to an earlier point in edit history. Accepts a number of steps or a time span.",
1343 doc: "Jump back to an earlier point in edit history. Accepts a number of steps or a time span.",
1209 fun: earlier,
1344 fun: earlier,
1210 completer: None,
1345 completer: None,
1211 },
1346 },
1212− Command {
1347+ TypableCommand {
1213 name: "later",
1348 name: "later",
1214 alias: Some("lat"),
1349 alias: Some("lat"),
1215 doc: "Jump to a later point in edit history. Accepts a number of steps or a time span.",
1350 doc: "Jump to a later point in edit history. Accepts a number of steps or a time span.",
1216 fun: later,
1351 fun: later,
1217 completer: None,
1352 completer: None,
1218 },
1353 },
1219− Command {
1354+ TypableCommand {
1220 name: "write-quit",
1355 name: "write-quit",
1221 alias: Some("wq"),
1356 alias: Some("wq"),
1222 doc: "Writes changes to disk and closes the current view. Accepts an optional path (:wq some/path.txt)",
1357 doc: "Writes changes to disk and closes the current view. Accepts an optional path (:wq some/path.txt)",
1223 fun: write_quit,
1358 fun: write_quit,
1224 completer: Some(completers::filename),
1359 completer: Some(completers::filename),
1225 },
1360 },
1226− Command {
1361+ TypableCommand {
1227 name: "write-quit!",
1362 name: "write-quit!",
1228 alias: Some("wq!"),
1363 alias: Some("wq!"),
1229 doc: "Writes changes to disk and closes the current view forcefully. Accepts an optional path (:wq! some/path.txt)",
1364 doc: "Writes changes to disk and closes the current view forcefully. Accepts an optional path (:wq! some/path.txt)",
1230 fun: force_write_quit,
1365 fun: force_write_quit,
1231 completer: Some(completers::filename),
1366 completer: Some(completers::filename),
1232 },
1367 },
1233− Command {
1368+ TypableCommand {
1234 name: "write-all",
1369 name: "write-all",
1235 alias: Some("wa"),
1370 alias: Some("wa"),
1236 doc: "Writes changes from all views to disk.",
1371 doc: "Writes changes from all views to disk.",
1237 fun: write_all,
1372 fun: write_all,
1238 completer: None,
1373 completer: None,
1239 },
1374 },
1240− Command {
1375+ TypableCommand {
1241 name: "write-quit-all",
1376 name: "write-quit-all",
1242 alias: Some("wqa"),
1377 alias: Some("wqa"),
1243 doc: "Writes changes from all views to disk and close all views.",
1378 doc: "Writes changes from all views to disk and close all views.",
1244 fun: write_all_quit,
1379 fun: write_all_quit,
1245 completer: None,
1380 completer: None,
1246 },
1381 },
1247− Command {
1382+ TypableCommand {
1248 name: "write-quit-all!",
1383 name: "write-quit-all!",
1249 alias: Some("wqa!"),
1384 alias: Some("wqa!"),
1250 doc: "Writes changes from all views to disk and close all views forcefully (ignoring unsaved changes).",
1385 doc: "Writes changes from all views to disk and close all views forcefully (ignoring unsaved changes).",
1251 fun: force_write_all_quit,
1386 fun: force_write_all_quit,
1252 completer: None,
1387 completer: None,
1253 },
1388 },
1254− Command {
1389+ TypableCommand {
1255 name: "quit-all",
1390 name: "quit-all",
1256 alias: Some("qa"),
1391 alias: Some("qa"),
1257 doc: "Close all views.",
1392 doc: "Close all views.",
1258 fun: quit_all,
1393 fun: quit_all,
1259 completer: None,
1394 completer: None,
1260 },
1395 },
1261− Command {
1396+ TypableCommand {
1262 name: "quit-all!",
1397 name: "quit-all!",
1263 alias: Some("qa!"),
1398 alias: Some("qa!"),
1264 doc: "Close all views forcefully (ignoring unsaved changes).",
1399 doc: "Close all views forcefully (ignoring unsaved changes).",
@@ -1268,10 +1403,10 @@ mod cmd {
1268
1403
1269 ];
1404 ];
1270
1405
1271− pub static COMMANDS: Lazy<HashMap<&'static str, &'static Command>> = Lazy::new(|| {
1406+ pub static COMMANDS: Lazy<HashMap<&'static str, &'static TypableCommand>> = Lazy::new(|| {
1272 let mut map = HashMap::new();
1407 let mut map = HashMap::new();
1273
1408
1274− for cmd in COMMAND_LIST {
1409+ for cmd in TYPABLE_COMMAND_LIST {
1275 map.insert(cmd.name, cmd);
1410 map.insert(cmd.name, cmd);
1276 if let Some(alias) = cmd.alias {
1411 if let Some(alias) = cmd.alias {
1277 map.insert(alias, cmd);
1412 map.insert(alias, cmd);
@@ -1282,7 +1417,7 @@ mod cmd {
1282 });
1417 });
1283}
1418}
1284
1419
1285−pub fn command_mode(cx: &mut Context) {
1420+fn command_mode(cx: &mut Context) {
1286 // TODO: completion items should have a info section that would get displayed in
1421 // TODO: completion items should have a info section that would get displayed in
1287 // a popup above the prompt when items are tabbed over
1422 // a popup above the prompt when items are tabbed over
1288
1423
@@ -1297,7 +1432,7 @@ pub fn command_mode(cx: &mut Context) {
1297 if parts.len() <= 1 {
1432 if parts.len() <= 1 {
1298 use std::{borrow::Cow, ops::Range};
1433 use std::{borrow::Cow, ops::Range};
1299 let end = 0..;
1434 let end = 0..;
1300− cmd::COMMAND_LIST
1435+ cmd::TYPABLE_COMMAND_LIST
1301 .iter()
1436 .iter()
1302 .filter(|command| command.name.contains(input))
1437 .filter(|command| command.name.contains(input))
1303 .map(|command| (end.clone(), Cow::Borrowed(command.name)))
1438 .map(|command| (end.clone(), Cow::Borrowed(command.name)))
@@ -1305,7 +1440,7 @@ pub fn command_mode(cx: &mut Context) {
1305 } else {
1440 } else {
1306 let part = parts.last().unwrap();
1441 let part = parts.last().unwrap();
1307
1442
1308− if let Some(cmd::Command {
1443+ if let Some(cmd::TypableCommand {
1309 completer: Some(completer),
1444 completer: Some(completer),
1310 ..
1445 ..
1311 }) = cmd::COMMANDS.get(parts[0])
1446 }) = cmd::COMMANDS.get(parts[0])
@@ -1346,7 +1481,7 @@ pub fn command_mode(cx: &mut Context) {
1346 prompt.doc_fn = Box::new(|input: &str| {
1481 prompt.doc_fn = Box::new(|input: &str| {
1347 let part = input.split(' ').next().unwrap_or_default();
1482 let part = input.split(' ').next().unwrap_or_default();
1348
1483
1349− if let Some(cmd::Command { doc, .. }) = cmd::COMMANDS.get(part) {
1484+ if let Some(cmd::TypableCommand { doc, .. }) = cmd::COMMANDS.get(part) {
1350 return Some(doc);
1485 return Some(doc);
1351 }
1486 }
1352
1487
@@ -1356,13 +1491,13 @@ pub fn command_mode(cx: &mut Context) {
1356 cx.push_layer(Box::new(prompt));
1491 cx.push_layer(Box::new(prompt));
1357}
1492}
1358
1493
1359−pub fn file_picker(cx: &mut Context) {
1494+fn file_picker(cx: &mut Context) {
1360 let root = find_root(None).unwrap_or_else(|| PathBuf::from("./"));
1495 let root = find_root(None).unwrap_or_else(|| PathBuf::from("./"));
1361 let picker = ui::file_picker(root);
1496 let picker = ui::file_picker(root);
1362 cx.push_layer(Box::new(picker));
1497 cx.push_layer(Box::new(picker));
1363}
1498}
1364
1499
1365−pub fn buffer_picker(cx: &mut Context) {
1500+fn buffer_picker(cx: &mut Context) {
1366 use std::path::{Path, PathBuf};
1501 use std::path::{Path, PathBuf};
1367 let current = cx.editor.view().doc;
1502 let current = cx.editor.view().doc;
1368
1503
@@ -1393,7 +1528,7 @@ pub fn buffer_picker(cx: &mut Context) {
1393 cx.push_layer(Box::new(picker));
1528 cx.push_layer(Box::new(picker));
1394}
1529}
1395
1530
1396−pub fn symbol_picker(cx: &mut Context) {
1531+fn symbol_picker(cx: &mut Context) {
1397 fn nested_to_flat(
1532 fn nested_to_flat(
1398 list: &mut Vec<lsp::SymbolInformation>,
1533 list: &mut Vec<lsp::SymbolInformation>,
1399 file: &lsp::TextDocumentIdentifier,
1534 file: &lsp::TextDocumentIdentifier,
@@ -1464,14 +1599,14 @@ pub fn symbol_picker(cx: &mut Context) {
1464}
1599}
1465
1600
1466// I inserts at the first nonwhitespace character of each line with a selection
1601// I inserts at the first nonwhitespace character of each line with a selection
1467−pub fn prepend_to_line(cx: &mut Context) {
1602+fn prepend_to_line(cx: &mut Context) {
1468 move_first_nonwhitespace(cx);
1603 move_first_nonwhitespace(cx);
1469 let doc = cx.doc();
1604 let doc = cx.doc();
1470 enter_insert_mode(doc);
1605 enter_insert_mode(doc);
1471}
1606}
1472
1607
1473// A inserts at the end of each line with a selection
1608// A inserts at the end of each line with a selection
1474−pub fn append_to_line(cx: &mut Context) {
1609+fn append_to_line(cx: &mut Context) {
1475 let (view, doc) = cx.current();
1610 let (view, doc) = cx.current();
1476 enter_insert_mode(doc);
1611 enter_insert_mode(doc);
1477
1612
@@ -1550,16 +1685,16 @@ fn open(cx: &mut Context, open: Open) {
1550}
1685}
1551
1686
1552// o inserts a new line after each line with a selection
1687// o inserts a new line after each line with a selection
1553−pub fn open_below(cx: &mut Context) {
1688+fn open_below(cx: &mut Context) {
1554 open(cx, Open::Below)
1689 open(cx, Open::Below)
1555}
1690}
1556
1691
1557// O inserts a new line before each line with a selection
1692// O inserts a new line before each line with a selection
1558−pub fn open_above(cx: &mut Context) {
1693+fn open_above(cx: &mut Context) {
1559 open(cx, Open::Above)
1694 open(cx, Open::Above)
1560}
1695}
1561
1696
1562−pub fn normal_mode(cx: &mut Context) {
1697+fn normal_mode(cx: &mut Context) {
1563 let (view, doc) = cx.current();
1698 let (view, doc) = cx.current();
1564
1699
1565 doc.mode = Mode::Normal;
1700 doc.mode = Mode::Normal;
@@ -1597,7 +1732,7 @@ fn switch_to_last_accessed_file(cx: &mut Context) {
1597 }
1732 }
1598}
1733}
1599
1734
1600−pub fn goto_mode(cx: &mut Context) {
1735+fn goto_mode(cx: &mut Context) {
1601 if let Some(count) = cx.count {
1736 if let Some(count) = cx.count {
1602 push_jump(cx.editor);
1737 push_jump(cx.editor);
1603
1738
@@ -1658,11 +1793,11 @@ pub fn goto_mode(cx: &mut Context) {
1658 })
1793 })
1659}
1794}
1660
1795
1661−pub fn select_mode(cx: &mut Context) {
1796+fn select_mode(cx: &mut Context) {
1662 cx.doc().mode = Mode::Select;
1797 cx.doc().mode = Mode::Select;
1663}
1798}
1664
1799
1665−pub fn exit_select_mode(cx: &mut Context) {
1800+fn exit_select_mode(cx: &mut Context) {
1666 cx.doc().mode = Mode::Normal;
1801 cx.doc().mode = Mode::Normal;
1667}
1802}
1668
1803
@@ -1722,7 +1857,7 @@ fn goto_impl(
1722 }
1857 }
1723}
1858}
1724
1859
1725−pub fn goto_definition(cx: &mut Context) {
1860+fn goto_definition(cx: &mut Context) {
1726 let (view, doc) = cx.current();
1861 let (view, doc) = cx.current();
1727 let language_server = match doc.language_server() {
1862 let language_server = match doc.language_server() {
1728 Some(language_server) => language_server,
1863 Some(language_server) => language_server,
@@ -1759,7 +1894,7 @@ pub fn goto_definition(cx: &mut Context) {
1759 );
1894 );
1760}
1895}
1761
1896
1762−pub fn goto_type_definition(cx: &mut Context) {
1897+fn goto_type_definition(cx: &mut Context) {
1763 let (view, doc) = cx.current();
1898 let (view, doc) = cx.current();
1764 let language_server = match doc.language_server() {
1899 let language_server = match doc.language_server() {
1765 Some(language_server) => language_server,
1900 Some(language_server) => language_server,
@@ -1796,7 +1931,7 @@ pub fn goto_type_definition(cx: &mut Context) {
1796 );
1931 );
1797}
1932}
1798
1933
1799−pub fn goto_implementation(cx: &mut Context) {
1934+fn goto_implementation(cx: &mut Context) {
1800 let (view, doc) = cx.current();
1935 let (view, doc) = cx.current();
1801 let language_server = match doc.language_server() {
1936 let language_server = match doc.language_server() {
1802 Some(language_server) => language_server,
1937 Some(language_server) => language_server,
@@ -1833,7 +1968,7 @@ pub fn goto_implementation(cx: &mut Context) {
1833 );
1968 );
1834}
1969}
1835
1970
1836−pub fn goto_reference(cx: &mut Context) {
1971+fn goto_reference(cx: &mut Context) {
1837 let (view, doc) = cx.current();
1972 let (view, doc) = cx.current();
1838 let language_server = match doc.language_server() {
1973 let language_server = match doc.language_server() {
1839 Some(language_server) => language_server,
1974 Some(language_server) => language_server,
@@ -1871,7 +2006,7 @@ fn goto_pos(editor: &mut Editor, pos: usize) {
1871 align_view(doc, view, Align::Center);
2006 align_view(doc, view, Align::Center);
1872}
2007}
1873
2008
1874−pub fn goto_first_diag(cx: &mut Context) {
2009+fn goto_first_diag(cx: &mut Context) {
1875 let editor = &mut cx.editor;
2010 let editor = &mut cx.editor;
1876 let (view, doc) = editor.current();
2011 let (view, doc) = editor.current();
1877
2012
@@ -1885,7 +2020,7 @@ pub fn goto_first_diag(cx: &mut Context) {
1885 goto_pos(editor, diag);
2020 goto_pos(editor, diag);
1886}
2021}
1887
2022
1888−pub fn goto_last_diag(cx: &mut Context) {
2023+fn goto_last_diag(cx: &mut Context) {
1889 let editor = &mut cx.editor;
2024 let editor = &mut cx.editor;
1890 let (view, doc) = editor.current();
2025 let (view, doc) = editor.current();
1891
2026
@@ -1899,7 +2034,7 @@ pub fn goto_last_diag(cx: &mut Context) {
1899 goto_pos(editor, diag);
2034 goto_pos(editor, diag);
1900}
2035}
1901
2036
1902−pub fn goto_next_diag(cx: &mut Context) {
2037+fn goto_next_diag(cx: &mut Context) {
1903 let editor = &mut cx.editor;
2038 let editor = &mut cx.editor;
1904 let (view, doc) = editor.current();
2039 let (view, doc) = editor.current();
1905
2040
@@ -1920,7 +2055,7 @@ pub fn goto_next_diag(cx: &mut Context) {
1920 goto_pos(editor, diag);
2055 goto_pos(editor, diag);
1921}
2056}
1922
2057
1923−pub fn goto_prev_diag(cx: &mut Context) {
2058+fn goto_prev_diag(cx: &mut Context) {
1924 let editor = &mut cx.editor;
2059 let editor = &mut cx.editor;
1925 let (view, doc) = editor.current();
2060 let (view, doc) = editor.current();
1926
2061
@@ -1942,7 +2077,7 @@ pub fn goto_prev_diag(cx: &mut Context) {
1942 goto_pos(editor, diag);
2077 goto_pos(editor, diag);
1943}
2078}
1944
2079
1945−pub fn signature_help(cx: &mut Context) {
2080+fn signature_help(cx: &mut Context) {
1946 let (view, doc) = cx.current();
2081 let (view, doc) = cx.current();
1947
2082
1948 let language_server = match doc.language_server() {
2083 let language_server = match doc.language_server() {
@@ -2218,19 +2353,19 @@ pub mod insert {
2218// TODO: each command could simply return a Option<transaction>, then the higher level handles
2353// TODO: each command could simply return a Option<transaction>, then the higher level handles
2219// storing it?
2354// storing it?
2220
2355
2221−pub fn undo(cx: &mut Context) {
2356+fn undo(cx: &mut Context) {
2222 let view_id = cx.view().id;
2357 let view_id = cx.view().id;
2223 cx.doc().undo(view_id);
2358 cx.doc().undo(view_id);
2224}
2359}
2225
2360
2226−pub fn redo(cx: &mut Context) {
2361+fn redo(cx: &mut Context) {
2227 let view_id = cx.view().id;
2362 let view_id = cx.view().id;
2228 cx.doc().redo(view_id);
2363 cx.doc().redo(view_id);
2229}
2364}
2230
2365
2231// Yank / Paste
2366// Yank / Paste
2232
2367
2233−pub fn yank(cx: &mut Context) {
2368+fn yank(cx: &mut Context) {
2234 // TODO: should selections be made end inclusive?
2369 // TODO: should selections be made end inclusive?
2235 let (view, doc) = cx.current();
2370 let (view, doc) = cx.current();
2236 let values: Vec<String> = doc
2371 let values: Vec<String> = doc
@@ -2295,7 +2430,7 @@ fn paste_impl(
2295 Some(transaction)
2430 Some(transaction)
2296}
2431}
2297
2432
2298−pub fn replace_with_yanked(cx: &mut Context) {
2433+fn replace_with_yanked(cx: &mut Context) {
2299 let reg_name = cx.selected_register.name();
2434 let reg_name = cx.selected_register.name();
2300 let (view, doc, registers) = cx.current_with_registers();
2435 let (view, doc, registers) = cx.current_with_registers();
2301
2436
@@ -2324,7 +2459,7 @@ pub fn replace_with_yanked(cx: &mut Context) {
2324// replace => replace
2459// replace => replace
2325// default insert
2460// default insert
2326
2461
2327−pub fn paste_after(cx: &mut Context) {
2462+fn paste_after(cx: &mut Context) {
2328 let reg_name = cx.selected_register.name();
2463 let reg_name = cx.selected_register.name();
2329 let (view, doc, registers) = cx.current_with_registers();
2464 let (view, doc, registers) = cx.current_with_registers();
2330
2465
@@ -2337,7 +2472,7 @@ pub fn paste_after(cx: &mut Context) {
2337 }
2472 }
2338}
2473}
2339
2474
2340−pub fn paste_before(cx: &mut Context) {
2475+fn paste_before(cx: &mut Context) {
2341 let reg_name = cx.selected_register.name();
2476 let reg_name = cx.selected_register.name();
2342 let (view, doc, registers) = cx.current_with_registers();
2477 let (view, doc, registers) = cx.current_with_registers();
2343
2478
@@ -2367,7 +2502,7 @@ fn get_lines(doc: &Document, view_id: ViewId) -> Vec<usize> {
2367 lines
2502 lines
2368}
2503}
2369
2504
2370−pub fn indent(cx: &mut Context) {
2505+fn indent(cx: &mut Context) {
2371 let count = cx.count();
2506 let count = cx.count();
2372 let (view, doc) = cx.current();
2507 let (view, doc) = cx.current();
2373 let lines = get_lines(doc, view.id);
2508 let lines = get_lines(doc, view.id);
@@ -2386,7 +2521,7 @@ pub fn indent(cx: &mut Context) {
2386 doc.append_changes_to_history(view.id);
2521 doc.append_changes_to_history(view.id);
2387}
2522}
2388
2523
2389−pub fn unindent(cx: &mut Context) {
2524+fn unindent(cx: &mut Context) {
2390 let count = cx.count();
2525 let count = cx.count();
2391 let (view, doc) = cx.current();
2526 let (view, doc) = cx.current();
2392 let lines = get_lines(doc, view.id);
2527 let lines = get_lines(doc, view.id);
@@ -2426,7 +2561,7 @@ pub fn unindent(cx: &mut Context) {
2426 doc.append_changes_to_history(view.id);
2561 doc.append_changes_to_history(view.id);
2427}
2562}
2428
2563
2429−pub fn format_selections(cx: &mut Context) {
2564+fn format_selections(cx: &mut Context) {
2430 let (view, doc) = cx.current();
2565 let (view, doc) = cx.current();
2431
2566
2432 // via lsp if available
2567 // via lsp if available
@@ -2472,7 +2607,7 @@ pub fn format_selections(cx: &mut Context) {
2472 doc.append_changes_to_history(view.id);
2607 doc.append_changes_to_history(view.id);
2473}
2608}
2474
2609
2475−pub fn join_selections(cx: &mut Context) {
2610+fn join_selections(cx: &mut Context) {
2476 use movement::skip_while;
2611 use movement::skip_while;
2477 let (view, doc) = cx.current();
2612 let (view, doc) = cx.current();
2478 let text = doc.text();
2613 let text = doc.text();
@@ -2516,7 +2651,7 @@ pub fn join_selections(cx: &mut Context) {
2516 doc.append_changes_to_history(view.id);
2651 doc.append_changes_to_history(view.id);
2517}
2652}
2518
2653
2519−pub fn keep_selections(cx: &mut Context) {
2654+fn keep_selections(cx: &mut Context) {
2520 // keep selections matching regex
2655 // keep selections matching regex
2521 let prompt = ui::regex_prompt(cx, "keep:".to_string(), move |view, doc, _, regex| {
2656 let prompt = ui::regex_prompt(cx, "keep:".to_string(), move |view, doc, _, regex| {
2522 let text = doc.text().slice(..);
2657 let text = doc.text().slice(..);
@@ -2529,7 +2664,7 @@ pub fn keep_selections(cx: &mut Context) {
2529 cx.push_layer(Box::new(prompt));
2664 cx.push_layer(Box::new(prompt));
2530}
2665}
2531
2666
2532−pub fn keep_primary_selection(cx: &mut Context) {
2667+fn keep_primary_selection(cx: &mut Context) {
2533 let (view, doc) = cx.current();
2668 let (view, doc) = cx.current();
2534
2669
2535 let range = doc.selection(view.id).primary();
2670 let range = doc.selection(view.id).primary();
@@ -2539,14 +2674,14 @@ pub fn keep_primary_selection(cx: &mut Context) {
2539
2674
2540//
2675//
2541
2676
2542−pub fn save(cx: &mut Context) {
2677+fn save(cx: &mut Context) {
2543 // Spawns an async task to actually do the saving. This way we prevent blocking.
2678 // Spawns an async task to actually do the saving. This way we prevent blocking.
2544
2679
2545 // TODO: handle save errors somehow?
2680 // TODO: handle save errors somehow?
2546 tokio::spawn(cx.doc().save());
2681 tokio::spawn(cx.doc().save());
2547}
2682}
2548
2683
2549−pub fn completion(cx: &mut Context) {
2684+fn completion(cx: &mut Context) {
2550 // trigger on trigger char, or if user calls it
2685 // trigger on trigger char, or if user calls it
2551 // (or on word char typing??)
2686 // (or on word char typing??)
2552 // after it's triggered, if response marked is_incomplete, update on every subsequent keypress
2687 // after it's triggered, if response marked is_incomplete, update on every subsequent keypress
@@ -2636,7 +2771,7 @@ pub fn completion(cx: &mut Context) {
2636 //}
2771 //}
2637}
2772}
2638
2773
2639−pub fn hover(cx: &mut Context) {
2774+fn hover(cx: &mut Context) {
2640 let (view, doc) = cx.current();
2775 let (view, doc) = cx.current();
2641
2776
2642 let language_server = match doc.language_server() {
2777 let language_server = match doc.language_server() {
@@ -2686,7 +2821,7 @@ pub fn hover(cx: &mut Context) {
2686}
2821}
2687
2822
2688// comments
2823// comments
2689−pub fn toggle_comments(cx: &mut Context) {
2824+fn toggle_comments(cx: &mut Context) {
2690 let (view, doc) = cx.current();
2825 let (view, doc) = cx.current();
2691 let transaction = comment::toggle_line_comments(doc.text(), doc.selection(view.id));
2826 let transaction = comment::toggle_line_comments(doc.text(), doc.selection(view.id));
2692
2827
@@ -2696,7 +2831,7 @@ pub fn toggle_comments(cx: &mut Context) {
2696
2831
2697// tree sitter node selection
2832// tree sitter node selection
2698
2833
2699−pub fn expand_selection(cx: &mut Context) {
2834+fn expand_selection(cx: &mut Context) {
2700 let (view, doc) = cx.current();
2835 let (view, doc) = cx.current();
2701
2836
2702 if let Some(syntax) = doc.syntax() {
2837 if let Some(syntax) = doc.syntax() {
@@ -2706,7 +2841,7 @@ pub fn expand_selection(cx: &mut Context) {
2706 }
2841 }
2707}
2842}
2708
2843
2709−pub fn match_brackets(cx: &mut Context) {
2844+fn match_brackets(cx: &mut Context) {
2710 let (view, doc) = cx.current();
2845 let (view, doc) = cx.current();
2711
2846
2712 if let Some(syntax) = doc.syntax() {
2847 if let Some(syntax) = doc.syntax() {
@@ -2720,7 +2855,7 @@ pub fn match_brackets(cx: &mut Context) {
2720
2855
2721//
2856//
2722
2857
2723−pub fn jump_forward(cx: &mut Context) {
2858+fn jump_forward(cx: &mut Context) {
2724 let count = cx.count();
2859 let count = cx.count();
2725 let (view, doc) = cx.current();
2860 let (view, doc) = cx.current();
2726
2861
@@ -2734,7 +2869,7 @@ pub fn jump_forward(cx: &mut Context) {
2734 };
2869 };
2735}
2870}
2736
2871
2737−pub fn jump_backward(cx: &mut Context) {
2872+fn jump_backward(cx: &mut Context) {
2738 let count = cx.count();
2873 let count = cx.count();
2739 let (view, doc) = cx.current();
2874 let (view, doc) = cx.current();
2740
2875
@@ -2752,7 +2887,7 @@ pub fn jump_backward(cx: &mut Context) {
2752 };
2887 };
2753}
2888}
2754
2889
2755−pub fn window_mode(cx: &mut Context) {
2890+fn window_mode(cx: &mut Context) {
2756 cx.on_next_key(move |cx, event| {
2891 cx.on_next_key(move |cx, event| {
2757 if let KeyEvent {
2892 if let KeyEvent {
2758 code: KeyCode::Char(ch),
2893 code: KeyCode::Char(ch),
@@ -2770,12 +2905,14 @@ pub fn window_mode(cx: &mut Context) {
2770 })
2905 })
2771}
2906}
2772
2907
2773−pub fn rotate_view(cx: &mut Context) {
2908+fn rotate_view(cx: &mut Context) {
2774 cx.editor.focus_next()
2909 cx.editor.focus_next()
2775}
2910}
2776
2911
2777// split helper, clear it later
2912// split helper, clear it later
2778use helix_view::editor::Action;
2913use helix_view::editor::Action;
2914+
2915+use self::cmd::TypableCommand;
2779fn split(cx: &mut Context, action: Action) {
2916fn split(cx: &mut Context, action: Action) {
2780 use helix_view::editor::Action;
2917 use helix_view::editor::Action;
2781 let (view, doc) = cx.current();
2918 let (view, doc) = cx.current();
@@ -2791,21 +2928,21 @@ fn split(cx: &mut Context, action: Action) {
2791 doc.set_selection(view.id, selection);
2928 doc.set_selection(view.id, selection);
2792}
2929}
2793
2930
2794−pub fn hsplit(cx: &mut Context) {
2931+fn hsplit(cx: &mut Context) {
2795 split(cx, Action::HorizontalSplit);
2932 split(cx, Action::HorizontalSplit);
2796}
2933}
2797
2934
2798−pub fn vsplit(cx: &mut Context) {
2935+fn vsplit(cx: &mut Context) {
2799 split(cx, Action::VerticalSplit);
2936 split(cx, Action::VerticalSplit);
2800}
2937}
2801
2938
2802−pub fn wclose(cx: &mut Context) {
2939+fn wclose(cx: &mut Context) {
2803 let view_id = cx.view().id;
2940 let view_id = cx.view().id;
2804 // close current split
2941 // close current split
2805 cx.editor.close(view_id, /* close_buffer */ false);
2942 cx.editor.close(view_id, /* close_buffer */ false);
2806}
2943}
2807
2944
2808−pub fn select_register(cx: &mut Context) {
2945+fn select_register(cx: &mut Context) {
2809 cx.on_next_key(move |cx, event| {
2946 cx.on_next_key(move |cx, event| {
2810 if let KeyEvent {
2947 if let KeyEvent {
2811 code: KeyCode::Char(ch),
2948 code: KeyCode::Char(ch),
@@ -2817,7 +2954,7 @@ pub fn select_register(cx: &mut Context) {
2817 })
2954 })
2818}
2955}
2819
2956
2820−pub fn space_mode(cx: &mut Context) {
2957+fn space_mode(cx: &mut Context) {
2821 cx.on_next_key(move |cx, event| {
2958 cx.on_next_key(move |cx, event| {
2822 if let KeyEvent {
2959 if let KeyEvent {
2823 code: KeyCode::Char(ch),
2960 code: KeyCode::Char(ch),
@@ -2839,7 +2976,7 @@ pub fn space_mode(cx: &mut Context) {
2839 })
2976 })
2840}
2977}
2841
2978
2842−pub fn view_mode(cx: &mut Context) {
2979+fn view_mode(cx: &mut Context) {
2843 cx.on_next_key(move |cx, event| {
2980 cx.on_next_key(move |cx, event| {
2844 if let KeyEvent {
2981 if let KeyEvent {
2845 code: KeyCode::Char(ch),
2982 code: KeyCode::Char(ch),
@@ -2882,7 +3019,7 @@ pub fn view_mode(cx: &mut Context) {
2882 })
3019 })
2883}
3020}
2884
3021
2885−pub fn left_bracket_mode(cx: &mut Context) {
3022+fn left_bracket_mode(cx: &mut Context) {
2886 cx.on_next_key(move |cx, event| {
3023 cx.on_next_key(move |cx, event| {
2887 if let KeyEvent {
3024 if let KeyEvent {
2888 code: KeyCode::Char(ch),
3025 code: KeyCode::Char(ch),
@@ -2898,7 +3035,7 @@ pub fn left_bracket_mode(cx: &mut Context) {
2898 })
3035 })
2899}
3036}
2900
3037
2901−pub fn right_bracket_mode(cx: &mut Context) {
3038+fn right_bracket_mode(cx: &mut Context) {
2902 cx.on_next_key(move |cx, event| {
3039 cx.on_next_key(move |cx, event| {
2903 if let KeyEvent {
3040 if let KeyEvent {
2904 code: KeyCode::Char(ch),
3041 code: KeyCode::Char(ch),
@@ -2913,3 +3050,29 @@ pub fn right_bracket_mode(cx: &mut Context) {
2913 }
3050 }
2914 })
3051 })
2915}
3052}
3053+
3054+impl fmt::Display for Command {
3055+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3056+ let Command(name, _) = self;
3057+ f.write_str(name)
3058+ }
3059+}
3060+
3061+impl std::str::FromStr for Command {
3062+ type Err = anyhow::Error;
3063+
3064+ fn from_str(s: &str) -> Result<Self, Self::Err> {
3065+ Command::COMMAND_LIST
3066+ .iter()
3067+ .copied()
3068+ .find(|cmd| cmd.0 == s)
3069+ .ok_or_else(|| anyhow!("No command named '{}'", s))
3070+ }
3071+}
3072+
3073+impl fmt::Debug for Command {
3074+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3075+ let Command(name, _) = self;
3076+ f.debug_tuple("Command").field(name).finish()
3077+ }
3078+}
helix-term/src/config.rsnew file+33
@@ -0,0 +1,33 @@
1+use anyhow::{Error, Result};
2+use std::{collections::HashMap, str::FromStr};
3+
4+use serde::{de::Error as SerdeError, Deserialize, Serialize};
5+
6+use crate::keymap::{parse_keymaps, Keymaps};
7+
8+#[derive(Default)]
9+pub struct Config {
10+ pub keymaps: Keymaps,
11+}
12+
13+#[derive(Serialize, Deserialize)]
14+struct TomlConfig {
15+ keys: Option<HashMap<String, HashMap<String, String>>>,
16+}
17+
18+impl<'de> Deserialize<'de> for Config {
19+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
20+ where
21+ D: serde::Deserializer<'de>,
22+ {
23+ let config = TomlConfig::deserialize(deserializer)?;
24+ Ok(Self {
25+ keymaps: config
26+ .keys
27+ .map(|r| parse_keymaps(&r))
28+ .transpose()
29+ .map_err(|e| D::Error::custom(format!("Error deserializing keymap: {}", e)))?
30+ .unwrap_or_else(Keymaps::default),
31+ })
32+ }
33+}
helix-term/src/keymap.rs+500−188
@@ -1,7 +1,14 @@
1−use crate::commands::{self, Command};
1+use crate::commands;
2+pub use crate::commands::Command;
3+use anyhow::{anyhow, Error, Result};
2use helix_core::hashmap;
4use helix_core::hashmap;
3use helix_view::document::Mode;
5use helix_view::document::Mode;
6+use std::{
4−use std::collections::HashMap;
7+ collections::HashMap,
8+ fmt::Display,
9+ ops::{Deref, DerefMut},
10+ str::FromStr,
11+};
5
12
6// Kakoune-inspired:
13// Kakoune-inspired:
7// mode = {
14// mode = {
@@ -95,8 +102,10 @@ use std::collections::HashMap;
95// #[cfg(feature = "term")]
102// #[cfg(feature = "term")]
96pub use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
103pub use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
97
104
105+#[derive(Clone, Debug)]
98−pub type Keymap = HashMap<KeyEvent, Command>;
106+pub struct Keymap(pub HashMap<KeyEvent, Command>);
107+#[derive(Clone, Debug)]
99−pub type Keymaps = HashMap<Mode, Keymap>;
108+pub struct Keymaps(pub HashMap<Mode, Keymap>);
100
109
101#[macro_export]
110#[macro_export]
102macro_rules! key {
111macro_rules! key {
@@ -132,188 +141,491 @@ macro_rules! alt {
132 };
141 };
133}
142}
134
143
135−pub fn default() -> Keymaps {
144+impl Default for Keymaps {
136− let normal = hashmap!(
145+ fn default() -> Self {
137− key!('h') => commands::move_char_left as Command,
146+ let normal = Keymap(hashmap!(
138− key!('j') => commands::move_line_down,
147+ key!('h') => Command::move_char_left,
139− key!('k') => commands::move_line_up,
148+ key!('j') => Command::move_line_down,
140− key!('l') => commands::move_char_right,
149+ key!('k') => Command::move_line_up,
141−
150+ key!('l') => Command::move_char_right,
142− key!(Left) => commands::move_char_left,
151+
143− key!(Down) => commands::move_line_down,
152+ key!(Left) => Command::move_char_left,
144− key!(Up) => commands::move_line_up,
153+ key!(Down) => Command::move_line_down,
145− key!(Right) => commands::move_char_right,
154+ key!(Up) => Command::move_line_up,
146−
155+ key!(Right) => Command::move_char_right,
147− key!('t') => commands::find_till_char,
156+
148− key!('f') => commands::find_next_char,
157+ key!('t') => Command::find_till_char,
149− key!('T') => commands::till_prev_char,
158+ key!('f') => Command::find_next_char,
150− key!('F') => commands::find_prev_char,
159+ key!('T') => Command::till_prev_char,
151− // and matching set for select mode (extend)
160+ key!('F') => Command::find_prev_char,
152− //
161+ // and matching set for select mode (extend)
153− key!('r') => commands::replace,
162+ //
154− key!('R') => commands::replace_with_yanked,
163+ key!('r') => Command::replace,
155−
164+ key!('R') => Command::replace_with_yanked,
156− key!(Home) => commands::move_line_start,
165+
157− key!(End) => commands::move_line_end,
166+ key!(Home) => Command::move_line_start,
158−
167+ key!(End) => Command::move_line_end,
159− key!('w') => commands::move_next_word_start,
168+
160− key!('b') => commands::move_prev_word_start,
169+ key!('w') => Command::move_next_word_start,
161− key!('e') => commands::move_next_word_end,
170+ key!('b') => Command::move_prev_word_start,
162−
171+ key!('e') => Command::move_next_word_end,
163− key!('v') => commands::select_mode,
172+
164− key!('g') => commands::goto_mode,
173+ key!('v') => Command::select_mode,
165− key!(':') => commands::command_mode,
174+ key!('g') => Command::goto_mode,
166−
175+ key!(':') => Command::command_mode,
167− key!('i') => commands::insert_mode,
176+
168− key!('I') => commands::prepend_to_line,
177+ key!('i') => Command::insert_mode,
169− key!('a') => commands::append_mode,
178+ key!('I') => Command::prepend_to_line,
170− key!('A') => commands::append_to_line,
179+ key!('a') => Command::append_mode,
171− key!('o') => commands::open_below,
180+ key!('A') => Command::append_to_line,
172− key!('O') => commands::open_above,
181+ key!('o') => Command::open_below,
173− // [<space> ]<space> equivalents too (add blank new line, no edit)
182+ key!('O') => Command::open_above,
174−
183+ // [<space> ]<space> equivalents too (add blank new line, no edit)
175−
184+
176− key!('d') => commands::delete_selection,
185+
177− // TODO: also delete without yanking
186+ key!('d') => Command::delete_selection,
178− key!('c') => commands::change_selection,
187+ // TODO: also delete without yanking
179− // TODO: also change delete without yanking
188+ key!('c') => Command::change_selection,
180−
189+ // TODO: also change delete without yanking
181− // key!('r') => commands::replace_with_char,
190+
182−
191+ // key!('r') => Command::replace_with_char,
183− key!('s') => commands::select_regex,
192+
184− alt!('s') => commands::split_selection_on_newline,
193+ key!('s') => Command::select_regex,
185− key!('S') => commands::split_selection,
194+ alt!('s') => Command::split_selection_on_newline,
186− key!(';') => commands::collapse_selection,
195+ key!('S') => Command::split_selection,
187− alt!(';') => commands::flip_selections,
196+ key!(';') => Command::collapse_selection,
188− key!('%') => commands::select_all,
197+ alt!(';') => Command::flip_selections,
189− key!('x') => commands::select_line,
198+ key!('%') => Command::select_all,
190− key!('X') => commands::extend_line,
199+ key!('x') => Command::select_line,
191− // or select mode X?
200+ key!('X') => Command::extend_line,
192− // extend_to_whole_line, crop_to_whole_line
201+ // or select mode X?
193−
202+ // extend_to_whole_line, crop_to_whole_line
194−
203+
195− key!('m') => commands::match_brackets,
204+
196− // TODO: refactor into
205+ key!('m') => Command::match_brackets,
197− // key!('m') => commands::select_to_matching,
206+ // TODO: refactor into
198− // key!('M') => commands::back_select_to_matching,
207+ // key!('m') => commands::select_to_matching,
199− // select mode extend equivalents
208+ // key!('M') => commands::back_select_to_matching,
200−
209+ // select mode extend equivalents
201− // key!('.') => commands::repeat_insert,
210+
202− // repeat_select
211+ // key!('.') => commands::repeat_insert,
203−
212+ // repeat_select
204− // TODO: figure out what key to use
213+
205− // key!('[') => commands::expand_selection, ??
214+ // TODO: figure out what key to use
206− key!('[') => commands::left_bracket_mode,
215+ // key!('[') => Command::expand_selection, ??
207− key!(']') => commands::right_bracket_mode,
216+ key!('[') => Command::left_bracket_mode,
208−
217+ key!(']') => Command::right_bracket_mode,
209− key!('/') => commands::search,
218+
210− // ? for search_reverse
219+ key!('/') => Command::search,
211− key!('n') => commands::search_next,
220+ // ? for search_reverse
212− key!('N') => commands::extend_search_next,
221+ key!('n') => Command::search_next,
213− // N for search_prev
222+ key!('N') => Command::extend_search_next,
214− key!('*') => commands::search_selection,
223+ // N for search_prev
215−
224+ key!('*') => Command::search_selection,
216− key!('u') => commands::undo,
225+
217− key!('U') => commands::redo,
226+ key!('u') => Command::undo,
218−
227+ key!('U') => Command::redo,
219− key!('y') => commands::yank,
228+
220− // yank_all
229+ key!('y') => Command::yank,
221− key!('p') => commands::paste_after,
230+ // yank_all
222− // paste_all
231+ key!('p') => Command::paste_after,
223− key!('P') => commands::paste_before,
232+ // paste_all
224−
233+ key!('P') => Command::paste_before,
225− key!('>') => commands::indent,
234+
226− key!('<') => commands::unindent,
235+ key!('>') => Command::indent,
227− key!('=') => commands::format_selections,
236+ key!('<') => Command::unindent,
228− key!('J') => commands::join_selections,
237+ key!('=') => Command::format_selections,
229− // TODO: conflicts hover/doc
238+ key!('J') => Command::join_selections,
230− key!('K') => commands::keep_selections,
239+ // TODO: conflicts hover/doc
231− // TODO: and another method for inverse
240+ key!('K') => Command::keep_selections,
232−
241+ // TODO: and another method for inverse
233− // TODO: clashes with space mode
242+
234− key!(' ') => commands::keep_primary_selection,
243+ // TODO: clashes with space mode
235−
244+ key!(' ') => Command::keep_primary_selection,
236− // key!('q') => commands::record_macro,
245+
237− // key!('Q') => commands::replay_macro,
246+ // key!('q') => Command::record_macro,
238−
247+ // key!('Q') => Command::replay_macro,
239− // ~ / apostrophe => change case
248+
240− // & align selections
249+ // ~ / apostrophe => change case
241− // _ trim selections
250+ // & align selections
242−
251+ // _ trim selections
243− // C / altC = copy (repeat) selections on prev/next lines
252+
244−
253+ // C / altC = copy (repeat) selections on prev/next lines
245− key!(Esc) => commands::normal_mode,
254+
246− key!(PageUp) => commands::page_up,
255+ key!(Esc) => Command::normal_mode,
247− key!(PageDown) => commands::page_down,
256+ key!(PageUp) => Command::page_up,
248− ctrl!('b') => commands::page_up,
257+ key!(PageDown) => Command::page_down,
249− ctrl!('f') => commands::page_down,
258+ ctrl!('b') => Command::page_up,
250− ctrl!('u') => commands::half_page_up,
259+ ctrl!('f') => Command::page_down,
251− ctrl!('d') => commands::half_page_down,
260+ ctrl!('u') => Command::half_page_up,
252−
261+ ctrl!('d') => Command::half_page_down,
253− ctrl!('w') => commands::window_mode,
262+
254−
263+ ctrl!('w') => Command::window_mode,
255− // move under <space>c
264+
256− ctrl!('c') => commands::toggle_comments,
265+ // move under <space>c
257− key!('K') => commands::hover,
266+ ctrl!('c') => Command::toggle_comments,
258−
267+ key!('K') => Command::hover,
259− // z family for save/restore/combine from/to sels from register
268+
260−
269+ // z family for save/restore/combine from/to sels from register
261− // supposedly ctrl!('i') but did not work
270+
262− key!(Tab) => commands::jump_forward,
271+ // supposedly ctrl!('i') but did not work
263− ctrl!('o') => commands::jump_backward,
272+ key!(Tab) => Command::jump_forward,
264− // ctrl!('s') => commands::save_selection,
273+ ctrl!('o') => Command::jump_backward,
265−
274+ // ctrl!('s') => Command::save_selection,
266− key!(' ') => commands::space_mode,
275+
267− key!('z') => commands::view_mode,
276+ key!(' ') => Command::space_mode,
268−
277+ key!('z') => Command::view_mode,
269− key!('"') => commands::select_register,
278+
270− );
279+ key!('"') => Command::select_register,
271− // TODO: decide whether we want normal mode to also be select mode (kakoune-like), or whether
280+ ));
272− // we keep this separate select mode. More keys can fit into normal mode then, but it's weird
281+ // TODO: decide whether we want normal mode to also be select mode (kakoune-like), or whether
273− // because some selection operations can now be done from normal mode, some from select mode.
282+ // we keep this separate select mode. More keys can fit into normal mode then, but it's weird
274− let mut select = normal.clone();
283+ // because some selection operations can now be done from normal mode, some from select mode.
275− select.extend(
284+ let mut select = normal.clone();
276− hashmap!(
285+ select.0.extend(
277− key!('h') => commands::extend_char_left as Command,
286+ hashmap!(
278− key!('j') => commands::extend_line_down,
287+ key!('h') => Command::extend_char_left,
279− key!('k') => commands::extend_line_up,
288+ key!('j') => Command::extend_line_down,
280− key!('l') => commands::extend_char_right,
289+ key!('k') => Command::extend_line_up,
281−
290+ key!('l') => Command::extend_char_right,
282− key!(Left) => commands::extend_char_left,
291+
283− key!(Down) => commands::extend_line_down,
292+ key!(Left) => Command::extend_char_left,
284− key!(Up) => commands::extend_line_up,
293+ key!(Down) => Command::extend_line_down,
285− key!(Right) => commands::extend_char_right,
294+ key!(Up) => Command::extend_line_up,
286−
295+ key!(Right) => Command::extend_char_right,
287− key!('w') => commands::extend_next_word_start,
296+
288− key!('b') => commands::extend_prev_word_start,
297+ key!('w') => Command::extend_next_word_start,
289− key!('e') => commands::extend_next_word_end,
298+ key!('b') => Command::extend_prev_word_start,
290−
299+ key!('e') => Command::extend_next_word_end,
291− key!('t') => commands::extend_till_char,
300+
292− key!('f') => commands::extend_next_char,
301+ key!('t') => Command::extend_till_char,
293−
302+ key!('f') => Command::extend_next_char,
294− key!('T') => commands::extend_till_prev_char,
303+
295− key!('F') => commands::extend_prev_char,
304+ key!('T') => Command::extend_till_prev_char,
296− key!(Home) => commands::extend_line_start,
305+ key!('F') => Command::extend_prev_char,
297− key!(End) => commands::extend_line_end,
306+ key!(Home) => Command::extend_line_start,
298− key!(Esc) => commands::exit_select_mode,
307+ key!(End) => Command::extend_line_end,
299− )
308+ key!(Esc) => Command::exit_select_mode,
300− .into_iter(),
309+ )
301− );
310+ .into_iter(),
302−
311+ );
303− hashmap!(
312+
304− // as long as you cast the first item, rust is able to infer the other cases
313+ Keymaps(hashmap!(
305− // TODO: select could be normal mode with some bindings merged over
314+ // as long as you cast the first item, rust is able to infer the other cases
306− Mode::Normal => normal,
315+ // TODO: select could be normal mode with some bindings merged over
307− Mode::Select => select,
316+ Mode::Normal => normal,
308− Mode::Insert => hashmap!(
317+ Mode::Select => select,
309− key!(Esc) => commands::normal_mode as Command,
318+ Mode::Insert => Keymap(hashmap!(
310− key!(Backspace) => commands::insert::delete_char_backward,
319+ key!(Esc) => Command::normal_mode as Command,
311− key!(Delete) => commands::insert::delete_char_forward,
320+ key!(Backspace) => Command::delete_char_backward,
312− key!(Enter) => commands::insert::insert_newline,
321+ key!(Delete) => Command::delete_char_forward,
313− key!(Tab) => commands::insert::insert_tab,
322+ key!(Enter) => Command::insert_newline,
314−
323+ key!(Tab) => Command::insert_tab,
315− ctrl!('x') => commands::completion,
324+ ctrl!('x') => Command::completion,
316− ctrl!('w') => commands::insert::delete_word_backward,
325+ ctrl!('w') => Command::delete_word_backward,
317− ),
326+ )),
318− )
327+ ))
328+ }
329+}
330+
331+// Newtype wrapper over keys to allow toml serialization/parsing
332+#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Hash)]
333+pub struct RepresentableKeyEvent(pub KeyEvent);
334+impl Display for RepresentableKeyEvent {
335+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
336+ let Self(key) = self;
337+ f.write_fmt(format_args!(
338+ "{}{}{}",
339+ if key.modifiers.contains(KeyModifiers::SHIFT) {
340+ "S-"
341+ } else {
342+ ""
343+ },
344+ if key.modifiers.contains(KeyModifiers::ALT) {
345+ "A-"
346+ } else {
347+ ""
348+ },
349+ if key.modifiers.contains(KeyModifiers::CONTROL) {
350+ "C-"
351+ } else {
352+ ""
353+ },
354+ ))?;
355+ match key.code {
356+ KeyCode::Backspace => f.write_str("backspace")?,
357+ KeyCode::Enter => f.write_str("ret")?,
358+ KeyCode::Left => f.write_str("left")?,
359+ KeyCode::Right => f.write_str("right")?,
360+ KeyCode::Up => f.write_str("up")?,
361+ KeyCode::Down => f.write_str("down")?,
362+ KeyCode::Home => f.write_str("home")?,
363+ KeyCode::End => f.write_str("end")?,
364+ KeyCode::PageUp => f.write_str("pageup")?,
365+ KeyCode::PageDown => f.write_str("pagedown")?,
366+ KeyCode::Tab => f.write_str("tab")?,
367+ KeyCode::BackTab => f.write_str("backtab")?,
368+ KeyCode::Delete => f.write_str("del")?,
369+ KeyCode::Insert => f.write_str("ins")?,
370+ KeyCode::Null => f.write_str("null")?,
371+ KeyCode::Esc => f.write_str("esc")?,
372+ KeyCode::Char('<') => f.write_str("lt")?,
373+ KeyCode::Char('>') => f.write_str("gt")?,
374+ KeyCode::Char('+') => f.write_str("plus")?,
375+ KeyCode::Char('-') => f.write_str("minus")?,
376+ KeyCode::Char(';') => f.write_str("semicolon")?,
377+ KeyCode::Char('%') => f.write_str("percent")?,
378+ KeyCode::F(i) => f.write_fmt(format_args!("F{}", i))?,
379+ KeyCode::Char(c) => f.write_fmt(format_args!("{}", c))?,
380+ };
381+ Ok(())
382+ }
383+}
384+
385+impl FromStr for RepresentableKeyEvent {
386+ type Err = Error;
387+
388+ fn from_str(s: &str) -> Result<Self, Self::Err> {
389+ let mut tokens: Vec<_> = s.split('-').collect();
390+ let code = match tokens.pop().ok_or_else(|| anyhow!("Missing key code"))? {
391+ "backspace" => KeyCode::Backspace,
392+ "space" => KeyCode::Char(' '),
393+ "ret" => KeyCode::Enter,
394+ "lt" => KeyCode::Char('<'),
395+ "gt" => KeyCode::Char('>'),
396+ "plus" => KeyCode::Char('+'),
397+ "minus" => KeyCode::Char('-'),
398+ "semicolon" => KeyCode::Char(';'),
399+ "percent" => KeyCode::Char('%'),
400+ "left" => KeyCode::Left,
401+ "right" => KeyCode::Right,
402+ "up" => KeyCode::Down,
403+ "home" => KeyCode::Home,
404+ "end" => KeyCode::End,
405+ "pageup" => KeyCode::PageUp,
406+ "pagedown" => KeyCode::PageDown,
407+ "tab" => KeyCode::Tab,
408+ "backtab" => KeyCode::BackTab,
409+ "del" => KeyCode::Delete,
410+ "ins" => KeyCode::Insert,
411+ "null" => KeyCode::Null,
412+ "esc" => KeyCode::Esc,
413+ single if single.len() == 1 => KeyCode::Char(single.chars().next().unwrap()),
414+ function if function.len() > 1 && function.starts_with('F') => {
415+ let function: String = function.chars().skip(1).collect();
416+ let function = str::parse::<u8>(&function)?;
417+ (function > 0 && function < 13)
418+ .then(|| KeyCode::F(function))
419+ .ok_or_else(|| anyhow!("Invalid function key '{}'", function))?
420+ }
421+ invalid => return Err(anyhow!("Invalid key code '{}'", invalid)),
422+ };
423+
424+ let mut modifiers = KeyModifiers::empty();
425+ for token in tokens {
426+ let flag = match token {
427+ "S" => KeyModifiers::SHIFT,
428+ "A" => KeyModifiers::ALT,
429+ "C" => KeyModifiers::CONTROL,
430+ _ => return Err(anyhow!("Invalid key modifier '{}-'", token)),
431+ };
432+
433+ if modifiers.contains(flag) {
434+ return Err(anyhow!("Repeated key modifier '{}-'", token));
435+ }
436+ modifiers.insert(flag);
437+ }
438+
439+ Ok(RepresentableKeyEvent(KeyEvent { code, modifiers }))
440+ }
441+}
442+
443+pub fn parse_keymaps(toml_keymaps: &HashMap<String, HashMap<String, String>>) -> Result<Keymaps> {
444+ let mut keymaps = Keymaps::default();
445+
446+ for (mode, map) in toml_keymaps {
447+ let mode = Mode::from_str(&mode)?;
448+ for (key, command) in map {
449+ let key = str::parse::<RepresentableKeyEvent>(&key)?;
450+ let command = str::parse::<Command>(&command)?;
451+ keymaps.0.get_mut(&mode).unwrap().0.insert(key.0, command);
452+ }
453+ }
454+ Ok(keymaps)
455+}
456+
457+impl Deref for Keymap {
458+ type Target = HashMap<KeyEvent, Command>;
459+ fn deref(&self) -> &Self::Target {
460+ &self.0
461+ }
462+}
463+
464+impl Deref for Keymaps {
465+ type Target = HashMap<Mode, Keymap>;
466+ fn deref(&self) -> &Self::Target {
467+ &self.0
468+ }
469+}
470+
471+impl DerefMut for Keymap {
472+ fn deref_mut(&mut self) -> &mut Self::Target {
473+ &mut self.0
474+ }
475+}
476+
477+impl DerefMut for Keymaps {
478+ fn deref_mut(&mut self) -> &mut Self::Target {
479+ &mut self.0
480+ }
481+}
482+
483+#[cfg(test)]
484+mod test {
485+ use crate::config::Config;
486+
487+ use super::*;
488+
489+ impl PartialEq for Command {
490+ fn eq(&self, other: &Self) -> bool {
491+ self.name() == other.name()
492+ }
493+ }
494+
495+ #[test]
496+ fn parsing_keymaps_config_file() {
497+ let sample_keymaps = r#"
498+ [keys.insert]
499+ y = "move_line_down"
500+ S-C-a = "delete_selection"
501+
502+ [keys.normal]
503+ A-F12 = "move_next_word_end"
504+ "#;
505+
506+ let config: Config = toml::from_str(sample_keymaps).unwrap();
507+ assert_eq!(
508+ *config
509+ .keymaps
510+ .0
511+ .get(&Mode::Insert)
512+ .unwrap()
513+ .0
514+ .get(&KeyEvent {
515+ code: KeyCode::Char('y'),
516+ modifiers: KeyModifiers::NONE
517+ })
518+ .unwrap(),
519+ Command::move_line_down
520+ );
521+ assert_eq!(
522+ *config
523+ .keymaps
524+ .0
525+ .get(&Mode::Insert)
526+ .unwrap()
527+ .0
528+ .get(&KeyEvent {
529+ code: KeyCode::Char('a'),
530+ modifiers: KeyModifiers::SHIFT | KeyModifiers::CONTROL
531+ })
532+ .unwrap(),
533+ Command::delete_selection
534+ );
535+ assert_eq!(
536+ *config
537+ .keymaps
538+ .0
539+ .get(&Mode::Normal)
540+ .unwrap()
541+ .0
542+ .get(&KeyEvent {
543+ code: KeyCode::F(12),
544+ modifiers: KeyModifiers::ALT
545+ })
546+ .unwrap(),
547+ Command::move_next_word_end
548+ );
549+ }
550+
551+ #[test]
552+ fn parsing_unmodified_keys() {
553+ assert_eq!(
554+ str::parse::<RepresentableKeyEvent>("backspace").unwrap(),
555+ RepresentableKeyEvent(KeyEvent {
556+ code: KeyCode::Backspace,
557+ modifiers: KeyModifiers::NONE
558+ })
559+ );
560+
561+ assert_eq!(
562+ str::parse::<RepresentableKeyEvent>("left").unwrap(),
563+ RepresentableKeyEvent(KeyEvent {
564+ code: KeyCode::Left,
565+ modifiers: KeyModifiers::NONE
566+ })
567+ );
568+
569+ assert_eq!(
570+ str::parse::<RepresentableKeyEvent>(",").unwrap(),
571+ RepresentableKeyEvent(KeyEvent {
572+ code: KeyCode::Char(','),
573+ modifiers: KeyModifiers::NONE
574+ })
575+ );
576+
577+ assert_eq!(
578+ str::parse::<RepresentableKeyEvent>("w").unwrap(),
579+ RepresentableKeyEvent(KeyEvent {
580+ code: KeyCode::Char('w'),
581+ modifiers: KeyModifiers::NONE
582+ })
583+ );
584+
585+ assert_eq!(
586+ str::parse::<RepresentableKeyEvent>("F12").unwrap(),
587+ RepresentableKeyEvent(KeyEvent {
588+ code: KeyCode::F(12),
589+ modifiers: KeyModifiers::NONE
590+ })
591+ );
592+ }
593+
594+ fn parsing_modified_keys() {
595+ assert_eq!(
596+ str::parse::<RepresentableKeyEvent>("S-minus").unwrap(),
597+ RepresentableKeyEvent(KeyEvent {
598+ code: KeyCode::Char('-'),
599+ modifiers: KeyModifiers::SHIFT
600+ })
601+ );
602+
603+ assert_eq!(
604+ str::parse::<RepresentableKeyEvent>("C-A-S-F12").unwrap(),
605+ RepresentableKeyEvent(KeyEvent {
606+ code: KeyCode::F(12),
607+ modifiers: KeyModifiers::SHIFT | KeyModifiers::CONTROL | KeyModifiers::ALT
608+ })
609+ );
610+
611+ assert_eq!(
612+ str::parse::<RepresentableKeyEvent>("S-C-2").unwrap(),
613+ RepresentableKeyEvent(KeyEvent {
614+ code: KeyCode::F(2),
615+ modifiers: KeyModifiers::SHIFT | KeyModifiers::CONTROL
616+ })
617+ );
618+ }
619+
620+ #[test]
621+ fn parsing_nonsensical_keys_fails() {
622+ assert!(str::parse::<RepresentableKeyEvent>("F13").is_err());
623+ assert!(str::parse::<RepresentableKeyEvent>("F0").is_err());
624+ assert!(str::parse::<RepresentableKeyEvent>("aaa").is_err());
625+ assert!(str::parse::<RepresentableKeyEvent>("S-S-a").is_err());
626+ assert!(str::parse::<RepresentableKeyEvent>("C-A-S-C-1").is_err());
627+ assert!(str::parse::<RepresentableKeyEvent>("FU").is_err());
628+ assert!(str::parse::<RepresentableKeyEvent>("123").is_err());
629+ assert!(str::parse::<RepresentableKeyEvent>("S--").is_err());
630+ }
319}
631}
helix-term/src/lib.rs+1
@@ -4,5 +4,6 @@ pub mod application;
4pub mod args;
4pub mod args;
5pub mod commands;
5pub mod commands;
6pub mod compositor;
6pub mod compositor;
7+pub mod config;
7pub mod keymap;
8pub mod keymap;
8pub mod ui;
9pub mod ui;
helix-term/src/main.rs+9−2
@@ -1,6 +1,6 @@
1use helix_term::application::Application;
1use helix_term::application::Application;
2use helix_term::args::Args;
2use helix_term::args::Args;
3−
3+use helix_term::config::Config;
4use std::path::PathBuf;
4use std::path::PathBuf;
5
5
6use anyhow::{Context, Result};
6use anyhow::{Context, Result};
@@ -89,10 +89,17 @@ FLAGS:
89 std::fs::create_dir_all(&conf_dir).ok();
89 std::fs::create_dir_all(&conf_dir).ok();
90 }
90 }
91
91
92+ let config = std::fs::read_to_string(conf_dir.join("config.toml"))
93+ .ok()
94+ .map(|s| toml::from_str(&s))
95+ .transpose()?
96+ .or_else(|| Some(Config::default()))
97+ .unwrap();
98+
92 setup_logging(logpath, args.verbosity).context("failed to initialize logging")?;
99 setup_logging(logpath, args.verbosity).context("failed to initialize logging")?;
93
100
94 // TODO: use the thread local executor to spawn the application task separately from the work pool
101 // TODO: use the thread local executor to spawn the application task separately from the work pool
95− let mut app = Application::new(args).context("unable to create new appliction")?;
102+ let mut app = Application::new(args, config).context("unable to create new application")?;
96 app.run().await.unwrap();
103 app.run().await.unwrap();
97
104
98 Ok(())
105 Ok(())
helix-term/src/ui/editor.rs+12−15
@@ -11,10 +11,7 @@ use helix_core::{
11 syntax::{self, HighlightEvent},
11 syntax::{self, HighlightEvent},
12 Position, Range,
12 Position, Range,
13};
13};
14−use helix_view::{
15− document::{IndentStyle, Mode},
16− Document, Editor, Theme, View,
14+use helix_view::{document::Mode, Document, Editor, Theme, View};
17−};
18use std::borrow::Cow;
15use std::borrow::Cow;
19
16
20use crossterm::{
17use crossterm::{
@@ -30,7 +27,7 @@ use tui::{
30};
27};
31
28
32pub struct EditorView {
29pub struct EditorView {
33− keymap: Keymaps,
30+ keymaps: Keymaps,
34 on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
31 on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
35 last_insert: (commands::Command, Vec<KeyEvent>),
32 last_insert: (commands::Command, Vec<KeyEvent>),
36 completion: Option<Completion>,
33 completion: Option<Completion>,
@@ -40,16 +37,16 @@ const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
40
37
41impl Default for EditorView {
38impl Default for EditorView {
42 fn default() -> Self {
39 fn default() -> Self {
43− Self::new()
40+ Self::new(Keymaps::default())
44 }
41 }
45}
42}
46
43
47impl EditorView {
44impl EditorView {
48− pub fn new() -> Self {
45+ pub fn new(keymaps: Keymaps) -> Self {
49 Self {
46 Self {
50− keymap: keymap::default(),
47+ keymaps,
51 on_next_key: None,
48 on_next_key: None,
52− last_insert: (commands::normal_mode, Vec::new()),
49+ last_insert: (commands::Command::normal_mode, Vec::new()),
53 completion: None,
50 completion: None,
54 }
51 }
55 }
52 }
@@ -546,8 +543,8 @@ impl EditorView {
546 }
543 }
547
544
548 fn insert_mode(&self, cx: &mut commands::Context, event: KeyEvent) {
545 fn insert_mode(&self, cx: &mut commands::Context, event: KeyEvent) {
549− if let Some(command) = self.keymap[&Mode::Insert].get(&event) {
546+ if let Some(command) = self.keymaps[&Mode::Insert].get(&event) {
550− command(cx);
547+ command.execute(cx);
551 } else if let KeyEvent {
548 } else if let KeyEvent {
552 code: KeyCode::Char(ch),
549 code: KeyCode::Char(ch),
553 ..
550 ..
@@ -568,7 +565,7 @@ impl EditorView {
568 // special handling for repeat operator
565 // special handling for repeat operator
569 key!('.') => {
566 key!('.') => {
570 // first execute whatever put us into insert mode
567 // first execute whatever put us into insert mode
571− (self.last_insert.0)(cxt);
568+ self.last_insert.0.execute(cxt);
572 // then replay the inputs
569 // then replay the inputs
573 for key in &self.last_insert.1 {
570 for key in &self.last_insert.1 {
574 self.insert_mode(cxt, *key)
571 self.insert_mode(cxt, *key)
@@ -584,8 +581,8 @@ impl EditorView {
584 // set the register
581 // set the register
585 cxt.selected_register = cxt.editor.selected_register.take();
582 cxt.selected_register = cxt.editor.selected_register.take();
586
583
587− if let Some(command) = self.keymap[&mode].get(&event) {
584+ if let Some(command) = self.keymaps[&mode].get(&event) {
588− command(cxt);
585+ command.execute(cxt);
589 }
586 }
590 }
587 }
591 }
588 }
@@ -699,7 +696,7 @@ impl Component for EditorView {
699 // how we entered insert mode is important, and we should track that so
696 // how we entered insert mode is important, and we should track that so
700 // we can repeat the side effect.
697 // we can repeat the side effect.
701
698
702− self.last_insert.0 = self.keymap[&mode][&key];
699+ self.last_insert.0 = self.keymaps[&mode][&key];
703 self.last_insert.1.clear();
700 self.last_insert.1.clear();
704 }
701 }
705 (Mode::Insert, Mode::Normal) => {
702 (Mode::Insert, Mode::Normal) => {
helix-view/src/document.rs+26−1
@@ -1,7 +1,9 @@
1−use anyhow::{Context, Error};
1+use anyhow::{anyhow, Context, Error};
2use std::cell::Cell;
2use std::cell::Cell;
3+use std::fmt::Display;
3use std::future::Future;
4use std::future::Future;
4use std::path::{Component, Path, PathBuf};
5use std::path::{Component, Path, PathBuf};
6+use std::str::FromStr;
5use std::sync::Arc;
7use std::sync::Arc;
6
8
7use helix_core::{
9use helix_core::{
@@ -86,6 +88,29 @@ impl fmt::Debug for Document {
86 }
88 }
87}
89}
88
90
91+impl Display for Mode {
92+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93+ match self {
94+ Mode::Normal => f.write_str("normal"),
95+ Mode::Select => f.write_str("select"),
96+ Mode::Insert => f.write_str("insert"),
97+ }
98+ }
99+}
100+
101+impl FromStr for Mode {
102+ type Err = Error;
103+
104+ fn from_str(s: &str) -> Result<Self, Self::Err> {
105+ match s {
106+ "normal" => Ok(Mode::Normal),
107+ "select" => Ok(Mode::Select),
108+ "insert" => Ok(Mode::Insert),
109+ _ => Err(anyhow!("Invalid mode '{}'", s)),
110+ }
111+ }
112+}
113+
89/// Like std::mem::replace() except it allows the replacement value to be mapped from the
114/// Like std::mem::replace() except it allows the replacement value to be mapped from the
90/// original value.
115/// original value.
91fn take_with<T, F>(mut_ref: &mut T, closure: F)
116fn take_with<T, F>(mut_ref: &mut T, closure: F)