Both cloning and dropping of these has quite a bit of overhead (despite
them being snapshots), so avoid where possible, especially in display
map syncing
Release Notes:
- N/A or Added/Fixed/Improved ...
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes#52973
## Problem
The file picker crashes with `highlight index N is not a valid UTF-8
boundary` when file paths contain Unicode characters whose lowercase
form expands to multiple codepoints. Turkish `İ` (U+0130) is the trigger
here: Rust's `char::to_lowercase()` turns it into `i` + combining dot
above (two codepoints). That expansion breaks the fuzzy matcher in two
ways:
1. The `j_regular` index mapping mixes the expanded lowercase index
space with the original character index space, so highlight positions
land on invalid byte boundaries.
2. The scoring matrices are allocated with the expanded length but
indexed with the original length as stride, so rows alias each other and
corrupt stored values.
Users with Turkish locale filenames were hitting this on v0.229.0 and
v0.230.0 stable.
## Fix
I went with simple 1:1 case mapping: a `simple_lowercase` helper in
`char_bag.rs` that takes only the first codepoint from `to_lowercase()`
and drops any trailing combining characters. For `İ` this gives `i`,
which is what anyone would actually type in a search query. The same
function is used in the matcher, the char bag pre-filter, and both
query-lowercasing call sites (`paths.rs` and `strings.rs`).
This gets rid of the `extra_lowercase_chars` BTreeMap, the `j_regular`
adjustment, and the matrix sizing discrepancy. The matcher now works
with a flat character array where `lowercase_candidate_chars.len() ==
candidate_chars.len()`, so there's no expanded-vs-original index space
to get wrong.
I also fixed `CharBag::insert`, which used `to_ascii_lowercase()` and
silently ignored non-ASCII characters. A file like `aİbİcdef.txt`
wouldn't show up when searching `ai` because `İ` was never registered as
`i` in the bag. It now goes through `simple_lowercase` too.
The alternative was keeping full case folding and fixing the index
tracking with a `Vec<usize>` mapping expanded positions back to
originals. That would work but keeps the dual-index-space complexity
that caused these bugs, plus adds a per-candidate allocation for the
mapping vector.
## Prior art
fzf uses Go's `unicode.To(unicode.LowerCase, r)`, which is simple case
mapping -- always one rune in, one rune out. `İ` maps to `i`, no
expansion.
VS Code's `String.toLowerCase()` does produce the expanded form, but the
scorer compares UTF-16 code units independently and sidesteps the
problem in practice.
Neither tool maintains a mapping between expanded and original index
spaces.
## Trade-off
Searching for the combining dot above (U+0307) won't match `İ` in a path
anymore. Nobody types combining characters in a file picker, and fzf
doesn't support it either.
## Screenshot
<img width="1282" height="458" alt="Screenshot 2026-04-02 at 09 56 34"
src="https://github.com/user-attachments/assets/720d327a-4855-4d4d-989e-cbd1c0657f97"
/>
Release Notes:
- Fixed a crash and improved matching and highlighting in the file
picker for paths with non-ASCII
characters (e.g., Turkish İ, ß, fi).
---------
Co-authored-by: Oleksiy Syvokon <oleksiy.syvokon@gmail.com>
Closes#44324
Release Notes:
- Uses the lowercase representation of the query for the matrix length
calculation to match the bounds size expected in `recursive_score_match`
We were using `std::path::Path::strip_prefix` to determine which
repository an absolute path belongs to, which doesn't work when the
paths are Windows-style but the code is running on unix. Replace it with
a platform-agnostic implementation of `strip_prefix`.
Release Notes:
- Fixed git features not working when a Windows host collaborates with a
unix guest
Closes#39865
Release Notes:
- Fixed file finder display when searching for files in history if you
had several worktrees opened in a workspace. It now displays the
worktree root name to avoid confusion if you have several files with
same name in different worktrees.
---------
Signed-off-by: Benjamin <5719034+bnjjj@users.noreply.github.com>
Closes https://github.com/zed-industries/zed/issues/38690Closes#37353
### Background
On Windows, paths are normally separated by `\`, unlike mac and linux
where they are separated by `/`. When editing code in a project that
uses a different path style than your local system (e.g. remoting from
Windows to Linux, using WSL, and collaboration between windows and unix
users), the correct separator for a path may differ from the "native"
separator.
Previously, to work around this, Zed converted paths' separators in
numerous places. This was applied to both absolute and relative paths,
leading to incorrect conversions in some cases.
### Solution
Many code paths in Zed use paths that are *relative* to either a
worktree root or a git repository. This PR introduces a dedicated type
for these paths called `RelPath`, which stores the path in the same way
regardless of host platform, and offers `Path`-like manipulation APIs.
RelPath supports *displaying* the path using either separator, so that
we can display paths in a style that is determined at runtime based on
the current project.
The representation of absolute paths is left untouched, for now.
Absolute paths are different from relative paths because (except in
contexts where we know that the path refers to the local filesystem)
they should generally be treated as opaque strings. Currently we use a
mix of types for these paths (std::path::Path, String, SanitizedPath).
Release Notes:
- N/A
---------
Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Peter Tripp <petertripp@gmail.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
Closes https://github.com/zed-industries/zed/issues/33668
The recursive case increments both indices by 1, but only one of the two
had a base case check in the function prologue so the other could spill
over into a different matrix row or out of bounds entirely.
Lacking a test as I haven't figured out a test case yet.
Release Notes:
- Fixed out of bounds panic in fuzzy matching
This PR fixes a crash in the fuzzy matcher that occurred when handling
Unicode or multibyte characters (such as Turkish `İ` or `ş`). The issue
was caused by the matcher attempting to index beyond the end of internal
arrays when lowercased Unicode characters expanded into multiple
codepoints, resulting in an out-of-bounds panic.
#### Root Cause
The loop in `recursive_score_match` used an upper bound (`limit`)
derived from `self.last_positions[query_idx]`, which could exceed the
actual length of the arrays being indexed, especially with multibyte
Unicode input.
#### Solution
The fix clamps the loop’s upper bound to the maximum valid index for the
arrays being accessed:
```rust
let max_valid_index = (prefix.len() + path_lowercased.len()).saturating_sub(1);
let safe_limit = limit.min(max_valid_index);
for j in path_idx..=safe_limit { ... }
```
This ensures all indexing is safe and prevents panics.
Closes#30269
Release Notes:
- N/A
---------
Signed-off-by: Umesh Yadav <git@umesh.dev>
Closes#25045
With the setting `"use_system_path_prompts": false`, previously, if the
completion target was a directory, no separator would be added after it,
requiring us to manually append a `/` or `\`. Now, if the completion
target is a directory, a `/` or `\` will be automatically added. On
Windows, both `/` and `\` are considered valid path separators.
https://github.com/user-attachments/assets/0594ce27-9693-4a49-ae0e-3ed29f62526a
Release Notes:
- N/A
This reverts commit 8e31d95140 due to a
panic.
```
{
"thread": "main",
"payload": "9 is not a valid char boundary in path \"crates/…/LiveKitBridge/\"",
"location_data": {
"file": "crates/file_finder/src/file_finder.rs",
"line": 646
}
}
```
Release Notes:
- N/A
This PR makes progress on #7711 by identifying any common prefix of the
paths in the file finder's search results, and replacing the "interior"
of that prefix---every path segment but the first and last---with `...`,
when a heuristic indicates that the longest path would otherwise
overflow the modal.
The elision is not applied to any segment that contains a match for the
search query.
There may be more work to do on #7711 in the case of long result paths
that do not share a significant common prefix.
Release Notes:
- Improved display of long paths in the file finder modal
Co-authored-by: Max <max@zed.dev>
For both the strings and paths multithreaded matching it would still
aggregate the response even though it is unneeded. It now checks cancel.
In the paths matcher, cancel is now checked within the loop, since it
was calling `match_candidates` even though no further results would be
computed.
Release Notes:
- N/A
* Removes `max_results` from the matcher interface as this is better
dealt with in consumers once all results are known. The current
implementation was quite inefficient as it was using binary search to
find insertion points and then doing an insert which copies the entire
suffix each time.
* There was a corner case where if the binary search found a match
candidate with the same score, it was dropped. Now fixed.
* Uses of `util::extend_sorted` when merging results from worker threads
also repeatedly uses binary search and insertion which copies the entire
suffix. A followup will remove that and its usage.
* Adds `util::truncate_to_bottom_n_sorted_by` which uses quickselect +
sort to efficiently get a sorted count limited result.
* Improves interface of Matcher::match_candidates by providing the match
positions to the build function. This allows for removal of the `Match`
trait. It also fixes a bug where the Match's own Ord wasn't being used,
which seems relevant to PathMatch for cases where scores are the same.
Release Notes:
- N/A
This PR:
- Makes slash commands easier to compose by adding a concept,
`CompletionIntent`. When using `tab` on a completion in the assistant
panel, that completion item will be expanded but the associated command
will not be run. Using `enter` will still either run the completion item
or continue command composition as before.
- Fixes a bug where running `/diagnostics` on a project with no
diagnostics will delete the entire command, rather than rendering an
empty header.
- Improves the autocomplete rendering for files, showing when
directories are selected and re-arranging the results to have the file
name or trailing directory show first.
<img width="642" alt="Screenshot 2024-08-13 at 8 12 43 PM"
src="https://github.com/user-attachments/assets/97c96cd2-741f-4f15-ad03-7cf78129a71c">
Release Notes:
- N/A
This fixes a panic in the fuzzy finder which someone ran into when
typing in a query that contained the lower-case version of a unicode
character that has more chars than its upper-case version.
It also fixes another problem which was that we didn't find a match if
both candidates and query contained upper-case characters whose
lower-case version had more chars.
Release Notes:
- Fixed a panic in fuzzy-finder that could occur when matching with
queries containing upper-case unicode characters whose lower-case
version has more chars.
Co-authored-by: bennetbo <bennetbo@gmx.de>