Anki is a smart spaced repetition flashcard program https://apps.ankiweb.net
Find a file
user1823 b00308e551
fix(search): normalize whitespace in search query parser (#4853)
## Linked issue (required)

Fixes https://github.com/ankitects/anki/issues/5046
Fixes https://github.com/ankitects/anki/issues/4215

Related:
https://forums.ankiweb.net/t/search-search-problem-in-browse-windows/38603

## Summary / motivation (required)

When the browser search query contains non-breaking spaces (for e.g.,
when text is copied and pasted from a webpage or a card rendered in the
reviewer webview), the parser embeds the \u{a0} characters inside the
SQL query rather than treating them as word separators. SQL then tries
to search them as literal substrings, which never matches the note's
stored field text (which uses normal spaces), resulting in zero results
even though matching notes exist.

The fix normalizes \u{a0} to a regular space in parse(), before any
parsing begins, so it is consistently treated as a word separator
everywhere.

## Steps to reproduce (required, use N/A if not applicable)

1. Create a note with the text "hello world".
2. Copy text having a non-breaking space between "hello world" into the
browser's search bar (I am unable to paste a sample here) and press
Enter.
3. See that no results are returned.

## How to test (required)

In this branch, the same steps return the note as expected.

Checking the SQL query:
Apply this patch, set environment variable `set RUST_LOG=anki=debug`
before running Anki and then repeat the repro steps:
```diff
diff --git a/rslib/src/search/mod.rs b/rslib/src/search/mod.rs
index 0dd52dbc3..4fdab340c 100644
--- a/rslib/src/search/mod.rs
+++ b/rslib/src/search/mod.rs
@@ -24,6 +24,7 @@ use rusqlite::params_from_iter;
 use rusqlite::types::FromSql;
 use sqlwriter::RequiredTable;
 use sqlwriter::SqlWriter;
+use tracing::debug;
 pub use writer::replace_search_node;

 use crate::browser_table::Column;
@@ -189,6 +190,7 @@ impl Collection {

         let (mut sql, args) = writer.build_query(&top_node, mode.required_table())?;
         self.add_order(&mut sql, item_type, mode)?;
+        debug!(sql = %sql, args = ?args);

         let mut stmt = self.storage.db.prepare(&sql)?;
         let ids: Vec<_> = stmt
```

On the main branch, the terminal prints:
```
DEBUG sql=select c.id from cards c, notes n where c.nid=n.id and ((n.sfld like ?1 escape '\' or n.flds like ?1 escape '\')) order by c.mod desc args=["%hello\u{a0}world%"]
```

On this branch, the terminal prints:
```
DEBUG sql=select c.id from cards c, notes n where c.nid=n.id and ((n.sfld like ?1 escape '\' or n.flds like ?1 escape '\') and (n.sfld like ?2 escape '\' or n.flds like ?2 escape '\')) order by c.mod desc args=["%hello%", "%world%"]
```

### Checklist (minimum)

- [ ] I ran `./ninja check` or an equivalent relevant check locally.
- [x] I added or updated tests when the change is non-trivial or
behavior changed.

### Details

<!-- Commands, manual steps, edge cases, and what you observed -->

## Before / after behavior (optional)

<!-- For bugfixes: behavior before vs after. For other types: N/A or a
short note. -->

## Risk / compatibility / migration (optional)

<!-- Breaking changes, rollout notes, or N/A for small / low-risk PRs
-->

## UI evidence (required for visual changes; otherwise N/A)

<!-- Screenshot or short video -->

## Scope

- [x] This PR is focused on one change (no unrelated edits).

---------

Co-authored-by: Abdo <abdo@abdnh.net>
2026-06-29 15:29:56 +03:00
.cargo Statically link MSVC runtime, removing the need to install the redistributable (#4166) 2025-07-05 15:03:14 +03:00
.config Drop workspace-hack in favor of workspace deps 2023-06-23 17:41:31 +10:00
.cursor/rules Make URL schemes dialog more ergonomic (#4002) 2025-05-15 16:17:33 +10:00
.github chore: Do not fail CI if Complexipy fails (#5060) 2026-06-25 18:47:34 +03:00
.idea.dist Update PyCharm docs (#4389) 2025-10-27 19:27:50 +07:00
.vscode.dist feat: Remove the uv launcher and old packaging code (#5019) 2026-06-17 03:02:52 +03:00
build chore: Sync with the 26.05 branch (#5041) 2026-06-19 16:36:51 +03:00
cargo Release infrastructure improvements (#4802) 2026-05-09 17:34:18 -04:00
docs fix(syncserver): use built-in --healthcheck instead of wget (#5044) 2026-06-26 14:02:47 +03:00
docs-site docs(chore): Replace certain pages with redirects (#5065) 2026-06-25 17:41:57 +03:00
ftl fix: Dont close deck config screen when optimizing all presets (#4981) 2026-06-25 16:42:39 +03:00
proto Feat: Expose card's decay and DR to custom scheduler (#4880) 2026-06-08 20:26:42 +03:00
pylib fix: Dont close deck config screen when optimizing all presets (#4981) 2026-06-25 16:42:39 +03:00
python Build and publish dev docs (#4579) 2026-03-31 08:26:59 -04:00
qt fix: Additional support for newlines in search (#5045) 2026-06-29 15:09:12 +03:00
rslib fix(search): normalize whitespace in search query parser (#4853) 2026-06-29 15:29:56 +03:00
tools chore: Sync with the 26.05 branch (#5041) 2026-06-19 16:36:51 +03:00
ts fix: Dont close deck config screen when optimizing all presets (#4981) 2026-06-25 16:42:39 +03:00
.complexipy.toml chore: Integrate Complexipy for complexity analysis (#4942) 2026-06-04 19:29:37 +03:00
.deny.toml Update to Rust 1.92 (#4461) 2026-01-11 18:50:16 +07:00
.dockerignore Add distroless Dockerfile and implement internal health check (#3366) 2024-08-29 17:05:33 +07:00
.dprint.json Prototype unified Mintlify docs site (#4882) 2026-06-12 16:27:33 +03:00
.eslintrc.cjs Include error message text on page 2024-06-24 15:35:47 +07:00
.gitattributes try again to improve GitHub's language stats 2021-01-20 13:20:45 +10:00
.gitignore chore: Integrate Complexipy for complexity analysis (#4942) 2026-06-04 19:29:37 +03:00
.gitmodules Briefcase Installer (#4629) 2026-05-05 17:29:18 -04:00
.mypy.ini Briefcase Installer (#4629) 2026-05-05 17:29:18 -04:00
.pre-commit-config.yaml chore: Run complexipy-diff as part of ninja check (#4987) 2026-06-10 21:04:15 +03:00
.prettierrc Switch back to Prettier for Svelte formatting 2025-01-13 15:53:55 +11:00
.python-version feat: Distribute compiled sources in Briefcase bundle (#4856) 2026-05-18 17:47:03 +03:00
.readthedocs.yaml Build and publish dev docs (#4579) 2026-03-31 08:26:59 -04:00
.ruff.toml Switch to Ruff (#4119) 2025-06-29 14:38:35 +07:00
.rustfmt-empty.toml Move away from Bazel (#2202) 2022-11-27 15:24:20 +10:00
.rustfmt.toml Move ascii_percent_encoding into a separate repo 2023-04-12 08:45:23 +10:00
.version chore: Sync with the 26.05 branch (#5041) 2026-06-19 16:36:51 +03:00
.yarnrc.yml chore: add release-age controls for uv and Yarn dependencies (#4761) 2026-04-30 13:38:42 -03:00
AGENTS.md chore: Updates just recipes and add AGENTS.md (#4943) 2026-06-03 12:02:18 -03:00
Cargo.lock feat: Remove the uv launcher and old packaging code (#5019) 2026-06-17 03:02:52 +03:00
Cargo.toml feat: Remove the uv launcher and old packaging code (#5019) 2026-06-17 03:02:52 +03:00
check Add a shortcut to auto-format before running checks 2023-07-02 09:59:05 +10:00
CLAUDE.md chore: Updates just recipes and add AGENTS.md (#4943) 2026-06-03 12:02:18 -03:00
CONTRIBUTORS fix: set minimum height for deck options (#5062) 2026-06-26 16:22:20 +03:00
justfile chore: Run complexipy-diff as part of ninja check (#4987) 2026-06-10 21:04:15 +03:00
LICENSE Drop Pauker and SuperMemo importers from legacy importer 2025-06-27 16:10:12 +07:00
ninja Migrate build system to uv (#4074) 2025-06-19 14:03:16 +07:00
package.json chore(deps): consolidated security updates (Dependabot batch) (#4934) 2026-06-03 15:10:39 -03:00
playwright.config.ts chore(e2e): add Playwright end-to-end test infrastructure (#4864) 2026-05-22 15:59:42 -03:00
pyproject.toml chore: Bump complexipy (#5058) 2026-06-23 17:19:15 +03:00
README.md docs: Remove duplicate intro in README (#5003) 2026-06-15 17:18:27 +03:00
release.just fix: Use Powershell to run just commands on Windows (#4921) 2026-06-01 16:49:39 +03:00
run Migrate build system to uv (#4074) 2025-06-19 14:03:16 +07:00
run.bat One step closer to tools/run-qt* on Windows 2025-09-01 13:39:46 +10:00
rust-toolchain.toml Update to Rust 1.92 (#4461) 2026-01-11 18:50:16 +07:00
SECURITY.md Update security.md. (#4790) 2026-05-11 13:04:24 -04:00
uv.lock chore: Bump complexipy (#5058) 2026-06-23 17:19:15 +03:00
yarn Do JS license output from top-level script 2026-02-11 14:26:53 +07:00
yarn.bat Switch to SvelteKit (#3077) 2024-03-31 09:16:31 +01:00
yarn.lock chore(deps): consolidated security updates (Dependabot batch) (#4934) 2026-06-03 15:10:39 -03:00

Anki

Build Status Documentation

This repo contains the source code for the computer version of Anki.

About

Anki is a spaced repetition program. Please see the website to learn more.

Getting Started

Contributing

Want to contribute to Anki? Check out the Contribution Guidelines.

For more information on building and developing, please see Development.

Contributors

The following people have contributed to Anki: CONTRIBUTORS

Anki Betas

If you'd like to try development builds of Anki but don't feel comfortable building the code, please see Anki betas.

License

Anki's license: LICENSE