feat(commands): expose floating pane toggle
- commit
- 9065670a743c1baa4f20ab163e564e268c1126b0
- author
- brice · · 2026-07-09 23:22 UTC
- committer
- brice · · 2026-07-10 06:37 UTC
- parent
- 773d08a
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
9 files · +91 -19
M internal/usecase/command/command.go +1 -0
| @@ -11,6 +11,7 @@ type Context interface { | |||
| 11 | 11 | SplitDown() error | |
| 12 | 12 | StackPane() error | |
| 13 | 13 | ToggleStack() error | |
| 14 | + | ToggleFloatingPane() error | |
| 14 | 15 | ClosePane() error | |
| 15 | 16 | FocusPaneLeft() error | |
| 16 | 17 | FocusPaneRight() error | |
| @@ -11,6 +11,7 @@ type Context interface { | |||||
| 11 | SplitDown() error | 11 | SplitDown() error | ||
| 12 | StackPane() error | 12 | StackPane() error | ||
| 13 | ToggleStack() error | 13 | ToggleStack() error | ||
| 14 | + | ToggleFloatingPane() error | |||
| 14 | ClosePane() error | 15 | ClosePane() error | ||
| 15 | FocusPaneLeft() error | 16 | FocusPaneLeft() error | ||
| 16 | FocusPaneRight() error | 17 | FocusPaneRight() error | ||
M internal/usecase/command/context_mock_test.go +44 -0
| @@ -1003,6 +1003,50 @@ func (_c *MockContext_StackPane_Call) RunAndReturn(run func() error) *MockContex | |||
| 1003 | 1003 | return _c | |
| 1004 | 1004 | } | |
| 1005 | 1005 | ||
| 1006 | + | // ToggleFloatingPane provides a mock function for the type MockContext | |
| 1007 | + | func (_mock *MockContext) ToggleFloatingPane() error { | |
| 1008 | + | ret := _mock.Called() | |
| 1009 | + | ||
| 1010 | + | if len(ret) == 0 { | |
| 1011 | + | panic("no return value specified for ToggleFloatingPane") | |
| 1012 | + | } | |
| 1013 | + | ||
| 1014 | + | var r0 error | |
| 1015 | + | if returnFunc, ok := ret.Get(0).(func() error); ok { | |
| 1016 | + | r0 = returnFunc() | |
| 1017 | + | } else { | |
| 1018 | + | r0 = ret.Error(0) | |
| 1019 | + | } | |
| 1020 | + | return r0 | |
| 1021 | + | } | |
| 1022 | + | ||
| 1023 | + | // MockContext_ToggleFloatingPane_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ToggleFloatingPane' | |
| 1024 | + | type MockContext_ToggleFloatingPane_Call struct { | |
| 1025 | + | *mock.Call | |
| 1026 | + | } | |
| 1027 | + | ||
| 1028 | + | // ToggleFloatingPane is a helper method to define mock.On call | |
| 1029 | + | func (_e *MockContext_Expecter) ToggleFloatingPane() *MockContext_ToggleFloatingPane_Call { | |
| 1030 | + | return &MockContext_ToggleFloatingPane_Call{Call: _e.mock.On("ToggleFloatingPane")} | |
| 1031 | + | } | |
| 1032 | + | ||
| 1033 | + | func (_c *MockContext_ToggleFloatingPane_Call) Run(run func()) *MockContext_ToggleFloatingPane_Call { | |
| 1034 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 1035 | + | run() | |
| 1036 | + | }) | |
| 1037 | + | return _c | |
| 1038 | + | } | |
| 1039 | + | ||
| 1040 | + | func (_c *MockContext_ToggleFloatingPane_Call) Return(err error) *MockContext_ToggleFloatingPane_Call { | |
| 1041 | + | _c.Call.Return(err) | |
| 1042 | + | return _c | |
| 1043 | + | } | |
| 1044 | + | ||
| 1045 | + | func (_c *MockContext_ToggleFloatingPane_Call) RunAndReturn(run func() error) *MockContext_ToggleFloatingPane_Call { | |
| 1046 | + | _c.Call.Return(run) | |
| 1047 | + | return _c | |
| 1048 | + | } | |
| 1049 | + | ||
| 1006 | 1050 | // ToggleStack provides a mock function for the type MockContext | |
| 1007 | 1051 | func (_mock *MockContext) ToggleStack() error { | |
| 1008 | 1052 | ret := _mock.Called() | |
| @@ -1003,6 +1003,50 @@ func (_c *MockContext_StackPane_Call) RunAndReturn(run func() error) *MockContex | |||||
| 1003 | return _c | 1003 | return _c | ||
| 1004 | } | 1004 | } | ||
| 1005 | 1005 | ||||
| 1006 | + | // ToggleFloatingPane provides a mock function for the type MockContext | |||
| 1007 | + | func (_mock *MockContext) ToggleFloatingPane() error { | |||
| 1008 | + | ret := _mock.Called() | |||
| 1009 | + | ||||
| 1010 | + | if len(ret) == 0 { | |||
| 1011 | + | panic("no return value specified for ToggleFloatingPane") | |||
| 1012 | + | } | |||
| 1013 | + | ||||
| 1014 | + | var r0 error | |||
| 1015 | + | if returnFunc, ok := ret.Get(0).(func() error); ok { | |||
| 1016 | + | r0 = returnFunc() | |||
| 1017 | + | } else { | |||
| 1018 | + | r0 = ret.Error(0) | |||
| 1019 | + | } | |||
| 1020 | + | return r0 | |||
| 1021 | + | } | |||
| 1022 | + | ||||
| 1023 | + | // MockContext_ToggleFloatingPane_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ToggleFloatingPane' | |||
| 1024 | + | type MockContext_ToggleFloatingPane_Call struct { | |||
| 1025 | + | *mock.Call | |||
| 1026 | + | } | |||
| 1027 | + | ||||
| 1028 | + | // ToggleFloatingPane is a helper method to define mock.On call | |||
| 1029 | + | func (_e *MockContext_Expecter) ToggleFloatingPane() *MockContext_ToggleFloatingPane_Call { | |||
| 1030 | + | return &MockContext_ToggleFloatingPane_Call{Call: _e.mock.On("ToggleFloatingPane")} | |||
| 1031 | + | } | |||
| 1032 | + | ||||
| 1033 | + | func (_c *MockContext_ToggleFloatingPane_Call) Run(run func()) *MockContext_ToggleFloatingPane_Call { | |||
| 1034 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 1035 | + | run() | |||
| 1036 | + | }) | |||
| 1037 | + | return _c | |||
| 1038 | + | } | |||
| 1039 | + | ||||
| 1040 | + | func (_c *MockContext_ToggleFloatingPane_Call) Return(err error) *MockContext_ToggleFloatingPane_Call { | |||
| 1041 | + | _c.Call.Return(err) | |||
| 1042 | + | return _c | |||
| 1043 | + | } | |||
| 1044 | + | ||||
| 1045 | + | func (_c *MockContext_ToggleFloatingPane_Call) RunAndReturn(run func() error) *MockContext_ToggleFloatingPane_Call { | |||
| 1046 | + | _c.Call.Return(run) | |||
| 1047 | + | return _c | |||
| 1048 | + | } | |||
| 1049 | + | ||||
| 1006 | // ToggleStack provides a mock function for the type MockContext | 1050 | // ToggleStack provides a mock function for the type MockContext | ||
| 1007 | func (_mock *MockContext) ToggleStack() error { | 1051 | func (_mock *MockContext) ToggleStack() error { | ||
| 1008 | ret := _mock.Called() | 1052 | ret := _mock.Called() | ||
M internal/usecase/command/registry.go +1 -0
| @@ -14,6 +14,7 @@ func Registry() []Command { | |||
| 14 | 14 | {Slug: "split-down", Code: "SPD", Name: "Split down", Desc: "Split the focused pane downward", Run: func(ctx Context) error { return ctx.SplitDown() }}, | |
| 15 | 15 | {Slug: "stack-pane", Code: "STP", Name: "Stack pane", Desc: "Create a new pane in a stack", Run: func(ctx Context) error { return ctx.StackPane() }}, | |
| 16 | 16 | {Slug: "toggle-stack", Code: "TST", Name: "Toggle stack", Desc: "Toggle the focused pane stack", Run: func(ctx Context) error { return ctx.ToggleStack() }}, | |
| 17 | + | {Slug: "toggle-floating-pane", Code: "FLT", Name: "Toggle floating pane", Desc: "Toggle the floating pane", Run: func(ctx Context) error { return ctx.ToggleFloatingPane() }}, | |
| 17 | 18 | {Slug: "close-pane", Code: "CLP", Name: "Close pane", Desc: "Close the focused pane", Run: func(ctx Context) error { return ctx.ClosePane() }}, | |
| 18 | 19 | {Slug: "focus-pane-left", Code: "FPL", Name: "Focus pane left", Desc: "Focus the pane to the left", Run: func(ctx Context) error { return ctx.FocusPaneLeft() }}, | |
| 19 | 20 | {Slug: "focus-pane-right", Code: "FPR", Name: "Focus pane right", Desc: "Focus the pane to the right", Run: func(ctx Context) error { return ctx.FocusPaneRight() }}, | |
| @@ -14,6 +14,7 @@ func Registry() []Command { | |||||
| 14 | {Slug: "split-down", Code: "SPD", Name: "Split down", Desc: "Split the focused pane downward", Run: func(ctx Context) error { return ctx.SplitDown() }}, | 14 | {Slug: "split-down", Code: "SPD", Name: "Split down", Desc: "Split the focused pane downward", Run: func(ctx Context) error { return ctx.SplitDown() }}, | ||
| 15 | {Slug: "stack-pane", Code: "STP", Name: "Stack pane", Desc: "Create a new pane in a stack", Run: func(ctx Context) error { return ctx.StackPane() }}, | 15 | {Slug: "stack-pane", Code: "STP", Name: "Stack pane", Desc: "Create a new pane in a stack", Run: func(ctx Context) error { return ctx.StackPane() }}, | ||
| 16 | {Slug: "toggle-stack", Code: "TST", Name: "Toggle stack", Desc: "Toggle the focused pane stack", Run: func(ctx Context) error { return ctx.ToggleStack() }}, | 16 | {Slug: "toggle-stack", Code: "TST", Name: "Toggle stack", Desc: "Toggle the focused pane stack", Run: func(ctx Context) error { return ctx.ToggleStack() }}, | ||
| 17 | + | {Slug: "toggle-floating-pane", Code: "FLT", Name: "Toggle floating pane", Desc: "Toggle the floating pane", Run: func(ctx Context) error { return ctx.ToggleFloatingPane() }}, | |||
| 17 | {Slug: "close-pane", Code: "CLP", Name: "Close pane", Desc: "Close the focused pane", Run: func(ctx Context) error { return ctx.ClosePane() }}, | 18 | {Slug: "close-pane", Code: "CLP", Name: "Close pane", Desc: "Close the focused pane", Run: func(ctx Context) error { return ctx.ClosePane() }}, | ||
| 18 | {Slug: "focus-pane-left", Code: "FPL", Name: "Focus pane left", Desc: "Focus the pane to the left", Run: func(ctx Context) error { return ctx.FocusPaneLeft() }}, | 19 | {Slug: "focus-pane-left", Code: "FPL", Name: "Focus pane left", Desc: "Focus the pane to the left", Run: func(ctx Context) error { return ctx.FocusPaneLeft() }}, | ||
| 19 | {Slug: "focus-pane-right", Code: "FPR", Name: "Focus pane right", Desc: "Focus the pane to the right", Run: func(ctx Context) error { return ctx.FocusPaneRight() }}, | 20 | {Slug: "focus-pane-right", Code: "FPR", Name: "Focus pane right", Desc: "Focus the pane to the right", Run: func(ctx Context) error { return ctx.FocusPaneRight() }}, | ||
M internal/usecase/command/registry_test.go +12 -1
| @@ -7,7 +7,7 @@ import ( | |||
| 7 | 7 | ||
| 8 | 8 | func TestRegistryCodesAndSlugsAreUniqueInOrder(t *testing.T) { | |
| 9 | 9 | commands := Registry() | |
| 10 | - | wantCodes := []string{"CNT", "CNS", "CLT", "SPR", "SPL", "SPU", "SPD", "STP", "TST", "CLP", "FPL", "FPR", "FPU", "FPD", "NXT", "PVT", "BSK", "FSK", "SSP", "VIS", "RNS", "RNT", "DET"} | |
| 10 | + | wantCodes := []string{"CNT", "CNS", "CLT", "SPR", "SPL", "SPU", "SPD", "STP", "TST", "FLT", "CLP", "FPL", "FPR", "FPU", "FPD", "NXT", "PVT", "BSK", "FSK", "SSP", "VIS", "RNS", "RNT", "DET"} | |
| 11 | 11 | ||
| 12 | 12 | if len(commands) != len(wantCodes) { | |
| 13 | 13 | t.Fatalf("Registry() returned %d commands, want %d", len(commands), len(wantCodes)) | |
| @@ -37,6 +37,16 @@ func TestRegistryCodesAndSlugsAreUniqueInOrder(t *testing.T) { | |||
| 37 | 37 | } | |
| 38 | 38 | } | |
| 39 | 39 | ||
| 40 | + | func TestRegistryIncludesFloatingPaneToggle(t *testing.T) { | |
| 41 | + | cmd, ok := ByCode("FLT") | |
| 42 | + | if !ok { | |
| 43 | + | t.Fatal("ByCode(\"FLT\") ok = false, want true") | |
| 44 | + | } | |
| 45 | + | if cmd.Slug != "toggle-floating-pane" || cmd.Code != "FLT" || cmd.Name != "Toggle floating pane" { | |
| 46 | + | t.Fatalf("floating command = %#v, want toggle-floating-pane/FLT/Toggle floating pane", cmd) | |
| 47 | + | } | |
| 48 | + | } | |
| 49 | + | ||
| 40 | 50 | func TestByCodeIsCaseInsensitive(t *testing.T) { | |
| 41 | 51 | for _, code := range []string{"CNT", "cnt", "CnT"} { | |
| 42 | 52 | cmd, ok := ByCode(code) | |
| @@ -67,6 +77,7 @@ func TestCommandRunCallsMatchingContextMethod(t *testing.T) { | |||
| 67 | 77 | {code: "SPD", expect: func(ctx *MockContext) { ctx.EXPECT().SplitDown().Return(nil).Once() }}, | |
| 68 | 78 | {code: "STP", expect: func(ctx *MockContext) { ctx.EXPECT().StackPane().Return(nil).Once() }}, | |
| 69 | 79 | {code: "TST", expect: func(ctx *MockContext) { ctx.EXPECT().ToggleStack().Return(nil).Once() }}, | |
| 80 | + | {code: "FLT", expect: func(ctx *MockContext) { ctx.EXPECT().ToggleFloatingPane().Return(nil).Once() }}, | |
| 70 | 81 | {code: "CLP", expect: func(ctx *MockContext) { ctx.EXPECT().ClosePane().Return(nil).Once() }}, | |
| 71 | 82 | {code: "FPL", expect: func(ctx *MockContext) { ctx.EXPECT().FocusPaneLeft().Return(nil).Once() }}, | |
| 72 | 83 | {code: "FPR", expect: func(ctx *MockContext) { ctx.EXPECT().FocusPaneRight().Return(nil).Once() }}, | |
| @@ -7,7 +7,7 @@ import ( | |||||
| 7 | 7 | ||||
| 8 | func TestRegistryCodesAndSlugsAreUniqueInOrder(t *testing.T) { | 8 | func TestRegistryCodesAndSlugsAreUniqueInOrder(t *testing.T) { | ||
| 9 | commands := Registry() | 9 | commands := Registry() | ||
| 10 | - | wantCodes := []string{"CNT", "CNS", "CLT", "SPR", "SPL", "SPU", "SPD", "STP", "TST", "CLP", "FPL", "FPR", "FPU", "FPD", "NXT", "PVT", "BSK", "FSK", "SSP", "VIS", "RNS", "RNT", "DET"} | 10 | + | wantCodes := []string{"CNT", "CNS", "CLT", "SPR", "SPL", "SPU", "SPD", "STP", "TST", "FLT", "CLP", "FPL", "FPR", "FPU", "FPD", "NXT", "PVT", "BSK", "FSK", "SSP", "VIS", "RNS", "RNT", "DET"} |
| 11 | 11 | ||||
| 12 | if len(commands) != len(wantCodes) { | 12 | if len(commands) != len(wantCodes) { | ||
| 13 | t.Fatalf("Registry() returned %d commands, want %d", len(commands), len(wantCodes)) | 13 | t.Fatalf("Registry() returned %d commands, want %d", len(commands), len(wantCodes)) | ||
| @@ -37,6 +37,16 @@ func TestRegistryCodesAndSlugsAreUniqueInOrder(t *testing.T) { | |||||
| 37 | } | 37 | } | ||
| 38 | } | 38 | } | ||
| 39 | 39 | ||||
| 40 | + | func TestRegistryIncludesFloatingPaneToggle(t *testing.T) { | |||
| 41 | + | cmd, ok := ByCode("FLT") | |||
| 42 | + | if !ok { | |||
| 43 | + | t.Fatal("ByCode(\"FLT\") ok = false, want true") | |||
| 44 | + | } | |||
| 45 | + | if cmd.Slug != "toggle-floating-pane" || cmd.Code != "FLT" || cmd.Name != "Toggle floating pane" { | |||
| 46 | + | t.Fatalf("floating command = %#v, want toggle-floating-pane/FLT/Toggle floating pane", cmd) | |||
| 47 | + | } | |||
| 48 | + | } | |||
| 49 | + | ||||
| 40 | func TestByCodeIsCaseInsensitive(t *testing.T) { | 50 | func TestByCodeIsCaseInsensitive(t *testing.T) { | ||
| 41 | for _, code := range []string{"CNT", "cnt", "CnT"} { | 51 | for _, code := range []string{"CNT", "cnt", "CnT"} { | ||
| 42 | cmd, ok := ByCode(code) | 52 | cmd, ok := ByCode(code) | ||
| @@ -67,6 +77,7 @@ func TestCommandRunCallsMatchingContextMethod(t *testing.T) { | |||||
| 67 | {code: "SPD", expect: func(ctx *MockContext) { ctx.EXPECT().SplitDown().Return(nil).Once() }}, | 77 | {code: "SPD", expect: func(ctx *MockContext) { ctx.EXPECT().SplitDown().Return(nil).Once() }}, | ||
| 68 | {code: "STP", expect: func(ctx *MockContext) { ctx.EXPECT().StackPane().Return(nil).Once() }}, | 78 | {code: "STP", expect: func(ctx *MockContext) { ctx.EXPECT().StackPane().Return(nil).Once() }}, | ||
| 69 | {code: "TST", expect: func(ctx *MockContext) { ctx.EXPECT().ToggleStack().Return(nil).Once() }}, | 79 | {code: "TST", expect: func(ctx *MockContext) { ctx.EXPECT().ToggleStack().Return(nil).Once() }}, | ||
| 80 | + | {code: "FLT", expect: func(ctx *MockContext) { ctx.EXPECT().ToggleFloatingPane().Return(nil).Once() }}, | |||
| 70 | {code: "CLP", expect: func(ctx *MockContext) { ctx.EXPECT().ClosePane().Return(nil).Once() }}, | 81 | {code: "CLP", expect: func(ctx *MockContext) { ctx.EXPECT().ClosePane().Return(nil).Once() }}, | ||
| 71 | {code: "FPL", expect: func(ctx *MockContext) { ctx.EXPECT().FocusPaneLeft().Return(nil).Once() }}, | 82 | {code: "FPL", expect: func(ctx *MockContext) { ctx.EXPECT().FocusPaneLeft().Return(nil).Once() }}, | ||
| 72 | {code: "FPR", expect: func(ctx *MockContext) { ctx.EXPECT().FocusPaneRight().Return(nil).Once() }}, | 83 | {code: "FPR", expect: func(ctx *MockContext) { ctx.EXPECT().FocusPaneRight().Return(nil).Once() }}, | ||
M internal/usecase/keys/bindings.go +1 -0
| @@ -17,6 +17,7 @@ func DefaultBindings() *Bindings { | |||
| 17 | 17 | altRunes := map[rune]Action{ | |
| 18 | 18 | ' ': ActionOpenPalette, | |
| 19 | 19 | 'a': ActionJumpAttention, | |
| 20 | + | 'f': ActionToggleFloatingPane, | |
| 20 | 21 | 'h': ActionFocusPaneLeft, | |
| 21 | 22 | 'j': ActionFocusPaneDown, | |
| 22 | 23 | 'k': ActionFocusPaneUp, | |
| @@ -17,6 +17,7 @@ func DefaultBindings() *Bindings { | |||||
| 17 | altRunes := map[rune]Action{ | 17 | altRunes := map[rune]Action{ | ||
| 18 | ' ': ActionOpenPalette, | 18 | ' ': ActionOpenPalette, | ||
| 19 | 'a': ActionJumpAttention, | 19 | 'a': ActionJumpAttention, | ||
| 20 | + | 'f': ActionToggleFloatingPane, | |||
| 20 | 'h': ActionFocusPaneLeft, | 21 | 'h': ActionFocusPaneLeft, | ||
| 21 | 'j': ActionFocusPaneDown, | 22 | 'j': ActionFocusPaneDown, | ||
| 22 | 'k': ActionFocusPaneUp, | 23 | 'k': ActionFocusPaneUp, | ||
M internal/usecase/keys/keyspec.go +16 -15
| @@ -49,21 +49,22 @@ func ParseKeySpec(spec string) (KeySpec, error) { | |||
| 49 | 49 | } | |
| 50 | 50 | ||
| 51 | 51 | var actionNames = map[Action]string{ | |
| 52 | - | ActionOpenPalette: "open-palette", | |
| 53 | - | ActionJumpAttention: "jump-attention", | |
| 54 | - | ActionFocusPaneLeft: "focus-pane-left", | |
| 55 | - | ActionFocusPaneRight: "focus-pane-right", | |
| 56 | - | ActionFocusPaneUp: "focus-pane-up", | |
| 57 | - | ActionFocusPaneDown: "focus-pane-down", | |
| 58 | - | ActionSwitchTab1: "switch-tab-1", | |
| 59 | - | ActionSwitchTab2: "switch-tab-2", | |
| 60 | - | ActionSwitchTab3: "switch-tab-3", | |
| 61 | - | ActionSwitchTab4: "switch-tab-4", | |
| 62 | - | ActionSwitchTab5: "switch-tab-5", | |
| 63 | - | ActionSwitchTab6: "switch-tab-6", | |
| 64 | - | ActionSwitchTab7: "switch-tab-7", | |
| 65 | - | ActionSwitchTab8: "switch-tab-8", | |
| 66 | - | ActionSwitchTab9: "switch-tab-9", | |
| 52 | + | ActionOpenPalette: "open-palette", | |
| 53 | + | ActionToggleFloatingPane: "toggle-floating-pane", | |
| 54 | + | ActionJumpAttention: "jump-attention", | |
| 55 | + | ActionFocusPaneLeft: "focus-pane-left", | |
| 56 | + | ActionFocusPaneRight: "focus-pane-right", | |
| 57 | + | ActionFocusPaneUp: "focus-pane-up", | |
| 58 | + | ActionFocusPaneDown: "focus-pane-down", | |
| 59 | + | ActionSwitchTab1: "switch-tab-1", | |
| 60 | + | ActionSwitchTab2: "switch-tab-2", | |
| 61 | + | ActionSwitchTab3: "switch-tab-3", | |
| 62 | + | ActionSwitchTab4: "switch-tab-4", | |
| 63 | + | ActionSwitchTab5: "switch-tab-5", | |
| 64 | + | ActionSwitchTab6: "switch-tab-6", | |
| 65 | + | ActionSwitchTab7: "switch-tab-7", | |
| 66 | + | ActionSwitchTab8: "switch-tab-8", | |
| 67 | + | ActionSwitchTab9: "switch-tab-9", | |
| 67 | 68 | } | |
| 68 | 69 | ||
| 69 | 70 | var actionsByName = func() map[string]Action { | |
| @@ -49,21 +49,22 @@ func ParseKeySpec(spec string) (KeySpec, error) { | |||||
| 49 | } | 49 | } | ||
| 50 | 50 | ||||
| 51 | var actionNames = map[Action]string{ | 51 | var actionNames = map[Action]string{ | ||
| 52 | - | ActionOpenPalette: "open-palette", | 52 | + | ActionOpenPalette: "open-palette", |
| 53 | - | ActionJumpAttention: "jump-attention", | 53 | + | ActionToggleFloatingPane: "toggle-floating-pane", |
| 54 | - | ActionFocusPaneLeft: "focus-pane-left", | 54 | + | ActionJumpAttention: "jump-attention", |
| 55 | - | ActionFocusPaneRight: "focus-pane-right", | 55 | + | ActionFocusPaneLeft: "focus-pane-left", |
| 56 | - | ActionFocusPaneUp: "focus-pane-up", | 56 | + | ActionFocusPaneRight: "focus-pane-right", |
| 57 | - | ActionFocusPaneDown: "focus-pane-down", | 57 | + | ActionFocusPaneUp: "focus-pane-up", |
| 58 | - | ActionSwitchTab1: "switch-tab-1", | 58 | + | ActionFocusPaneDown: "focus-pane-down", |
| 59 | - | ActionSwitchTab2: "switch-tab-2", | 59 | + | ActionSwitchTab1: "switch-tab-1", |
| 60 | - | ActionSwitchTab3: "switch-tab-3", | 60 | + | ActionSwitchTab2: "switch-tab-2", |
| 61 | - | ActionSwitchTab4: "switch-tab-4", | 61 | + | ActionSwitchTab3: "switch-tab-3", |
| 62 | - | ActionSwitchTab5: "switch-tab-5", | 62 | + | ActionSwitchTab4: "switch-tab-4", |
| 63 | - | ActionSwitchTab6: "switch-tab-6", | 63 | + | ActionSwitchTab5: "switch-tab-5", |
| 64 | - | ActionSwitchTab7: "switch-tab-7", | 64 | + | ActionSwitchTab6: "switch-tab-6", |
| 65 | - | ActionSwitchTab8: "switch-tab-8", | 65 | + | ActionSwitchTab7: "switch-tab-7", |
| 66 | - | ActionSwitchTab9: "switch-tab-9", | 66 | + | ActionSwitchTab8: "switch-tab-8", |
| 67 | + | ActionSwitchTab9: "switch-tab-9", | |||
| 67 | } | 68 | } | ||
| 68 | 69 | ||||
| 69 | var actionsByName = func() map[string]Action { | 70 | var actionsByName = func() map[string]Action { | ||
M internal/usecase/keys/keyspec_test.go +7 -3
| @@ -64,6 +64,7 @@ func TestActionNamesAreCanonical(t *testing.T) { | |||
| 64 | 64 | name string | |
| 65 | 65 | }{ | |
| 66 | 66 | {ActionOpenPalette, "open-palette"}, | |
| 67 | + | {ActionToggleFloatingPane, "toggle-floating-pane"}, | |
| 67 | 68 | {ActionJumpAttention, "jump-attention"}, | |
| 68 | 69 | {ActionFocusPaneLeft, "focus-pane-left"}, | |
| 69 | 70 | {ActionFocusPaneRight, "focus-pane-right"}, | |
| @@ -96,14 +97,17 @@ func TestActionNamesAreCanonical(t *testing.T) { | |||
| 96 | 97 | ||
| 97 | 98 | func TestBuildBindingsAppliesOverrides(t *testing.T) { | |
| 98 | 99 | bindings, warnings := BuildBindings(map[string]string{ | |
| 99 | - | "open-palette": "alt+o", | |
| 100 | - | "focus-pane-left": "alt+left", | |
| 101 | - | "switch-tab-2": "alt+2", | |
| 100 | + | "open-palette": "alt+o", | |
| 101 | + | "toggle-floating-pane": "alt+g", | |
| 102 | + | "focus-pane-left": "alt+left", | |
| 103 | + | "switch-tab-2": "alt+2", | |
| 102 | 104 | }) | |
| 103 | 105 | require.Empty(t, warnings) | |
| 104 | 106 | ||
| 105 | 107 | assertAltRuneAction(t, bindings, 'o', ActionOpenPalette) | |
| 106 | 108 | assertAltRuneUnbound(t, bindings, ' ') | |
| 109 | + | assertAltRuneAction(t, bindings, 'g', ActionToggleFloatingPane) | |
| 110 | + | assertAltRuneUnbound(t, bindings, 'f') | |
| 107 | 111 | assertAltArrowAction(t, bindings, 'D', ActionFocusPaneLeft) | |
| 108 | 112 | assertAltRuneUnbound(t, bindings, 'h') | |
| 109 | 113 | assertAltRuneAction(t, bindings, '2', ActionSwitchTab2) | |
| @@ -64,6 +64,7 @@ func TestActionNamesAreCanonical(t *testing.T) { | |||||
| 64 | name string | 64 | name string | ||
| 65 | }{ | 65 | }{ | ||
| 66 | {ActionOpenPalette, "open-palette"}, | 66 | {ActionOpenPalette, "open-palette"}, | ||
| 67 | + | {ActionToggleFloatingPane, "toggle-floating-pane"}, | |||
| 67 | {ActionJumpAttention, "jump-attention"}, | 68 | {ActionJumpAttention, "jump-attention"}, | ||
| 68 | {ActionFocusPaneLeft, "focus-pane-left"}, | 69 | {ActionFocusPaneLeft, "focus-pane-left"}, | ||
| 69 | {ActionFocusPaneRight, "focus-pane-right"}, | 70 | {ActionFocusPaneRight, "focus-pane-right"}, | ||
| @@ -96,14 +97,17 @@ func TestActionNamesAreCanonical(t *testing.T) { | |||||
| 96 | 97 | ||||
| 97 | func TestBuildBindingsAppliesOverrides(t *testing.T) { | 98 | func TestBuildBindingsAppliesOverrides(t *testing.T) { | ||
| 98 | bindings, warnings := BuildBindings(map[string]string{ | 99 | bindings, warnings := BuildBindings(map[string]string{ | ||
| 99 | - | "open-palette": "alt+o", | 100 | + | "open-palette": "alt+o", |
| 100 | - | "focus-pane-left": "alt+left", | 101 | + | "toggle-floating-pane": "alt+g", |
| 101 | - | "switch-tab-2": "alt+2", | 102 | + | "focus-pane-left": "alt+left", |
| 103 | + | "switch-tab-2": "alt+2", | |||
| 102 | }) | 104 | }) | ||
| 103 | require.Empty(t, warnings) | 105 | require.Empty(t, warnings) | ||
| 104 | 106 | ||||
| 105 | assertAltRuneAction(t, bindings, 'o', ActionOpenPalette) | 107 | assertAltRuneAction(t, bindings, 'o', ActionOpenPalette) | ||
| 106 | assertAltRuneUnbound(t, bindings, ' ') | 108 | assertAltRuneUnbound(t, bindings, ' ') | ||
| 109 | + | assertAltRuneAction(t, bindings, 'g', ActionToggleFloatingPane) | |||
| 110 | + | assertAltRuneUnbound(t, bindings, 'f') | |||
| 107 | assertAltArrowAction(t, bindings, 'D', ActionFocusPaneLeft) | 111 | assertAltArrowAction(t, bindings, 'D', ActionFocusPaneLeft) | ||
| 108 | assertAltRuneUnbound(t, bindings, 'h') | 112 | assertAltRuneUnbound(t, bindings, 'h') | ||
| 109 | assertAltRuneAction(t, bindings, '2', ActionSwitchTab2) | 113 | assertAltRuneAction(t, bindings, '2', ActionSwitchTab2) | ||
M internal/usecase/keys/router.go +1 -0
| @@ -37,6 +37,7 @@ const ( | |||
| 37 | 37 | ActionFocusPaneRight | |
| 38 | 38 | ActionFocusPaneUp | |
| 39 | 39 | ActionFocusPaneDown | |
| 40 | + | ActionToggleFloatingPane | |
| 40 | 41 | ) | |
| 41 | 42 | ||
| 42 | 43 | // Handler receives router outputs. Forward is called only for bytes that should | |
| @@ -37,6 +37,7 @@ const ( | |||||
| 37 | ActionFocusPaneRight | 37 | ActionFocusPaneRight | ||
| 38 | ActionFocusPaneUp | 38 | ActionFocusPaneUp | ||
| 39 | ActionFocusPaneDown | 39 | ActionFocusPaneDown | ||
| 40 | + | ActionToggleFloatingPane | |||
| 40 | ) | 41 | ) | ||
| 41 | 42 | ||||
| 42 | // Handler receives router outputs. Forward is called only for bytes that should | 43 | // Handler receives router outputs. Forward is called only for bytes that should | ||
M internal/usecase/keys/router_test.go +8 -0
| @@ -53,6 +53,7 @@ func TestDefaultBindingsParityRoutesBuiltInBindings(t *testing.T) { | |||
| 53 | 53 | want Action | |
| 54 | 54 | }{ | |
| 55 | 55 | {name: "palette", in: []byte{ESC, ' '}, want: ActionOpenPalette}, | |
| 56 | + | {name: "floating pane", in: []byte{ESC, 'f'}, want: ActionToggleFloatingPane}, | |
| 56 | 57 | {name: "jump attention", in: []byte{ESC, 'a'}, want: ActionJumpAttention}, | |
| 57 | 58 | {name: "focus left", in: []byte{ESC, 'h'}, want: ActionFocusPaneLeft}, | |
| 58 | 59 | {name: "focus down", in: []byte{ESC, 'j'}, want: ActionFocusPaneDown}, | |
| @@ -237,6 +238,13 @@ func switchTabActions() []Action { | |||
| 237 | 238 | } | |
| 238 | 239 | } | |
| 239 | 240 | ||
| 241 | + | func TestSwitchTabActionsRemainContiguous(t *testing.T) { | |
| 242 | + | for i, action := range switchTabActions() { | |
| 243 | + | require.Equal(t, ActionSwitchTab1+Action(i), action) | |
| 244 | + | } | |
| 245 | + | require.False(t, ActionToggleFloatingPane >= ActionSwitchTab1 && ActionToggleFloatingPane <= ActionSwitchTab9) | |
| 246 | + | } | |
| 247 | + | ||
| 240 | 248 | func TestRouterInterceptsAltAForJumpAttention(t *testing.T) { | |
| 241 | 249 | clk := &fakeClock{} | |
| 242 | 250 | h := &captureHandler{} | |
| @@ -53,6 +53,7 @@ func TestDefaultBindingsParityRoutesBuiltInBindings(t *testing.T) { | |||||
| 53 | want Action | 53 | want Action | ||
| 54 | }{ | 54 | }{ | ||
| 55 | {name: "palette", in: []byte{ESC, ' '}, want: ActionOpenPalette}, | 55 | {name: "palette", in: []byte{ESC, ' '}, want: ActionOpenPalette}, | ||
| 56 | + | {name: "floating pane", in: []byte{ESC, 'f'}, want: ActionToggleFloatingPane}, | |||
| 56 | {name: "jump attention", in: []byte{ESC, 'a'}, want: ActionJumpAttention}, | 57 | {name: "jump attention", in: []byte{ESC, 'a'}, want: ActionJumpAttention}, | ||
| 57 | {name: "focus left", in: []byte{ESC, 'h'}, want: ActionFocusPaneLeft}, | 58 | {name: "focus left", in: []byte{ESC, 'h'}, want: ActionFocusPaneLeft}, | ||
| 58 | {name: "focus down", in: []byte{ESC, 'j'}, want: ActionFocusPaneDown}, | 59 | {name: "focus down", in: []byte{ESC, 'j'}, want: ActionFocusPaneDown}, | ||
| @@ -237,6 +238,13 @@ func switchTabActions() []Action { | |||||
| 237 | } | 238 | } | ||
| 238 | } | 239 | } | ||
| 239 | 240 | ||||
| 241 | + | func TestSwitchTabActionsRemainContiguous(t *testing.T) { | |||
| 242 | + | for i, action := range switchTabActions() { | |||
| 243 | + | require.Equal(t, ActionSwitchTab1+Action(i), action) | |||
| 244 | + | } | |||
| 245 | + | require.False(t, ActionToggleFloatingPane >= ActionSwitchTab1 && ActionToggleFloatingPane <= ActionSwitchTab9) | |||
| 246 | + | } | |||
| 247 | + | ||||
| 240 | func TestRouterInterceptsAltAForJumpAttention(t *testing.T) { | 248 | func TestRouterInterceptsAltAForJumpAttention(t *testing.T) { | ||
| 241 | clk := &fakeClock{} | 249 | clk := &fakeClock{} | ||
| 242 | h := &captureHandler{} | 250 | h := &captureHandler{} | ||