Skip to content

Add accent-insensitive search option#13215

Open
yyyyyyyan wants to merge 2 commits intokeepassxreboot:developfrom
yyyyyyyan:feature/accent-insensitive-search
Open

Add accent-insensitive search option#13215
yyyyyyyan wants to merge 2 commits intokeepassxreboot:developfrom
yyyyyyyan:feature/accent-insensitive-search

Conversation

@yyyyyyyan
Copy link
Copy Markdown

@yyyyyyyan yyyyyyyan commented Apr 1, 2026

Add an "Accent sensitive" toggle to the search menu that allows
diacritics-insensitive matching. When disabled, both the search term and
entry field values are normalized by decomposing to Unicode NFD and
stripping combining marks, so that e.g. "pouzivatel" matches "používateľ"
and vice versa.

This follows the same signal flow as the existing case sensitivity toggle:
SearchWidget toggle -> signal -> DatabaseWidget::setSearchAccentSensitive()
-> EntrySearcher::setAccentSensitive() -> refreshSearch()

AI Disclosure: This PR was developed with assistance from Claude (Opus 4.6). I created an implementation plan in an interactive session with Claude, and Claude executed the initial implementation. After my review and tests I manually refined the code (added ASCII fast path, added some edge-case unit tests, fixed signal/multiplexer race condition in db open sequence).

Fixes #6181

Screenshots

Accent-sensitive search (default, no match for "testingabc" against accented entry):
image
Accent-insensitive search (unchecked, matches accented entry):
image

Testing strategy

  • Added testAccentInsensitiveSearch to TestEntrySearcher covering:
    • ASCII query matching accented entry and vice versa
    • Field-specific search with accent folding (title:, attachment:, tag:, group:)
    • Exact match (+) and exclude (!) modifiers
    • Case-sensitive combined with accent-insensitive
    • Default accent-sensitive behavior preserved
  • All existing tests pass unchanged
  • Manual testing with KeePassXC GUI

Type of change

  • ✅ New feature (change that adds functionality)

EntrySearcher was always initialized with accentSensitive=true
(constructor default), ignoring the user's saved config. Due to a
signal/multiplexer race in the database open sequence, the config
value from the UI toggle never reached the new DatabaseWidget. Now
read the config directly at construction time.
Copilot AI review requested due to automatic review settings April 1, 2026 00:12
@yyyyyyyan
Copy link
Copy Markdown
Author

yyyyyyyan commented Apr 1, 2026

A couple of things I'd like to get input on:

  1. 60f6259 fixes a bug where EntrySearcher was always constructed with hardcoded defaults, ignoring the user's saved config. I believe this went undetected because all existing toggle defaults were false, matching the constructor defaults, but I bumped into it with the new accentSensitive param defaulting to true. Not sure if this should stay here or be split into a separate issue/PR.

  2. I went with accent-sensitive as the default (preserving current behavior), but honestly as a Brazilian Portuguese speaker I'd prefer accent-insensitive as the default. I'd like to know if anyone has any inputs regarding this.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a user-facing search toggle to control accent/diacritics sensitivity in the KeePassXC GUI, wiring it through the existing SearchWidget → DatabaseWidget → EntrySearcher flow and implementing Unicode NFD + combining-mark stripping when accent-insensitive matching is enabled.

Changes:

  • Add an “Accent sensitive” toggle to the search menu and persist it via a new GUI config key.
  • Extend EntrySearcher to support accent-insensitive matching by stripping diacritics from both query terms and searched fields (with an ASCII fast path).
  • Add unit tests covering accent-insensitive matching across fields/modifiers and interactions with case sensitivity.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/TestEntrySearcher.h Adds a new test slot for accent-insensitive search.
tests/TestEntrySearcher.cpp Introduces comprehensive coverage for accent-insensitive search behavior and modifiers.
src/gui/SearchWidget.h Adds signal/slot and action member for the new toggle.
src/gui/SearchWidget.cpp Adds the menu action, persists the setting, and emits the new policy signal on DB changes.
src/gui/DatabaseWidget.h Adds a slot to apply the accent-sensitivity setting to searching.
src/gui/DatabaseWidget.cpp Initializes EntrySearcher from config and applies updates via setSearchAccentSensitive().
src/core/Tools.h Declares Tools::stripDiacritics() utility.
src/core/Tools.cpp Implements diacritics stripping via NFD decomposition + combining-mark removal (ASCII fast path).
src/core/EntrySearcher.h Extends constructor/state with accentSensitive and adds setters/getters.
src/core/EntrySearcher.cpp Applies diacritics stripping to both parsed terms (regex source) and matched fields/lists.
src/core/Config.h Adds new config key GUI_SearchAccentSensitive.
src/core/Config.cpp Registers the new config key with default true (accent-sensitive).

@droidmonkey
Copy link
Copy Markdown
Member

I am a fan of making the default more "liberal" and actually remove the option entirely. We already have an "exact match" prefix (ie, +t:acćent). If the user wants exact matching, accents are respected, otherwise the default is no accent matching.

@yyyyyyyan
Copy link
Copy Markdown
Author

Makes sense — implemented this approach. Accent-insensitive is now always the default, the toggle is removed, and + (exact match) forces accent-sensitive matching. So café matches "cafe"/"café", but +café only matches "café".

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.

Search for result including accent characters

3 participants