Skip to content

Add search function within patch viewer - #2

Open
roseoriorden wants to merge 3 commits into
igsilya:mainfrom
roseoriorden:main
Open

Add search function within patch viewer#2
roseoriorden wants to merge 3 commits into
igsilya:mainfrom
roseoriorden:main

Conversation

@roseoriorden

@roseoriorden roseoriorden commented Jul 15, 2026

Copy link
Copy Markdown

Add a vim-like expression search /
Supports regex like vim and has the same n/N controls

Assisted-by: Claude Sonnet 4.5, Claude Code

@roseoriorden
roseoriorden force-pushed the main branch 2 times, most recently from dd17c92 to e3679b1 Compare July 20, 2026 18:57
Allows search within patch files just like vim's search function (i.e.
supports regex, same n/N controls, etc).

Colors of the matches differ between dark and light mode.

Signed-off-by: Rosemarie O'Riorden <rosemarie@redhat.com>
/<up_arrow>/<down_arrow> search history just like in vim's search. Does
not persist across sessions.

Signed-off-by: Rosemarie O'Riorden <rosemarie@redhat.com>
Before this commit, esc and q had the same function inside the patch
viewer during a search, which is that they would both exit from the
search, and then with a second press they'd exit from the patch viewer
and go back to the list of patches.

With this commit, esc exits from the patch viewer even mid-search, and q
exits from the serach upon the first press, and exits from the patch
viewer upon the second press. However, if nothing was found in a search,
q will exit from the patch viewer immediately as if an active search was
not ongoing.

Signed-off-by: Rosemarie O'Riorden <rosemarie@redhat.com>

@dceara dceara left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @roseoriorden for this feature, I tried it locally and it seems to work quite nicely!

Here's some comments on the actual code from my side. Please take them with a grain of salt as it's the first time I really look closely at the leadlight code so some of them might be completely off.

Thanks,
Dumitru

Comment thread tui/model.go
viewportLoading bool
viewportOffset int
viewExpanded bool
searching bool

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Should all this search related state be part of a separate group of fields, lower. I mean, it applies to the compare mode too and that has its own separate section of fields.

Comment thread tui/search.go
func (m *Model) handleSearchInputMode(msg tea.KeyMsg, updateMatches func()) (tea.Model, tea.Cmd) {
key := msg.String()
switch key {
case "enter":

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I like the mixing of search logic with key handling. keys.go seems like the appropriate place to handle all input keys for all modes. Would it make sense to keep it like that?

Comment thread tui/model.go
viewportLoading bool
viewportOffset int
viewExpanded bool
searching bool

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

searching is a bit misleading IMO. It actually is used to reflect the "the user is typing the search query" state. Only once the search is "committed" (enter is hit) we actually are in searching state.

Comment thread tui/render.go
right = comparePadLine(right, rightWidth,
compareDiffKind(m.compare[1].kinds, idx))

left = m.highlightLineIfMatched(left, idx)

@dceara dceara Aug 7, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, if the match is only on one side (left or right) but not on the other we'll try to highlight on both sides and fail for the side that doesn't have the match.

But does that mean that we'd also potentially scroll incorrectly? Shouldn't the searchMatches be part of the compareSide struct instead?

Which means, shouldn't we have a let's call it SearchMatcher struct, that stores all matches. And we could potentially use it in three places:

  • for the viewportLines (in patch view mode)
  • for the left lines (in compare view mode)
  • for the right lines (in compare view mode)

Maybe that will also remove the need for the slightly unnatural if m.viewMode == viewCompare { checks in search.go, what do you think?

@igsilya

igsilya commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Some LLM review comments:

Commit 1: 9314b07 — "tui: search: Add vim-like expression search /<expr>." (3 issues, medium)

  1. \C always overrides \c — In parseVimSearchPattern, the \C check runs after \c, so \C always wins when both flags appear. Vim uses last-flag-wins semantics, so foo\C\c should be case-insensitive but isn't.
  2. Compare view duplicate matches — updateCompareSearchMatches indexes both sides of a compare view with the same lineIdx, producing duplicate matches for identical lines. Pressing n visits the same visual line twice. Also, highlightLineIfMatched doesn't distinguish panels, so both sides show current-match highlighting simultaneously.
  3. Stale searchMatches after content rebuild — switchToComment and the WindowSizeMsg handler rebuild viewport content without clearing or re-running search. Pressing n after navigating to a different comment or resizing jumps to stale line indices.

Commit 2: 0f38713 — "tui: search: Add search history." (2 issues, low)

  1. N to recall last search navigates to first match — recallLastSearch() always sets searchIdx = 0, so pressing N (which should go backward) starts at the first match instead of the last.
  2. Up arrow discards in-progress search text — Pressing up to browse history overwrites partially-typed input without saving it. Pressing down back to index -1 restores an empty string instead of the original partial text.

Commit 3: 4af6be2 — "tui: search: Make esc exit patch, q clear search then exit." (2 issues, low)

  1. Duplicated viewport exit cleanup — The "q" and "esc" cases in both handleViewportKey and handleCompareKey contain identical 7-field cleanup code. Should be extracted to a helper to avoid divergence.
  2. Typo in commit message — "serach" should be "search".

I didn't look too deep into the code myself, but one of the big things I tried to avoid in different places in leadlight is parsing and/or updating what was already rendered. This is fragile and hard to maintain. The search logic should be applied to the original text and then the rendering pass should already highlight the findings. This may require extra translation between original and rendered line indexes due to possible wrapping, but would solve a few problems like inability to find wrapped text or the need to re-apply the search and re-highlighting after resize or comment switching that the LLM review flagged above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants