on master help sign in
Goto mode use infobox

In the meantime, change gm to gc. Remove extra space in mode title.

commitbbbbfa9bcfb7b46d2ca2c8ac85891b1393bfe401 authorIvan Tham <pickfire@riseup.net> dateSat 3 Jul 2021, 23:39 (5 years ago) parent5977b07
2 files changed+226−231
helix-term/src/commands.rs+220−225 helix-term/src/keymap.rs+6−6
helix-term/src/commands.rs+220−225
@@ -165,17 +165,12 @@ impl Command {
165165 move_char_right,
166166 move_line_up,
167167 move_line_down,
168− move_line_end,
169− move_line_start,
170− move_first_nonwhitespace,
171168 move_next_word_start,
172169 move_prev_word_start,
173170 move_next_word_end,
174171 move_next_long_word_start,
175172 move_prev_long_word_start,
176173 move_next_long_word_end,
177− move_file_start,
178− move_file_end,
179174 extend_next_word_start,
180175 extend_prev_word_start,
181176 extend_next_word_end,
@@ -187,7 +182,6 @@ impl Command {
187182 find_prev_char,
188183 extend_till_prev_char,
189184 extend_prev_char,
190− extend_first_nonwhitespace,
191185 replace,
192186 page_up,
193187 page_down,
@@ -197,8 +191,6 @@ impl Command {
197191 extend_char_right,
198192 extend_line_up,
199193 extend_line_down,
200− extend_line_end,
201− extend_line_start,
202194 select_all,
203195 select_regex,
204196 split_selection,
@@ -229,11 +221,17 @@ impl Command {
229221 goto_definition,
230222 goto_type_definition,
231223 goto_implementation,
224+ goto_file_start,
225+ goto_file_end,
232226 goto_reference,
233227 goto_first_diag,
234228 goto_last_diag,
235229 goto_next_diag,
236230 goto_prev_diag,
231+ goto_line_start,
232+ goto_line_end,
233+ goto_line_end_newline,
234+ goto_first_nonwhitespace,
237235 signature_help,
238236 insert_tab,
239237 insert_newline,
@@ -360,7 +358,7 @@ fn move_line_down(cx: &mut Context) {
360358 doc.set_selection(view.id, selection);
361359}
362360
363−fn move_line_end(cx: &mut Context) {
361+fn goto_line_end(cx: &mut Context) {
364362 let (view, doc) = current!(cx.editor);
365363
366364 let selection = doc.selection(view.id).transform(|range| {
@@ -371,13 +369,33 @@ fn move_line_end(cx: &mut Context) {
371369 let pos = graphemes::nth_prev_grapheme_boundary(text.slice(..), pos, 1);
372370 let pos = range.head.max(pos).max(text.line_to_char(line));
373371
372+ Range::new(
373+ match doc.mode {
374+ Mode::Normal | Mode::Insert => pos,
375+ Mode::Select => range.anchor,
376+ },
377+ pos,
378+ )
379+ });
380+
381+ doc.set_selection(view.id, selection);
382+}
383+
384+fn goto_line_end_newline(cx: &mut Context) {
385+ let (view, doc) = current!(cx.editor);
386+
387+ let selection = doc.selection(view.id).transform(|range| {
388+ let text = doc.text();
389+ let line = text.char_to_line(range.head);
390+
391+ let pos = line_end_char_index(&text.slice(..), line);
374392 Range::new(pos, pos)
375393 });
376394
377395 doc.set_selection(view.id, selection);
378396}
379397
380−fn move_line_start(cx: &mut Context) {
398+fn goto_line_start(cx: &mut Context) {
381399 let (view, doc) = current!(cx.editor);
382400
383401 let selection = doc.selection(view.id).transform(|range| {
@@ -386,13 +404,19 @@ fn move_line_start(cx: &mut Context) {
386404
387405 // adjust to start of the line
388406 let pos = text.line_to_char(line);
389− Range::new(pos, pos)
407+ Range::new(
408+ match doc.mode {
409+ Mode::Normal => range.anchor,
410+ Mode::Select | Mode::Insert => pos,
411+ },
412+ pos,
413+ )
390414 });
391415
392416 doc.set_selection(view.id, selection);
393417}
394418
395−fn move_first_nonwhitespace(cx: &mut Context) {
419+fn goto_first_nonwhitespace(cx: &mut Context) {
396420 let (view, doc) = current!(cx.editor);
397421
398422 let selection = doc.selection(view.id).transform(|range| {
@@ -401,7 +425,14 @@ fn move_first_nonwhitespace(cx: &mut Context) {
401425
402426 if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) {
403427 let pos = pos + text.line_to_char(line_idx);
404− Range::new(pos, pos)
428+ Range::new(
429+ match doc.mode {
430+ Mode::Normal => pos,
431+ Mode::Select => range.anchor,
432+ Mode::Insert => unreachable!(),
433+ },
434+ pos,
435+ )
405436 } else {
406437 range
407438 }
@@ -410,6 +441,37 @@ fn move_first_nonwhitespace(cx: &mut Context) {
410441 doc.set_selection(view.id, selection);
411442}
412443
444+fn goto_window_top(cx: &mut Context) {
445+ let (view, doc) = current!(cx.editor);
446+
447+ let scrolloff = PADDING.min(view.area.height as usize / 2); // TODO: user pref
448+ let line = (view.first_line + scrolloff).min(view.last_line(doc).saturating_sub(scrolloff));
449+ let pos = doc.text().line_to_char(line);
450+
451+ doc.set_selection(view.id, Selection::point(pos));
452+}
453+
454+fn goto_window_center(cx: &mut Context) {
455+ let (view, doc) = current!(cx.editor);
456+
457+ let scrolloff = PADDING.min(view.area.height as usize / 2); // TODO: user pref
458+ let line = view.first_line + (view.area.height as usize / 2);
459+ let line = line.min(view.last_line(doc).saturating_sub(scrolloff));
460+ let pos = doc.text().line_to_char(line);
461+
462+ doc.set_selection(view.id, Selection::point(pos));
463+}
464+
465+fn goto_window_bottom(cx: &mut Context) {
466+ let (view, doc) = current!(cx.editor);
467+
468+ let scrolloff = PADDING.min(view.area.height as usize / 2); // TODO: user pref
469+ let line = view.last_line(doc).saturating_sub(scrolloff);
470+ let pos = doc.text().line_to_char(line);
471+
472+ doc.set_selection(view.id, Selection::point(pos));
473+}
474+
413475// TODO: move vs extend could take an extra type Extend/Move that would
414476// Range::new(if Move { pos } if Extend { range.anchor }, pos)
415477// since these all really do the same thing
@@ -486,13 +548,13 @@ fn move_next_long_word_end(cx: &mut Context) {
486548 doc.set_selection(view.id, selection);
487549}
488550
489−fn move_file_start(cx: &mut Context) {
551+fn goto_file_start(cx: &mut Context) {
490552 push_jump(cx.editor);
491553 let (view, doc) = current!(cx.editor);
492554 doc.set_selection(view.id, Selection::point(0));
493555}
494556
495−fn move_file_end(cx: &mut Context) {
557+fn goto_file_end(cx: &mut Context) {
496558 push_jump(cx.editor);
497559 let (view, doc) = current!(cx.editor);
498560 let text = doc.text();
@@ -671,24 +733,6 @@ fn extend_prev_char(cx: &mut Context) {
671733 )
672734}
673735
674−fn extend_first_nonwhitespace(cx: &mut Context) {
675− let (view, doc) = current!(cx.editor);
676−
677− let selection = doc.selection(view.id).transform(|range| {
678− let text = doc.text();
679− let line_idx = text.char_to_line(range.head);
680−
681− if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) {
682− let pos = pos + text.line_to_char(line_idx);
683− Range::new(range.anchor, pos)
684− } else {
685− range
686− }
687− });
688−
689− doc.set_selection(view.id, selection);
690−}
691−
692736fn replace(cx: &mut Context) {
693737 let mut buf = [0u8; 4]; // To hold utf8 encoded char.
694738
@@ -834,38 +878,6 @@ fn extend_line_down(cx: &mut Context) {
834878 doc.set_selection(view.id, selection);
835879}
836880
837−fn extend_line_end(cx: &mut Context) {
838− let (view, doc) = current!(cx.editor);
839−
840− let selection = doc.selection(view.id).transform(|range| {
841− let text = doc.text();
842− let line = text.char_to_line(range.head);
843−
844− let pos = line_end_char_index(&text.slice(..), line);
845− let pos = graphemes::nth_prev_grapheme_boundary(text.slice(..), pos, 1);
846− let pos = range.head.max(pos).max(text.line_to_char(line));
847−
848− Range::new(range.anchor, pos)
849− });
850−
851− doc.set_selection(view.id, selection);
852−}
853−
854−fn extend_line_start(cx: &mut Context) {
855− let (view, doc) = current!(cx.editor);
856−
857− let selection = doc.selection(view.id).transform(|range| {
858− let text = doc.text();
859− let line = text.char_to_line(range.head);
860−
861− // adjust to start of the line
862− let pos = text.line_to_char(line);
863− Range::new(range.anchor, pos)
864− });
865−
866− doc.set_selection(view.id, selection);
867−}
868−
869881fn select_all(cx: &mut Context) {
870882 let (view, doc) = current!(cx.editor);
871883
@@ -1940,7 +1952,7 @@ fn symbol_picker(cx: &mut Context) {
19401952
19411953// I inserts at the first nonwhitespace character of each line with a selection
19421954fn prepend_to_line(cx: &mut Context) {
1943− move_first_nonwhitespace(cx);
1955+ goto_first_nonwhitespace(cx);
19441956 let doc = doc_mut!(cx.editor);
19451957 enter_insert_mode(doc);
19461958}
@@ -2106,7 +2118,7 @@ fn push_jump(editor: &mut Editor) {
21062118 view.jumps.push(jump);
21072119}
21082120
2109−fn switch_to_last_accessed_file(cx: &mut Context) {
2121+fn goto_last_accessed_file(cx: &mut Context) {
21102122 let alternate_file = view!(cx.editor).last_accessed_doc;
21112123 if let Some(alt) = alternate_file {
21122124 cx.editor.switch(alt, Action::Replace);
@@ -2115,7 +2127,15 @@ fn switch_to_last_accessed_file(cx: &mut Context) {
21152127 }
21162128}
21172129
2118−fn goto_mode(cx: &mut Context) {
2130+fn select_mode(cx: &mut Context) {
2131+ doc_mut!(cx.editor).mode = Mode::Select;
2132+}
2133+
2134+fn exit_select_mode(cx: &mut Context) {
2135+ doc_mut!(cx.editor).mode = Mode::Normal;
2136+}
2137+
2138+fn goto_prehook(cx: &mut Context) -> bool {
21192139 if let Some(count) = cx.count {
21202140 push_jump(cx.editor);
21212141
@@ -2123,63 +2143,10 @@ fn goto_mode(cx: &mut Context) {
21232143 let line_idx = std::cmp::min(count.get() - 1, doc.text().len_lines().saturating_sub(2));
21242144 let pos = doc.text().line_to_char(line_idx);
21252145 doc.set_selection(view.id, Selection::point(pos));
2126− return;
2146+ true
2147+ } else {
2148+ false
21272149 }
2128−
2129− cx.on_next_key(move |cx, event| {
2130− if let KeyEvent {
2131− code: KeyCode::Char(ch),
2132− ..
2133− } = event
2134− {
2135− // TODO: temporarily show GOTO in the mode list
2136− let doc = doc_mut!(cx.editor);
2137− match (doc.mode, ch) {
2138− (_, 'g') => move_file_start(cx),
2139− (_, 'e') => move_file_end(cx),
2140− (_, 'a') => switch_to_last_accessed_file(cx),
2141− (Mode::Normal, 'h') => move_line_start(cx),
2142− (Mode::Normal, 'l') => move_line_end(cx),
2143− (Mode::Select, 'h') => extend_line_start(cx),
2144− (Mode::Select, 'l') => extend_line_end(cx),
2145− (_, 'd') => goto_definition(cx),
2146− (_, 'y') => goto_type_definition(cx),
2147− (_, 'r') => goto_reference(cx),
2148− (_, 'i') => goto_implementation(cx),
2149− (Mode::Normal, 's') => move_first_nonwhitespace(cx),
2150− (Mode::Select, 's') => extend_first_nonwhitespace(cx),
2151−
2152− (_, 't') | (_, 'm') | (_, 'b') => {
2153− let (view, doc) = current!(cx.editor);
2154−
2155− let scrolloff = PADDING.min(view.area.height as usize / 2); // TODO: user pref
2156−
2157− let last_line = view.last_line(doc);
2158−
2159− let line = match ch {
2160− 't' => (view.first_line + scrolloff),
2161− 'm' => (view.first_line + (view.area.height as usize / 2)),
2162− 'b' => last_line.saturating_sub(scrolloff),
2163− _ => unreachable!(),
2164− }
2165− .min(last_line.saturating_sub(scrolloff));
2166−
2167− let pos = doc.text().line_to_char(line);
2168−
2169− doc.set_selection(view.id, Selection::point(pos));
2170− }
2171− _ => (),
2172− }
2173− }
2174− })
2175−}
2176−
2177−fn select_mode(cx: &mut Context) {
2178− doc_mut!(cx.editor).mode = Mode::Select;
2179−}
2180−
2181−fn exit_select_mode(cx: &mut Context) {
2182− doc_mut!(cx.editor).mode = Mode::Normal;
21832150}
21842151
21852152fn goto_impl(
@@ -3412,107 +3379,6 @@ fn select_register(cx: &mut Context) {
34123379 })
34133380}
34143381
3415−macro_rules! mode_info {
3416− // TODO: reuse $mode for $stat
3417− (@join $first:expr $(,$rest:expr)*) => {
3418− concat!($first, $(", ", $rest),*)
3419− };
3420− {$mode:ident, $stat:ident, $name:literal, $(#[doc = $desc:literal] $($key:tt)|+ => $func:expr),+,} => {
3421− #[doc = $name]
3422− #[doc = ""]
3423− #[doc = "<table><tr><th>key</th><th>desc</th></tr><tbody>"]
3424− $(
3425− #[doc = "<tr><td>"]
3426− // TODO switch to this once we use rust 1.54
3427− // right now it will produce multiple rows
3428− // #[doc = mode_info!(@join $($key),+)]
3429− $(
3430− #[doc = $key]
3431− )+
3432− // <-
3433− #[doc = "</td><td>"]
3434− #[doc = $desc]
3435− #[doc = "</td></tr>"]
3436− )+
3437− #[doc = "</tbody></table>"]
3438− pub fn $mode(cx: &mut Context) {
3439− static $stat: OnceCell<Info> = OnceCell::new();
3440− cx.editor.autoinfo = Some($stat.get_or_init(|| Info::key(
3441− $name,
3442− vec![$((&[$($key.parse().unwrap()),+], $desc)),+],
3443− )));
3444− use helix_core::hashmap;
3445− // TODO: try and convert this to match later
3446− let map = hashmap! {
3447− $($($key.parse::<KeyEvent>().unwrap() => $func as for<'r, 's> fn(&'r mut Context<'s>)),+),*
3448− };
3449− cx.on_next_key_mode(map);
3450− }
3451− };
3452−}
3453−
3454−mode_info! {
3455− space_mode, SPACE_MODE, "space mode",
3456− /// file picker
3457− "f" => file_picker,
3458− /// buffer picker
3459− "b" => buffer_picker,
3460− /// symbol picker
3461− "s" => symbol_picker,
3462− /// window mode
3463− "w" => window_mode,
3464− /// yank joined to clipboard
3465− "y" => yank_joined_to_clipboard,
3466− /// yank main selection to clipboard
3467− "Y" => yank_main_selection_to_clipboard,
3468− /// paste system clipboard after selections
3469− "p" => paste_clipboard_after,
3470− /// paste system clipboard before selections
3471− "P" => paste_clipboard_before,
3472− /// replace selections with clipboard
3473− "R" => replace_selections_with_clipboard,
3474− /// keep primary selection
3475− "space" => keep_primary_selection,
3476−}
3477−
3478−// TODO: generated, delete it later
3479−// /// space mode
3480−// ///
3481−// /// | key | desc |
3482−// /// |-----|------|
3483−// /// | f | file picker |
3484−// /// | b | buffer picker |
3485−// /// ...
3486−// pub fn space_mode(cx: &mut Context) {
3487−// cx.editor.autoinfo = Some(Info::key(
3488−// "space mode",
3489−// vec![
3490−// (vec!["f".parse().unwrap()], "file picker"),
3491−// (vec!["b".parse().unwrap()], "buffer picker"),
3492−// (vec!["s".parse().unwrap()], "symbol picker"),
3493−// (vec!["w".parse().unwrap()], "window mode"),
3494−// (vec!["y".parse().unwrap()], "yank joined to clipboard"),
3495−// (vec!["Y".parse().unwrap()], "yank main selection to clipboard"),
3496−// (vec!["p".parse().unwrap()], "paste system clipboard after selections"),
3497−// (vec!["P".parse().unwrap()], "paste system clipboard before selections"),
3498−// (vec!["R".parse().unwrap()], "replace selections with clipboard"),
3499−// (vec![" ".parse().unwrap()], "keep primary selection"),
3500−// ],
3501−// ));
3502−// let mut map: HashMap<KeyEvent, fn(&mut Context)> = HashMap::with_capacity(10);
3503−// map.insert("f".parse().unwrap(), file_picker);
3504−// map.insert("b".parse().unwrap(), buffer_picker);
3505−// map.insert("s".parse().unwrap(), symbol_picker);
3506−// map.insert("w".parse().unwrap(), window_mode);
3507−// map.insert("y".parse().unwrap(), yank_joined_to_clipboard);
3508−// map.insert("Y".parse().unwrap(), yank_main_selection_to_clipboard);
3509−// map.insert("p".parse().unwrap(), paste_clipboard_after);
3510−// map.insert("P".parse().unwrap(), paste_clipboard_before);
3511−// map.insert("R".parse().unwrap(), replace_selections_with_clipboard);
3512−// map.insert(" ".parse().unwrap(), keep_primary_selection);
3513−// cx.on_next_key_mode(map);
3514−// }
3515−
35163382fn view_mode(cx: &mut Context) {
35173383 cx.on_next_key(move |cx, event| {
35183384 if let KeyEvent {
@@ -3734,3 +3600,132 @@ fn surround_delete(cx: &mut Context) {
37343600 }
37353601 })
37363602}
3603+
3604+/// Do nothing, just for modeinfo.
3605+fn noop(_cx: &mut Context) -> bool {
3606+ false
3607+}
3608+
3609+/// Generate modeinfo.
3610+///
3611+/// If prehook returns true then it will stop the rest.
3612+macro_rules! mode_info {
3613+ // TODO: reuse $mode for $stat
3614+ (@join $first:expr $(,$rest:expr)*) => {
3615+ concat!($first, $(", ", $rest),*)
3616+ };
3617+ (@name #[doc = $name:literal] $(#[$rest:meta])*) => {
3618+ $name
3619+ };
3620+ {
3621+ #[doc = $name:literal] $(#[$doc:meta])* $mode:ident, $stat:ident,
3622+ $(#[doc = $desc:literal] $($key:tt)|+ => $func:expr),+,
3623+ } => {
3624+ mode_info! {
3625+ #[doc = $name]
3626+ $(#[$doc])*
3627+ $mode, $stat, noop,
3628+ $(
3629+ #[doc = $desc]
3630+ $($key)|+ => $func
3631+ ),+,
3632+ }
3633+ };
3634+ {
3635+ #[doc = $name:literal] $(#[$doc:meta])* $mode:ident, $stat:ident, $prehook:expr,
3636+ $(#[doc = $desc:literal] $($key:tt)|+ => $func:expr),+,
3637+ } => {
3638+ #[doc = $name]
3639+ $(#[$doc])*
3640+ #[doc = ""]
3641+ #[doc = "<table><tr><th>key</th><th>desc</th></tr><tbody>"]
3642+ $(
3643+ #[doc = "<tr><td>"]
3644+ // TODO switch to this once we use rust 1.54
3645+ // right now it will produce multiple rows
3646+ // #[doc = mode_info!(@join $($key),+)]
3647+ $(
3648+ #[doc = $key]
3649+ )+
3650+ // <-
3651+ #[doc = "</td><td>"]
3652+ #[doc = $desc]
3653+ #[doc = "</td></tr>"]
3654+ )+
3655+ #[doc = "</tbody></table>"]
3656+ pub fn $mode(cx: &mut Context) {
3657+ if $prehook(cx) {
3658+ return;
3659+ }
3660+ static $stat: OnceCell<Info> = OnceCell::new();
3661+ cx.editor.autoinfo = Some($stat.get_or_init(|| Info::key(
3662+ $name.trim(),
3663+ vec![$((&[$($key.parse().unwrap()),+], $desc)),+],
3664+ )));
3665+ use helix_core::hashmap;
3666+ // TODO: try and convert this to match later
3667+ let map = hashmap! {
3668+ $($($key.parse::<KeyEvent>().unwrap() => $func as for<'r, 's> fn(&'r mut Context<'s>)),+),*
3669+ };
3670+ cx.on_next_key_mode(map);
3671+ }
3672+ };
3673+}
3674+
3675+mode_info! {
3676+ /// space mode
3677+ space_mode, SPACE_MODE,
3678+ /// file picker
3679+ "f" => file_picker,
3680+ /// buffer picker
3681+ "b" => buffer_picker,
3682+ /// symbol picker
3683+ "s" => symbol_picker,
3684+ /// window mode
3685+ "w" => window_mode,
3686+ /// yank joined to clipboard
3687+ "y" => yank_joined_to_clipboard,
3688+ /// yank main selection to clipboard
3689+ "Y" => yank_main_selection_to_clipboard,
3690+ /// paste system clipboard after selections
3691+ "p" => paste_clipboard_after,
3692+ /// paste system clipboard before selections
3693+ "P" => paste_clipboard_before,
3694+ /// replace selections with clipboard
3695+ "R" => replace_selections_with_clipboard,
3696+ /// keep primary selection
3697+ "space" => keep_primary_selection,
3698+}
3699+
3700+mode_info! {
3701+ /// goto mode
3702+ ///
3703+ /// When specified with a count, it will go to that line without entering the mode.
3704+ goto_mode, GOTO_MODE, goto_prehook,
3705+ /// file start
3706+ "g" => goto_file_start,
3707+ /// file end
3708+ "e" => goto_file_end,
3709+ /// line start
3710+ "h" => goto_line_start,
3711+ /// line end
3712+ "l" => goto_line_end,
3713+ /// line first non blank
3714+ "s" => goto_first_nonwhitespace,
3715+ /// definition
3716+ "d" => goto_definition,
3717+ /// type references
3718+ "y" => goto_type_definition,
3719+ /// references
3720+ "r" => goto_reference,
3721+ /// implementation
3722+ "i" => goto_implementation,
3723+ /// window top
3724+ "t" => goto_window_top,
3725+ /// window center
3726+ "c" => goto_window_center,
3727+ /// window bottom
3728+ "b" => goto_window_bottom,
3729+ /// last accessed file
3730+ "a" => goto_last_accessed_file,
3731+}
helix-term/src/keymap.rs+6−6
@@ -171,8 +171,8 @@ impl Default for Keymaps {
171171 key!('r') => Command::replace,
172172 key!('R') => Command::replace_with_yanked,
173173
174− key!(Home) => Command::move_line_start,
175− key!(End) => Command::move_line_end,
174+ key!(Home) => Command::goto_line_start,
175+ key!(End) => Command::goto_line_end,
176176
177177 key!('w') => Command::move_next_word_start,
178178 key!('b') => Command::move_prev_word_start,
@@ -303,8 +303,8 @@ impl Default for Keymaps {
303303
304304 key!('T') => Command::extend_till_prev_char,
305305 key!('F') => Command::extend_prev_char,
306− key!(Home) => Command::extend_line_start,
307− key!(End) => Command::extend_line_end,
306+ key!(Home) => Command::goto_line_start,
307+ key!(End) => Command::goto_line_end,
308308 key!(Esc) => Command::exit_select_mode,
309309 )
310310 .into_iter(),
@@ -327,8 +327,8 @@ impl Default for Keymaps {
327327 key!(Right) => Command::move_char_right,
328328 key!(PageUp) => Command::page_up,
329329 key!(PageDown) => Command::page_down,
330− key!(Home) => Command::move_line_start,
331− key!(End) => Command::move_line_end,
330+ key!(Home) => Command::goto_line_start,
331+ key!(End) => Command::goto_line_end_newline,
332332 ctrl!('x') => Command::completion,
333333 ctrl!('w') => Command::delete_word_backward,
334334 ),