v0.33.2 57f21d6

transform_legacy_test.go RawHistoryFolder

1package config
2
3import (
4	"testing"
5
6	"github.com/stretchr/testify/assert"
7	"github.com/stretchr/testify/require"
8)
9
10func TestTransformLegacyActions_SliceFormat(t *testing.T) {
11	transformer := NewLegacyConfigTransformer()
12
13	rawConfig := map[string]any{
14		"workspace": map[string]any{
15			"pane_mode": map[string]any{
16				"actions": map[string]any{
17					"focus-left": []any{"h", "arrowleft"},
18				},
19			},
20		},
21	}
22
23	transformer.TransformLegacyActions(rawConfig)
24
25	actions := rawConfig["workspace"].(map[string]any)["pane_mode"].(map[string]any)["actions"].(map[string]any)
26	binding, ok := actions["focus-left"].(map[string]any)
27	require.True(t, ok, "should be converted to map")
28
29	keys, ok := binding["keys"].([]string)
30	require.True(t, ok, "keys should be []string")
31	assert.Equal(t, []string{"h", "arrowleft"}, keys)
32	assert.NotEmpty(t, binding["desc"], "should have description from defaults")
33}
34
35func TestTransformLegacyActions_AllModes(t *testing.T) {
36	transformer := NewLegacyConfigTransformer()
37
38	rawConfig := map[string]any{
39		"workspace": map[string]any{
40			"pane_mode": map[string]any{
41				"actions": map[string]any{
42					"focus-left": []any{"h"},
43				},
44			},
45			"tab_mode": map[string]any{
46				"actions": map[string]any{
47					"next-tab": []any{"l"},
48				},
49			},
50			"resize_mode": map[string]any{
51				"actions": map[string]any{
52					"resize-increase": []any{"+"},
53				},
54			},
55			"shortcuts": map[string]any{
56				"actions": map[string]any{
57					"close_pane": []any{"ctrl+w"},
58				},
59			},
60		},
61		"session": map[string]any{
62			"session_mode": map[string]any{
63				"actions": map[string]any{
64					"session-manager": []any{"m"},
65				},
66			},
67		},
68	}
69
70	transformer.TransformLegacyActions(rawConfig)
71
72	// Check pane mode
73	paneActions := rawConfig["workspace"].(map[string]any)["pane_mode"].(map[string]any)["actions"].(map[string]any)
74	paneFocus, ok := paneActions["focus-left"].(map[string]any)
75	require.True(t, ok)
76	assert.Equal(t, []string{"h"}, paneFocus["keys"])
77
78	// Check tab mode
79	tabActions := rawConfig["workspace"].(map[string]any)["tab_mode"].(map[string]any)["actions"].(map[string]any)
80	tabNext, ok := tabActions["next-tab"].(map[string]any)
81	require.True(t, ok)
82	assert.Equal(t, []string{"l"}, tabNext["keys"])
83
84	// Check resize mode
85	resizeActions := rawConfig["workspace"].(map[string]any)["resize_mode"].(map[string]any)["actions"].(map[string]any)
86	resizeInc, ok := resizeActions["resize-increase"].(map[string]any)
87	require.True(t, ok)
88	assert.Equal(t, []string{"+"}, resizeInc["keys"])
89
90	// Check shortcuts
91	shortcutActions := rawConfig["workspace"].(map[string]any)["shortcuts"].(map[string]any)["actions"].(map[string]any)
92	closePane, ok := shortcutActions["close-pane"].(map[string]any)
93	require.True(t, ok, "underscore action names are renamed to the hyphen form")
94	assert.NotContains(t, shortcutActions, "close_pane")
95	assert.Equal(t, []string{"ctrl+w"}, closePane["keys"])
96
97	// Check session mode
98	sessionActions := rawConfig["session"].(map[string]any)["session_mode"].(map[string]any)["actions"].(map[string]any)
99	sessionMgr, ok := sessionActions["session-manager"].(map[string]any)
100	require.True(t, ok)
101	assert.Equal(t, []string{"m"}, sessionMgr["keys"])
102}
103
104func TestTransformLegacyActions_AlreadyNewFormat(t *testing.T) {
105	transformer := NewLegacyConfigTransformer()
106
107	rawConfig := map[string]any{
108		"workspace": map[string]any{
109			"pane_mode": map[string]any{
110				"actions": map[string]any{
111					"focus-left": map[string]any{
112						"keys": []string{"h"},
113						"desc": "Custom description",
114					},
115				},
116			},
117		},
118	}
119
120	transformer.TransformLegacyActions(rawConfig)
121
122	actions := rawConfig["workspace"].(map[string]any)["pane_mode"].(map[string]any)["actions"].(map[string]any)
123	binding := actions["focus-left"].(map[string]any)
124
125	// Should remain unchanged
126	assert.Equal(t, "Custom description", binding["desc"])
127}
128
129func TestTransformLegacyActions_MissingSection(t *testing.T) {
130	transformer := NewLegacyConfigTransformer()
131
132	rawConfig := map[string]any{
133		"appearance": map[string]any{
134			"theme": "dark",
135		},
136	}
137
138	// Should not panic
139	transformer.TransformLegacyActions(rawConfig)
140
141	// Config should be unchanged
142	assert.Equal(t, "dark", rawConfig["appearance"].(map[string]any)["theme"])
143}
144
145func TestTransformLegacyActions_EmptyConfig(t *testing.T) {
146	transformer := NewLegacyConfigTransformer()
147
148	rawConfig := map[string]any{}
149
150	// Should not panic
151	transformer.TransformLegacyActions(rawConfig)
152
153	assert.Empty(t, rawConfig)
154}
155
156func TestTransformLegacyActions_PartialPath(t *testing.T) {
157	transformer := NewLegacyConfigTransformer()
158
159	rawConfig := map[string]any{
160		"workspace": map[string]any{
161			"pane_mode": map[string]any{
162				// actions section missing
163			},
164		},
165	}
166
167	// Should not panic
168	transformer.TransformLegacyActions(rawConfig)
169
170	// Verify config is unchanged
171	require.NotNil(t, rawConfig["workspace"])
172}
173
174func TestTransformLegacyEngineConfig_RemovesDeprecatedContextMenuHandler(t *testing.T) {
175	transformer := NewLegacyConfigTransformer()
176
177	rawConfig := map[string]any{
178		"engine": map[string]any{
179			"cef": map[string]any{
180				"enable_context_menu_handler": true,
181			},
182		},
183	}
184
185	transformer.TransformLegacyEngineConfig(rawConfig)
186
187	cef := rawConfig["engine"].(map[string]any)["cef"].(map[string]any)
188	_, exists := cef["enable_context_menu_handler"]
189	assert.False(t, exists)
190}
191
192func TestTransformLegacyEngineConfig_MigratesLegacyCEFWindowlessFrameRateDefault(t *testing.T) {
193	transformer := NewLegacyConfigTransformer()
194	rawConfig := map[string]any{
195		"engine": map[string]any{
196			"cef": map[string]any{
197				"windowless_frame_rate": 60,
198			},
199		},
200	}
201
202	transformer.TransformLegacyEngineConfig(rawConfig)
203
204	cef := rawConfig["engine"].(map[string]any)["cef"].(map[string]any)
205	assert.Equal(t, true, cef["adaptive_windowless_frame_rate"])
206	assert.Equal(t, 0, cef["windowless_frame_rate"])
207	assert.Equal(t, defaultCEFWindowlessFrameRateMax, cef["windowless_frame_rate_max"])
208}
209
210func TestTransformLegacyEngineConfig_UpgradesObsoletePreciseScrollDefault(t *testing.T) {
211	transformer := NewLegacyConfigTransformer()
212	rawConfig := map[string]any{
213		"engine": map[string]any{
214			"cef": map[string]any{
215				"input": map[string]any{
216					"scroll_precise_multiplier": 0.35,
217				},
218			},
219		},
220	}
221
222	transformer.TransformLegacyEngineConfig(rawConfig)
223
224	input := rawConfig["engine"].(map[string]any)["cef"].(map[string]any)["input"].(map[string]any)
225	assert.InDelta(t, defaultCEFScrollPreciseMultiplier, input["scroll_precise_multiplier"], 0.001)
226}
227
228func TestTransformLegacyEngineConfig_MigratesLegacyTouchpadScrollKey(t *testing.T) {
229	transformer := NewLegacyConfigTransformer()
230	rawConfig := map[string]any{
231		"engine": map[string]any{
232			"cef": map[string]any{
233				"input": map[string]any{
234					"scroll_touchpad_multiplier": 0.35,
235				},
236			},
237		},
238	}
239
240	transformer.TransformLegacyEngineConfig(rawConfig)
241
242	input := rawConfig["engine"].(map[string]any)["cef"].(map[string]any)["input"].(map[string]any)
243	assert.InDelta(t, defaultCEFScrollPreciseMultiplier, input["scroll_precise_multiplier"], 0.001)
244	assert.NotContains(t, input, "scroll_touchpad_multiplier")
245}
246
247func TestTransformLegacyEngineConfig_PreservesCustomPreciseScrollMultiplier(t *testing.T) {
248	transformer := NewLegacyConfigTransformer()
249	rawConfig := map[string]any{
250		"engine": map[string]any{
251			"cef": map[string]any{
252				"input": map[string]any{
253					"scroll_precise_multiplier": 4.0,
254				},
255			},
256		},
257	}
258
259	transformer.TransformLegacyEngineConfig(rawConfig)
260
261	input := rawConfig["engine"].(map[string]any)["cef"].(map[string]any)["input"].(map[string]any)
262	assert.InDelta(t, 4.0, input["scroll_precise_multiplier"], 0.001)
263}
264
265func TestTransformLegacyEngineConfig_PreservesCustomLegacyTouchpadScrollMultiplier(t *testing.T) {
266	transformer := NewLegacyConfigTransformer()
267	rawConfig := map[string]any{
268		"engine": map[string]any{
269			"cef": map[string]any{
270				"input": map[string]any{
271					"scroll_touchpad_multiplier": 4.0,
272				},
273			},
274		},
275	}
276
277	transformer.TransformLegacyEngineConfig(rawConfig)
278
279	input := rawConfig["engine"].(map[string]any)["cef"].(map[string]any)["input"].(map[string]any)
280	assert.InDelta(t, 4.0, input["scroll_precise_multiplier"], 0.001)
281	assert.NotContains(t, input, "scroll_touchpad_multiplier")
282}
283
284func TestTransformLegacyEngineConfig_PrefersCustomLegacyTouchpadOverGeneratedPreciseDefault(t *testing.T) {
285	transformer := NewLegacyConfigTransformer()
286	rawConfig := map[string]any{
287		"engine": map[string]any{
288			"cef": map[string]any{
289				"input": map[string]any{
290					"scroll_touchpad_multiplier": 4.0,
291					"scroll_precise_multiplier":  0.35,
292				},
293			},
294		},
295	}
296
297	transformer.TransformLegacyEngineConfig(rawConfig)
298
299	input := rawConfig["engine"].(map[string]any)["cef"].(map[string]any)["input"].(map[string]any)
300	assert.InDelta(t, 4.0, input["scroll_precise_multiplier"], 0.001)
301	assert.NotContains(t, input, "scroll_touchpad_multiplier")
302}
303
304func TestTransformLegacyEngineConfig_PreservesExplicitCEFWindowlessFrameRate(t *testing.T) {
305	transformer := NewLegacyConfigTransformer()
306	rawConfig := map[string]any{
307		"engine": map[string]any{
308			"cef": map[string]any{
309				"adaptive_windowless_frame_rate": false,
310				"windowless_frame_rate":          60,
311			},
312		},
313	}
314
315	transformer.TransformLegacyEngineConfig(rawConfig)
316
317	cef := rawConfig["engine"].(map[string]any)["cef"].(map[string]any)
318	assert.Equal(t, false, cef["adaptive_windowless_frame_rate"])
319	assert.Equal(t, 60, cef["windowless_frame_rate"])
320	_, hasMax := cef["windowless_frame_rate_max"]
321	assert.False(t, hasMax)
322}
323
324func TestTransformLegacyEngineConfig_PreservesNonDefaultCEFWindowlessFrameRate(t *testing.T) {
325	transformer := NewLegacyConfigTransformer()
326	rawConfig := map[string]any{
327		"engine": map[string]any{
328			"cef": map[string]any{
329				"windowless_frame_rate": 144,
330			},
331		},
332	}
333
334	transformer.TransformLegacyEngineConfig(rawConfig)
335
336	cef := rawConfig["engine"].(map[string]any)["cef"].(map[string]any)
337	_, hasAdaptive := cef["adaptive_windowless_frame_rate"]
338	assert.False(t, hasAdaptive)
339	assert.Equal(t, 144, cef["windowless_frame_rate"])
340}
341
342func TestTransformLegacyEngineConfig_MissingCefSection(t *testing.T) {
343	transformer := NewLegacyConfigTransformer()
344	rawConfig := map[string]any{
345		"engine": map[string]any{
346			"webkit": map[string]any{"enabled": true},
347		},
348	}
349
350	transformer.TransformLegacyEngineConfig(rawConfig)
351
352	engine, ok := rawConfig["engine"].(map[string]any)
353	require.True(t, ok)
354	assert.NotNil(t, engine)
355	assert.Equal(t, true, engine["webkit"].(map[string]any)["enabled"])
356}
357
358func TestTransformLegacyEngineConfig_MissingContextMenuHandlerKey(t *testing.T) {
359	transformer := NewLegacyConfigTransformer()
360	rawConfig := map[string]any{
361		"engine": map[string]any{
362			"cef": map[string]any{
363				"other_setting": true,
364			},
365		},
366	}
367
368	transformer.TransformLegacyEngineConfig(rawConfig)
369
370	cef := rawConfig["engine"].(map[string]any)["cef"].(map[string]any)
371	assert.True(t, cef["other_setting"].(bool))
372	_, exists := cef["enable_context_menu_handler"]
373	assert.False(t, exists)
374}
375
376func TestTransformLegacyEngineConfig_NonMapEngineSection(t *testing.T) {
377	transformer := NewLegacyConfigTransformer()
378	rawConfig := map[string]any{
379		"engine": "invalid",
380	}
381
382	transformer.TransformLegacyEngineConfig(rawConfig)
383
384	assert.Equal(t, "invalid", rawConfig["engine"])
385}
386
387func TestTransformLegacyActions_UnknownAction(t *testing.T) {
388	transformer := NewLegacyConfigTransformer()
389
390	rawConfig := map[string]any{
391		"workspace": map[string]any{
392			"pane_mode": map[string]any{
393				"actions": map[string]any{
394					"unknown-action": []any{"x"},
395				},
396			},
397		},
398	}
399
400	transformer.TransformLegacyActions(rawConfig)
401
402	actions := rawConfig["workspace"].(map[string]any)["pane_mode"].(map[string]any)["actions"].(map[string]any)
403	binding, ok := actions["unknown-action"].(map[string]any)
404	require.True(t, ok)
405	assert.Equal(t, []string{"x"}, binding["keys"])
406	assert.Empty(t, binding["desc"], "unknown action should have empty description")
407}
408
409func TestTransformLegacyActions_RenamesUnderscoreNamesInEveryActionMap(t *testing.T) {
410	raw := map[string]any{
411		"workspace": map[string]any{
412			"shortcuts":   map[string]any{"actions": map[string]any{"toggle_floating_pane": map[string]any{"keys": []any{"alt+g"}}}},
413			"prefix_mode": map[string]any{"actions": map[string]any{"enter_resize_mode": map[string]any{"keys": []any{"z"}}}},
414			"pane_mode":   map[string]any{"actions": map[string]any{"focus_left": map[string]any{"keys": []any{"z"}}}},
415			"tab_mode":    map[string]any{"actions": map[string]any{"new_tab": map[string]any{"keys": []any{"z"}}}},
416			"resize_mode": map[string]any{"actions": map[string]any{"resize_increase_left": map[string]any{"keys": []any{"z"}}}},
417			"vim_mode":    map[string]any{"actions": map[string]any{"vim_scroll_left": map[string]any{"keys": []any{"z"}}}},
418		},
419		"session": map[string]any{
420			"session_mode": map[string]any{"actions": map[string]any{"session_manager": map[string]any{"keys": []any{"z"}}}},
421		},
422	}
423
424	NewLegacyConfigTransformer().TransformLegacyActions(raw)
425
426	want := map[string]string{
427		"shortcuts":   "toggle-floating-pane",
428		"prefix_mode": "enter-resize-mode",
429		"pane_mode":   "focus-left",
430		"tab_mode":    "new-tab",
431		"resize_mode": "resize-increase-left",
432		"vim_mode":    "vim-scroll-left",
433	}
434	workspace := raw["workspace"].(map[string]any)
435	for section, name := range want {
436		actions := workspace[section].(map[string]any)["actions"].(map[string]any)
437		assert.Len(t, actions, 1, section)
438		assert.Contains(t, actions, name, section)
439	}
440	sessionActions := raw["session"].(map[string]any)["session_mode"].(map[string]any)["actions"].(map[string]any)
441	assert.Len(t, sessionActions, 1)
442	assert.Contains(t, sessionActions, "session-manager")
443}
444
445func TestTransformLegacyActions_HyphenSpellingWinsOverUnderscore(t *testing.T) {
446	raw := map[string]any{
447		"workspace": map[string]any{
448			"pane_mode": map[string]any{"actions": map[string]any{
449				"stack_pane": map[string]any{"keys": []any{"q"}},
450				"stack-pane": map[string]any{"keys": []any{"w"}},
451			}},
452		},
453	}
454
455	NewLegacyConfigTransformer().TransformLegacyActions(raw)
456
457	actions := raw["workspace"].(map[string]any)["pane_mode"].(map[string]any)["actions"].(map[string]any)
458	require.Len(t, actions, 1)
459	assert.Equal(t, []any{"w"}, actions["stack-pane"].(map[string]any)["keys"])
460}
461
462func TestTransformLegacyActions_RenamesUnderscoreNamesInSliceFormat(t *testing.T) {
463	raw := map[string]any{
464		"workspace": map[string]any{
465			"pane_mode": map[string]any{"actions": map[string]any{"stack_pane": []any{"q"}}},
466		},
467	}
468
469	NewLegacyConfigTransformer().TransformLegacyActions(raw)
470
471	actions := raw["workspace"].(map[string]any)["pane_mode"].(map[string]any)["actions"].(map[string]any)
472	binding, ok := actions["stack-pane"].(map[string]any)
473	require.True(t, ok)
474	assert.Equal(t, []string{"q"}, binding["keys"])
475	assert.Equal(t, DefaultConfig().Workspace.PaneMode.Actions["stack-pane"].Desc, binding["desc"])
476}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close