detached 88b49b5

fix(omnibox): pass query through chain to prevent ghost text race condition

commit
88b49b5ae5753478ec6c1789a7dbf94e576802fe
author
brice · · 2026-01-24 03:55 UTC
parent
478ef93
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +22 -15
M internal/ui/component/omnibox.go +22 -15
@@ -876,16 +876,19 @@ func (o *Omnibox) acceptGhostCompletion() {
876876 }
877877
878878 // updateGhostFromSuggestion updates ghost text based on the current autocomplete suggestion.
879-func (o *Omnibox) updateGhostFromSuggestion() {
879+// The query parameter is the search query that was used, ensuring we don't use stale input.
880+func (o *Omnibox) updateGhostFromSuggestion(query string) {
880881 if o.autocompleteUC == nil {
881882 return
882883 }
883884
884885 o.mu.RLock()
885- userInput := o.realInput
886886 bangMode := o.bangMode
887887 o.mu.RUnlock()
888888
889+ // Use the query that triggered this search, not realInput which may have changed
890+ userInput := query
891+
889892 // Don't show ghost text in bang mode (it has its own completion)
890893 if bangMode || userInput == "" {
891894 o.clearGhostText()
@@ -1060,8 +1063,8 @@ func (o *Omnibox) performSearch() {
10601063 })
10611064 }
10621065
1063- // Marshal back to GTK main thread
1064- o.idleAddUpdateSuggestions(suggestions)
1066+ // Marshal back to GTK main thread, passing query to ensure ghost text uses correct input
1067+ o.idleAddUpdateSuggestions(suggestions, query)
10651068 }()
10661069 }
10671070
@@ -1078,7 +1081,7 @@ func (o *Omnibox) loadInitialHistory() {
10781081
10791082 switch o.initialBehavior {
10801083 case "none":
1081- o.idleAddUpdateSuggestions(nil)
1084+ o.idleAddUpdateSuggestions(nil, "")
10821085 return
10831086
10841087 case "most_visited", "recent", "":
@@ -1123,7 +1126,7 @@ func (o *Omnibox) loadInitialHistory() {
11231126 }
11241127 }
11251128
1126- o.idleAddUpdateSuggestions(suggestions)
1129+ o.idleAddUpdateSuggestions(suggestions, "")
11271130 }()
11281131 }
11291132
@@ -1161,7 +1164,7 @@ func (o *Omnibox) loadFavorites(query string) {
11611164 })
11621165 }
11631166
1164- o.idleAddUpdateFavorites(favorites)
1167+ o.idleAddUpdateFavorites(favorites, query)
11651168 }()
11661169 }
11671170
@@ -1263,7 +1266,8 @@ func (o *Omnibox) updateBangSuggestions(suggestions []BangSuggestion) {
12631266 }
12641267
12651268 // updateSuggestions updates the list with history suggestions.
1266-func (o *Omnibox) updateSuggestions(suggestions []Suggestion) {
1269+// The query parameter is the search query that triggered this update.
1270+func (o *Omnibox) updateSuggestions(suggestions []Suggestion, query string) {
12671271 o.mu.Lock()
12681272 o.bangMode = false
12691273 o.bangSuggestions = nil
@@ -1284,14 +1288,15 @@ func (o *Omnibox) updateSuggestions(suggestions []Suggestion) {
12841288 if rowCount > 0 {
12851289 o.selectIndex(0)
12861290 // Update ghost text from first suggestion
1287- o.updateGhostFromSuggestion()
1291+ o.updateGhostFromSuggestion(query)
12881292 } else {
12891293 o.clearGhostText()
12901294 }
12911295 }
12921296
12931297 // updateFavorites updates the list with favorites.
1294-func (o *Omnibox) updateFavorites(favorites []Favorite) {
1298+// The query parameter is the search query that triggered this update.
1299+func (o *Omnibox) updateFavorites(favorites []Favorite, query string) {
12951300 o.mu.Lock()
12961301 o.bangMode = false
12971302 o.bangSuggestions = nil
@@ -1312,7 +1317,7 @@ func (o *Omnibox) updateFavorites(favorites []Favorite) {
13121317 if rowCount > 0 {
13131318 o.selectIndex(0)
13141319 // Update ghost text from first favorite
1315- o.updateGhostFromSuggestion()
1320+ o.updateGhostFromSuggestion(query)
13161321 } else {
13171322 o.clearGhostText()
13181323 }
@@ -2034,9 +2039,10 @@ func (o *Omnibox) UpdateZoomIndicator(factor float64) {
20342039 }
20352040
20362041 // idleAddUpdateSuggestions schedules updateSuggestions on the GTK main thread.
2037-func (o *Omnibox) idleAddUpdateSuggestions(suggestions []Suggestion) {
2042+// The query parameter is passed through to ensure ghost text uses the correct input.
2043+func (o *Omnibox) idleAddUpdateSuggestions(suggestions []Suggestion, query string) {
20382044 var cb glib.SourceFunc = func(data uintptr) bool {
2039- o.updateSuggestions(suggestions)
2045+ o.updateSuggestions(suggestions, query)
20402046 return false // One-shot callback
20412047 }
20422048 glib.IdleAdd(&cb, 0)
@@ -2058,9 +2064,10 @@ func (o *Omnibox) getFavoriteURLs(ctx context.Context) map[string]struct{} {
20582064 }
20592065
20602066 // idleAddUpdateFavorites schedules updateFavorites on the GTK main thread.
2061-func (o *Omnibox) idleAddUpdateFavorites(favorites []Favorite) {
2067+// The query parameter is passed through to ensure ghost text uses the correct input.
2068+func (o *Omnibox) idleAddUpdateFavorites(favorites []Favorite, query string) {
20622069 var cb glib.SourceFunc = func(data uintptr) bool {
2063- o.updateFavorites(favorites)
2070+ o.updateFavorites(favorites, query)
20642071 return false // One-shot callback, return false to remove source
20652072 }
20662073 glib.IdleAdd(&cb, 0)
@@ -876,16 +876,19 @@ func (o *Omnibox) acceptGhostCompletion() {
876 }876 }
877 877
878 // updateGhostFromSuggestion updates ghost text based on the current autocomplete suggestion.878 // updateGhostFromSuggestion updates ghost text based on the current autocomplete suggestion.
879-func (o *Omnibox) updateGhostFromSuggestion() {879+// The query parameter is the search query that was used, ensuring we don't use stale input.
880+func (o *Omnibox) updateGhostFromSuggestion(query string) {
880 if o.autocompleteUC == nil {881 if o.autocompleteUC == nil {
881 return882 return
882 }883 }
883 884
884 o.mu.RLock()885 o.mu.RLock()
885- userInput := o.realInput
886 bangMode := o.bangMode886 bangMode := o.bangMode
887 o.mu.RUnlock()887 o.mu.RUnlock()
888 888
889+ // Use the query that triggered this search, not realInput which may have changed
890+ userInput := query
891+
889 // Don't show ghost text in bang mode (it has its own completion)892 // Don't show ghost text in bang mode (it has its own completion)
890 if bangMode || userInput == "" {893 if bangMode || userInput == "" {
891 o.clearGhostText()894 o.clearGhostText()
@@ -1060,8 +1063,8 @@ func (o *Omnibox) performSearch() {
1060 })1063 })
1061 }1064 }
1062 1065
1063- // Marshal back to GTK main thread1066+ // Marshal back to GTK main thread, passing query to ensure ghost text uses correct input
1064- o.idleAddUpdateSuggestions(suggestions)1067+ o.idleAddUpdateSuggestions(suggestions, query)
1065 }()1068 }()
1066 }1069 }
1067 1070
@@ -1078,7 +1081,7 @@ func (o *Omnibox) loadInitialHistory() {
1078 1081
1079 switch o.initialBehavior {1082 switch o.initialBehavior {
1080 case "none":1083 case "none":
1081- o.idleAddUpdateSuggestions(nil)1084+ o.idleAddUpdateSuggestions(nil, "")
1082 return1085 return
1083 1086
1084 case "most_visited", "recent", "":1087 case "most_visited", "recent", "":
@@ -1123,7 +1126,7 @@ func (o *Omnibox) loadInitialHistory() {
1123 }1126 }
1124 }1127 }
1125 1128
1126- o.idleAddUpdateSuggestions(suggestions)1129+ o.idleAddUpdateSuggestions(suggestions, "")
1127 }()1130 }()
1128 }1131 }
1129 1132
@@ -1161,7 +1164,7 @@ func (o *Omnibox) loadFavorites(query string) {
1161 })1164 })
1162 }1165 }
1163 1166
1164- o.idleAddUpdateFavorites(favorites)1167+ o.idleAddUpdateFavorites(favorites, query)
1165 }()1168 }()
1166 }1169 }
1167 1170
@@ -1263,7 +1266,8 @@ func (o *Omnibox) updateBangSuggestions(suggestions []BangSuggestion) {
1263 }1266 }
1264 1267
1265 // updateSuggestions updates the list with history suggestions.1268 // updateSuggestions updates the list with history suggestions.
1266-func (o *Omnibox) updateSuggestions(suggestions []Suggestion) {1269+// The query parameter is the search query that triggered this update.
1270+func (o *Omnibox) updateSuggestions(suggestions []Suggestion, query string) {
1267 o.mu.Lock()1271 o.mu.Lock()
1268 o.bangMode = false1272 o.bangMode = false
1269 o.bangSuggestions = nil1273 o.bangSuggestions = nil
@@ -1284,14 +1288,15 @@ func (o *Omnibox) updateSuggestions(suggestions []Suggestion) {
1284 if rowCount > 0 {1288 if rowCount > 0 {
1285 o.selectIndex(0)1289 o.selectIndex(0)
1286 // Update ghost text from first suggestion1290 // Update ghost text from first suggestion
1287- o.updateGhostFromSuggestion()1291+ o.updateGhostFromSuggestion(query)
1288 } else {1292 } else {
1289 o.clearGhostText()1293 o.clearGhostText()
1290 }1294 }
1291 }1295 }
1292 1296
1293 // updateFavorites updates the list with favorites.1297 // updateFavorites updates the list with favorites.
1294-func (o *Omnibox) updateFavorites(favorites []Favorite) {1298+// The query parameter is the search query that triggered this update.
1299+func (o *Omnibox) updateFavorites(favorites []Favorite, query string) {
1295 o.mu.Lock()1300 o.mu.Lock()
1296 o.bangMode = false1301 o.bangMode = false
1297 o.bangSuggestions = nil1302 o.bangSuggestions = nil
@@ -1312,7 +1317,7 @@ func (o *Omnibox) updateFavorites(favorites []Favorite) {
1312 if rowCount > 0 {1317 if rowCount > 0 {
1313 o.selectIndex(0)1318 o.selectIndex(0)
1314 // Update ghost text from first favorite1319 // Update ghost text from first favorite
1315- o.updateGhostFromSuggestion()1320+ o.updateGhostFromSuggestion(query)
1316 } else {1321 } else {
1317 o.clearGhostText()1322 o.clearGhostText()
1318 }1323 }
@@ -2034,9 +2039,10 @@ func (o *Omnibox) UpdateZoomIndicator(factor float64) {
2034 }2039 }
2035 2040
2036 // idleAddUpdateSuggestions schedules updateSuggestions on the GTK main thread.2041 // idleAddUpdateSuggestions schedules updateSuggestions on the GTK main thread.
2037-func (o *Omnibox) idleAddUpdateSuggestions(suggestions []Suggestion) {2042+// The query parameter is passed through to ensure ghost text uses the correct input.
2043+func (o *Omnibox) idleAddUpdateSuggestions(suggestions []Suggestion, query string) {
2038 var cb glib.SourceFunc = func(data uintptr) bool {2044 var cb glib.SourceFunc = func(data uintptr) bool {
2039- o.updateSuggestions(suggestions)2045+ o.updateSuggestions(suggestions, query)
2040 return false // One-shot callback2046 return false // One-shot callback
2041 }2047 }
2042 glib.IdleAdd(&cb, 0)2048 glib.IdleAdd(&cb, 0)
@@ -2058,9 +2064,10 @@ func (o *Omnibox) getFavoriteURLs(ctx context.Context) map[string]struct{} {
2058 }2064 }
2059 2065
2060 // idleAddUpdateFavorites schedules updateFavorites on the GTK main thread.2066 // idleAddUpdateFavorites schedules updateFavorites on the GTK main thread.
2061-func (o *Omnibox) idleAddUpdateFavorites(favorites []Favorite) {2067+// The query parameter is passed through to ensure ghost text uses the correct input.
2068+func (o *Omnibox) idleAddUpdateFavorites(favorites []Favorite, query string) {
2062 var cb glib.SourceFunc = func(data uintptr) bool {2069 var cb glib.SourceFunc = func(data uintptr) bool {
2063- o.updateFavorites(favorites)2070+ o.updateFavorites(favorites, query)
2064 return false // One-shot callback, return false to remove source2071 return false // One-shot callback, return false to remove source
2065 }2072 }
2066 glib.IdleAdd(&cb, 0)2073 glib.IdleAdd(&cb, 0)

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