fix(vim): deduplicate highlighted web views
- commit
- 14d539b05ef54924cbb40277326b7475a2e973ec
- author
- brice · · 2026-08-18 05:13 UTC
- parent
- 3a00c38
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +15 -3
M internal/ui/app.go +9 -1
| @@ -2205,7 +2205,14 @@ func (a *App) navigateVimSequenceAction(ctx context.Context, bw *browserWindow, | |||
| 2205 | 2205 | } | |
| 2206 | 2206 | if bw != nil && (action == "heading-next" || action == "heading-prev") { | |
| 2207 | 2207 | if _, ok := wv.(port.SemanticNavigable); ok { | |
| 2208 | - | bw.vimNavigationHighlightedWebViews = append(bw.vimNavigationHighlightedWebViews, wv) | |
| 2208 | + | if bw.vimNavigationHighlightedWebViewIDs == nil { | |
| 2209 | + | bw.vimNavigationHighlightedWebViewIDs = make(map[port.WebViewID]struct{}) | |
| 2210 | + | } | |
| 2211 | + | id := wv.ID() | |
| 2212 | + | if _, exists := bw.vimNavigationHighlightedWebViewIDs[id]; !exists { | |
| 2213 | + | bw.vimNavigationHighlightedWebViewIDs[id] = struct{}{} | |
| 2214 | + | bw.vimNavigationHighlightedWebViews = append(bw.vimNavigationHighlightedWebViews, wv) | |
| 2215 | + | } | |
| 2209 | 2216 | } | |
| 2210 | 2217 | } | |
| 2211 | 2218 | } | |
| @@ -2224,6 +2231,7 @@ func (a *App) clearVimNavigationHighlight(ctx context.Context, bw *browserWindow | |||
| 2224 | 2231 | } | |
| 2225 | 2232 | } | |
| 2226 | 2233 | bw.vimNavigationHighlightedWebViews = nil | |
| 2234 | + | bw.vimNavigationHighlightedWebViewIDs = nil | |
| 2227 | 2235 | } | |
| 2228 | 2236 | ||
| 2229 | 2237 | func (a *App) vimNavigationHighlightColor() string { | |
| @@ -2205,7 +2205,14 @@ func (a *App) navigateVimSequenceAction(ctx context.Context, bw *browserWindow, | |||||
| 2205 | } | 2205 | } | ||
| 2206 | if bw != nil && (action == "heading-next" || action == "heading-prev") { | 2206 | if bw != nil && (action == "heading-next" || action == "heading-prev") { | ||
| 2207 | if _, ok := wv.(port.SemanticNavigable); ok { | 2207 | if _, ok := wv.(port.SemanticNavigable); ok { | ||
| 2208 | - | bw.vimNavigationHighlightedWebViews = append(bw.vimNavigationHighlightedWebViews, wv) | 2208 | + | if bw.vimNavigationHighlightedWebViewIDs == nil { |
| 2209 | + | bw.vimNavigationHighlightedWebViewIDs = make(map[port.WebViewID]struct{}) | |||
| 2210 | + | } | |||
| 2211 | + | id := wv.ID() | |||
| 2212 | + | if _, exists := bw.vimNavigationHighlightedWebViewIDs[id]; !exists { | |||
| 2213 | + | bw.vimNavigationHighlightedWebViewIDs[id] = struct{}{} | |||
| 2214 | + | bw.vimNavigationHighlightedWebViews = append(bw.vimNavigationHighlightedWebViews, wv) | |||
| 2215 | + | } | |||
| 2209 | } | 2216 | } | ||
| 2210 | } | 2217 | } | ||
| 2211 | } | 2218 | } | ||
| @@ -2224,6 +2231,7 @@ func (a *App) clearVimNavigationHighlight(ctx context.Context, bw *browserWindow | |||||
| 2224 | } | 2231 | } | ||
| 2225 | } | 2232 | } | ||
| 2226 | bw.vimNavigationHighlightedWebViews = nil | 2233 | bw.vimNavigationHighlightedWebViews = nil | ||
| 2234 | + | bw.vimNavigationHighlightedWebViewIDs = nil | |||
| 2227 | } | 2235 | } | ||
| 2228 | 2236 | ||||
| 2229 | func (a *App) vimNavigationHighlightColor() string { | 2237 | func (a *App) vimNavigationHighlightColor() string { | ||
M internal/ui/app_vim_mode_test.go +3 -1
| @@ -183,7 +183,7 @@ type semanticNavigatingWebView struct { | |||
| 183 | 183 | func newSemanticNavigatingWebView(t *testing.T, id port.WebViewID) *semanticNavigatingWebView { | |
| 184 | 184 | t.Helper() | |
| 185 | 185 | wv := portmocks.NewMockWebView(t) | |
| 186 | - | wv.EXPECT().ID().Return(id).Once() | |
| 186 | + | wv.EXPECT().ID().Return(id) | |
| 187 | 187 | return &semanticNavigatingWebView{MockWebView: wv} | |
| 188 | 188 | } | |
| 189 | 189 | ||
| @@ -528,11 +528,13 @@ func TestVimMode_Leave_ClearsNavigationHighlight(t *testing.T) { | |||
| 528 | 528 | wv := newSemanticNavigatingWebView(t, 8) | |
| 529 | 529 | app.contentCoord.RegisterPopupWebView(paneID, wv) | |
| 530 | 530 | app.navigateVimSequenceAction(context.Background(), bw, "heading-next", 1) | |
| 531 | + | app.navigateVimSequenceAction(context.Background(), bw, "heading-next", 1) | |
| 531 | 532 | ||
| 532 | 533 | app.handleModeChange(context.Background(), bw, input.ModeVim, input.ModeNormal) | |
| 533 | 534 | ||
| 534 | 535 | assert.Equal(t, 1, wv.clearNavigationHighlightCalls) | |
| 535 | 536 | assert.Empty(t, bw.vimNavigationHighlightedWebViews) | |
| 537 | + | assert.Empty(t, bw.vimNavigationHighlightedWebViewIDs) | |
| 536 | 538 | } | |
| 537 | 539 | ||
| 538 | 540 | func TestVimMode_Leave_ClearsNavigationHighlightAfterPaneSwitch(t *testing.T) { | |
| @@ -183,7 +183,7 @@ type semanticNavigatingWebView struct { | |||||
| 183 | func newSemanticNavigatingWebView(t *testing.T, id port.WebViewID) *semanticNavigatingWebView { | 183 | func newSemanticNavigatingWebView(t *testing.T, id port.WebViewID) *semanticNavigatingWebView { | ||
| 184 | t.Helper() | 184 | t.Helper() | ||
| 185 | wv := portmocks.NewMockWebView(t) | 185 | wv := portmocks.NewMockWebView(t) | ||
| 186 | - | wv.EXPECT().ID().Return(id).Once() | 186 | + | wv.EXPECT().ID().Return(id) |
| 187 | return &semanticNavigatingWebView{MockWebView: wv} | 187 | return &semanticNavigatingWebView{MockWebView: wv} | ||
| 188 | } | 188 | } | ||
| 189 | 189 | ||||
| @@ -528,11 +528,13 @@ func TestVimMode_Leave_ClearsNavigationHighlight(t *testing.T) { | |||||
| 528 | wv := newSemanticNavigatingWebView(t, 8) | 528 | wv := newSemanticNavigatingWebView(t, 8) | ||
| 529 | app.contentCoord.RegisterPopupWebView(paneID, wv) | 529 | app.contentCoord.RegisterPopupWebView(paneID, wv) | ||
| 530 | app.navigateVimSequenceAction(context.Background(), bw, "heading-next", 1) | 530 | app.navigateVimSequenceAction(context.Background(), bw, "heading-next", 1) | ||
| 531 | + | app.navigateVimSequenceAction(context.Background(), bw, "heading-next", 1) | |||
| 531 | 532 | ||||
| 532 | app.handleModeChange(context.Background(), bw, input.ModeVim, input.ModeNormal) | 533 | app.handleModeChange(context.Background(), bw, input.ModeVim, input.ModeNormal) | ||
| 533 | 534 | ||||
| 534 | assert.Equal(t, 1, wv.clearNavigationHighlightCalls) | 535 | assert.Equal(t, 1, wv.clearNavigationHighlightCalls) | ||
| 535 | assert.Empty(t, bw.vimNavigationHighlightedWebViews) | 536 | assert.Empty(t, bw.vimNavigationHighlightedWebViews) | ||
| 537 | + | assert.Empty(t, bw.vimNavigationHighlightedWebViewIDs) | |||
| 536 | } | 538 | } | ||
| 537 | 539 | ||||
| 538 | func TestVimMode_Leave_ClearsNavigationHighlightAfterPaneSwitch(t *testing.T) { | 540 | func TestVimMode_Leave_ClearsNavigationHighlightAfterPaneSwitch(t *testing.T) { | ||
M internal/ui/browser_window.go +3 -1
| @@ -58,7 +58,8 @@ type browserWindow struct { | |||
| 58 | 58 | vimModePaneID entity.PaneID | |
| 59 | 59 | // vimNavigationHighlightedWebViews holds every WebView with a semantic Vim | |
| 60 | 60 | // navigation target that must be cleared when this window leaves Vim Mode. | |
| 61 | - | vimNavigationHighlightedWebViews []port.WebView | |
| 61 | + | vimNavigationHighlightedWebViews []port.WebView | |
| 62 | + | vimNavigationHighlightedWebViewIDs map[port.WebViewID]struct{} | |
| 62 | 63 | } | |
| 63 | 64 | ||
| 64 | 65 | func (bw *browserWindow) detachInputForDestroy() { | |
| @@ -117,6 +118,7 @@ func (bw *browserWindow) clearShellState() { | |||
| 117 | 118 | bw.activeSidebarKind = nativeSidebarNone | |
| 118 | 119 | bw.vimModePaneID = "" | |
| 119 | 120 | bw.vimNavigationHighlightedWebViews = nil | |
| 121 | + | bw.vimNavigationHighlightedWebViewIDs = nil | |
| 120 | 122 | } | |
| 121 | 123 | ||
| 122 | 124 | func (bw *browserWindow) initChrome(ctx context.Context, a *App) { | |
| @@ -58,7 +58,8 @@ type browserWindow struct { | |||||
| 58 | vimModePaneID entity.PaneID | 58 | vimModePaneID entity.PaneID | ||
| 59 | // vimNavigationHighlightedWebViews holds every WebView with a semantic Vim | 59 | // vimNavigationHighlightedWebViews holds every WebView with a semantic Vim | ||
| 60 | // navigation target that must be cleared when this window leaves Vim Mode. | 60 | // navigation target that must be cleared when this window leaves Vim Mode. | ||
| 61 | - | vimNavigationHighlightedWebViews []port.WebView | 61 | + | vimNavigationHighlightedWebViews []port.WebView |
| 62 | + | vimNavigationHighlightedWebViewIDs map[port.WebViewID]struct{} | |||
| 62 | } | 63 | } | ||
| 63 | 64 | ||||
| 64 | func (bw *browserWindow) detachInputForDestroy() { | 65 | func (bw *browserWindow) detachInputForDestroy() { | ||
| @@ -117,6 +118,7 @@ func (bw *browserWindow) clearShellState() { | |||||
| 117 | bw.activeSidebarKind = nativeSidebarNone | 118 | bw.activeSidebarKind = nativeSidebarNone | ||
| 118 | bw.vimModePaneID = "" | 119 | bw.vimModePaneID = "" | ||
| 119 | bw.vimNavigationHighlightedWebViews = nil | 120 | bw.vimNavigationHighlightedWebViews = nil | ||
| 121 | + | bw.vimNavigationHighlightedWebViewIDs = nil | |||
| 120 | } | 122 | } | ||
| 121 | 123 | ||||
| 122 | func (bw *browserWindow) initChrome(ctx context.Context, a *App) { | 124 | func (bw *browserWindow) initChrome(ctx context.Context, a *App) { | ||