Skip to content

fix(search): add search.smart_case config option#3322

Open
daresTheDevil wants to merge 6 commits intoatuinsh:mainfrom
daresTheDevil:fix/case-sensitive-config
Open

fix(search): add search.smart_case config option#3322
daresTheDevil wants to merge 6 commits intoatuinsh:mainfrom
daresTheDevil:fix/case-sensitive-config

Conversation

@daresTheDevil
Copy link
Copy Markdown

@daresTheDevil daresTheDevil commented Mar 22, 2026

Adds a search.smart_case config option that controls whether fuzzy/fulltext search uses smart-case matching. Defaults to true (current behavior preserved).

When true: all-lowercase queries match case-insensitively, uppercase queries match case-sensitively (via SQLite GLOB vs LIKE).
When false: all queries match case-insensitively regardless of casing.

This addresses the TODO at database.rs:513, which says "smart case mode could be made configurable like in fzf".

[search]
smart_case = false  # always case-insensitive, even with uppercase in query

What changed

  • settings.rs: added smart_case: bool to the Search config struct (default true)
  • database.rs: the GLOB-vs-LIKE decision now checks smart_case && token.has_uppercase() instead of just token.has_uppercase()
  • engines/db.rs: highlight functions respect smart_case too, so highlights stay consistent with query results. FzfV2 case sensitivity and fulltext term matching both updated.
  • engines/daemon.rs: daemon regex fallback path also threads smart_case through to highlights
  • config.toml: documented under [search]
  • Tests for both smart_case=true and smart_case=false behavior

Known limitations

  • SQLite LIKE is only case-insensitive for ASCII A-Z (pre-existing, not introduced here)
  • skim and daemon-fuzzy modes have their own case handling and don't read this setting yet. They could be wired up in a follow-up

Credits

This has been requested since 2022. Thanks to everyone in the thread:

Closes #625

Checks

  • I am happy for maintainers to push small adjustments to this PR, to speed up the review cycle
  • I have checked that there are no existing pull requests for the same thing

When smart_case is true (the default), the existing behavior is
preserved: all-lowercase queries match case-insensitively via LIKE,
while queries containing uppercase characters match case-sensitively
via GLOB.

When smart_case is false, all queries match case-insensitively
regardless of casing. This addresses the long-standing request for
case-insensitive fuzzy search without needing to switch to skim or
daemon-fuzzy mode.

Closes atuinsh#625
The highlight functions had their own hardcoded smart-case logic via
has_uppercase() checks, independent of the new search.smart_case
config. This caused a mismatch where the SQL query would find results
case-insensitively but highlights would fail to match.

- Pass smart_case through to get_highlight_indices_fulltext
- Set FzfV2 case sensitivity to Insensitive when smart_case is false
- Lowercase search terms when matching against lowered command text
  to prevent mismatches (e.g., "Git" vs "git clone")
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 22, 2026

Greptile Summary

Adds a search.smart_case config option (default true) that controls whether fuzzy/fulltext search uses smart-case matching, addressing a long-standing TODO in the codebase. The implementation is solid across the database, db engine, and settings layers, but there's one bug in the daemon engine path.

  • Bug: daemon.rs hardcodes CaseMatching::Smart for the non-regex fuzzy highlight path, so smart_case = false has no effect there
  • The SQL GLOB/LIKE logic, FzfV2 case sensitivity, and fulltext highlight helper are all correctly wired up
  • New tests cover both smart_case=true and smart_case=false behavior at the database level

Important Files Changed

Filename Overview
crates/atuin/src/command/client/search/engines/daemon.rs Adds smart_case to the daemon engine struct and threads it through to the regex highlight path, but the non-regex fuzzy highlight path still hardcodes CaseMatching::Smart, so smart_case = false is silently ignored there.
crates/atuin-client/src/database.rs Core change — adds smart_case: bool parameter to Database::search trait and Sqlite::search impl; switches GLOB vs LIKE based on smart_case && token.has_uppercase(). Tests are correct.
crates/atuin/src/command/client/search/engines/db.rs Propagates smart_case to FzfV2 case sensitivity and fulltext highlight helper; looks correct.
crates/atuin-client/src/settings.rs Adds smart_case: bool to Search config struct with serde default true and matching Default impl. Clean.

Comments Outside Diff (1)

  1. crates/atuin/src/command/client/search/engines/daemon.rs, line 204 (link)

    P2 smart_case ignored in daemon fuzzy highlight path

    CaseMatching::Smart is hardcoded here, so setting smart_case = false has no effect on the daemon's fuzzy highlight indices (only the regex path above respects self.smart_case).

Last reviewed commit: "fix(search): respect..."

The daemon gRPC service and Nucleo index were hardcoding
CaseMatching::Smart, silently ignoring search.smart_case = false.
Now the setting propagates through SearchComponent → SearchGrpcService →
SearchIndex::search and through the daemon highlight path.
SearchComponent::new() hardcoded smart_case=true. If the user had
smart_case=false in config, searches before the history loader finished
(potentially minutes on large histories) would use the wrong case mode.
Now settings are read at the top of start() before spawning the loader.

Also adds a dedicated test exercising smart_case=true vs false through
the daemon search index (Nucleo CaseMatching::Smart vs Ignore).
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.

Case insensitive fuzzy search mode

1 participant