on master help sign in
commands: Add goto first non-whitespace char of line
commit48df05b16dba362366b63284c2e551c51995fb66 authorWojciech Kępka <wojtek.kepka@protonmail.com> dateTue 8 Jun 2021, 08:25 (5 years ago) parentb873fb9
3 files changed+43−11
book/src/keymap.md+1 helix-core/src/lib.rs+2−10 helix-term/src/commands.rs+40−1
book/src/keymap.md+1
@@ -133,6 +133,7 @@ Jumps to various locations.
133133| e | Go to the end of the file |
134134| h | Go to the start of the line |
135135| l | Go to the end of the line |
136+| s | Go to first non-whitespace character of the line |
136137| t | Go to the top of the screen |
137138| m | Go to the middle of the screen |
138139| b | Go to the bottom of the screen |
helix-core/src/lib.rs+2−10
@@ -18,16 +18,8 @@ pub mod syntax;
1818mod transaction;
1919pub mod words;
2020
21−pub(crate) fn find_first_non_whitespace_char2(line: RopeSlice) -> Option<usize> {
22− // find first non-whitespace char
23− for (start, ch) in line.chars().enumerate() {
24− // TODO: could use memchr with chunks?
25− if ch != ' ' && ch != '\t' && ch != '\n' {
26− return Some(start);
27− }
28− }
29−
30− None
21+pub fn find_first_non_whitespace_char2(line: RopeSlice) -> Option<usize> {
22+ line.chars().position(|ch| !ch.is_whitespace())
3123}
3224pub(crate) fn find_first_non_whitespace_char(text: RopeSlice, line_num: usize) -> Option<usize> {
3325 let line = text.line(line_num);
helix-term/src/commands.rs+40−1
@@ -1,5 +1,6 @@
11use helix_core::{
2− comment, coords_at_pos, find_root, graphemes, indent, match_brackets,
2+ comment, coords_at_pos, find_first_non_whitespace_char2, find_root, graphemes, indent,
3+ match_brackets,
34 movement::{self, Direction},
45 object, pos_at_coords,
56 regex::{self, Regex},
@@ -216,6 +217,24 @@ pub fn move_line_start(cx: &mut Context) {
216217 doc.set_selection(view.id, selection);
217218}
218219
220+pub fn move_first_nonwhitespace(cx: &mut Context) {
221+ let (view, doc) = cx.current();
222+
223+ let selection = doc.selection(view.id).transform(|range| {
224+ let text = doc.text();
225+ let line_idx = text.char_to_line(range.head);
226+
227+ if let Some(pos) = find_first_non_whitespace_char2(text.line(line_idx)) {
228+ let pos = pos + text.line_to_char(line_idx);
229+ Range::new(pos, pos)
230+ } else {
231+ range
232+ }
233+ });
234+
235+ doc.set_selection(view.id, selection);
236+}
237+
219238// TODO: move vs extend could take an extra type Extend/Move that would
220239// Range::new(if Move { pos } if Extend { range.anchor }, pos)
221240// since these all really do the same thing
@@ -421,6 +440,24 @@ pub fn extend_prev_char(cx: &mut Context) {
421440 )
422441}
423442
443+pub fn extend_first_nonwhitespace(cx: &mut Context) {
444+ let (view, doc) = cx.current();
445+
446+ let selection = doc.selection(view.id).transform(|range| {
447+ let text = doc.text();
448+ let line_idx = text.char_to_line(range.head);
449+
450+ if let Some(pos) = find_first_non_whitespace_char2(text.line(line_idx)) {
451+ let pos = pos + text.line_to_char(line_idx);
452+ Range::new(range.anchor, pos)
453+ } else {
454+ range
455+ }
456+ });
457+
458+ doc.set_selection(view.id, selection);
459+}
460+
424461pub fn replace(cx: &mut Context) {
425462 // need to wait for next key
426463 cx.on_next_key(move |cx, event| {
@@ -1288,6 +1325,8 @@ pub fn goto_mode(cx: &mut Context) {
12881325 (_, 'y') => goto_type_definition(cx),
12891326 (_, 'r') => goto_reference(cx),
12901327 (_, 'i') => goto_implementation(cx),
1328+ (Mode::Normal, 's') => move_first_nonwhitespace(cx),
1329+ (Mode::Select, 's') => extend_first_nonwhitespace(cx),
12911330
12921331 (_, 't') | (_, 'm') | (_, 'b') => {
12931332 let (view, doc) = cx.current();