on master help sign in
commands: Add goto first non-whitespace char of line
48df05bWojciech Kępka
book/src/keymap.md+1
@@ -133,6 +133,7 @@ Jumps to various locations.
133| e | Go to the end of the file |
133| e | Go to the end of the file |
134| h | Go to the start of the line |
134| h | Go to the start of the line |
135| l | Go to the end of the line |
135| l | Go to the end of the line |
136+| s | Go to first non-whitespace character of the line |
136| t | Go to the top of the screen |
137| t | Go to the top of the screen |
137| m | Go to the middle of the screen |
138| m | Go to the middle of the screen |
138| b | Go to the bottom of the screen |
139| b | Go to the bottom of the screen |
helix-core/src/lib.rs+2−10
@@ -18,16 +18,8 @@ pub mod syntax;
18mod transaction;
18mod transaction;
19pub mod words;
19pub mod words;
20
20
21−pub(crate) fn find_first_non_whitespace_char2(line: RopeSlice) -> Option<usize> {
21+pub fn find_first_non_whitespace_char2(line: RopeSlice) -> Option<usize> {
22− // find first non-whitespace char
22+ line.chars().position(|ch| !ch.is_whitespace())
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
31}
23}
32pub(crate) fn find_first_non_whitespace_char(text: RopeSlice, line_num: usize) -> Option<usize> {
24pub(crate) fn find_first_non_whitespace_char(text: RopeSlice, line_num: usize) -> Option<usize> {
33 let line = text.line(line_num);
25 let line = text.line(line_num);
helix-term/src/commands.rs+40−1
@@ -1,5 +1,6 @@
1use helix_core::{
1use 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,
3 movement::{self, Direction},
4 movement::{self, Direction},
4 object, pos_at_coords,
5 object, pos_at_coords,
5 regex::{self, Regex},
6 regex::{self, Regex},
@@ -216,6 +217,24 @@ pub fn move_line_start(cx: &mut Context) {
216 doc.set_selection(view.id, selection);
217 doc.set_selection(view.id, selection);
217}
218}
218
219
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+
219// TODO: move vs extend could take an extra type Extend/Move that would
238// TODO: move vs extend could take an extra type Extend/Move that would
220// Range::new(if Move { pos } if Extend { range.anchor }, pos)
239// Range::new(if Move { pos } if Extend { range.anchor }, pos)
221// since these all really do the same thing
240// since these all really do the same thing
@@ -421,6 +440,24 @@ pub fn extend_prev_char(cx: &mut Context) {
421 )
440 )
422}
441}
423
442
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+
424pub fn replace(cx: &mut Context) {
461pub fn replace(cx: &mut Context) {
425 // need to wait for next key
462 // need to wait for next key
426 cx.on_next_key(move |cx, event| {
463 cx.on_next_key(move |cx, event| {
@@ -1288,6 +1325,8 @@ pub fn goto_mode(cx: &mut Context) {
1288 (_, 'y') => goto_type_definition(cx),
1325 (_, 'y') => goto_type_definition(cx),
1289 (_, 'r') => goto_reference(cx),
1326 (_, 'r') => goto_reference(cx),
1290 (_, 'i') => goto_implementation(cx),
1327 (_, 'i') => goto_implementation(cx),
1328+ (Mode::Normal, 's') => move_first_nonwhitespace(cx),
1329+ (Mode::Select, 's') => extend_first_nonwhitespace(cx),
1291
1330
1292 (_, 't') | (_, 'm') | (_, 'b') => {
1331 (_, 't') | (_, 'm') | (_, 'b') => {
1293 let (view, doc) = cx.current();
1332 let (view, doc) = cx.current();