Skip to content

feat(search): add unicode character removal for fuzzy matching#4360

Open
4yinn wants to merge 30 commits intoFlow-Launcher:devfrom
4yinn:feature/4149-diacritics-insensitive-search
Open

feat(search): add unicode character removal for fuzzy matching#4360
4yinn wants to merge 30 commits intoFlow-Launcher:devfrom
4yinn:feature/4149-diacritics-insensitive-search

Conversation

@4yinn
Copy link
Copy Markdown

@4yinn 4yinn commented Mar 15, 2026

Description

Added a preprocessing step to FuzzySearch to normalize Unicode characters and remove diacritics before matching.

This allows searches to be accent-insensitive, improving usability. For example, searching for camera will also match câmera.

Related Issue

Closes #4149


Summary by cubic

Adds accent-insensitive fuzzy matching behind a settings toggle to improve results for queries with diacritics. Also wires matcher behavior to settings and exposes a public event so plugins can reset caches when matching rules change (relates to #4149).

  • Summary of changes
    • Changed: StringMatcher only normalizes when Settings.IgnoreAccents is true; still applies case folding when opt.IgnoreCase is true; listens to Settings.PropertyChanged and updates UserSettingSearchPrecision when QuerySearchPrecision changes; updated General Settings copy to say “all results.”
    • Added: Settings.IgnoreAccents with a General Settings toggle and English strings; header icon (“áé”) for the Ignore Accents card; Settings.StringMatcherBehaviorChanged event exposed via IPublicAPI/PublicAPIInstance; Plugins/Flow.Launcher.Plugin.Program subscribes to reset its cache when matcher behavior changes; fast Normalize(string) using a static accent map with null/empty guards and stackalloc/ArrayPool<char>; tests updated to pass Settings into StringMatcher.
    • Removed: Restart InfoBar/button and related command; direct precision updates from Settings.QuerySearchPrecision setter (now handled by StringMatcher); previously added non-English translations for the new setting.
    • Memory: One static map; normalization skipped unless enabled; per-call buffers use stack for small inputs and ArrayPool<char> for large inputs; plugin handlers unsubscribed in Dispose to avoid leaks.
    • Security: Pure, deterministic string preprocessing; no new I/O or external dependencies; low risk.
    • Tests: No new tests; existing tests updated to construct StringMatcher with Settings.

Written for commit 7607844. Summary will update on new commits.

Introduced a string preprocessing step in FuzzySearch that removes unicode characters. This improves the search
     experience by allowing users to find results regardless of accents or special formatting.
@github-actions github-actions bot added this to the 2.2.0 milestone Mar 15, 2026
@gitstream-cm
Copy link
Copy Markdown

gitstream-cm bot commented Mar 15, 2026

🥷 Code experts: Jack251970

Jack251970 has most 👩‍💻 activity in the files.
Jack251970 has most 🧠 knowledge in the files.

See details

Flow.Launcher.Infrastructure/StringMatcher.cs

Activity based on git-commit:

Jack251970
MAR
FEB
JAN
DEC
NOV
OCT

Knowledge based on git-blame:
Jack251970: 100%

✨ Comment /gs review for LinearB AI review. Learn how to automate it here.

@gitstream-cm
Copy link
Copy Markdown

gitstream-cm bot commented Mar 15, 2026

Be a legend 🏆 by adding a before and after screenshot of the changes you made, especially if they are around UI/UX.

@coderabbitai coderabbitai bot added the enhancement New feature or request label Mar 15, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 15, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new SensitiveAccents setting and UI, implements an accent-normalization map and Normalize(string), and integrates accent-aware normalization into StringMatcher.FuzzyMatch to make fuzzy search optionally diacritic-sensitive.

Changes

Cohort / File(s) Summary
Core matching
Flow.Launcher.Infrastructure/StringMatcher.cs
Adds Settings _settings field, uses _settings.SensitiveAccents in FuzzyMatch, moves matching to an accent-/case-normalized fullStringToCompare, updates AllPreviousCharsMatched callsite, and adds AccentMap + public static string Normalize(string). Minor formatting tweaks.
User settings model
Flow.Launcher.Infrastructure/UserSettings/Settings.cs
Adds public bool SensitiveAccents property with backing field and change-notification in the setter.
Settings UI / VM
Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs, Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
ViewModel now accepts IPublicAPI, exposes SensitiveAccents and SensitiveAccentsRestartRequired, adds RestartApp command; XAML adds a settings card with a toggle bound to the new property and an info bar prompting restart.
Localization
Flow.Launcher/Languages/*.xaml (many locales, e.g., en.xaml, pt-br.xaml, ar.xaml, cs.xaml, ...)
Adds sensitiveAccent, sensitiveAccentToolTip, sensitiveAccentRestartTitle, sensitiveAccentRestartMessage, and sensitiveAccentRestartButton resource strings across locale files to support the new setting UI and restart messaging.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User
participant SettingsUI as "Settings UI"
participant Settings as "Settings (model)"
participant Search as "Search Engine"
participant Matcher as "StringMatcher"

User->>SettingsUI: Toggle SensitiveAccents
SettingsUI->>Settings: Update SensitiveAccents
Settings-->>SettingsUI: Persist / notify
User->>Search: Enter query
Search->>Settings: Read SensitiveAccents
Search->>Matcher: FuzzyMatch(query, item, options)
Matcher->>Matcher: Normalize(query/item) based on SensitiveAccents
Matcher-->>Search: Match score/result
Search-->>User: Display results

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • jjw24
  • Jack251970
  • taooceros
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat(search): add unicode character removal for fuzzy matching' clearly summarizes the main change—adding accent-insensitive search functionality through Unicode normalization.
Linked Issues check ✅ Passed The PR fulfills the core requirements of issue #4149: enabling diacritics-agnostic searches (example: 'camera' matches 'câmera'), implementing a toggleable option in Settings with UI, and providing localized strings across multiple languages.
Out of Scope Changes check ✅ Passed All changes are directly related to the accent-insensitive search feature: StringMatcher normalization logic, Settings property, Settings UI with toggle/restart prompt, and comprehensive localization across 23 language files.
Description check ✅ Passed The PR description clearly explains the feature being added: preprocessing to normalize Unicode and remove diacritics for accent-insensitive fuzzy matching, with concrete example (camera matching câmera) and reference to the related issue #4149.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
Flow.Launcher.Infrastructure/StringMatcher.cs (1)

66-73: Consider adding an option to toggle diacritics-insensitive matching.

The linked issue #4149 specifically requests an option to enable/disable diacritics-insensitive matching, similar to the Everything app. The current implementation always removes accents with no way to opt out.

If this is an intentional scope reduction, consider documenting it. Otherwise, you could add a setting flag:

if (_settings.IgnoreDiacritics)
{
    query = RemoveAccents(query);
    stringToCompare = RemoveAccents(stringToCompare);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Flow.Launcher.Infrastructure/StringMatcher.cs` around lines 66 - 73,
FuzzyMatch currently always calls RemoveAccents on query and stringToCompare
which forces diacritics-insensitive matching; add a configurable toggle (e.g. a
boolean setting like _settings.IgnoreDiacritics) and only call RemoveAccents
when that flag is true, updating FuzzyMatch's logic and any settings class to
expose the option so consumers can enable/disable diacritics-insensitive
matching; reference the RemoveAccents and FuzzyMatch methods and the MatchOption
usage when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Flow.Launcher.Infrastructure/StringMatcher.cs`:
- Around line 249-250: In StringMatcher.cs (inside the method using
Char.GetUnicodeCategory), rename the local variable unicodedCategory to
unicodeCategory to fix the typo and consistency, and also replace the type alias
use from Char.GetUnicodeCategory(c) to char.GetUnicodeCategory(c) for C# keyword
consistency; update any subsequent references to the variable name
(unicodeCategory) accordingly.
- Around line 72-73: The matching indices are computed on the post-RemoveAccents
normalized string but applied to the original input, causing mismatches when
combining marks change string length; update the flow in the method that calls
RemoveAccents to 1) keep the original stringToCompare (save
originalStringToCompare before normalization), 2) produce an index mapping from
normalized-to-original positions when RemoveAccents transforms the string
(similar to the existing TranslationMapping for alphabet translation), and 3)
before returning, remap all indices in indexList and any positions inside
acronymMatchData using that accent-removal mapping (in addition to the existing
TranslationMapping) so returned indices align with the original string. Ensure
the mapping logic is used wherever indices are translated back (same spot where
TranslationMapping is applied).

---

Nitpick comments:
In `@Flow.Launcher.Infrastructure/StringMatcher.cs`:
- Around line 66-73: FuzzyMatch currently always calls RemoveAccents on query
and stringToCompare which forces diacritics-insensitive matching; add a
configurable toggle (e.g. a boolean setting like _settings.IgnoreDiacritics) and
only call RemoveAccents when that flag is true, updating FuzzyMatch's logic and
any settings class to expose the option so consumers can enable/disable
diacritics-insensitive matching; reference the RemoveAccents and FuzzyMatch
methods and the MatchOption usage when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0172967a-b14a-495a-9c63-e4cd5ee587ab

📥 Commits

Reviewing files that changed from the base of the PR and between c9dbc33 and d2f8663.

📒 Files selected for processing (1)
  • Flow.Launcher.Infrastructure/StringMatcher.cs

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Flow.Launcher.Infrastructure/StringMatcher.cs">

<violation number="1" location="Flow.Launcher.Infrastructure/StringMatcher.cs:72">
P1: `query` is not revalidated after accent stripping, so mark-only Unicode input can become empty and crash at `querySubstrings[0]`.</violation>

<violation number="2" location="Flow.Launcher.Infrastructure/StringMatcher.cs:73">
P2: Match indices are computed after accent-stripping but never mapped back to original string positions, causing incorrect highlight offsets for decomposed Unicode text.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Jack251970 and others added 2 commits March 16, 2026 01:17
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
@Jack251970 Jack251970 added the review in progress Indicates that a review is in progress for this PR label Mar 15, 2026
@prlabeler prlabeler bot added the bug Something isn't working label Mar 15, 2026
Copy link
Copy Markdown
Member

@Jack251970 Jack251970 left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution! But from AI's review, this removal may change the length of the string which can cause some issues like incorrect highlights.

@Jack251970 Jack251970 removed the enhancement New feature or request label Mar 15, 2026
@coderabbitai coderabbitai bot removed the bug Something isn't working label Mar 15, 2026
@VictoriousRaptor
Copy link
Copy Markdown
Contributor

Please don't involve too much code format changes so we can trace changes in the future.

Copy link
Copy Markdown
Member

@jjw24 jjw24 left a comment

Choose a reason for hiding this comment

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

Thank you for your PR.

StringMatcher is performance-critical, so we must minimize additional overhead. Please address the following:

  1. Consolidate Loops: The current implementation performs an initial loop to identify and remove Unicode characters, followed by a second loop for the main matching logic. Please merge it into the main loop to avoid redundant iteration.

  2. Conditional Unicode Processing: We should make Unicode removal optional—for instance, by adding a toggle for non-English languages. This prevents unnecessary processing when the feature isn't required.

@4yinn
Copy link
Copy Markdown
Author

4yinn commented Mar 16, 2026

Thank you for your PR.

StringMatcher is performance-critical, so we must minimize additional overhead. Please address the following:

  1. Consolidate Loops: The current implementation performs an initial loop to identify and remove Unicode characters, followed by a second loop for the main matching logic. Please merge it into the main loop to avoid redundant iteration.
  2. Conditional Unicode Processing: We should make Unicode removal optional—for instance, by adding a toggle for non-English languages. This prevents unnecessary processing when the feature isn't required.

Yes, I can see the problem. I'll think about a way to implement it with better optimizations.

@Jack251970 Jack251970 added the enhancement New feature or request label Mar 17, 2026
4yinn added 2 commits March 26, 2026 23:59
… option

Implemented a string normalization method to handle accented characters, improving search consistency and preventing query mismatches.

Added an accent mapping dictionary for common diacritics
Implemented normalization using Span<char> and stackalloc to reduce heap allocations and improve performance
Introduced a user-controlled toggle (SensitiveAccents) to enable or disable normalization dynamically
Prepared the system for cache-aware queries based on normalization settings
@coderabbitai coderabbitai bot removed the enhancement New feature or request label Mar 27, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Flow.Launcher.Infrastructure/StringMatcher.cs (1)

27-31: ⚠️ Potential issue | 🔴 Critical

NullReferenceException: _settings is null when using this constructor.

The workaround constructor does not initialize _settings, but FuzzyMatch unconditionally accesses _settings.SensitiveAccents on lines 92-93. This will throw NullReferenceException for all unit tests using this constructor.

🐛 Proposed fix with null-conditional fallback
 // This is a workaround to allow unit tests to set the instance
 public StringMatcher(IAlphabet alphabet)
 {
     _alphabet = alphabet;
+    _settings = null; // Explicitly null for tests - FuzzyMatch will use default behavior
 }

And update the usage on lines 92-93:

-var fullStringToCompare = _settings.SensitiveAccents ? fullStringToCompareAndNormalize : fullStringToCompareWithoutCase;
-var queryToCompare = _settings.SensitiveAccents ? queryWithoutCaseAndNormalize : queryWithoutCase;
+var sensitiveAccents = _settings?.SensitiveAccents ?? false;
+var fullStringToCompare = sensitiveAccents ? fullStringToCompareAndNormalize : fullStringToCompareWithoutCase;
+var queryToCompare = sensitiveAccents ? queryWithoutCaseAndNormalize : queryWithoutCase;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Flow.Launcher.Infrastructure/StringMatcher.cs` around lines 27 - 31, The
workaround constructor StringMatcher(IAlphabet alphabet) leaves the field
_settings null which causes FuzzyMatch to throw a NullReferenceException when
accessing _settings.SensitiveAccents; fix by ensuring _settings is initialized
or guarded: either initialize _settings to a sensible default inside the
constructor (e.g., _settings = new MatchingSettings() or the default settings
object used by the other ctor) or change FuzzyMatch to use a null-safe check
like (_settings?.SensitiveAccents ?? <defaultBool>) and
(_settings?.PreferDiacritics ?? <defaultBool>) where those flags are read;
update the constructor or the FuzzyMatch usage accordingly (referencing the
StringMatcher constructor, the _settings field, and the FuzzyMatch method).
🧹 Nitpick comments (5)
Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml (1)

94-101: Missing Margin causes inconsistent spacing.

The new SettingsCard lacks a Margin attribute. Other cards in this file use Margin="0 4 0 0" for tight grouping or Margin="0 14 0 0" for section breaks. Additionally, this card is missing a HeaderIcon which most other cards have.

Consider whether this setting should be grouped with other search-related settings (e.g., near querySearchPrecision or ShouldUsePinyin) rather than between notification and window positioning settings.

🎨 Proposed fix to add margin and icon
             <ui:SettingsCard
+                Margin="0 14 0 0"
                 Description="{DynamicResource sensitiveAccentToolTip}"
                 Header="{DynamicResource sensitiveAccent}">
+                <ui:SettingsCard.HeaderIcon>
+                    <ui:FontIcon Glyph="&#xe8d3;" />
+                </ui:SettingsCard.HeaderIcon>
                 <ui:ToggleSwitch
                     IsOn="{Binding Settings.SensitiveAccents}"
                     OffContent="{DynamicResource disable}"
                     OnContent="{DynamicResource enable}" />
             </ui:SettingsCard>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml` around lines 94 -
101, The new SettingsCard for the SensitiveAccents toggle lacks the standard
Margin and HeaderIcon used by other cards, causing inconsistent spacing and
missing iconography; update the SettingsCard element (the one containing the
ToggleSwitch bound to Settings.SensitiveAccents) to include a Margin attribute
(use either "0 4 0 0" for tight grouping or "0 14 0 0" for a section break to
match surrounding cards) and add the appropriate HeaderIcon attribute to match
other cards, and consider relocating this SettingsCard near related search
settings such as querySearchPrecision or ShouldUsePinyin if it logically groups
with search-related options instead of being placed between notifications and
window positioning.
Flow.Launcher.Infrastructure/StringMatcher.cs (3)

257-267: Implementation is efficient but has a stack size assumption.

Using stackalloc char[value.Length] is performant for typical search queries. For extremely long strings, this could cause stack overflow, though this is unlikely in practice for search operations.

If robustness is needed for arbitrary input lengths, consider a threshold:

Span<char> buffer = value.Length <= 256 
    ? stackalloc char[value.Length] 
    : new char[value.Length];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Flow.Launcher.Infrastructure/StringMatcher.cs` around lines 257 - 267, The
Normalize method uses stackalloc with value.Length which can overflow the stack
for very long inputs; modify Normalize to allocate the temporary buffer on the
stack only for small inputs (e.g., <= 256 chars) and fall back to a
heap-allocated char[] for larger inputs, then copy/use that buffer when mapping
characters from AccentMap and returning the new string; update references inside
Normalize to treat the heap array as a Span<char> so the rest of the logic
(char.ToLowerInvariant, AccentMap.TryGetValue, new string(...)) is unchanged.

5-7: Unused imports can be removed.

System.Globalization and System.Text don't appear to be used in the current implementation. These may be leftover from a previous approach using NormalizationForm.FormD.

🧹 Proposed cleanup
 using CommunityToolkit.Mvvm.DependencyInjection;
 using Flow.Launcher.Plugin.SharedModels;
 using System;
 using System.Collections.Generic;
-using System.Globalization;
 using System.Linq;
-using System.Text;
 using Flow.Launcher.Infrastructure.UserSettings;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Flow.Launcher.Infrastructure/StringMatcher.cs` around lines 5 - 7, Remove the
unused using directives System.Globalization and System.Text from the top of
StringMatcher.cs; locate the using block in the StringMatcher file (where
System.Linq is present) and delete the two unused imports so only necessary
namespaces remain, leaving System.Linq (and any other actually referenced
namespaces) intact to avoid warnings and keep the file tidy.

246-256: AccentMap has limited language coverage.

The map covers common Western European accents but misses characters from:

  • Polish: ą→a, ę→e, ł→l, ń→n, ś→s, ź→z, ż→z
  • Turkish: ğ→g, ş→s, ı→i
  • Czech/Slovak: ř→r, š→s, ž→z, č→c, ě→e, ů→u
  • Nordic: ø→o, æ→ae
  • And others

This manual approach trades comprehensiveness for performance and avoids the index-mapping complexity of Unicode normalization (FormD). Consider documenting this limitation or expanding the map based on user feedback.

➕ Expanded AccentMap example
 private static readonly Dictionary<char, char> AccentMap = new()
 {
     ['á'] = 'a', ['à'] = 'a', ['ã'] = 'a', ['â'] = 'a', ['ä'] = 'a', ['å'] = 'a',
+    ['ą'] = 'a', ['æ'] = 'a',
     ['é'] = 'e', ['è'] = 'e', ['ê'] = 'e', ['ë'] = 'e',
+    ['ę'] = 'e', ['ě'] = 'e',
     ['í'] = 'i', ['ì'] = 'i', ['î'] = 'i', ['ï'] = 'i',
+    ['ı'] = 'i',
     ['ó'] = 'o', ['ò'] = 'o', ['õ'] = 'o', ['ô'] = 'o', ['ö'] = 'o',
+    ['ø'] = 'o',
     ['ú'] = 'u', ['ù'] = 'u', ['û'] = 'u', ['ü'] = 'u',
+    ['ů'] = 'u',
     ['ç'] = 'c',
+    ['č'] = 'c',
     ['ñ'] = 'n',
+    ['ń'] = 'n',
     ['ý'] = 'y', ['ÿ'] = 'y',
+    ['ł'] = 'l',
+    ['ś'] = 's', ['š'] = 's', ['ş'] = 's',
+    ['ź'] = 'z', ['ż'] = 'z', ['ž'] = 'z',
+    ['ř'] = 'r',
+    ['ğ'] = 'g'
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Flow.Launcher.Infrastructure/StringMatcher.cs` around lines 246 - 256, The
AccentMap dictionary in StringMatcher (AccentMap) only covers a subset of
Western European diacritics; update the AccentMap to include the missing
language characters (Polish: ą, ę, ł, ń, ś, ź, ż; Turkish: ğ, ş, ı;
Czech/Slovak: ř, š, ž, č, ě, ů; Nordic: ø, æ→map 'æ'→"ae" decision) or
alternatively add a clear comment above the AccentMap field explaining its
limited coverage and why Unicode FormD normalization was avoided; if expanding,
ensure you add both lowercase and uppercase mappings where needed and keep the
dictionary initialization in AccentMap consistent with existing style.
Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs (1)

199-211: ViewModel property is unused — XAML binds directly to Settings.

The XAML at line 98 binds to {Binding Settings.SensitiveAccents}, bypassing this ViewModel property entirely. Either:

  1. Remove this unused property, or
  2. Update the XAML to bind to {Binding SensitiveAccents} to use the ViewModel wrapper

If there's intent to add custom logic in the setter later (like EnableDialogJump does), keep the property and update the XAML binding.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs` around
lines 199 - 211, The SensitiveAccents property on SettingsPaneGeneralViewModel
is unused because the XAML binds directly to Settings.SensitiveAccents; either
remove the SensitiveAccents property from SettingsPaneGeneralViewModel (and any
references) or update the XAML binding at the control (currently {Binding
Settings.SensitiveAccents}) to {Binding SensitiveAccents} so the ViewModel
wrapper is used; if you intend to keep room for future setter logic (like
EnableDialogJump) keep the property and change the XAML, otherwise delete the
property to avoid dead code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Flow.Launcher.Infrastructure/StringMatcher.cs`:
- Around line 92-93: The boolean name _settings.SensitiveAccents is inverted vs
behavior: when true the code strips accents (accent-insensitive). Rename the
setting to a clear name like IgnoreAccents or AccentInsensitiveSearch (and
update its usages) or invert the logic where fullStringToCompare and
queryToCompare are selected; specifically update the Settings property, any
corresponding ViewModel property, related XAML binding keys, and localization
labels so the new name matches behavior, and replace references to
_settings.SensitiveAccents in StringMatcher (affecting fullStringToCompare and
queryToCompare) with the new property or inverted condition.

---

Outside diff comments:
In `@Flow.Launcher.Infrastructure/StringMatcher.cs`:
- Around line 27-31: The workaround constructor StringMatcher(IAlphabet
alphabet) leaves the field _settings null which causes FuzzyMatch to throw a
NullReferenceException when accessing _settings.SensitiveAccents; fix by
ensuring _settings is initialized or guarded: either initialize _settings to a
sensible default inside the constructor (e.g., _settings = new
MatchingSettings() or the default settings object used by the other ctor) or
change FuzzyMatch to use a null-safe check like (_settings?.SensitiveAccents ??
<defaultBool>) and (_settings?.PreferDiacritics ?? <defaultBool>) where those
flags are read; update the constructor or the FuzzyMatch usage accordingly
(referencing the StringMatcher constructor, the _settings field, and the
FuzzyMatch method).

---

Nitpick comments:
In `@Flow.Launcher.Infrastructure/StringMatcher.cs`:
- Around line 257-267: The Normalize method uses stackalloc with value.Length
which can overflow the stack for very long inputs; modify Normalize to allocate
the temporary buffer on the stack only for small inputs (e.g., <= 256 chars) and
fall back to a heap-allocated char[] for larger inputs, then copy/use that
buffer when mapping characters from AccentMap and returning the new string;
update references inside Normalize to treat the heap array as a Span<char> so
the rest of the logic (char.ToLowerInvariant, AccentMap.TryGetValue, new
string(...)) is unchanged.
- Around line 5-7: Remove the unused using directives System.Globalization and
System.Text from the top of StringMatcher.cs; locate the using block in the
StringMatcher file (where System.Linq is present) and delete the two unused
imports so only necessary namespaces remain, leaving System.Linq (and any other
actually referenced namespaces) intact to avoid warnings and keep the file tidy.
- Around line 246-256: The AccentMap dictionary in StringMatcher (AccentMap)
only covers a subset of Western European diacritics; update the AccentMap to
include the missing language characters (Polish: ą, ę, ł, ń, ś, ź, ż; Turkish:
ğ, ş, ı; Czech/Slovak: ř, š, ž, č, ě, ů; Nordic: ø, æ→map 'æ'→"ae" decision) or
alternatively add a clear comment above the AccentMap field explaining its
limited coverage and why Unicode FormD normalization was avoided; if expanding,
ensure you add both lowercase and uppercase mappings where needed and keep the
dictionary initialization in AccentMap consistent with existing style.

In `@Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs`:
- Around line 199-211: The SensitiveAccents property on
SettingsPaneGeneralViewModel is unused because the XAML binds directly to
Settings.SensitiveAccents; either remove the SensitiveAccents property from
SettingsPaneGeneralViewModel (and any references) or update the XAML binding at
the control (currently {Binding Settings.SensitiveAccents}) to {Binding
SensitiveAccents} so the ViewModel wrapper is used; if you intend to keep room
for future setter logic (like EnableDialogJump) keep the property and change the
XAML, otherwise delete the property to avoid dead code.

In `@Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml`:
- Around line 94-101: The new SettingsCard for the SensitiveAccents toggle lacks
the standard Margin and HeaderIcon used by other cards, causing inconsistent
spacing and missing iconography; update the SettingsCard element (the one
containing the ToggleSwitch bound to Settings.SensitiveAccents) to include a
Margin attribute (use either "0 4 0 0" for tight grouping or "0 14 0 0" for a
section break to match surrounding cards) and add the appropriate HeaderIcon
attribute to match other cards, and consider relocating this SettingsCard near
related search settings such as querySearchPrecision or ShouldUsePinyin if it
logically groups with search-related options instead of being placed between
notifications and window positioning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f7968958-51cd-4ab8-b534-538cd66e811a

📥 Commits

Reviewing files that changed from the base of the PR and between f905530 and 0588fa6.

📒 Files selected for processing (6)
  • Flow.Launcher.Infrastructure/StringMatcher.cs
  • Flow.Launcher.Infrastructure/UserSettings/Settings.cs
  • Flow.Launcher/Languages/en.xaml
  • Flow.Launcher/Languages/pt-br.xaml
  • Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
  • Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
✅ Files skipped from review due to trivial changes (2)
  • Flow.Launcher/Languages/en.xaml
  • Flow.Launcher/Languages/pt-br.xaml

@4yinn
Copy link
Copy Markdown
Author

4yinn commented Mar 27, 2026

@jjw24 @Jack251970

Sorry for the delay — I’ve been thinking about ways to implement this and reached a possible solution, but I got a bit stuck in one situation and wanted to ask for your help if possible.

I implemented this approach to handle the issue:

private static readonly Dictionary<char, char> AccentMap = new()
{
    ['á'] = 'a', ['à'] = 'a', ['ã'] = 'a', ['â'] = 'a', ['ä'] = 'a', ['å'] = 'a',
    ['é'] = 'e', ['è'] = 'e', ['ê'] = 'e', ['ë'] = 'e',
    ['í'] = 'i', ['ì'] = 'i', ['î'] = 'i', ['ï'] = 'i',
    ['ó'] = 'o', ['ò'] = 'o', ['õ'] = 'o', ['ô'] = 'o', ['ö'] = 'o',
    ['ú'] = 'u', ['ù'] = 'u', ['û'] = 'u', ['ü'] = 'u',
    ['ç'] = 'c',
    ['ñ'] = 'n',
    ['ý'] = 'y', ['ÿ'] = 'y'
};

public static string Normalize(string value)
{
    Span<char> buffer = stackalloc char[value.Length];
    for (int i = 0; i < value.Length; i++)
    {
        var c = char.ToLowerInvariant(value[i]);
        buffer[i] = AccentMap.TryGetValue(c, out var mapped) ? mapped : c;
    }

    return new string(buffer);
}

And I’m using it like this inside method FuzzyMatch:

var fullStringToCompareAndNormalize = opt.IgnoreCase ? Normalize(stringToCompare) : stringToCompare;
var queryWithoutCaseAndNormalize = opt.IgnoreCase ? Normalize(query) : query;

var fullStringToCompareWithoutCase = opt.IgnoreCase ? stringToCompare.ToLower() : stringToCompare;
var queryWithoutCase = opt.IgnoreCase ? query.ToLower() : query;

var fullStringToCompare = _settings.SensitiveAccents ? fullStringToCompareAndNormalize : fullStringToCompareWithoutCase;
var queryToCompare = _settings.SensitiveAccents ? queryWithoutCaseAndNormalize : queryWithoutCase;

This logic is only triggered when the user enables the option via a button in the general settings.

However, I realized that since the apps/results are cached in memory during queries, I would need to reset the cache whenever the user enables or disables this option so that the behavior updates correctly.

I got a bit stuck on how to handle this situation properly and would appreciate any guidance on the best approach.

Ensures the system restarts automatically when the Sensitive Accents option is enabled or disabled Improves user experience by making the restart action explicit
@coderabbitai coderabbitai bot added the enhancement New feature or request label Mar 28, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Flow.Launcher/Languages/pt-br.xaml`:
- Line 117: The resource entry with x:Key="typingStartEn" contains an accidental
trailing space after the closing </system:String> tag; edit the XAML element for
typingStartEn to remove the trailing whitespace so the line ends exactly with
</system:String> with no extra space, preserving the existing text content
"Sempre Começar Digitando em Modo Inglês".
- Around line 124-128: Add the five missing resource keys (sensitiveAccent,
sensitiveAccentToolTip, sensitiveAccentRestartTitle,
sensitiveAccentRestartMessage, sensitiveAccentRestartButton) into each of the
listed language resource XAML files (the ones currently missing these keys) as
system:String x:Key entries; if you don't have translations yet, populate them
with English fallback text matching the intent in the pt-br example so WPF
DynamicResource lookups in SettingsPaneGeneral.xaml resolve and do not render
blank UI; ensure keys are spelled exactly as shown and placed in the same
resource dictionary structure as other language entries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8cc778ed-764e-4122-b3b8-a8bff44783de

📥 Commits

Reviewing files that changed from the base of the PR and between 0588fa6 and c270c9d.

📒 Files selected for processing (4)
  • Flow.Launcher/Languages/en.xaml
  • Flow.Launcher/Languages/pt-br.xaml
  • Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
  • Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
🚧 Files skipped from review as they are similar to previous changes (3)
  • Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
  • Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
  • Flow.Launcher/Languages/en.xaml

Comment on lines +124 to +128
<system:String x:Key="sensitiveAccent">Ativar a distinção de acentuação na consulta de programas.</system:String>
<system:String x:Key="sensitiveAccentToolTip">Quando esta opção for alterada, será necessária uma reinicialização. Quando ativada, você poderá encontrar programas que contêm caracteres acentuados com mais facilidade.</system:String>
<system:String x:Key="sensitiveAccentRestartTitle">Reinicialização Necessária</system:String>
<system:String x:Key="sensitiveAccentRestartMessage">Alterar a sensibilidade de acentos requer a reinicialização do Flow Launcher para surtir efeito.</system:String>
<system:String x:Key="sensitiveAccentRestartButton">Reiniciar Agora</system:String>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find language files missing the sensitiveAccent* keys

echo "=== Language files missing sensitiveAccent keys ==="
for file in $(fd -e xaml . Flow.Launcher/Languages/); do
  missing=""
  for key in sensitiveAccent sensitiveAccentToolTip sensitiveAccentRestartTitle sensitiveAccentRestartMessage sensitiveAccentRestartButton; do
    if ! grep -q "x:Key=\"$key\"" "$file"; then
      missing="$missing $key"
    fi
  done
  if [ -n "$missing" ]; then
    echo "$file: missing$missing"
  fi
done

Repository: Flow-Launcher/Flow.Launcher

Length of output: 4324


Add sensitiveAccent keys to 25 other language files to prevent blank UI text.*

The five new keys (sensitiveAccent, sensitiveAccentToolTip, sensitiveAccentRestartTitle, sensitiveAccentRestartMessage, sensitiveAccentRestartButton) are missing from 25 language files. When WPF cannot resolve DynamicResource bindings in SettingsPaneGeneral.xaml, it renders blank text for those UI elements, creating a poor experience for users of those languages.

The missing files are: ar, cs, da, de, es, es-419, fr, he, it, ja, ko, nb, nb-NO, nl, pl, pt-pt, ru, sk, sr, sr-Cyrl-RS, tr, uk-UA, vi, zh-cn, zh-tw.

Add these keys to all language files—at minimum with English fallback text if translations are not yet available.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Flow.Launcher/Languages/pt-br.xaml` around lines 124 - 128, Add the five
missing resource keys (sensitiveAccent, sensitiveAccentToolTip,
sensitiveAccentRestartTitle, sensitiveAccentRestartMessage,
sensitiveAccentRestartButton) into each of the listed language resource XAML
files (the ones currently missing these keys) as system:String x:Key entries; if
you don't have translations yet, populate them with English fallback text
matching the intent in the pt-br example so WPF DynamicResource lookups in
SettingsPaneGeneral.xaml resolve and do not render blank UI; ensure keys are
spelled exactly as shown and placed in the same resource dictionary structure as
other language entries.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Flow.Launcher/Languages/en.xaml">

<violation number="1" location="Flow.Launcher/Languages/en.xaml:128">
P3: The label text says "Enable accent sensitivity", but the setting is named "ignoreAccents" and the tooltip describes accent-insensitive matching. This contradicts the actual behavior and can mislead users.</violation>
</file>

<file name="Flow.Launcher.Infrastructure/StringMatcher.cs">

<violation number="1" location="Flow.Launcher.Infrastructure/StringMatcher.cs:94">
P2: IgnoreAccents now forces lowercasing even when IgnoreCase is false, so case-sensitive searches become effectively case-insensitive whenever accent removal is enabled.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@prlabeler prlabeler bot added Code Refactor enhancement New feature or request labels Mar 31, 2026
Move the "Ignore Accents" settings card and its InfoBar to appear after the "showAtTopmost" card and before the "language" card in SettingsPaneGeneral.xaml. Adjust margin values for proper spacing. No functional changes to the controls.
Clarified the "ignoreAccents" setting and tooltip to indicate that accents are ignored when searching all results, not just programs. Updated wording from "programs" to "results".
Copy link
Copy Markdown
Member

@Jack251970 Jack251970 left a comment

Choose a reason for hiding this comment

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

Can you tell me why we need to restart Flow when change accent option?

I think _settings.IgnoreAccents in StringMatcher.cs can be changed during runtime and we do not need to restart Flow for taking this into effect.

@4yinn
Copy link
Copy Markdown
Author

4yinn commented Mar 31, 2026

Can you tell me why we need to restart Flow when change accent option?

I think _settings.IgnoreAccents in StringMatcher.cs can be changed during runtime and we do not need to restart Flow for taking this into effect.

I also wanted it to work that way, but I couldn’t find a way to do it without needing to restart. I had thought about using the method to reset the memory cache that exists in Main in the PluginProgram, but I wasn’t able to use it or call it in any way.

@Jack251970
Copy link
Copy Markdown
Member

Can you tell me why we need to restart Flow when change accent option?
I think _settings.IgnoreAccents in StringMatcher.cs can be changed during runtime and we do not need to restart Flow for taking this into effect.

I also wanted it to work that way, but I couldn’t find a way to do it without needing to restart. I had thought about using the method to reset the memory cache that exists in Main in the PluginProgram, but I wasn’t able to use it or call it in any way.

This is because Program plugin constructs memory cache for program results.

private static readonly MemoryCacheOptions cacheOptions = new() { SizeLimit = 1560 };
private static MemoryCache cache = new(cacheOptions);

@Jack251970
Copy link
Copy Markdown
Member

One possible solution is to reload plugins when this option changes

@Jack251970
Copy link
Copy Markdown
Member

Jack251970 commented Apr 2, 2026

@jjw24 How do you think about this issue?

@4yinn
Copy link
Copy Markdown
Author

4yinn commented Apr 2, 2026

One possible solution is to reload plugins when this option changes

I had noticed that the StringMatcher method is implemented as a singleton, so it ends up being a bit complicated. That’s why I was thinking about restarting the flow for this option, since it involves the query mechanism.

StringMatcher now listens to Settings.PropertyChanged and updates
UserSettingSearchPrecision when QuerySearchPrecision changes.
Removed direct assignment from Settings setter to centralize
update logic within StringMatcher.
Introduce StringMatcherBehaviorChanged event in Settings, triggered on changes to string matching properties. Expose the event via PublicAPIInstance for plugin subscriptions. Update Program plugin to reset cache on event, and ensure proper event unsubscription in Dispose. Refactor IgnoreAccents property for consistency.
@Jack251970
Copy link
Copy Markdown
Member

Jack251970 commented Apr 3, 2026

@4yinn I added a new API event to handle this. Since I do not have any programs with accent-contained names, could you please help me check if this option can take effect for Program plugin without restarting? Note that you need to wait for a moment after you changed this option and test.

@VictoriousRaptor
Copy link
Copy Markdown
Contributor

@4yinn I added a new API event to handle this. Since I do not have any programs with accent-contained names, could you please help me check if this option can take effect for Program plugin without restarting? Note that you need to wait for a moment after you changed this option and test.

Why not use the same architecture of PinyinAlphabet? It's doesn't require a restart.

@Jack251970
Copy link
Copy Markdown
Member

@4yinn I added a new API event to handle this. Since I do not have any programs with accent-contained names, could you please help me check if this option can take effect for Program plugin without restarting? Note that you need to wait for a moment after you changed this option and test.

Why not use the same architecture of PinyinAlphabet? It's doesn't require a restart.

I think 5yinn is talking about the cache behavior of Program plugin.

@Jack251970
Copy link
Copy Markdown
Member

Program plugin will cache the query and results in memory. And we meed to reset it when these settings change.

@VictoriousRaptor
Copy link
Copy Markdown
Contributor

Program plugin will cache the query and results in memory. And we meed to reset it when these settings change.

Oh sorry I forget there's a memory cache.

@4yinn
Copy link
Copy Markdown
Author

4yinn commented Apr 3, 2026

@4yinn I added a new API event to handle this. Since I do not have any programs with accent-contained names, could you please help me check if this option can take effect for Program plugin without restarting? Note that you need to wait for a moment after you changed this option and test.

That works.

Removed restart note from ignoreAccents tooltip. Added a new constructor and UpdateApp method to SettingsPaneGeneralViewModel for triggering asynchronous app updates.
Copy link
Copy Markdown
Member

@Jack251970 Jack251970 left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks for your contribution! Please wait for jjw24's review

@Jack251970
Copy link
Copy Markdown
Member

@jjw24 Hi, this PR looks good to me! Could you please finally review it? Thanks!

@Jack251970 Jack251970 requested review from jjw24 and removed request for jjw24 April 3, 2026 16:47
@Jack251970 Jack251970 removed review in progress Indicates that a review is in progress for this PR Code Refactor labels Apr 3, 2026
Added a HeaderIcon to the "Ignore Accents" SettingsCard in SettingsPaneGeneral.xaml, using a TextBlock with "áé" to visually represent the feature.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhancement] Allow for searching without diacritics match (e.g., “camera” = “câmera”)

4 participants