47825fe00a8f8aba46a7552fcdf99918bad8cb21
5223
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
47825fe00a |
Properly measure invisible character replacement (#62478)
Another discovery during search-on-type work. Follow-up to https://github.com/zed-industries/zed/pull/19298 and https://github.com/zed-industries/zed/pull/19846 With the `"soft_wrap": "editor_width"`, I should have no text contents overflowing the editor width. Before (scrollbar shown incorrectly): <img width="2032" height="1162" alt="before" src="https://github.com/user-attachments/assets/8b89ba6d-f98b-4ea0-89f2-8a22e968c438" /> After: <img width="2032" height="1162" alt="after" src="https://github.com/user-attachments/assets/93f62c2b-4981-48ae-aebd-8b0021348787" /> Release Notes: - Fixed invisible symbol replacement width calculation |
||
|
|
0ad5441b53 |
editor: Align selections by display position instead of byte column (#61997)
# Objective Fixes #60192 Closes https://github.com/zed-industries/zed/issues/62308 `editor: align selection` lines cursors up by their buffer column, and that column counts bytes. If a multi-byte character sits before the cursor, the byte column is larger than the position the cursor is actually drawn at, so the row gets padded with the wrong number of spaces. The issue reports it with `←` (3 bytes) and `π` (2 bytes): ``` a ← 1 # one bc ← π # two ``` Put a cursor on each `#`, run the action, and the result is still misaligned: ``` a ← 1 # one bc ← π # two ``` This is not the columnar selection bug fixed in #57097. That one was `select_columns` in `selection.rs`, where the output is a selection range. This one is `align_selections` in `editor.rs`, where the output is inserted spaces, so the same byte-column assumption was left behind in a second place, and fixing it here needs a rounding step that the first fix did not. ## Solution Measure each cursor by its x offset in the laid-out display row (`DisplaySnapshot::x_for_display_point`), take the target for a column as the furthest x across the rows, then turn the difference into whole spaces by dividing by the advance width of `' '`. The offset that carries into later columns becomes an x offset instead of a column count. The display map has already expanded tabs by the time the row is laid out, so a leading tab now counts as its expanded width instead of as a single byte. Two things I would look at first in review: - The division rounds instead of truncating. The x offsets are built by repeated float addition, so a gap that should be exactly three spaces can arrive as 2.9999998, and truncating inserts two. - The function returns early if the space advance is missing or zero. Dividing by zero gives `inf`, which saturates to a huge `u32` and then tries to allocate that many spaces. I did not add any public items and did not touch `selection.rs`. ## Testing `cargo test -p editor align` on Windows: 6 passed, 0 failed. That is the new test plus the two existing `align_selections` tests, which I did not change and which still pass. `test_align_selections_with_multibyte_chars` covers the repro from the issue, a second column whose offset has to carry past a multi-byte character in the first, a leading tab, a non-BMP character, and a case where multi-byte characters sit after the cursors and nothing should move. I also checked that the test catches the bug rather than just passing: reverting the change in `editor.rs` and keeping the test makes it fail on the repro, inserting four spaces where three are right. Putting the change back makes it pass. The two older align tests pass either way, since they are pure ASCII. What I have not covered: - Wide CJK characters, combining marks, and ZWJ clusters. These should be right by construction, since the code measures advances rather than counting characters, but I have no tests for them. The headless text system behind `gpui::test` gives every BMP character the same advance, so a test there would assert the test double's behavior rather than the real renderer's. - Proportional fonts. Aligning with inserted spaces cannot be exact when glyph widths vary. The code rounds to the nearest whole space. - Soft-wrapped rows. I measure x from the start of the wrapped row but still group cursors by buffer row, so two cursors on one buffer row that sit either side of a wrap boundary get measured from different origins, and the carried offset crosses that boundary as if they shared one. The old byte-column code did not have that particular failure. I left it alone because fixing it is a different change, but I would rather flag it than have you find it. - I work on Windows and have no macOS machine. The arithmetic is platform independent, so I do not expect a difference, but I have not checked. To try it: paste the two lines from the issue, put a cursor on each `#` with `editor: select next`, then run `editor: align selection`. The two `#` should line up. ## Self-Review Checklist: - [ ] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed `editor: align selections` misaligning rows and Vim `ctrl-d` / `ctrl-u` / `ctrl-f` leaving the cursor behind on lines with multi-byte characters or tabs. --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
bc463bc205 |
Send correct line endings to language servers (#59941)
# Objective Zed normalizes all buffer text to `LF` internally, but was sending that `LF`-normalized text to language servers even for `CRLF` files. This caused servers such as ESLint (with a `linebreak-style` rule) to report a false error on every line. Fixes #38453 ## Solution Send the buffer's actual line endings to the language server instead: - `didOpen` and full-document `didChange` now send `text_with_line_endings()`, and incremental changes apply the buffer's line ending to each edit. - Normalize the line endings returning from the LSP before computing changed regions - This effectively incorporates the fix from #59151, which happens to be the reason this change was [originally reverted](https://github.com/zed-industries/zed/commit/1b6cde7032fea1ff38f885419ab4e79c4f8c3231). As such, that PR should likely be integrated first. - Detect when a buffer's line ending differs from what a server was last sent and force a full-document resync, without this the server would keep stale line endings, as the incremental change tracking does not consider line ending differences. - Route the `UpdateLineEnding` operation to `on_buffer_edited` so toggling line endings via the status bar notifies the server immediately rather than waiting for the file to be edited or reopened. ## Testing - Did you test these changes? If so, how? Yes, in addition to new unit test coverage, I used a test project with ESLint configured with the `linebreak-style` rule set to enforce CRLF line endings to verify that the LSP integration worked as expected. - Are there any parts that need more testing? The original reversion seems to have been due to a regression in which LSP formatting would cause the editor to scroll to the bottom. I'm not seeing this in my reproduction, and I believe this was due to a failure to normalize line endings coming back from the LSP, but I don't know the exact circumstances that led to the original reversion, so there might be some additional things to test there. - How can other people (reviewers) test your changes? Is there anything specific they need to know? Not really! As mentioned above, configuring ESLint with the `linebreak-style` rule is probably the easiest way to test. - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? I tested on Linux, but I don't believe it's relevant. ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase https://github.com/user-attachments/assets/881c5758-a5de-433c-8fd6-3cad7478aa90 --- Release Notes: - Fixed an issue where language servers received incorrect line endings for `CRLF` files, causing linters and formatters to report false errors. --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
770a977c68 |
editor: Keep the cursor before text inserted by on-type formatting (#61823)
## Why `textDocument/onTypeFormatting` edits that insert or replace text at an empty cursor use its right bias and move it past the new text. In paired tags, pressing Enter can therefore leave the cursor on the closing tag instead of between the tags. ## What - Capture a left-biased pin for each empty cursor before requesting on-type formatting. - Skip cursor tracking unless a matching language server advertises the trigger. - Restore only unchanged empty cursors whose displacement is fully covered by formatting transaction ranges, so intervening user edits are preserved. - Reset vertical movement state when restoring a cursor. ## Testing - `cargo test -p editor test_on_type_formatting` (5 passed) - `./script/clippy -p editor` ## References - Fixes https://github.com/zed-industries/zed/issues/61574 Release Notes: - Fixed the cursor being moved past text inserted or replaced at its position during on-type formatting. --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
52894d3f48 |
Respect the filterText of LSP completion items during completion filtering (#62433)
# Objective Closes #61646. For code completions, Zed currently fuzzy-matches against `CodeLabel::filter_text()`: https://github.com/zed-industries/zed/blob/d4010e91ccc9635853698af160f15bb479a484d0/crates/editor/src/code_context_menus.rs#L337-L343 `CodeLabel::filter_text()` is a substring of `CodeLabel.text`, which is essentially the text itself. `CodeLabel.text` is constructed by Zed's per-language adapters from the `label` and `detail` fields of the completion items returned by the language server — the exact construction differs from adapter to adapter, but the source data is the same. In effect, `CodeLabel.text` ≈ `label` + `detail`. Zed therefore filters on the server-returned `label` and `detail`, while the server-returned `filterText` field is silently ignored.Per the LSP spec: ``` /** * A string that should be used when filtering a set of * completion items. When omitted, the label is used as the * filter text for this item. */ filterText?: string; ``` we should use `filterText` when it is provided. Normally, language servers populate `filterText` as a substring of `label`, so the current behavior works fine. But for certain language servers or functions, `filterText` can be entirely unrelated to `label` and `detail`. For example, for `std::path::Path::parent()` in Rust, rust-analyzer returns: ```json { "label": "parent()", "labelDetails": { "detail": "(alias dirname)", "description": "fn(&self) -> Option<&Path>" }, "kind": 2, "preselect": true, "sortText": "7ffffff6", "filterText": "parentdirname", ... } ``` Typing `dirname` therefore never surfaces this completion. The root design issue behind this bug is that `CodeLabel` is not well-suited to filtering LSP completions. ## Solution `CodeLabel` and its related methods are kept untouched: the struct is reused across the repo and is only unsuitable for filtering LSP completions. Instead, the changes are made in `CompletionSource` and `Completion`, each gaining a `filter_text()` method: - `CompletionSource::filter_text()` handles LSP completions, returning the server-provided `filterText` and falling back to the `label` when `filterText` is absent. - `Completion::filter_text()` is the general entry point used for fuzzy matching; for non-LSP completions it falls back to the existing `label.filter_text()`. The fuzzy match target is switched from `CodeLabel::filter_text()` to `Completion::filter_text()` — that is the core change. Since the fuzzy match target is no longer guaranteed to be a substring of the displayed `CodeLabel.text`, the matched characters no longer have a direct position in the displayed text to highlight. Bold highlights are therefore only rendered when `CodeLabel::filter_text()` equals `Completion::filter_text()`. This is a safe choice, though not an ideal one. ## Testing Added a new GPUI test covering the new behavior; also built and tested with a before/after comparison, attached in the Showcase section. ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase | Before | After | |:--:|:--:| | <img width="708" height="308" alt="Before" src="https://github.com/user-attachments/assets/ca2e3820-7ea1-4dcc-a91f-28aab71aecc5" /> | <img width="696" height="248" alt="After" src="https://github.com/user-attachments/assets/334e240b-64d5-495b-aef6-772456b993ba" /> | --- Release Notes: - Improved completion filtering for lsp completions. |
||
|
|
a034d87024 |
project: Don't let a canceled caller leak a loading worktree forever (#61009)
# Objective `WorktreeStore::find_or_create_worktree` inserts the shared worktree-creation task into `loading_worktrees` and relies on the task it returns to each caller to remove that entry once creation resolves. But the creation task keeps running through the clone the map itself holds, while the map cleanup lives only in the callers' returned tasks. If every caller is cancelled before creation resolves, the resolved task stays in `loading_worktrees` forever, retaining the `Entity<Worktree>` captured in its result (a `Shared` task memoizes its output). Such a worktree can never be released: `remove_worktree` only unlists it, so its background scan keeps running and its snapshot keeps growing for the lifetime of the window. The stale entry also keeps `initial_scan_complete` permanently `false` (that flag is `loading_worktrees.is_empty() && …`). Callers are cancelled routinely — worktree creation is async and can take seconds on a large tree, while the tasks awaiting it are owned by UI that the user can close at any time (a tab or pane, a debugger panel resolving a path, an agent session, or the whole window). See the existing note in `crates/zed/src/zed.rs` that external-file worktrees are "released on file close". Observed in the wild: a home-directory worktree removed from the project kept scanning for hours and grew Zed past 45 GB; neither removing the folder nor ending the agent session freed it — only quitting Zed. (The scan-amplification half of that incident is #60988.) ## Solution Spawn the map cleanup as its own detached task, next to the map insertion, so a loading entry always leaves `loading_worktrees` when loading resolves regardless of what happens to the callers. The returned per-caller task is unchanged apart from no longer owning that cleanup. ## Testing - Added `test_worktree_released_when_creation_caller_is_cancelled`: it requests a worktree, drops the returned task immediately (as a cancelled caller would), lets creation complete, removes the worktree, and asserts the entity is released. It fails on `main` and passes with this change. - Full worktree-related project integration suite is green (45/45). ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed a memory leak where a worktree whose creation was requested by a since-cancelled task (e.g. a folder opened as its owning tab/panel/window closed) could never be released, leaving its background scan running and its snapshot growing for the lifetime of the window. --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
371a7d4ba2 |
editor: Honor lsp_results_location in the cmd-click references fallback (#61187)
Follow-up to #59838, implementing what was discussed at the end of #59829: cmd-click navigation now respects `lsp_results_location` when go-to-definition falls back to find-all-references (invited in https://github.com/zed-industries/zed/issues/59829#issuecomment-4989966493: "It would! Feel free to hook that up if you'd like to!"). ## Problem Cmd-clicking a symbol's definition falls back to find-all-references, but the results always open in a multibuffer even with `"lsp_results_location": "picker"`. The hover-link click path calls the editor navigation methods directly, so the action handlers registered by `lsp_locations` never get a chance to intercept. ## Solution - `handle_click_hovered_link`'s fallback now dispatches the `FindAllReferences` action (with `open_results_in: None`, deferring to the global setting) instead of calling the method, so the `lsp_locations` handler can intercept it, or propagate to the editor's built-in handler when the setting is `multi_buffer`, preserving today's behavior exactly. - The plain cmd-click arm of `cmd_click_reveal_task` now runs the definition query via `go_to_definition_of_kind` (no internal references fallback) instead of `go_to_definition`, so the click path has a single fallback decision point: the dispatching one. Without this, the method's baked-in fallback opened a multibuffer before the dispatch could run. - `go_to_definition_of_kind` visibility widened to `pub(crate)` for the call from `hover_links.rs`. Shift/alt click variants (type definition, splits) are untouched. Keyboard invocations were already intercepted and are unchanged. ## Testing - New test `test_cmd_click_fallback_honors_lsp_results_location` in `lsp_locations`, following the module's existing test patterns: fake LSP returning no definition and two references, `lsp_results_location: picker`, simulated cmd-click at the cursor's pixel position, asserts the picker opens. The test fails without this change. - `cargo nextest run -p lsp_locations`: 6/6. - `cargo nextest run -p editor -E 'test(hover) or test(fallback) or test(go_to_definition) or test(references)'`: 54/54. - `cargo fmt` and `./script/clippy` clean. - Verified manually in a release build: with the setting on, cmd-clicking a definition opens the picker; with it off, behavior is unchanged. Per the contributing guidelines' note on AI assistance: this change was developed with heavy AI assistance (Claude Code). I have reviewed and understand the full diff and the reasoning behind each hunk, and I'm the one answering review feedback. Release Notes: - Fixed cmd-click go-to-definition falling back to a references multibuffer even when `lsp_results_location` is set to `picker`. |
||
|
|
803467f37f |
text_finder: Highlight the query as a regex when the regex filter is on (#61128)
the buffer search and project search bars both already load the `regex` language and put it on their query buffer when the regex filter is on. the text finder never got that, so a regex you type in there is just plain text. did the same thing here. the only slightly annoying part was that the picker's head editor is `pub(crate)`, so text_finder couldn't get at it. added a small `query_editor()` accessor to `Picker` for that. `adjust_query_regex_language` mirrors the two existing ones. test asserts the query buffer's language is regex with the filter on, and gone once it's off. Closes #59945. Release Notes: - Improved the text finder by highlighting the query as a regex when the regex filter is on --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
c24358d96c |
deps: Bump pathfinder_simd & fix upcoming rustc warnings (#62170)
While building Zed with nightly rustc I've noticed it doesn't compile because of good old pathfinder_simd. It also emits a bunch of warnings about use of f64 literals where f32 is expected, so I've fixed them - it should make future upgrades more straightforward. |
||
|
|
101ca00a13 |
editor: Treat punctuation as separate word boundaries (#62224)
# Objective Fixes FR-149 - Make word movement and selection predictable around punctuation. - Prior art: [#58882](https://github.com/zed-industries/zed/pull/58882), [#61916](https://github.com/zed-industries/zed/pull/61916), and their revert [#62213](https://github.com/zed-industries/zed/pull/62213). ## Solution - Treat words and punctuation runs as separate movement units in both directions. - This adds a stop between leading punctuation and its following word, such as `.▏foo`. ## Testing - Added table-driven coverage for reported examples, delimiters, operators, punctuation runs, and Unicode punctuation. - Tested editor movement, selection, and deletion behavior. ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed word movement and selection stopping on the wrong side of punctuation. |
||
|
|
65a5c89a9e |
language: Defer auto-indent until parsing catches up (#62024)
# Objective Prevent auto-indent from using stale syntax when text changes during a background parse. ## Solution Keep auto-indent pending and parsing active until the current parse completes. ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Editor: Fixed auto-indent occasionally using stale syntax after rapid edits. |
||
|
|
4b407d0fe4 |
editor: Revert punctuation word movement changes (#62213)
Reverted due to regressions in editor word navigation around punctuation: - https://github.com/zed-industries/zed/pull/58882#issuecomment-5138947209 - https://github.com/zed-industries/zed/pull/58882#issuecomment-5153627453 - https://github.com/zed-industries/zed/pull/62065#issuecomment-5178850523 Release Notes: - Reverted #61916 and #58882 |
||
|
|
2d9e6278e7 |
vim: Fix cursor placement after switching to Helix normal mode with a multi-key binding (#62080)
# Objective Closes #41744 When a multi-key binding switches from Insert mode to Helix normal mode, subsequent editing actions can place the cursor incorrectly. While a printable multi-key binding is pending, Zed temporarily inserts the pending keys into the buffer. Once the binding is matched, Zed dispatches the associated action and then deletes the pending text. Because the buffer uses a CRDT, the deleted text remains as tombstoned fragments. The cursor can still resolve to the correct visible offset while its selection anchor remains associated with one of those fragments, affecting the ordering of subsequent edits. Among the explicit Vim mode-switch actions, `SwitchToHelixNormalMode` is the only one that preserves the existing selections: https://github.com/zed-industries/zed/blob/ce6f3af5f7ae2bbdb002c8ce5cc38e96179de811/crates/vim/src/vim.rs#L713-L719 As a result, the tombstone-associated anchor is carried into Helix normal mode, where later actions such as `o` can place the cursor on the wrong side of the inserted newline. The affected path is narrow: a multi-key binding must invoke `SwitchToHelixNormalMode` from Insert mode. This creates pending text and then preserves the resulting selection anchors when entering Helix normal mode. There are two possible layers at which to address this. At the Editor layer, selections could be refreshed whenever pending text is removed so that they no longer reference deleted fragments. At the Vim layer, the Helix mode transition can explicitly guard against preserving those anchors. PR #50918 attempted the broader Editor-layer solution by refreshing selections after resolving a multi-key binding. However, as discussed in this review comment: https://github.com/zed-industries/zed/pull/50918#issuecomment-4411319469, the more appropriate scope for this reported issue is the Vim mode-switching layer. ## Solution Based on PR #50918 and its review feedback, this PR limits the fix to `SwitchToHelixNormalMode`. When that action is triggered by pending Insert-mode keystrokes, Vim refreshes the preserved selection anchors after the pending text is removed. This keeps the fix scoped to the affected Helix transition without changing other multi-key bindings. ## Testing Added a GPUI regression test. ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed incorrect cursor placement after using a multi-key binding to leave insert mode in Vim or Helix mode. --------- Co-authored-by: dino <dinojoaocosta@gmail.com> |
||
|
|
bbd198f57b |
editor: Resolve semantic token overlaps by configured server order (#61735)
`RawSemanticTokens` says tokens later in the list override earlier ones on overlap, but there's no list to be later in — the tokens are stored in a `HashMap<LanguageServerId, _>` and the sort that flattens them keys only on `range.start`: https://github.com/zed-industries/zed/blob/9dcc3c29099724321c34797d18573d28a2c9df25/crates/project/src/lsp_store/semantic_tokens.rs#L702-L709 https://github.com/zed-industries/zed/blob/9dcc3c29099724321c34797d18573d28a2c9df25/crates/editor/src/semantic_tokens.rs#L312-L314 `sort_by` is stable, so two servers' tokens at the same position keep whatever order the map iteration gave them, and which one wins changes between sessions. You hit this any time two servers tokenize the same range. In my case a server that highlights an embedded query inside a string literal renders fine, then loses the range to svelte-language-server once that refreshes. Setting `languages.Svelte.language_servers` doesn't help, since that isn't what decides it. Breaking ties by `server_id` makes it deterministic. `SemanticTokenHighlight` already carries the id and `LanguageServerId` is `Ord`, so it's just the tiebreak. Worth noting the servers do arrive as an ordered `Vec` from `language_server_ids_for_buffer` and that order is dropped when they go into the `HashMap`. Sorting by id gets determinism but not that original order; if preserving it is what was intended I'm happy to submit a PR for that functionality instead. Release Notes: - Fixed semantic token highlighting being applied unpredictably when more than one language server provides tokens for the same range. |
||
|
|
c6e0868cb8 |
vim: Fix insert above auto-indentation (#52594)
Fixes InsertLineAbove (Shift + O) auto-indent handling by deciding whether to trim the first character or the last character based on direction. Previously, inserting a line above would auto format the line the cursor was originally on and not the new line (instead of the correct behavior which is to do the opposite). No tests because I couldn't find any Vim-specific auto-indent tests and this only affects Vim mode. If I'm missing something there LMK and I'll take a closer look. Closes #52588. Release Notes: - Vim: Fixed auto-indentation for insert above action. --------- Co-authored-by: dino <dinojoaocosta@gmail.com> |
||
|
|
35cb7558a9 |
editor: Add configurable git gutter width setting (#61304)
## Summary
Adds a `gutter.git_gutter_width` setting that lets users pin the width,
in pixels, of the git diff hunk indicators in the editor gutter.
Previously the width was always derived from the buffer font size
(`floor(0.275 * line_height)`), which can render too thin or too thick
depending on font family and display pixel density. This adds an
optional override:
```json
{
"gutter": {
"git_gutter_width": 6
}
}
```
When the setting is unset (`null`, the default), the width continues to
scale with the buffer font size, so existing behavior is unchanged.
## Motivation
Requested in [discussion
#27799](https://github.com/zed-industries/zed/discussions/27799). Beyond
the width itself, participants noted the git hunk hit target is
extremely narrow and hard to click. Because the hunk hitbox reuses the
painted strip bounds, setting a wider `git_gutter_width` also widens the
clickable area, addressing that complaint with the same setting.
## Changes
- `settings_content`: new `git_gutter_width: Option<f32>` on
`GutterContent`.
- `editor`: mirror field on the resolved `Gutter`; `gutter_strip_width`
now consults the setting, and the value flows through `diff_hunk_bounds`
(including the deleted-hunk marker) and the gutter layout anchors for
line numbers, folds, and expand toggles.
- `settings` (VS Code import): pass through the new field.
- Docs + `default.json`: document the new setting.
## Notes
- `Eq` was dropped from `GutterContent`/`Gutter` because `f32` is not
`Eq`; this matches the existing `EditorSettingsContent` convention for
float-bearing settings.
Release Notes:
- Added a `gutter.git_gutter_width` setting to configure the width of
git diff indicators in the editor gutter
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
90d024b88a |
Fix the display point out of range panic due to wrong coordinate space used (#62055)
Closes ZED-AKV Closes ZED-ANB Follow-up to https://github.com/zed-industries/zed/pull/61153 The original PR mixed up the coordinate spaces, and the newly added methods used buffer rows (as the existing code did before that PR), but the new code uses `FoldMap` which expects a tab row instead of the buffer one. Release Notes: - Fixed a panic when selections are added by tab-expanded column |
||
|
|
5e1fd392f6 |
git: Add diff_base setting for showing changes since the default branch (#61501)
# Objective Let git indicators — the editor gutter, file colors, and `git::Diff` — show all changes on the current branch relative to its merge base with the default branch, instead of only uncommitted changes. Supersedes #60398; thanks to @samuelcolvin for the original implementation and motivation. Closes FR-135 ## Solution - New `git.diff_base` setting (`"head"` | `"default_branch"`), applied live and toggleable per session from the editor controls menu ("Diff Against Default Branch"). - Statuses come from a real merge-base-to-worktree tree diff (`git diff --merge-base`), so local edits that revert branch changes correctly show as unchanged. - `GitStore` shares one `DiffBufferList` per repository with the Branch Diff view; `repo_snapshots` and `project_path_git_status` keep returning index/worktree truth, while display surfaces use separate `display_*` APIs. - `BufferDiff` now records what its base is (`DiffBaseKind`); hunks whose base isn't HEAD are read-only in the gutter — stage/restore buttons and keybindings are inert, so committed work can't be silently rewritten. - `git::Diff` follows the setting; new `git::DiffHead` always opens the HEAD diff; `git::BranchDiff` is renamed `git::DiffBranch` (deprecated alias kept). Tradeoffs / known limitations: - Hunk-level staging is unavailable while in `default_branch` mode (whole-file staging via the git panel still works). Staging just the uncommitted sub-ranges of a branch hunk is a follow-up. - Remote hosts running an older server ignore the new `GetTreeDiff.includes_worktree` proto field and degrade to committed-changes-only branch diffs. - Repositories with no resolvable default branch fall back to HEAD-relative behavior; a failed first resolution retries on the next branch-list change. ## Testing - Real-git-repo tests for the merge-base-to-worktree diff's edge cases: files recreated after index deletion, committed deletions recreated on disk, and symlinks. - GPUI tests for status semantics (a branch change reverted on disk shows clean), `git::Diff` routing, live setting changes, and read-only hunk enforcement (restore/stage leave buffer and index untouched). ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Git: Added a `git.diff_base` setting (`"head"` or `"default_branch"`) that makes the editor gutter, file colors, and diff view show all changes on the current branch since its merge base with the default branch, instead of only uncommitted changes. --------- Co-authored-by: Ben Kunkle <ben@zed.dev> |
||
|
|
f56ff65c92 |
editor: Stop opt-left on the right side of punctuation between words (#61916)
Closes #61884 Release Notes: - Fixed Option+Left stopping to the left of punctuation between words Video URL: https://files.catbox.moe/8d5g2t.mp4 |
||
|
|
5f180e06dc |
editor: Add multiple_selections key context (#60018)
# Objective - I would like to have the Vim command `z z` do different actions depending on if I have multiple selections. If I only have one selection I want `z z` to center my only selection (current behavior); however, if I have multiple selections I would like `z z` to run `editor::OpenSelectionsInMultibuffer`. This PR does not add this specific functionality (happy to be convinced otherwise), but it does let users add this type of functionality. ## Solution - Add a new key context field that is active only when there are multiple selections. ## Testing - Changes are quite small. A test on this would really be testing other parts of the code-base more than this change. ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase https://github.com/user-attachments/assets/2559e218-d6fa-492a-949b-f4ab8b880f28 This allows us to add something like the following to the `keymap.json`: ```json { "context": "VimControl && !menu && multiple_selections", "bindings": { "z z": "editor::OpenSelectionsInMultibuffer", }, }, ``` which lets us change the functionality of a keystroke based on if there are multiple selections. This video has the following keystroke sequence: `z z` (Center the cursor normally) `5 g l` (Select the 5 next occurrences of the word under the cursor) `z z` (Open selections in multi-buffer) (Previously `z z` in this context would just center the last selection) https://github.com/user-attachments/assets/a0dc1813-5420-4cda-a1ec-99e6681680e7 --- Release Notes: - Added `multiple_selections` keystroke context Co-authored-by: Lukas Wirth <lukas@zed.dev> |
||
|
|
79cc17c216 |
gpui: Give divs (the option for) a better scrolling experience (#61797)
# Objective Closes #56127 Right now there is an issue where in certain contexts, specifically GPUI div elements, horizontal scrolling w/ a trackpad works very poorly, even when trying to scroll sideways, vertical scrolling will happen. This happens with both a trackpad and scrollwheel. The showcase video also shows the broken behavior. ## Solution The way that scrolling works in the editor is really nice, scrolls are locked to the axis they started on but with enough force can change axis. so by taking the ongoing scroll handler from the editor and moving it up into GPUI, we can get that same functionality for GPUI. I somewhat "take over" the functionality of the existing `restrict_scroll_to_axis` property, because it seems like its purpose was basically trying to fix this problem already, just unsuccessfully. The alternative, and would make things overall nicer to look at, would be to tie this to `allow_concurrent_scroll`, but the effect radius tied to that option is way bigger, so im holding off on that atleast right now. ## Testing Theres a good number of scroll related tests generally, so I don't think i've perturbed any common existing behaviors. attach the functionality to `restrict_scroll_to_axis` really limits the surface. There are also some new tests for the ongoing_scroll behaviors. Tested on my mac w/ both trackpad and mouse. ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase Video show casing all the behaviors: broken w/ trackpad working w/ trackpad broken w/ scroll working w/ scroll https://github.com/user-attachments/assets/1935cd2d-a255-4a77-81d5-daa9f72470a6 --- Release Notes: - markdown_preview: Fix horizontal scrolling inside certain elements |
||
|
|
200fb85c90 |
language: Fix incomplete bracket matches during error recovery (#61604)
Closes #61410 ## Summary Tree-sitter bracket queries can become incomplete when recovering from syntax errors. The existing code handled incorrect matches by collecting the delimiters returned by the query and pairing them again in order. The query missed the innermost closing parenthesis. The repair therefore worked on an incomplete sequence, paired the innermost opening parenthesis with the next available close, and shifted every remaining pair outward. ## Solution This change records the syntax-node kinds used by each bracket pattern and, when a syntax layer contains errors, checks the concrete leaves inside those error nodes for delimiters missing from the query output. When the opening and closing kinds are different and the error range remains balanced, the missing delimiters are included in the existing reconstruction. ## Testing Reproduced the issue using the C snippet from #22679 and verified that each parenthesis is matched with its correctly nested partner. Also verified that unbalanced code does not cause the recovery logic to invent a missing bracket pair. ## How to Review The relevant changes are in `BufferSnapshot::fetch_bracket_ranges` in `crates/language/src/buffer.rs`. The important distinction is between the bracket-query output and the concrete syntax leaves inside an error node. Previously, the repair could only rearrange delimiters present in the query output. It can now detect when that output is incomplete, add the missing real delimiters, and run the existing reconstruction over the complete set. ## 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)](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 Release Notes: * Fixed incorrect bracket matching when syntax-error recovery omitted a delimiter. --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
6109c2e6d8 |
grammars: Support linked editing of custom elements in JSX and TSX (#58578)
JSX and TSX only treated the dot as a tag-name character during linked-editing, so renaming a tag to a custom element name such as `custom-el` stopped mirroring the closing tag at the dash. A follow-up to https://github.com/zed-industries/zed/pull/43179, really. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [ ] 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 - [ ] Performance impact has been considered and is acceptable Closes #58553. Release Notes: - Added support for linked-editing of custom elements in JSX/TSX --------- Co-authored-by: MrSubidubi <finn@zed.dev> |
||
|
|
33f1112fc2 |
theme: Specify color format and pattern in schema (#49136)
## Purpose
While developing a theme for Zed I remarked that the schema available
doesn't mark colors in a way that Zed recognize to display the color
annotations:
```json
{
"$schema": "https://zed.dev/schema/themes/v0.2.0.json"
}
```
<img width="119" height="190" alt="Screenshot 2026-02-25 at 23 15 10"
src="https://github.com/user-attachments/assets/3f1bb703-cb26-4630-9598-3a7cb8873c20"
/>
But Zed has support for it when colors are marked with `"format":
"color"` in the schema:
<img width="127" height="186" alt="Screenshot 2026-02-25 at 23 14 58"
src="https://github.com/user-attachments/assets/1a9387a4-613a-4cf8-a3af-f6ac201bdf54"
/>
So I searched if the schema file was Open Source somewhere, discovered
it was generated from the codebase and attempted the change.
## Implementation
This is essentially done using a `ThemeColor` wrapper for the color
strings that implements `JsonSchema` with a dedicated regex for
validation and the color format specified. This ends up with a new
`$defs` of `Color` being specified and used:
```json
{
"$defs": {
"Color": {
"type": "string",
"format": "color",
"pattern": "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"
}
}
}
```
## Other
- There is a small change I made for testing to the schema_generator
that adds an `--output` / `-o` flag. When provided it writes the
generated schema JSON to the specified file path instead of printing to
stdout. This was done to made testing easier but I can remove it or
split it to a separate PR (It's already a separate commit)
- For this PR to do anything a new schema version will need to be
published at something like `https://zed.dev/schema/themes/v0.3.0.json`
and documentation needs to be updated to point to it.
Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing (Manual testing, couldn't find any existing good place to test
it)
- [x] Done a self-review taking into account security and performance
aspects
- [x] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
Release Notes:
- N/A
---------
Co-authored-by: MrSubidubi <finn@zed.dev>
|
||
|
|
fa1d0362b4 |
Fix mouse multi cursor placement for wrapped lines (#61590)
Closes https://github.com/zed-industries/zed/issues/25237 https://github.com/user-attachments/assets/0f1d54a0-c8f1-4b49-8327-73375a2e1f4e Release Notes: - Fixed mouse multi cursor placement for wrapped lines --------- Co-authored-by: Finn Evers <finn@zed.dev> |
||
|
|
7b030b5008 |
project: Fix panic when an inlay hint response outlives its buffer version (#61747)
# Objective Fix a nightly crash where Zed aborts on a bounds-check panic in the inlay hint cache. `LspStore::inlay_hints` captures a `RowChunk` before awaiting the language server, then reads the hint cache with that chunk's id afterwards. If the buffer shrank meanwhile, `latest_lsp_data` has already rebuilt the cache with fewer chunks, so the stale id indexes out of bounds. Chunks are 50 rows, so the reported crash only needed a ~150-250 line deletion, not a drastic edit. Regressed in #61523. Nightly only; the regressing commit is in no release tag, so this never reached preview or stable. Fixes ZED-AGY Fixes FR-142 ## Solution - Return an empty result when the buffer version no longer matches, so a stale chunk id can never index the rebuilt cache. The stale *writes* were already guarded by this version check; only the read sat outside it. - An empty result is already treated as "not fetched", so the chunk is simply re-requested on the next refresh. ## Testing `test_inlay_hint_response_after_buffer_shrinks` reproduces the abort deterministically and passes with the fix. Note that it drives the race through a fake language server rather than a real one. ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - N/A |
||
|
|
edd906859c |
Revert force hardmark settings for documentation hover (#61622)
Closes https://github.com/zed-industries/zed/issues/60777 # Objective - Due to #59426, forcing docString softmark to hardmark, an regression has appeared. It causes all docString on different language to load newLine (`\n`) by default, instead of soft wrap. - By checking the issue, turns out we should not setting `soft_break_as_hard_break: true`, this will make the hover docString less - Details can refer to #60777 ## Solution - Revert settings by removing the line in `hover_popover.rs:742` ## Testing - About the testing result and comparsion, you can refer to #60777 - Updated test `test_hover_markdown_preserves_soft_breaks` to `test_hover_markdown_soft_breaks_reflow_per_commonmark` ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed an issue where hover documentation could have too many line breaks. --------- Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
afd17238ed |
editor: Check anchor validity for hovered links (#61686)
Closes FR-112 Otherwise, if a multibuffer snapshot changed under the hood for example one of the paths changed, without doing an explicit validity check, we would panic during comparison instead. Release Notes: - Fixed panic when hovering links during an under-the-hood multibuffer changes. |
||
|
|
a3ac036eb6 |
Fix: prevent completion labels and docs from being truncated (#61324)
# Objective - Fixes #60374 ## Solution - Moved the `documentation_label` from the `ListItem::end_slot` into the main `h_flex` layout alongside the `main_label`. - Added `.flex_shrink(1.0)` and `.truncate()` to the documentation container. - To allow it to shrink gracefully when space is tight. - Removed `.w_full()` from the main label's container. - To prevent it from aggressively occupying all available horizontal space, allowing the layout to balance the width between the label and the documentation. ## Testing - Visual and layout fixes tested manually by verifying the completion menu in `settings.json`. - All unit tests in the affected crate passed: `cargo test -p editor`. ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase ### Visual change <img width="813" height="314" alt="スクリーンショット 2026-07-20 143707" src="https://github.com/user-attachments/assets/1daa3fa4-a479-465a-b7fd-cc803242c8a2" /> --- Release Notes: - Fixed(CompletionsMenu): prevent completion labels and docs from being truncated. --------- Co-authored-by: Nathan Sobo <nathan@zed.dev> |
||
|
|
051e5c6a88 |
editor: Skip untitled buffers when saving a multi-buffer (#61380)
Closes #60041. ## Summary When Project Search results contain both a saved file and an untitled buffer, running `workspace: reload` did nothing (quitting hit the same path). The log showed: ``` ERROR [.../workspace.rs] buffer doesn't have a file ``` ## Root cause The Project Search results view is an `Editor` over a multi-buffer, and its `Item::save` delegates to `Editor::save`. For a multi-buffer, `Editor::save` collects every dirty buffer and calls `Project::save_buffers`. An untitled excerpt has no file, so `BufferStore::save_buffer` returns `Err("buffer doesn't have a file")`. That error propagates through `Pane::save_item` → `save_all_internal` → `prepare_to_close`, which then reports the workspace as not ready to close, so `reload`/quit silently aborts. A singleton untitled buffer avoids this because `Editor::can_save` returns `false` for it (the workspace routes it to `save_as`); for a multi-buffer, `can_save` is always `true`. ## Fix Exclude file-less (untitled) buffers when collecting the buffers to save for a multi-buffer. Untitled buffers can only be written via `save_as`, so a bulk multi-buffer save now persists the file-backed excerpts and leaves untitled ones untouched. This also addresses the same latent issue in other multi-buffer views (diagnostics, find-all-references) that can excerpt an untitled buffer. ## Testing - Added `test_save_multi_buffer_with_untitled_buffer_skips_untitled` in `crates/editor/src/items.rs`: it builds a multi-buffer over a file-backed buffer and an untitled buffer (both dirty), then asserts `save` succeeds, the file-backed buffer is persisted, and the untitled buffer stays dirty. Fails before this change (`buffer doesn't have a file`), passes after. - `cargo test -p editor --lib save` (13 tests) and `cargo test -p search --lib project_search` (22 tests) pass. - `./script/clippy -p editor` is clean. Manual repro (before): new untitled buffer with text → Project Search for a term matching both it and a saved file → `workspace: reload` → no-op with `buffer doesn't have a file` in the log. After: reload proceeds. Release Notes: - Fixed `workspace: reload` and quitting silently doing nothing when Project Search results included an unsaved untitled buffer. |
||
|
|
f16101a758 |
editor: Fix sticky header drag cancels autoscroll (#53592)
In #52636 I seem to have introduced this bug, where mouse-down on a sticky header followed by an immediate mouse movements sometimes dropped the pending autoscroll request (to jump to the sticky header line): https://github.com/zed-industries/zed/blob/1fcf9749a6ed384ae346ddf2049ae76e0cc430a9/crates/editor/src/scroll.rs#L453 Fixed by exiting early from `mouse_dragged` when `editor.has_autoscroll_request()`. What sometimes happened before this fix: https://github.com/user-attachments/assets/b6dd6650-b5f0-4ea1-ae70-9c4cf69e31a0 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 Release Notes: - Fixed bug where clicking on a sticky header would sometimes not jump to that line in the editor |
||
|
|
e9c484165d |
Fix adding breakpoint/bookmark via gutter context menu using the wrong row (#59269)
# Objective Fixes #59265 ## Solution Use `display_point_to_anchor` to properly convert `DisplayRow` to `Anchor`, instead of manually constructing `Point` directly as done currently. ## Testing - Tested manually, see video in issue - Tested on windows ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase <details> <summary>Click to view showcase</summary> My super cool demos here </details> --- Release Notes: - Fixed the gutter context menu adding breakpoints/bookmarks to the wrong row in multibuffers. --------- Co-authored-by: dino <dinojoaocosta@gmail.com> |
||
|
|
90b1549310 |
Properly replace hints per each chunk (#61562)
Follow-up to hints part of https://github.com/zed-industries/zed/pull/61523 Instead of appending to the chunk data, we have to replace as we always get all hints for the entire chunk. Given that we order and invalidate the requests by `Version`, this can only happen when a racy requests from e.g. same file split or /refresh and edit etc. happens. Release Notes: - N/A |
||
|
|
e12992cae3 |
editor: Fix crash when copying and pasting using multiple cursors (#61545)
Fixes a crash when copying and pasting a mixed empty/nonempty multi-cursor selection. `Editor::do_copy` is meant to insert a newline after each non-final selection that doesn't copy the entire line, but `prev_selection_was_entire_line` is currently based on the current selection instead of the previous one. `Editor::cut_common` does already correctly track this state across loop iterations. This can lead to an out of bounds crash when pasting because the indexing logic in `Editor::do_paste` relies on these newlines being added correctly, depending on `is_entire_line`. Repro: ``` foo bar ``` 1. select `foo` 2. option-click after `bar` to add an empty selection ``` «fooˇ» barˇ ``` 3. copy 4. paste ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed a crash when copying and pasting using multiple cursors. |
||
|
|
97d854b89b |
project: Prevent empty external formatter output from clearing buffers (#61276)
## Context Zed runs external formatters as stdin/stdout filters: it writes the current buffer to stdin and replaces the buffer with the formatter's stdout. Commands such as `cargo fmt` instead rewrite files on disk and exit successfully without producing stdout, causing Zed to interpret the empty output as the formatted contents and clear a non-empty buffer. The fix treats empty stdout from a successful external formatter as no output when the original buffer is non-empty. Zed leaves the buffer unchanged and displays a notification explaining that the formatter did not return formatted contents. The formatter documentation now clarifies the stdin/stdout contract and recommends using the Rust language server or invoking `rustfmt` directly. Closes #56344 Behavior before the fix : [Screencast from 2026-07-19 02-58-29.webm](https://github.com/user-attachments/assets/61fc5bf9-4420-43f1-94a3-394bbbc4f2a0) Behavior after the fix : [Screencast from 2026-07-19 02-55-35.webm](https://github.com/user-attachments/assets/21062065-6b95-4cd4-8200-506f5681d98e) ## How to Review **crates/project/src/lsp_store.rs** Start with `format_via_external_command`, which now checks whether a successful formatter returned empty stdout while the input buffer was non-empty. In that case, it returns `None` before constructing a diff, preventing the buffer contents from being replaced with an empty string. Then review the external formatter branch in `apply_formatter`: when it receives `None`, it skips extending the formatting transaction, logs the condition, and emits an `LspStoreEvent::Notification` explaining why the buffer was left unchanged. **crates/editor/src/editor_tests.rs** Adds a GPUI regression test that configures an external command to consume stdin and return no stdout. It uses platform-specific commands for Windows and Unix, invokes manual formatting on a non-empty Rust buffer, and verifies that the buffer remains unchanged, the operation is not recorded as a formatter failure, and a notification is emitted. **docs/src/reference/all-settings.md** Extends the external formatter documentation to state that formatters must return the formatted buffer through stdout. It calls out file-rewriting tools such as `cargo fmt` as incompatible and recommends using the Rust language server or `rustfmt --emit stdout`. ## Self-Review Checklist - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the UI/UX checklist - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed external formatters that produce no output clearing non-empty buffers |
||
|
|
b0f145f4ae |
editor: Preserve preview tabs for references multibuffers (#61021)
When `preview_tabs.enabled` and `preview_tabs.enable_preview_multibuffer_from_code_navigation` are enabled, Find All References opens its multibuffer as a preview tab. A references multibuffer is assembled from existing buffers, which may already contain edits when the preview is created. When creating the multibuffer to display the references, it ends up emitting edit events as excerpts are added, this will later cause `Pane::handle_item_edit` to run, which would see these buffer edits since `Buffer::preview_version` and unpreview the tab, as the `Buffer::preview_version` value was not being updated after edits. Refresh the multibuffer’s preview baseline before opening a newly created code-navigation multibuffer as a preview. Background diff updates then preserve the preview, while subsequent user edits still make it permanent. This leaves the existing editor event and search invalidation behavior unchanged. Release Notes: - Fixed `editor: find all references` preview tabs becoming permanent tabs if any of the buffers had been edited before --------- Co-authored-by: dino <dinojoaocosta@gmail.com> |
||
|
|
146a6bf9cd |
Calculate buffer chunks lazily (#61523)
<img width="1728" height="1084" alt="image" src="https://github.com/user-attachments/assets/a560b10a-6e26-43ff-b830-c528e2dc6798" /> Before, each edit in a 50 MB plaintext file would trigger a lot of anchor calculations (as chunk is 50 lines only) done on main thread + did extra work when no language grammar or brackets were available. The PR now moves all anchor calculations into `computed_chunks: Mutex<HashMap<usize, RowChunk>>,` cache miss. `TreeSitterData` is wrapped in `Arc` and shared as a part of the snapshot, hence the need for `Mutex` and internal mutability here. Trace after the changes: <img width="1728" height="1084" alt="image" src="https://github.com/user-attachments/assets/cb258980-8461-4559-b725-aef63c835e60" /> Release Notes: - Improved input performance in large files --------- Co-authored-by: Finn Evers <finn@zed.dev> |
||
|
|
040a169d3d |
Fall back to file names in breadcumbs with LSP symbols (#61511)
Closes https://github.com/zed-industries/zed/issues/60905 Also makes the breadcrumbs to update on settings change — before, one needed to focus them. Release Notes: - Improved breadcrumbs for `"document_symbols": "on"` case when no LSP data is sent |
||
|
|
e49d280094 |
language: Refactor available_languages into its own struct (#61388)
This adds a dedicated `AvailableLanguages` struct in preparation for a more cabable language matching based on a given language config. No functional changes, just shuffling some code around for this round. Also made the LanguageMatcher non-cloneable in favor of wrapping it in an Arc, since cloning is rather expensive for this and having a reference is sufficient in all cases right now. Release Notes: - N/A --------- Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com> |
||
|
|
7cdf2ae6b6 |
language: Fix JSON rainbow bracket colors across chunks (#60741)
## Context
JSON rainbow bracket colors could shift across row chunk boundaries when
large ancestor objects were omitted by the bounded tree-sitter bracket
query window. This fixes the chunk-local depth reset by caching bracket
chunk data together with the active bracket stack after each chunk, then
using that inherited stack when later chunks are computed. For JSON
object braces that are too large for the bounded query, the
implementation recovers active ancestor `{...}` pairs from the syntax
tree so sibling object braces keep the same color even when jumping
directly to a later chunk.
Closes #50185
## How to Review
`crates/language/src/buffer/row_chunk.rs` adds indexed access to row
chunks so bracket cache computation can walk from the nearest cached
chunk up to the requested chunk.
`crates/language/src/buffer.rs` changes bracket range caching from
storing only per-chunk matches to storing per-chunk matches plus the
active bracket stack after that chunk. It preserves the existing bounded
query and greedy-match repair behavior, while adding JSON-specific
ancestor recovery for large object braces omitted by
`MAX_BYTES_TO_QUERY`.
`crates/language/src/buffer_tests.rs` adds a JSON regression fixture
that exceeds `MAX_BYTES_TO_QUERY`, fetches a later chunk before earlier
chunks, and verifies all same-depth sibling object braces keep the same
`color_index` across row chunks.
Video of manual test below :
[Screencast from 2026-07-10
08-09-30.webm](https://github.com/user-attachments/assets/fca6425b-b79c-4097-b4af-148db6aa23d9)
## Self-Review Checklist
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the UI/UX checklist
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Fixed JSON rainbow bracket colors changing across row chunk
boundaries.
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
|
||
|
|
b64e5dc886 |
project: Track inlay hint / code lens / document symbol registrations by ID (#55340)
extends the precedent set by #43703 (thanks @reflectronic) so that `textDocument/inlayHint`, `textDocument/codeLens`, and `textDocument/documentSymbol` correctly track multiple dynamic registrations by id. previously each register call overwrote the slot on `ServerCapabilities`, and unregister either silently did nothing (inlayHint, documentSymbol — no match arm) or wiped the entire capability (codeLens). now each method keeps its own id-keyed map on `DynamicRegistrations`; the field on `ServerCapabilities` is cleared only when the last registration is removed. textDocument-sync notifications (`didChange`/`didSave`/`willSave`) are deliberately out of scope — their fan-out semantics are ambiguous in the spec and were flagged on @smitbarmase's earlier exploration in #36876. verified with `cargo check -p project -p lsp -p editor`, `cargo test -p project --test integration -- multi_registration` (3/3 passing), and `cargo fmt -p project`. closes part of #37838. Release Notes: - Improved support for language servers that dynamically register inlay hints, code lens, or document symbols multiple times. --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
6e1e050738 |
editor: Fix horizontal autoscroll only revealing part of search matches (#61487)
# Objective - While navigating between search matches with word wrap off via Select Next match and Select Previous Match, the horizontal autoscroll would sometimes fail to scroll the viewport enough to show the full searched word. This happened in both the cases of Previous match and next search till we had to come to the first appearance of the word. - Fixes #61409 ## Solution - In `/crates/editor/src/scroll/autoscroll.rs` the function `autoscroll_horizontally`, the scroll bounds were both being derived from `selection.head()`, I inferred that head is the position of the cursor at the end of the selection. But for the correct `start_column` and `end_column` it needs to depend not just on the cursor at the end. So rather than using the head for the calculation for both, I have calculated them using `selection.start` and `selection.end` directly to display pts. - Falling back to col 0 or the full line length when the start or end lies on different row than the current being processed. Now it scrolls enough to reveal the entire match. ## Testing - Did you test these changes? If so, how? 1. Ran full tests using `cargo test` on the full repository 2. Manual test present in the showcase section - How can other people (reviewers) test your changes? Is there anything specific they need to know? Disable word wrap, create a file with a short match near the start of a long unwrapped line and another match further right, search for it, and use Select Next/Previous Match to jump between them. The full match should scroll into view each time. - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? Tested on macOS Tahoe 26.5.2 ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable ## Showcase https://github.com/user-attachments/assets/ace25b26-560d-47f1-a905-f04879f23f15 --- Release Notes: - Fixed horizontal autoscroll not fully revealing search matches when navigating between matches with word wrap disabled. |
||
|
|
c3422b97a9 |
snippets: Strip path separators from language snippet file names (#61421)
Closes #59620 ## Problem A language whose name contains a `/`, such as a custom `PL/X` extension (`lsp_id` → `pl/x`), broke snippets end to end: - The `snippets: configure snippets` action wrote the file to `~/.config/zed/snippets/pl/x.json`, i.e. inside a `pl/` subdirectory. - The snippet scanner reads `snippets/` non-recursively and skips directories, so that file was never loaded and the snippet could never be used. - The completion lookup keyed off the raw `lsp_id` (`pl/x`), which wouldn't have matched the file-stem key even if the file had been scanned. So Zed's own UI created a snippet file it could never read back. ## Fix Add `LanguageName::snippet_scope_id()` (the `lsp_id` with `/` and `\` removed) and use it everywhere a language maps to its snippet file name or lookup key: - the Configure Snippets writer and its "already configured" label, and - the two completion lookups in `editor`. `PL/X` now maps to a flat `plx.json`, as suggested in the issue. The `editor::InsertSnippet` action is intentionally left unchanged: its `language` field is documented to be the snippet file name stem, which is already separator-free. Note: files created under the old behavior (nested `foo/bar.json`) aren't migrated; re-running Configure Snippets writes the corrected flat file. ## Testing - Added a `language_core` unit test asserting `snippet_scope_id()` strips `/` and `\` (e.g. `PL/X` → `plx`). - `cargo test -p language_core` passes. - `./script/clippy -p language_core -p language` passes. - Docs Prettier passes. Release Notes: - Fixed snippets being unusable for languages whose name contains a `/` character |
||
|
|
c4c55bade4 |
Add wrapper type for language servers in settings (#61398)
This is in preparation for making configuration of language servers more comfortable for users/extensions. Release Notes: - N/A |
||
|
|
94792bdbfc |
Respect disabled trailing whitespace removal (#58776)
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 #58686 Release Notes: - Fixed `.editorconfig` re-enabling trailing whitespace removal when Zed settings disable it. --------- Co-authored-by: Martin Ye <martin@zed.dev> |
||
|
|
482963d5e9 |
editor: Treat single punctuation as a word boundary (#58882)
## Context `next_word_end` and `previous_word_start` have a special case that skips a punctuation character so that word movement treats the punctuation as a part of the word (`|.foo` -> `.foo|`). But the check only looked at whether the next char was punctuation, not whitespace, so it skipped single punctuation marks. foo |. bar -> foo . bar| (should be `foo .| bar`) Fix: only skip the punctuation when it's next to a word char. Closes #55601 ## How to Review In any buffer, type `foo . bar`: - Cursor before `.`, alt-right -> stops after the `.` (`foo .| bar`), not at the end of `bar`. - Cursor after `.`, alt-left -> stops before the `.` (`foo |. bar`). ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed word movement skipping single punctuation characters surrounded by whitespace. --------- Co-authored-by: Martin Ye <martin@zed.dev> Co-authored-by: MartinYe1234 <52641447+MartinYe1234@users.noreply.github.com> |
||
|
|
547a6cd599 |
editor: Fix right-click context menus failing to appear at pixel-snapped scroll positions (#61348)
# Objective Fixes #56136. The issue was initially reported while an agent was generating code. Later reports reproduced it during manual editing and with AI disabled, ruling out the agent as the cause. At fractional display scales, the editor's right-click context menu could fail to appear at specific discrete scroll positions. Scrolling one line at a time could make the menu alternate between hidden and visible, even when its anchor remained within the viewport. ## Solution The editor-specific changes in #54728 started pixel-snapping the vertical scroll position used by `EditorElement` to derive and render visible rows. However, `Editor::display_to_pixel_point` continued using the raw scroll position for its visibility check and vertical projection. When a downward pixel snap crossed an integer display-row boundary, `EditorElement` treated the preceding row as the visible range start. `display_to_pixel_point` then rejected that row as being above the raw viewport. Mouse context menu layout returned early before inspecting the actual clicked anchor, leaving the menu state present without rendering its element. This change applies the same line-height and display-scale pixel snapping in `display_to_pixel_point`. Its visibility check and coordinate projection now use the coordinate space actually rendered by the editor. A test-support-only scale-factor setter was also added so GPUI tests can exercise fractional display scaling without depending on the host display. ## Testing Added a GPUI regression test using 100 plain-text lines so the editor can scroll. The test uses a `1.25` scale factor and a `14px` font with `1.3` relative line height. The resulting `18.2px` line height is rounded to `18px`, producing exactly `22.5` device pixels per row. At a raw scroll position of one row, midpoint-toward-zero snapping maps `22.5` device pixels to `22`, or approximately `0.978` rows. This reproduces the old boundary mismatch where the snapped visible range starts at row zero while the raw visibility check starts at row one. The test pins these values with precondition assertions. Row five and an in-bounds click keep the actual source visible, while checking the rendered bounds of the `Copy` item ensures the test does not pass merely because menu state was created. Manually verified that scrolling from the start of a document no longer makes the context menu alternate between hidden and visible. ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase Before: https://github.com/user-attachments/assets/e14c030f-1587-4172-aed2-fecfbfcb65ed After: https://github.com/user-attachments/assets/3080db1e-46eb-4e20-a619-b1608f233668 --- Release Notes: - Fixed editor right-click context menus intermittently failing to appear at certain scroll positions. |
||
|
|
1817b43c80 |
editor: Do not reserve inline blame scroll width when blame is in the status bar (#61422)
## Problem When `git.inline_blame.location` is `status_bar` (anything other than `inline`), nothing is painted inline at the end of the line — yet the horizontal scroll range still reserves the longest row's inline blame width (introduced in #23374), letting the editor scroll into blank space when a line is long enough to scroll. Reported in #24752 (e.g. @Nemesis19's comment: still reproduces with `location: status_bar`). ## Fix Skip the blame width reservation in the scroll range when the blame location isn't `Inline` — one guard in the `longest_line_blame_width` closure in `element.rs`. This is independent of the soft-wrap fix for #24752 (#61319) and can land on its own. ## Test `test_status_bar_blame_location_reserves_no_scroll_width` asserts that, with a long line and no soft wrap, moving blame from inline to the status bar shrinks the horizontal scroll range (the reservation is dropped). It uses a fixed-width test `BlameRenderer`, since the default test renderer returns `None` and never exercised the reservation. Release Notes: - Fixed inline git blame reserving horizontal scroll space even when blame is displayed in the status bar |
||
|
|
54c5db8346 |
Disable LSP for files with very long lines (#61447)
Release Notes: - Fixed ui stutters when opening large single line files like minified javascript due to running LSP requests against them |
||
|
|
b6dac6ca30 |
Add gutter action to clear run status (#61375)
# Objective Add a way to manually clear completed run statuses from the gutter. Folow-up to #61095 This follows up on feedback from the previous gutter run status PR. Currently, editing a file clears run statuses automatically, but there was no direct way to clear a completed status without changing the file. ## Solution - Added a `Clear Run Status` entry to the gutter context menu. - Show the entry only when the row has a completed run status. - Clear the stored run status for that runnable when selected. - Keep running statuses unchanged, so this does not behave like task cancellation. ## Testing - Ran `cargo check -p editor`. - Manually ran a test from the gutter and waited for a completed status. - Verified right-clicking the gutter icon shows `Clear Run Status`. - Verified selecting `Clear Run Status` clears the status icon. - Verified the action is not shown for rows without a completed run status. ## 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 adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable [after.webm](https://github.com/user-attachments/assets/1ae284e1-03b1-4842-a63e-9d66c290b325) --- Release Notes: - Improved gutter run statuses by allowing completed statuses to be cleared from the gutter context menu. |