@@ -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 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);
220+pub fn move_first_nonwhitespace(cx: &mut Context) {
221+ let (view, doc) = cx.current();
223+ let selection = doc.selection(view.id).transform(|range| {
224+ let text = doc.text();
225+ let line_idx = text.char_to_line(range.head);
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)
235+ doc.set_selection(view.id, selection);
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) {
443+pub fn extend_first_nonwhitespace(cx: &mut Context) {
444+ let (view, doc) = cx.current();
446+ let selection = doc.selection(view.id).transform(|range| {
447+ let text = doc.text();
448+ let line_idx = text.char_to_line(range.head);
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)
458+ doc.set_selection(view.id, selection);
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),
1292 (_, 't') | (_, 'm') | (_, 'b') => {
1331 (_, 't') | (_, 'm') | (_, 'b') => {
1293 let (view, doc) = cx.current();
1332 let (view, doc) = cx.current();