diff --git a/FEATURES.md b/FEATURES.md index 79f02ac..8d23b2e 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -21,7 +21,7 @@ - [x] `0` - Move to start of line - [x] `$` - Move to end of line - [x] `_` - Move to first non-whitespace -- [ ] `^` - Move to first non-whitespace (alias for `_`) +- [x] `^` - Move to first non-whitespace (alias for `_`) - [ ] `|` - Move to column N ### File Movement @@ -410,7 +410,7 @@ Buffers are in-memory representations of files. A buffer exists for each open fi ### Well Tested - [x] Basic motions (h, j, k, l) - [x] Word motions (w, e, b) -- [x] Jump motions (G, gg, 0, $, _) +- [x] Jump motions (G, gg, 0, $, _, ^) - [x] Scroll actions (ctrl+u, ctrl+d) - [x] Delete operator (d, dd) - [x] Yank operator (y, yy) diff --git a/internal/editor/integration_motion_jump_test.go b/internal/editor/integration_motion_jump_test.go index 09ac491..eaee020 100644 --- a/internal/editor/integration_motion_jump_test.go +++ b/internal/editor/integration_motion_jump_test.go @@ -306,3 +306,82 @@ func TestMoveToLineContentStart(t *testing.T) { } }) } + +func TestMoveToLineContentStartAlias(t *testing.T) { + t.Run("test '^' from middle of line with no leading whitespace", func(t *testing.T) { + lines := []string{"hello world"} + tm := newTestModelWithLinesAndCursorPos(t, lines, action.Position{Col: 6, Line: 0}) + sendKeys(tm, "^") + + m := getFinalModel(t, tm) + if m.CursorX() != 0 { + t.Errorf("CursorX() = %d, want 0", m.CursorX()) + } + }) + + t.Run("test '^' from middle of line with leading whitespace", func(t *testing.T) { + lines := []string{" hello world"} + tm := newTestModelWithLinesAndCursorPos(t, lines, action.Position{Col: 10, Line: 0}) + sendKeys(tm, "^") + + m := getFinalModel(t, tm) + if m.CursorX() != 4 { + t.Errorf("CursorX() = %d, want 4", m.CursorX()) + } + }) + + t.Run("test '^' from start of line with leading whitespace", func(t *testing.T) { + lines := []string{" hello world"} + tm := newTestModelWithLinesAndCursorPos(t, lines, action.Position{Col: 0, Line: 0}) + sendKeys(tm, "^") + + m := getFinalModel(t, tm) + if m.CursorX() != 4 { + t.Errorf("CursorX() = %d, want 4", m.CursorX()) + } + }) + + t.Run("test '^' from start of line with no leading whitespace", func(t *testing.T) { + lines := []string{"hello world"} + tm := newTestModelWithLinesAndCursorPos(t, lines, action.Position{Col: 0, Line: 0}) + sendKeys(tm, "^") + + m := getFinalModel(t, tm) + if m.CursorX() != 0 { + t.Errorf("CursorX() = %d, want 0", m.CursorX()) + } + }) + + t.Run("test '^' from middle of line with only whitespace", func(t *testing.T) { + lines := []string{" "} + tm := newTestModelWithLinesAndCursorPos(t, lines, action.Position{Col: 2, Line: 0}) + sendKeys(tm, "^") + + m := getFinalModel(t, tm) + if m.CursorX() != 4 { + t.Errorf("CursorX() = %d, want 4", m.CursorX()) + } + }) + + t.Run("test '^' from end of line with only whitespace", func(t *testing.T) { + lines := []string{" "} + tm := newTestModelWithLinesAndCursorPos(t, lines, action.Position{Col: 4, Line: 0}) + sendKeys(tm, "^") + + m := getFinalModel(t, tm) + if m.CursorX() != 4 { + t.Errorf("CursorX() = %d, want 4", m.CursorX()) + } + }) + + t.Run("test '^' on empty line", func(t *testing.T) { + lines := []string{""} + tm := newTestModelWithLinesAndCursorPos(t, lines, action.Position{Col: 0, Line: 0}) + sendKeys(tm, "^") + + m := getFinalModel(t, tm) + if m.CursorX() != 0 { + t.Errorf("CursorX() = %d, want 0", m.CursorX()) + } + }) +} diff --git a/internal/input/keymap.go b/internal/input/keymap.go index ae596d3..6a9f0a8 100644 --- a/internal/input/keymap.go +++ b/internal/input/keymap.go @@ -25,6 +25,7 @@ func NewNormalKeymap() *Keymap { "0": motion.MoveToLineStart{}, "$": motion.MoveToLineEnd{}, "_": motion.MoveToLineContentStart{}, + "^": motion.MoveToLineContentStart{}, "w": motion.MoveForwardWord{Count: 1}, "e": motion.MoveForwardWordEnd{Count: 1}, "b": motion.MoveBackwardWord{Count: 1}, @@ -69,6 +70,7 @@ func NewVisualKeymap() *Keymap { "0": motion.MoveToLineStart{}, "$": motion.MoveToLineEnd{}, "_": motion.MoveToLineContentStart{}, + "^": motion.MoveToLineContentStart{}, "w": motion.MoveForwardWord{Count: 1}, "e": motion.MoveForwardWordEnd{Count: 1}, "b": motion.MoveBackwardWord{Count: 1},