Closes https://github.com/zed-industries/zed/issues/9307
Use SDK 26 and require newer macOS.
The latter allows to remove a few deprecated API usages.
Release Notes:
- [GPUI] BREAKING: bumped minimum macOS version to 12 (Monterey)
Reject objc, cocoa, and related legacy wrappers in workspace packages
and unused workspace dependency declarations.
Cover aliases, optional and target-specific dependencies, build and dev
dependencies, and permitted objc2 replacements.
Release Notes:
- N/A
Reuse a thread-local `Option<MainThreadMarker>` for platform creation,
display queries, and display-link assertions.
Preserve background-thread rejection and cache both successful and
unsuccessful checks.
An instrumented standalone check observed one pthread_main_np call per
thread across repeated main-thread and worker-thread lookups; the
uncached implementation failed with 1000 calls.
Release Notes:
- [GPUI] Cache macOS main-thread checks per thread.
Migrate window classes, callbacks, native geometry, ownership, and
appearance handling to objc2.
Remove the temporary legacy adapters and the now-unused objc and cocoa
dependencies.
Release Notes:
- [GPUI] Migrate macOS window and appearance bindings to objc2
Migrate application delegates, menus, platform services, and retained
AppKit objects while preserving checked ivar access.
Kept legacy window string allocation and an appearance pointer cast
until the window migration (next PR).
Release Notes:
- [GPUI] Migrate macOS application and menu bindings to objc2
---------
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Use typed pasteboards and retained Foundation objects while preserving
clipboard formats and metadata.
Cover embedded NUL and Unicode text and autorelease lifetimes.
Release Notes:
- [GPUI] Migrated macOS clipboard bindings to objc2
Use retained NSScreen objects and objc2 main-thread checks while
preserving refresh-rate and display-bounds fallbacks.
Release Notes:
- [GPUI] Migrated macOS display and display-link bindings to objc2
Use typed NSEvent bindings, objc2 Carbon object pointers, and a
null-safe NSString conversion.
Keep the legacy string conversion and cast the two window event call
sites until window bindings migrate.
Release Notes:
- [GPUI] Migrated macOS keyboard and event bindings to objc2
Match the invariant check to the existing longest-first ordering for
equal-start layers.
Preserve file context during parsing and cover initial Markdown parsing
and Unicode edits with a regression test.
Release Notes:
- N/A
Co-authored-by: Andrew Mitchell <alamottemitchell@gmail.com>
Follow-up to
https://github.com/zed-industries/zed/pull/65398#pullrequestreview-5471402233
Not implemented from the feedback:
- Returning the cached `Arc` runs from `highlight_text` behind a
newtype: read-only callers now use `highlight_text_resolved(..).runs`
directly, while `CodeLabel` callers still need an owned `Vec`, which
`highlight_text` now collects once.
- Caching the `Rope` itself to avoid the copy on a cache miss: this
needs a new rope-to-rope comparison and roughly doubles the memory each
entry retains, so the cache keeps one `String` copy.
Release Notes:
- N/A
Noticed hangs when opening certain agent threads, hang sample: [Sample
of Zed
Nightly.txt](https://github.com/user-attachments/files/33056833/Sample.of.Zed.Nightly.txt)
Focus cleanup scanned the entire handle registry before each app effect,
amplifying work while restoring large conversations. Queue final handle
drops instead and process only those IDs at the existing cleanup
boundary, preserving blur and pending-input behavior.
Recheck subagent registration after loading to avoid duplicate views and
subscriptions from concurrent loads.
Add targeted regression coverage and a focus-cleanup benchmark.
Release Notes:
- Fixed threads with subagents restoration causing hangs
- [GPUI] Improved focus cleanup performance when many focus handles are
retained or released
Fixes https://github.com/zed-industries/zed/issues/65320
Key cached highlights by typed event indices instead of source offsets,
which can collide during tab expansion. Add a layout regression test
covering the crash and related edge cases.
Release Notes:
- Fixed crashes on tab-indented code blocks
## First commit
Preload initial project panel icons before drawing
Preload the initial project panel icons through the asset cache.
Measurements (warm macOS launches):
- Time to complete Catppuccin Frappe icons: reductions of 4.0 ms and
16.2 ms in two runs of 10 pairs.
- Time to first nonblack captured frame: increases of 16.6 ms and 2.3
ms; all 16 icons were present in every candidate launch.
## Second commit
Reuse unchanged icon families during startup instead of loading them
twice.
Reuse icon families with unchanged source bytes and paths until
extensions finish loading.
Include file paths in theme-read errors.
Measurements (warm macOS launches):
- Time to first captured complete Catppuccin Frappe icons: paired median
reductions of 6.2 ms and 5.0 ms in two runs of 10 pairs.
- Capture gaps in the repeat run reached 216.7 ms.
Release Notes:
- Improved startup time when custom icons are used
Measurements (warm macOS launches):
- Time to captured active-file header, restoring 256 tabs with 17
selections each: reductions of 37.8 ms and 35.5 ms in two runs of 30
pairs.
- With one caret per tab: paired median reduction of 4.7 ms.
- Without tab restoration: no observed improvement.
Release Notes:
- Improved Zed's startup time when hundreds of selections are restored
Closes https://github.com/zed-industries/zed/issues/64962
Update CPAL and rodio to remove the uninterruptible ALSA write retry
loop.
Discard failed output streams so subsequent notifications can reopen the
device.
Preserve recoverable underruns, cancel stale error handlers on teardown,
and keep existing output buffer defaults.
Release Notes:
- Fixed audio thread failing to properly stop on errors
Closes https://github.com/zed-industries/zed/issues/65099
Before:
https://github.com/user-attachments/assets/36673f06-75b2-4d36-a8d9-4aaaa6bc1768
After:
https://github.com/user-attachments/assets/86a4e129-e1de-465f-9e70-fcec9d421c03
Issues:
- The host checked files that were not yet open for matches, but treated
every already-open buffer as a candidate.
Earlier queries leave thousands of buffers open, so later queries sent
all of them to the client: 3,339 buffers for 51 real matches in one
case.
Open buffers are now checked against their in-memory text first, which
also stops ordinary non-SSH searches from reporting open buffers with no
matches.
- The host queued every candidate buffer's full contents without waiting
for the client, so data from cancelled queries piled up in front of the
new query's results.
A single worker shared by all searches now sends at most 64 buffers or
about 1 MiB before waiting for the client to acknowledge them, and skips
batches from cancelled searches.
- Cancelling a search could stop a buffer transfer after the buffer was
already marked as shared, so later searches waited forever for its
contents.
`BufferStore` now owns transfers, so dropping the caller no longer
cancels them.
Release Notes:
- Fixed hanging during remote project search
## First commit:
Avoid process enumeration during macOS startup
Read the real UID with getuid() instead of enumerating processes.
Keep the channel offsets, UID wrapping, and handshake unchanged.
Measurements (warm macOS launches):
- Time to presentation: median 525 ms -> 479 ms over 30 paired launches
(release-fast).
## Second commit:
Reduce startup work and redraw before window reveal
Skip redundant settings recomputation, migration formatting, and
colour-scale construction.
Request a redraw of hidden macOS windows before revealing them.
Measurements (warm macOS launches):
- Startup work, time to selected presentation: paired median reductions
of 7.5 ms and 10.7 ms in two runs of 30 pairs.
- Window reveal, time to first captured text: paired median reduction of
18.4 ms over 30 pairs.
Release Notes:
- Slightly improved Zed's startup time in general case
- [GPUI] N/A
Follows-up https://github.com/zed-industries/zed/pull/64862 with similar
fixes:
* replaces a few dependencies that are almost not used, removes unused
dependencies
* removed unused features
* removes `test-support` dependencies from the regular tree
* cleans up build scripts
Despite the large diff, the changes are rather mechanical, split by
commits.
Result:
<img width="1172" height="77" alt="image"
src="https://github.com/user-attachments/assets/7d8aeb58-7cec-4246-8157-d07fc585f81f"
/>
Release Notes:
- N/A
- [GPUI] N/A
When agent shows file contents and edits that concurrently, sometimes
there's a lot of bracket flickering.
Avoid eager bracket invalidation until the colorization task is fully
awaited.
Release Notes:
- Reduced bracket flickering during agentic edits
Based on
https://github.com/zed-industries/zed/issues/38799#issuecomment-5758593194
finding.
Project search treated any NUL-free file as text, then read it whole,
ran chardetng over every byte (~34 MiB/s) and decoded it into a String
before searching. For raw assets (.yuv/.raw/.onnx/.glb/.tif in #38799)
that cost ~8 s and ~2.6× the file size in RAM per worker.
- Bound chardetng to a 1 MiB window starting at the first non-ASCII
byte.
- Stream-decode legacy/UTF-16 files in project search (`DecodingReader`)
instead of materializing them.
- Scan the single-line text pre-filter in 64 KiB blocks instead of
`read_line`.
- Run the control-byte check even without NULs; treat all high bytes as
text so legacy encodings (windows-1251, GBK, ...) stay text.
- Add magic numbers for TIFF, glTF, ELF, Mach-O, gzip/xz/zstd, SQLite,
HDF5, NumPy, safetensors, GGUF, fonts, media containers and others.
- Move the classifier into a `file_content` crate and use it for git
commit blobs too, replacing the separate NUL-only `is_binary_content`.
Measured on a 200 MiB NUL-free raw file: 8.5 s → ~1.5 s per file, no
whole-file allocation.
Release Notes:
- Improved project search speed and memory usage over misclassified
binary files
For semantic highlights, we fall back to tree-sitter highlights in the
multi buffers, as we limit the semantic tokens with the excerpt size.
Before, the fall back was also limited to the buffer excerpt, which did
not give enough highlights.
<img width="1728" height="421" alt="better_fallback"
src="https://github.com/user-attachments/assets/99517162-2d57-4369-994d-9cba8a9ac463"
/>
Release Notes:
- Fixed breadcrumb highlighting outside excerpts
Closes https://github.com/zed-industries/zed/issues/64444
Ensures that with `ITERATIONS=200000 OPERATIONS=30`, each of the
following tests pass: `test_random_inlays`, `test_random_folds`,
`test_random_tabs`, `test_random_wraps`, `test_random_display_map`,
`test_random_blocks`, and 12/14 shards of the diff test.
Each comment contains the bug and the fix explanation.
Release Notes:
- N/A
Based on
https://github.com/zed-industries/zed/pull/64464#issuecomment-5763255028
With `RUST_BACKTRACE=1` set, `anyhow` captures a backtrace for every
error it constructs; on macOS this costs ~30ms per error, on Linux ~µs.
`Project::test` alone hits several such errors during setup
(`canonicalize`/`load` of the non-existent global `tasks.json` and
`debug.json`, worktree creation, buffer open), adding ~0.8s of fixed
cost per test on macOS.
`RUST_LIB_BACKTRACE=0` is checked by std before `RUST_BACKTRACE`, so
panics keep their backtraces while library error captures stop.
Set only on the macOS test jobs (`run_tests.yml`, `release.yml`), since
Linux and Windows pay nothing for the captures and keep the error-origin
backtraces in failure output.
Measured with `cargo nextest run -p editor --lib`: macOS 119s → 72s,
Linux 78s → 76s (noise).
Trade-off on macOS: a failing `.unwrap()` on an `anyhow::Error` in CI
output loses the backtrace of where the error was constructed; the panic
backtrace at the unwrap site is unaffected.
Release Notes:
- N/A
Move GPUI window/entity updates, view rendering, listener adapters,
arena allocation, and scheduler task setup into shared implementations.
Keep the generic entry points as thin adapters.
Uses pretty much the same technique prior PRs did, but this time on
`gpui` which I am not too confident about — can close it if seems
dangerous, but the binary strip win looks good to try.
`cargo llvm-lines --locked -p gpui`:
Before (01c555b435): 934803 lines, 33225 copies
After: 926925 lines (-0.8%), 33454 copies (+0.7%)
With compiler wrappers disabled, `cargo clean --target-dir
target/binary-size-split-build` and `cargo build --locked -p zed`:
Before: 135.6s wall, 1569.8s CPU, zed binary 1175563800 bytes
After: 142.5s wall (+5.1%), 1589.0s CPU (+1.2%), zed binary 1144068952
bytes (-2.7%, -31.5MB)
Release Notes:
- N/A
- [GPUI] Shared GPUI and scheduler implementations to reduce the final
binary size of the app
Closes https://github.com/zed-industries/zed/issues/64118https://github.com/user-attachments/assets/f2152411-6011-4d87-8c8e-d83bed0afbbc
`ty` returns completion text without a `textEdit` or a completion-list
default edit range:
```json
{"label":"value2=","insertText":"value2="}
```
The [LSP
specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItem)
explicitly states:
> The `insertText` is subject to interpretation by the client side.
For the reported example, `ˇ` marks the cursor:
```py
test(value1, ˇvalue2)
```
Previously, Zed inferred only a range covering the existing `value2`.
Without an insert range, completion acceptance bypassed
`completions.lsp_insert_mode` and replaced that argument with `value2=`,
producing:
```py
test(value1, value2=)
```
The patch also infers an insert range, ending at the cursor rather than
the end of the word.
This lets the existing setting choose between inserting before `value2`
and replacing it.
When a prefix has already been typed, insertion replaces that prefix
while preserving the text after the cursor.
With the unchanged default, `"replace_suffix"`, Zed replaces text after
the cursor only when it is a suffix of the completion’s label.
Here, `value2` is not a suffix of `value2=`, so Zed inserts and
produces:
```py
test(value1, value2=value2)
```
The suffix and subsequence checks now use the raw LSP `label`, not
decorated menu text or `filterText`.
These checks select the edit range; `insertText` still supplies the
inserted text in this example.
Release Notes:
- Fixed insertion modes for completions without edit ranges
* Preserve bundle compiler flags commit commit is a follow-up to
https://github.com/zed-industries/zed/pull/63432
Linux bundling script had custom `RUSTFLAGS` set which eradicated prior
config.
Moved all flags into `.cargo/bundle-config.toml` instead.
* Trim audio features and compress resources commit removes unused
`rodio` features and enables resource compression
The win from the latter seems not that large, so can revert if needed,
but seemed very easy to enable.
With compiler wrappers disabled, `cargo clean --target-dir
target/binary-size-split-build` and `cargo build --locked -p zed`:
Before: 135.6s wall, 1569.8s CPU, zed binary 1175563800 bytes
After: 130.5s wall (-3.8%), 1488.7s CPU (-5.2%), zed binary 1171527064
bytes (-0.3%, -4.0MB)
With `ZED_BUNDLE=true CXXFLAGS=-stdlib=libc++` and `cargo --config
.cargo/bundle-config.toml build --locked --release -p zed -p cli
--target aarch64-apple-darwin`:
Before: 369.4s wall, 3861.6s CPU, zed binary 381662328 bytes
After: 369.4s wall (+0.01%), 3866.1s CPU (+0.1%), zed binary 377836968
bytes (-1.0%, -3.8MB)
Release Notes:
- N/A
Prerequisite to https://github.com/zed-industries/zed/pull/64257
* gpui: build inspector IDs only when the window's inspector exists,
drop the inspector maps on close, skip inspector state lookups for
absent or non-selected IDs
* gpui: create inspector element renderers per inspector instance
instead of at registration
* inspector_ui: create the project and editors lazily per inspector
session, keep the init task and subscriptions cancellable, hold the
inspector weakly from the completion provider
* lsp: bound the shutdown output drain by the same timeout as the
shutdown request
* gpui_platform: add an `inspector_render` benchmark that builds with
the inspector in or out
Mean time per rendered frame with the inspector closed; overhead is
relative to the inspector compiled out.
| Benchmark | Frame, compiled out | Frame, `main` | Frame, this PR |
Overhead on `main` | Overhead on this PR |
| --- | ---: | ---: | ---: | ---: | ---: |
| 1,000 bare divs, never opened | 3.352 ms | 3.505 ms | 3.362 ms | +153
µs (+4.57%) | +10 µs (+0.29%) |
| 1,000 bare divs, opened and closed twice | 3.349 ms | 3.508 ms | 3.364
ms | +159 µs (+4.76%) | +15 µs (+0.46%) |
| 100 bare divs, opened and closed twice | 317.0 µs | 336.3 µs | 317.6
µs | +19 µs (+6.10%) | +1 µs (+0.18%) |
| 10,000 bare divs, never opened | 56.617 ms | 58.925 ms | 56.472 ms |
+2.31 ms (+4.08%) | -145 µs (-0.26%) |
| 500 panel rows with text, hover, click, never opened | 4.173 ms |
4.451 ms | 4.179 ms | +278 µs (+6.67%) | +7 µs (+0.16%) |
| 500 panel rows with text, hover, click, opened and closed twice |
4.197 ms | 4.448 ms | 4.159 ms | +252 µs (+6.00%) | -37 µs (-0.89%) |
| 50 panel rows with text, hover, click, never opened | 523.4 µs | 543.3
µs | 522.3 µs | +20 µs (+3.80%) | -1 µs (-0.21%) |
| 500 panel rows with text, hover, click, opened and closed twice,
another window inspected | 4.191 ms | 4.469 ms | 4.176 ms | +278 µs
(+6.64%) | -15 µs (-0.35%) |
| editor, cursor moves (`crates/benchmarks`, `release-fast`) | 940.9 µs
| 943.0 µs | 942.5 µs | +2 µs (+0.23%) | +2 µs (+0.17%) |
Release Notes:
- N/A
- [GPUI] Optimize GPUI inspector's runtime cost by an order of magnitude
This reverts commit 91bf967e27.
As discussed with @\ChristopherBiscardi , this has to be implemented
differently, making a first step towards that.
Release Notes:
- N/A
Closes https://github.com/zed-industries/zed/issues/63730
Related to #52340 and #43460
Based on https://github.com/zed-industries/zed/pull/63138 and needs it
to be merged first.
`BufferSnapshot::chunks` re-ran the tree-sitter highlight query over the
requested range on every call, so scrolling and cursor movement paid the
full query cost per frame.
Highlight runs are now computed once per 50-row chunk (the same
`RowChunks` that back bracket colorization), resolved against the
current `HighlightMap` at compute time, merged into `(Range<usize>,
HighlightId)` runs, and kept in a cost-budgeted per-buffer LRU (10MB).
Entries reuse the `ResolvedHighlights` representation from #63138: each
stores the grammars and maps it was resolved against and is served only
while every map is pointer-identical (`is_current`), so theme changes
and grammar reloads invalidate structurally, with no invalidation hooks
and no global state.
Edits and reparses drop the cache through the existing `TreeSitterData`
swap.
Chunks spanning more than 64KB bypass the cache and fall back to direct
querying, so files with giant lines behave as before.
## Benchmarks
`editor_render_highlighted` and `editor_render_highlighted_minimap`
benches (`cargo bench -p benchmarks --bench editor_render`) move the
cursor through a generated 10K-line Rust file with real tree-sitter
highlighting, with and without the minimap.
The benches are new in this PR, so the baseline is the same binary with
`ZED_DISABLE_HIGHLIGHT_CACHE=1`, which routes rendering through the
pre-existing direct-query path.
Run 1 — baseline, cache disabled:
```
editor_render_highlighted time: [1.7164 ms 1.7377 ms 1.7604 ms]
editor_render_highlighted_minimap time: [12.273 ms 12.407 ms 12.549 ms]
```
Run 2 — cache enabled:
```
editor_render_highlighted time: [1.4572 ms 1.4597 ms 1.4627 ms]
change: [-16.378% -14.944% -13.526%] (p = 0.00 < 0.05)
editor_render_highlighted_minimap time: [9.2750 ms 9.3213 ms 9.3744 ms]
change: [-25.800% -24.872% -23.930%] (p = 0.00 < 0.05)
```
Over the same runs, GPUI reports window draw mean 0.81 ms → 0.68 ms and
p99 1.06 ms → 0.76 ms without the minimap, and mean 5.14 ms → 4.12 ms,
p99 6.12 ms → 4.75 ms with it.
The relative deltas held across two independent sessions (-14.1…-14.9%
and -20.7…-24.9%), while absolute times drift a few percent with machine
state.
Release Notes:
- Improved editor rendering performance in syntax-highlighted files,
especially with the minimap enabled
Part of https://github.com/zed-industries/zed/issues/57349
`MarkdownElement` re-highlights every code block with tree-sitter on
every frame.
Each visible code block pays a full parse per frame, which makes agent
panel streaming and hover popovers janky once the content grows.
This PR computes code block highlights once, in the background parse
task, instead of at render time:
- Highlights are resolved into `(Range<usize>, HighlightId)` runs at
parse time; each entry stores the grammar and `HighlightMap` it was
resolved against and is served only while the grammar still holds the
pointer-identical map (`ResolvedHighlights::is_current`), so theme
changes need no invalidation hooks, and a block whose entry went stale
falls back to direct fragment highlighting at render until the next
parse.
- `Language::highlight_text_resolved` memoizes results in a
content-keyed LRU (4MB budget, 512KB per-entry cap), so the re-parses
triggered by streaming appends reuse the highlights of unchanged blocks
instead of re-running tree-sitter on them.
- Blocks larger than the per-entry cap bypass the cache and are
highlighted directly, as before.
`CostBudgetedLru` is generic rather than specialized because a follow-up
PR reuses it for per-buffer highlight caching in
`BufferSnapshot::chunks`; the capture flattening keyed by grammar index
(`HighlightCaptureRef`) is shared with that follow-up as well.
## Benchmarks
`cargo bench -p benchmarks --bench markdown_renderer` renders generated
documents that mix paragraphs, headings, tables, and Rust code blocks.
**Run 1 — baseline, `main` (`d6449a9e3f`):**
```
Markdown render/min_bytes/5000 time: [1.2956 ms 1.2974 ms 1.3016 ms]
Markdown render/min_bytes/10000 time: [1.7919 ms 1.7955 ms 1.8026 ms]
Markdown render/min_bytes/50000 time: [6.5510 ms 6.6241 ms 6.6671 ms]
Markdown render/min_bytes/250000 time: [37.644 µs 2.4819 ms 4.3791 ms] <- bimodal already on main
```
**Run 2 — this branch:**
```
Markdown render/min_bytes/5000 time: [940.11 µs 945.28 µs 948.79 µs]
change: [-29.317% -28.385% -27.607%] (p = 0.00 < 0.05)
Markdown render/min_bytes/10000 time: [1.1162 ms 1.1331 ms 1.1407 ms]
change: [-40.859% -39.151% -37.719%] (p = 0.00 < 0.05)
Markdown render/min_bytes/50000 time: [2.4943 ms 2.9225 ms 3.1051 ms]
change: [-80.088% -67.885% -58.058%] (p = 0.00 < 0.05)
Markdown render/min_bytes/250000 time: [43.442 µs 44.661 µs 45.514 µs]
change: [-98.210% -95.806% +5.5167%] (p = 0.17 > 0.05)
```
Over the same runs, GPUI reports window draw mean 2.22 ms → 1.26 ms, p99
31.2 ms → 16.0 ms, and the share of frames that blow the frame budget
drops from 3.9% to 2.0%.
Note: at the 250,000-byte case root blocks render lazily, which makes
the measurement bimodal and inconclusive in both configurations.
Release Notes:
- Improved rendering performance of markdown code blocks (agent panel,
hover popovers, markdown preview)
Closes https://github.com/zed-industries/zed/discussions/51582
During testing, spotted that if I toggle off the hints with the modifier
keys, they are resurrected on scrolling — fixed that in a small commit
on top.
Release Notes:
- Supported commands in inlay hints
I think this is the final attempt of mine to do anything about the
binary size, as those fixes become relatively intricate and I like them
less and less.
Each commit has its own improvement stats and mainly reduce generic-code
duplication through shared non-generic helpers and `#[inline(never)]`
boundaries.
Total reduction stats:
With sccache disabled, `cargo clean` and `cargo build --release -p zed`:
Before: zed binary 396716472 bytes
After: zed binary 359416504 bytes (-9.40%, -37.29MB)
Release Notes:
- N/A
---------
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
Before this change, two different guests requesting the same kind of
request would cancel each other's requests and only the latest one would
have arrived to the latest to query that.
Release Notes:
- Fixed collab LSP requests cancelling each other sometimes
When working with multiple windows, one agent worked in the external
hard drive's directory.
After finishing working on that external dir, I've closed the entire
window (the other one, unrelated, remained) only to discover I cannot
unmount the external hard drive.
Investigated, and seems that that manual `ref_count` is buggy — replaced
that with `WeakEntry` for both agent and ACP parts.
Also added leaked project logging into reliability.
Release Notes:
- Fixed projects leaking via agents panel
Not sure if this PR is 100% good, since it removes symbols from local
traces, leaving addresses only — but this what Windows seem to do
already, and we have those symbols on Sentry to restore from, which were
needed to normally read Linux perf traces already (see `perf.zip` in
https://github.com/zed-industries/zed/issues/59689#issuecomment-5452720248)
On the upside, uncompressed binaries' are now ~100MB less, which is a
lot — hence decided to open the PR for the review
-------------------
Strips debug info and local symbols from shipped macOS and Linux
binaries, matching what Windows already does with PDBs: `bundle-mac` now
always runs `dsymutil` before a new `strip -x` step, and `bundle-linux`
adds `--discard-all` plus `zed.dbg`/`remote_server.dbg` extraction via
`--only-keep-debug`.
Sentry uploads are unchanged and precede stripping.
The macOS binary UUID and the GNU build id survive stripping, so
user-provided traces stay recoverable without rebuilding at the
reporter's commit: `sample` output resolves via `atos` against the
archived `zed.dwarf`, and `perf.data` via `perf buildid-cache` against
the Sentry-archived binary.
zed binary:
macOS (2026-08-29 nightly): 420478976 -> 316503984 bytes (-24.7%,
-104.0MB)
Linux (v1.17.2 aarch64): 439417448 -> 336967256 bytes (-23.3%, -102.5MB)
Downloads shrink far less, symbols compress ~7:1: the macOS DMG loses
only ~15MB.
Release Notes:
- Improved installed size on macOS and Linux by stripping debug symbols
from release binaries (roughly -25%)
Closes https://github.com/zed-industries/zed/issues/62391
Workspace diagnostics were polled incorrectly:
* Workspace pulls were cancelled and re-sent every 120s.
Pyright-family servers hold the request open forever and re-analyze the
whole workspace on every new one, so the retry loop leaked memory; the
request is now kept open (long-poll), as the spec recommends.
* No re-trigger after a server closed a workspace pull.
ty closes the request when diagnostics change and expects the client to
re-pull
* `ServerCancelled` / `retriggerRequest` was ignored.
The spec defaults cancellation data to `{ retriggerRequest: true }`, so
these errors now retrigger workspace pulls and retry document pulls
instead of failing them.
* LSP error codes were erased with `anyhow!(message)`
Errors now carry a typed `lsp::ResponseError` through the chain,
replacing four copies of `ends_with(...)` matching against
rust-analyzer's literal message texts.
* Workspace partial results were not tied to their request.
Stale or numeric-token `$/progress` reports were applied as current data
and could poison `previousResultIds` until a server restart; reports are
now matched against the exact in-flight token.
* `interFileDependencies` was never read.
Every edit re-pulled all other open buffers even for servers declaring
no inter-file dependencies (e.g. ruff); cross-buffer pulls are now gated
on that capability.
* Cross-buffer pulls were unpaced.
All open buffers were pulled back-to-back after each edit debounce; they
are now paced at one document per 500ms.
* The failure valve killed workspace pulls permanently.
51 hard errors over a server's lifetime silently stopped its workspace
pulls until restart
* Concurrent refresh waiters could be dropped instantly.
The capacity-1 refresh channel resolved a second concurrent waiter to
`false` immediately, and request errors dropped queued refreshes;
waiters now queue and carry over to the retried pull.
Release Notes:
- Fixed basedpyright memory leak caused by incorrect workspace
diagnostics polling
Fix flacks like
https://github.com/zed-industries/zed/actions/runs/33546906778/job/99987145940?pr=63573
`test_extension_store_with_test_extension` gives the extension builder
`target` as its cache at
`crates/extension_host/src/extension_store_test.rs:801`.
`install_dev_extension` performs a real extension and grammar build at
`crates/extension_host/src/extension_host.rs:1139`.
When `target/wasi-sdk` is absent (e.g. after `target/` cleanup), the
builder downloads and extracts it at
`crates/extension/src/extension_builder.rs:511`.
In the failed run, downloading and extracting the SDK consumed 59
seconds, grammar compilation began at 60 seconds, and the timeout fired
immediately.
Release Notes:
- N/A
Closes https://github.com/zed-industries/zed/issues/59689
Symbolicating the reporter's profile (21s of typing, Windows) showed
`Window::draw` taking 63% of the main thread at ~62 redraws/s (p95 draw
= 40ms), and 30.9% of the *entire* main thread inside
`EditorconfigStore::properties()`:
every `LanguageSettings` lookup — several per frame via
`DisplayMap::tab_size`, `MultiBuffer::language_settings`, indent guides,
and `LspStore::supports_range_formatting` — re-ran the full editorconfig
resolution (ancestor walk plus `ec4rs::Section::apply_to` over every key
of every section) and then cloned the whole `LanguageSettings` struct.
Real-world `.editorconfig` files carry hundreds of irrelevant keys
(`dotnet_*`, `ij_*`, ...), so the per-call map assembly dominated:
`RtlAllocateHeap` 7.3%, `memcmp` 7.0%, `memcpy` 6.6%,
`Properties::find_idx` 5.9% self time.
## Commit 1: resolve only the editorconfig properties Zed reads
- `EditorconfigStore::properties()` now applies only the 7 keys Zed
consumes (`indent_style`, `indent_size`, `tab_width`, `end_of_line`,
`max_line_length`, `insert_final_newline`, `trim_trailing_whitespace`)
instead of materializing every key of every matching section.
- Worktrees with no editorconfig at all now return `None` early instead
of allocating an empty `Properties` and forcing a `LanguageSettings`
clone on every lookup.
- Adds an `editor_render_with_editorconfig` benchmark: maximized window,
file-backed buffer at `src/app/components/editor_pane.ts`, a root
`.editorconfig` shaped after [the second
report](https://github.com/zed-industries/zed/issues/59689#issuecomment-5475124891)
(no `root = true`, a `[*]` section with ~3200 keys, 25 sections with
large `{...}` alternation lists) plus a 4-level nested chain, and a
typing workload (insert/delete + cursor moves).
```
main (770aac345c): time: [37.905 ms 38.005 ms 38.107 ms]
time: [38.069 ms 38.157 ms 38.248 ms]
this commit: time: [2.8547 ms 2.8618 ms 2.8702 ms]
time: [2.8372 ms 2.8435 ms 2.8510 ms]
```
The candidate is ~13x faster.
GPUI frame report over the same workload: window draw p50 12.71 ms →
0.76 ms, p99 23.38 ms → 1.22 ms; frame budget overruns 556 → 0.
## Commit 2: cache resolved `LanguageSettings` per buffer
- `Buffer` and `BufferSnapshot` now hold `resolved_settings:
Arc<LanguageSettings>`, recomputed by a `SettingsStore` global observer.
- Global methods for retrieving the settings are still there, but buffer
only uses them for multi buffer's `language_settings_at` with an
injected language different from the buffer's primary; cold scopes such
as extension API, LSP workspace configuration, server startup still use
the global resolution
Same benchmark, same method, on top of commit 1 (alternated runs, quiet
machine):
```
commit 1: time: [2.8547 ms 2.8618 ms 2.8702 ms]
time: [2.8372 ms 2.8435 ms 2.8510 ms]
this commit: time: [1.6256 ms 1.6325 ms 1.6403 ms]
change: [-42.685% -42.270% -41.854%] (p = 0.00 < 0.05)
time: [1.6221 ms 1.6284 ms 1.6352 ms]
```
Window draw p50 0.76 ms → 0.47 ms, p99 1.22 ms → 0.69 ms, max 1.02 ms;
zero frame-budget overruns on both sides.
Combined, the benchmark's per-keystroke cost drops ~23x vs `main`
(37.9–38.2 ms → 1.62–1.64 ms).
Release Notes:
- Improved typing and rendering performance in projects that use
`.editorconfig` files.
* ensures The Rule cannot be accidentall removed
* fixes the perf command example to capture call graphs, to avoid
round-trips like
https://github.com/zed-industries/zed/issues/59689#issuecomment-5453623044
(samples without call graphs are not quite usable for investigating bugs
like these)
Release Notes:
- N/A
Cargo joins `build.rustflags` across config sources, so the base
`symbol-mangling-version=v0` and `tokio_unstable` flags still apply
after the newly added config.
With sccache disabled, `cargo build --release -p zed` on macOS:
Before: zed binary 420677880 bytes
After: zed binary 375109048 bytes (-10.8%, -45.6MB)
After `strip -x` of both
(https://github.com/zed-industries/zed/pull/63429):
313879024 -> 307727432 bytes (-2.0%, -6.2MB)
The rest of the delta is the smaller symbol table. `drop_glue`
instantiations: 101778 -> 65041.
Release Notes:
- N/A
Follow-up to https://github.com/zed-industries/zed/pull/63232
Cuts closure monomorphization in settings and editor: settings file
updates now share one boxed write-pipeline instantiation instead of
inlining the `SettingsContent` clone, both `serde_json::to_value` calls,
and the JSON diff at every call site; `Editor::register_action` and the
settings enum dropdown get the same treatment. Behavior and public
signatures are unchanged; thin generic shims remain.
Symbol table of the release binary:
Before: `update_settings_file_inner` 229 instantiations, inlined
`serde_json` `SerializeMap` machinery 6547 copies
After: 3 instantiations, 673 copies (-89.7%); `render_dropdown` is no
longer instantiated at all
With sccache disabled, `cargo clean` and `cargo build --release -p zed`:
Before: zed binary 420677880 bytes
After: zed binary 396999464 bytes (-5.6%, -23.7MB)
Release Notes:
- N/A