Skip to content

Conversation

@sei40kr
Copy link
Contributor

@sei40kr sei40kr commented Dec 10, 2025

Summary

This PR adds an interactive fuzzy finder picker component for branch and commit selection:

  • Fuzzy matching with real-time filtering
  • Keyboard navigation (↓/↑, Ctrl-n/p)
  • Select/Cancel with Enter/Esc/Ctrl-c
  • Visual highlighting of matched characters

Implementation Details

  • New picker module with fuzzy matching logic
  • ui/picker for rendering the picker interface
  • Configurable keybinds and styling via config.toml
  • Test coverage for core picker functionality

Test Plan

  • Unit tests (only for state)

🤖 Generated with Claude Code

@sei40kr
Copy link
Contributor Author

sei40kr commented Dec 10, 2025

Since app.rs has become quite complex, it might be a good idea to adopt ratatui's component pattern.

layout_span(layout, (state.prompt_text.as_ref().into(), prompt_style));
layout_span(layout, (" › ".into(), Style::new().cyan().dim()));
layout_span(layout, (state.input_state.value().into(), Style::new()));
layout_span(layout, (CARET.into(), Style::new()));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I want to change the caret shape to indicate that it is in input mode.

@sei40kr sei40kr force-pushed the feat/interactive-picker branch 2 times, most recently from 305af73 to f0d61e0 Compare December 11, 2025 14:17
@sei40kr sei40kr changed the title feat: add interactive picker for branch and commit selection feat: add interactive picker Dec 11, 2025
@sei40kr sei40kr marked this pull request as ready for review December 11, 2025 14:29
@sei40kr sei40kr force-pushed the feat/interactive-picker branch 2 times, most recently from 1cb919e to cc0754c Compare December 11, 2025 14:31
@sei40kr
Copy link
Contributor Author

sei40kr commented Dec 11, 2025

Testing Note

This PR adds the picker component itself, but it's not integrated into any operations yet. To test the picker functionality in action, please check out #470 which converts branch operations to use this picker:

gh pr checkout 470
cargo run

Then try branch operations like:

  • b b - Checkout branch (uses picker)
  • b x - Delete branch (uses picker)

You'll be able to see the fuzzy filtering, navigation, and selection in action.

Implement an interactive fuzzy finder picker component:
- Fuzzy matching with real-time filtering
- Keyboard navigation (↓/↑, Ctrl-n/p)
- Select with Enter, cancel with Esc/Ctrl-c
- Visual highlighting of matched characters

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@sei40kr sei40kr force-pushed the feat/interactive-picker branch from cc0754c to 948ad2d Compare December 12, 2025 02:59
@altsem
Copy link
Owner

altsem commented Dec 12, 2025

I just wanted to leave some answer at least. I'm a bit busy with other things, but I think this (and the search functionality too) are nice additions to Gitu. I'll try find some time to review and test this properly soon

@sei40kr
Copy link
Contributor Author

sei40kr commented Dec 24, 2025

Like LayoutTree does, Picker might also need to expand its standalone snapshot tests.
Issue #470 writes snapshot tests for the branch picker, but the patterns are extremely numerous. Writing these tests for every picker would be wasteful and difficult to maintain.

- Add picker UI layout tests with snapshots
- Remove duplicate picker tests from src/tests/picker.rs
- Consolidate case-insensitive test into src/picker.rs
- Clean up 39 unreferenced snapshot files
Add tests covering generic picker functionality needed for branch
selection and similar use cases:

Logic tests (src/picker.rs):
- Scrolling through many items (20+)
- Navigation after filtering
- Custom input selection with state transitions
- Navigation with custom input at end of list

UI tests (src/ui/picker.rs):
- Scroll display at middle position
- Scroll display near end
- No matches display
- Filtered results with navigation

All tests are organized by category (basic → edge cases) and
placed next to related tests for better maintainability.
@sei40kr
Copy link
Contributor Author

sei40kr commented Dec 25, 2025

Like LayoutTree does, Picker might also need to expand its standalone snapshot tests. Issue #470 writes snapshot tests for the branch picker, but the patterns are extremely numerous. Writing these tests for every picker would be wasteful and difficult to maintain.

Done.

README.md Outdated
Comment on lines 43 to 44
> [!NOTE]
> Picker keybinds will be configurable in a future release.
Copy link
Owner

Choose a reason for hiding this comment

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

Might as well do this right away. shouldn't be too much extra

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@altsem
Copy link
Owner

altsem commented Dec 28, 2025

Do you think a tab/shift-tab keybinding would be a nice default in addition to ctrl-n. ctrl-p?

Add support for customizing picker keybindings through the config file.
Users can now configure next, previous, done, and cancel actions under
[bindings.picker] section in their config.toml.
@sei40kr
Copy link
Contributor Author

sei40kr commented Jan 13, 2026

@altsem

Do you think a tab/shift-tab keybinding would be a nice default in addition to ctrl-n. ctrl-p?

I think that's a good idea. Especially for users familiar with Magit, many are likely accustomed to using Tab/Shift-Tab for navigation.

base_style: Style,
config: &Config,
) {
let graphemes: Vec<&str> = text.graphemes(true).collect();
Copy link
Owner

Choose a reason for hiding this comment

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

Don't need to collect this before iterating, saves an allocation!

selection_line = { mods = "BOLD" } # Selected item style
matched = { fg = "yellow", mods = "BOLD" } # Fuzzy-matched characters highlight
```

Copy link
Owner

Choose a reason for hiding this comment

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

I think this doesn't belong in the README, it's a bit too specific. There's documentation about configuration inside of default_config.toml.
Having the code comments inside of default_config.toml seems like the better approach.

Copy link
Owner

Choose a reason for hiding this comment

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

Leftover?

}
}

pub struct PickerBindings {
Copy link
Owner

Choose a reason for hiding this comment

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

I realize this was indeed not as smooth to add, due to how the existing bindings always relate to a menu.
Could clean it up in the future, think this is good enough for now!

Comment on lines +236 to +237
picker.next = ["down", "ctrl+n"]
picker.previous = ["up", "ctrl+p"]
Copy link
Owner

Choose a reason for hiding this comment

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

I believe the tab bindings would be like this.

Suggested change
picker.next = ["down", "ctrl+n"]
picker.previous = ["up", "ctrl+p"]
picker.next = ["down", "ctrl+n", "tab"]
picker.previous = ["up", "ctrl+p", "shift+tab"]

Copy link
Owner

@altsem altsem left a comment

Choose a reason for hiding this comment

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

Actually, I'll just merge this to master. And make the small adjustments i commented on. I think it looks pretty good!
I suppose in the other PR, things will get more interesting when it is actually put to use!

@altsem altsem merged commit 954f455 into altsem:master Jan 14, 2026
2 of 3 checks passed
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.

2 participants