fbda4f862a2fb03b854636b17bd04be3db3d94de
5172
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b3acc7518a |
Fix blame hover popover not showing on first trigger when inline blame is disabled (#50769)
● Closes #50285 ## Summary When `inline_blame` is disabled in settings and `editor::BlameHover` is triggered, the blame popover fails to appear on the first invocation because `start_git_blame` kicks off an async task to fetch blame data, but `blame_hover` immediately tries to read that data synchronously before it's available. This fix registers a one-shot observation on the blame entity when it's freshly created, so the popover is shown once blame data has finished generating. 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 - [x] Done a self-review taking into account security and performance aspects - [ ] ~Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)~ (No UI changes) ## Videos: ### Before: https://github.com/user-attachments/assets/d2ca4f8e-8186-49af-9908-5a82bfca0de2 Ps: pressed the keybind two times ### After: https://github.com/user-attachments/assets/08575cd9-2bbc-462d-92fa-f0e7ef23d8d6 ## Release Notes: - Fixed blame hover popover not appearing on first trigger when inline blame is disabled (#50285) --------- Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com> |
||
|
|
081adc3286 |
Show per-file diff stat in multibuffer headers (#60299)
Renders the per-file added/removed line counts (already tracked per buffer by the multibuffer) in each diff multibuffer header, matching the overall stat shown in the branch diff toolbar. ### Before <img width="1624" height="1061" alt="Screenshot 2026-07-02 at 11 50 42 AM" src="https://github.com/user-attachments/assets/3e46a90c-a4e1-4b72-8ec9-80931a7e4a9a" /> ### After <img width="1624" height="1061" alt="Screenshot 2026-07-02 at 12 03 58 PM" src="https://github.com/user-attachments/assets/25900790-82f5-44e9-9a3b-731a3090f23e" /> Release Notes: - Improved diff multibuffer headers to show per-file added/removed line counts --------- Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
a955005b6f |
editor: Update gutter hover tooltip on modifier changes (#58880)
## Context The gutter hover button can represent either adding a breakpoint or adding a bookmark depending on whether the secondary modifier is pressed. Previously, the visible tooltip did not update when the modifier changed while the popover was already open. This moves the gutter hover intent into a shared state used by both the button and tooltip, so the label, metadata, icon, color, click action, and displayed keybinding all stay in sync. Closes #58821 ## How to Review `crates/editor/src/editor.rs` adds a shared gutter hover intent type and a custom tooltip view that reads the current modifier state while rendering. Review that bookmark and breakpoint behavior map consistently across the button icon/color, click action, tooltip label, metadata, and keybinding. `crates/editor/src/editor_tests.rs` adds a regression test covering the tooltip intent selection with and without the secondary modifier. Manual test after the fix below : [Screencast from 2026-06-09 01-30-23.webm](https://github.com/user-attachments/assets/ef465eb9-e659-44da-93a6-a511c84e9401) ## 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 the breakpoint/bookmark gutter popover not updating when modifier keys change. --------- Co-authored-by: Danilo Leal <daniloleal09@gmail.com> |
||
|
|
27b7fc4cbb |
editor: Fix panic when restoring empty selections on undo/redo (#59372)
Closes FR-81. Also updates the error log, as the linked issue is closed. Release Notes: - Fixed panic when restoring empty selections on undo/redo |
||
|
|
818e0d1ca4 |
editor: Fix panic when confirming completion across buffers (#59471)
In multi-buffer context, `do_completion` resolved the completion's replace range anchors against whichever buffer currently held the newest selection. If that selection had moved into a different file's excerpt (while the menu remained open), the anchors belonged to a different buffer and caused a panic. This PR adds a guard to confirm the completion buffer and selection buffer is the same. Closes FR-76. --- Release Notes: - Fixed a panic that could occur when confirming a completion in a multibuffer if the cursor had moved into a different file |
||
|
|
1a2bf64b5b |
editor: Accumulate consecutive KillRingCut line kills (#51761)
Closes #22490 ## Summary • Keep kill-ring state as a dedicated global structure (`text`, `metadata`, `row`, `column`, `buffer_id`) instead of a raw clipboard wrapper. • Update `editor::KillRingCut` to append consecutive empty-line kills only when all are true: - one selection only - previous selection was empty - same row/column - same buffer • Preserve existing behavior for non-empty and multi-selection cuts by replacing the ring payload instead of appending. • Preserve yank metadata behavior through the existing clipboard selection metadata path. • Add regression coverage in `editor_tests::test_kill_ring_cut_accumulates_multi_line_kills`. ## Verification • `cargo fmt --all -- --check` • `./script/check-keymaps` • `./script/clippy -p editor` • `cargo test -p editor -- --nocapture` • `cargo test -p editor editor_tests::test_kill_ring_cut_accumulates_multi_line_kills -- --exact` • `cargo test -p editor test_cut_line_ends -- --exact` ## Manual • `Ctrl+K`, `Ctrl+K`, `Ctrl+Y` with repeated line-end kills at same caret location pastes concatenated text. • Non-empty selection cut and multi-selection still replace as before (no unintended append). • Cut in one buffer then cut in another does not cross-append. • Moving caret between kills does not append. • Undo/redo around cut+yank path works cleanly. Release Notes: - Fixed Emacs-style kill-ring behavior so successive `KillRingCut` operations at the same caret context are accumulated and pasted together via one `KillRingYank`, while preserving existing non-empty selection and multi-selection semantics. --------- Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com> |
||
|
|
6bfbd4d09d |
Fix relative line number calculation when the first row is wrapped (#53759)
fixes #53726 Compute the base delta from the first visible, non-filtered row instead of using rows.start. This avoids off-by-one numbering when rows.start is a wrapped continuation that gets filtered out. Ensure deleted lines still advance the relative counter even when they are not numbered. Add a test for the wrapped scrolling case. P.S. This is my first time using rust for a serious pull request and let's say I'm a little bit rusty with it. 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 #ISSUE Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com> |
||
|
|
21b0a6821e |
editor: Add intelligent bracket colorization (#51580)
Closes #50210 Bracket colorization currently runs theme accents through `ensure_minimum_contrast`, which can significantly alter authored palette colors in order to satisfy a single foreground-vs-background target. In practice, that makes rainbow brackets drift away from the theme colors they are supposed to reflect. This PR keeps bracket colorization as a single automatic behavior, but changes how the palette is derived: - preserve authored accent colors when they already work - adjust only the specific accents that are too weak against the editor background - only reorder the palette when adjacent bracket levels are still too similar after that The implementation is staged because bracket colorization has to handle two separate problems: - **Bracket-vs-Background Readability**: is an individual bracket visible against the editor background? - **Adjacent Bracket Separation**: can you still distinguish one nesting level from the next? Those two constraints are also why the implementation cannot simply defer to whatever accent colors the theme provides. Some themes, including the bundled One and Ayu, do not define explicit accents at all, so bracket colorization falls back to Zed's shared default accent palette. That palette was not designed with any particular theme's background in mind, which makes the background contrast check especially important for those cases. That is why the previous `ensure_minimum_contrast(..., 55.0)` path is not enough on its own. It addresses foreground-vs-background readability, but if lightness-only adjustment cannot satisfy that target it escalates to saturation reduction and then black/white fallback. This PR instead keeps the bracket-specific background fix bounded and lightness-only in OKLCH, so the resulting color stays much closer to the authored accent. ### How it works - **Background Contrast Correction**: accents that fall below the APCA floor against the editor background are adjusted by changing OKLCH lightness only, preserving hue and chroma - **Adjacency Correction**: if the resulting palette is still too weak between neighboring nesting levels, the palette is reordered conservatively to improve adjacent separation That ordering matters. Doing the reorder first can easily be undone by the background-fix step. ### Color measurement - background contrast uses APCA, via the existing `apca_contrast` path - adjacent bracket separation uses OKLab Euclidean distance APCA is the right tool for bracket-vs-background readability, but it is not designed to compare two foreground colors against each other. OKLab distance is a better fit for detecting when neighboring bracket levels are perceptually too close. ### Thresholds - background APCA floor: `30` on dark themes, `35` on light themes - adjacent OKLab target: `0.08` on dark themes, `0.10` on light themes - light-theme reorder tolerance band: reorder only triggers below `0.095`, avoiding threshold-artifact reordering on palettes that are visually fine but narrowly miss `0.10` ### Validation - `cargo test -p editor bracket_colorization` - manual comparison on bundled One, Ayu, and Gruvbox theme families Release Notes: - Improved bracket colorization by preserving theme accent colors when possible and applying targeted contrast fixes only when needed. --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
9177c5e747 |
Make the existing bookmark action add an unnamed bookmark again (#60185)
Zed don't surface the fact that bookmarks can have labels/names now anywhere except opening the bookmark name editor every time you place one. This keeps the label/name mechanism but makes the default add bookmark action add an unnamed one. Next step is probably to enable holding shift (or some other modifier) to place a named bookmark, or even a setting to switch between default named vs unnamed. Release Notes: - N/A |
||
|
|
04db1abc6a |
editor: Use visible cursor position for rename (#55542)
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 #54160 Summary: In Helix mode, rename could fail when the cursor was visually positioned on the last character of a symbol. The editor renders some Vim/Helix selections with the visible cursor offset from the selection head, but rename was still using the raw selection head as the LSP prepare-rename position. This updates rename to use the visible cursor position for point lookup when cursor offset rendering is active. It also adds regression coverage for Helix rename and Vim visual rename. Verification: - `cargo check -p editor` - `cargo fmt --check --package editor --package vim` - `git diff --check -- crates\editor\src\editor.rs crates\vim\src\test.rs` - `test::test_rename` - `test::test_helix_rename_uses_visible_cursor_position` - `test::test_visual_rename_uses_visible_cursor_position` Release Notes: - Fixed Helix mode rename when the cursor is visually on the last character of a symbol. --------- Co-authored-by: dino <dinojoaocosta@gmail.com> |
||
|
|
0acb09e5a3 |
git: Allow showing inline blame in the status bar (#59696)
# Add status bar location for inline git blame ## Objective - Closes #12133 by implementing the VS Code-like behavior for status bar git blame ## Solution Adds a VS Code–style option to render the current-line git blame in the status bar instead of inline at the cursor. Whats new - A new location setting: Under the inline_blame configuration, you can now choose between two options: - `inline` (Default) — Keeps things exactly as they are, showing the blame details at the end of your current line. - `status_bar` — Moves the blame info down to the bottom status bar. - The Status Bar Item: When you choose the status bar option, a clean new button appears at the bottom left of your window. It features a Git icon, the author's name, how long ago the change was made, and the commit summary - Behaves as it does before, but displayed at a different location - Clicking the status bar item will instantly open up the full Git commit details like at vscode - Settings UI: - The "Location" control is now a `DynamicItem` nested under "Enabled", so it only appears when inline blame is enabled. - Registers a dropdown renderer for `InlineBlameLocation`. - Updates `all-settings.md` and `visual-customization.md` for the new option. ## Testing - Manually tested both modes by toggling `git.inline_blame.location` between `inline` and `status_bar`: - `inline` keeps the existing behavior unchanged. - `status_bar` shows the blame for the focused line in the status bar, hides the inline blame, updates on cursor movement, clears when no editor is focused, and opens the blame commit on click. - Verified the settings UI shows the "Location" dropdown only when inline blame is enabled. - Tested with `show_commit_summary` toggled when location is `status_bar` (for inline location, nothing is changed) Areas that could use more testing: - Remote (collab) editors ? Reviewers can test by setting the following in `settings.json` and moving the cursor across blamed lines: ```json { "git": { "inline_blame": { "enabled": true, "location": "status_bar" } } } ``` ## Showcase <details> <summary>Click to view showcase</summary> ### Video https://github.com/user-attachments/assets/6d9f490d-7bf3-473c-9562-8351546dfaf9 ### Screenshots <img width="894" height="749" alt="image" src="https://github.com/user-attachments/assets/c6a49008-899d-4a0c-9098-1ffb9e28252b" /> </details> Release Notes: - Added a `git.inline_blame.location` setting to render current-line git blame in the status bar instead of inline. |
||
|
|
97e5d4f630 |
solo_diff_view: Show changes only by default (#59958)
# Objective - Default the solo diff view to show changed hunks with excerpted context instead of expanding the entire file. - Closes https://github.com/zed-industries/zed/issues/59489 ## Solution - Initialize the solo diff editor with excerpts around changed hunks. - Add a header toggle for switching between the excerpted changes-only view and the full-file view. - Keep both sides of the split diff visually aligned when excerpts are collapsed. ## Testing - `cargo check -p git_ui` - `cargo test -p git_ui` ## 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> Changes-only default view: <img width="1624" height="1030" alt="solo_diff_view_new_default_changes_only_view" src="https://github.com/user-attachments/assets/c6fb937e-8882-415c-8476-b9c3019c152e" /> Toggled full-file view: <img width="1624" height="1030" alt="solo_diff_view_toggled_to_full_file_view" src="https://github.com/user-attachments/assets/3d736d00-178a-4dfa-b1ff-d3d15e33b30e" /> </details> --- Release Notes: - Improved solo diff view to show changed hunks by default with a toggle for viewing the full file. --------- Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com> |
||
|
|
c43971d69c |
Handle dynamic registration of semantic tokens capability (#60015)
In C# files nothing gets semantic highlighting — class fields and other identifiers are colored by tree-sitter only, so a private field looks the same as a plain local variable. The reason: Roslyn (the C# language server) doesn't declare `semanticTokensProvider` statically in its `initialize` response. It registers it **dynamically** (`client/registerCapability`), and only when the client advertises `textDocument.semanticTokens.dynamicRegistration = true`. Zed advertised `false` and didn't handle such a registration, so Roslyn never offered semantic tokens at all. (rust-analyzer/gopls are unaffected — they declare the capability statically.) This is the first of two PRs. This one makes Roslyn actually **send** semantic tokens. The companion PR (#60027) maps Roslyn's C#-specific token types to theme styles — without it the tokens arrive but most are dropped, since their types aren't in Zed's default rules. ## Solution - Advertise `textDocument.semanticTokens.dynamicRegistration = true`. - Handle the `textDocument/semanticTokens` registration and unregistration so the capability is stored, following the existing arms for `documentLink`, diagnostics, etc. ## Testing - Added a test that dynamically registers and unregisters `textDocument/semanticTokens` and checks the stored capability appears and is cleared. - Verified manually against Roslyn on a C# project: Zed now sends `textDocument/semanticTokens/full` and gets tokens back; before this change there was no semantic-token traffic at all. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Support dynamic registration of the `textDocument/semanticTokens` capability. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9afbcb75c5 |
Refresh code lens after dynamic registration (#60009)
Follow-up to https://github.com/zed-industries/zed/pull/54100 Closes https://github.com/zed-industries/zed/issues/58864 Release Notes: - Fixed code lens not being refresh after the corresponding capability was dynamically registered --------- Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
5e3499ee3a |
Hide code lens' blocks in folds (#60011)
Closes https://github.com/zed-industries/zed/issues/57053 Decided to be on a safest side and made only the code lens' blocks to hide in folds: let me know if that's not desired and we should hide all blocks (e.g. ctrl-enter for inline assist block is also not hiding and looks odd). Ran `ITERATIONS=100000 OPERATIONS=10 cargo test --profile release-fast -p editor --lib display_map::tests::test_random_display_map -- --nocapture` and it passes successfully. `SEED=770 OPERATIONS=50 cargo test --profile release-fast -p editor --lib display_map::tests::test_random_display_map` seems to fail even on `main` Before: <img width="392" height="155" alt="before" src="https://github.com/user-attachments/assets/44d1a500-7512-49de-a9bd-2837c33353a9" /> After: <img width="338" height="159" alt="after" src="https://github.com/user-attachments/assets/671791d8-a443-4ca2-abb2-8049fe8910a7" /> Release Notes: - Fixed code lens not hiding for folded regions |
||
|
|
7387a146a4 |
Unify behavior of delete/cut to line boundary actions (#59627)
I noticed that our line deletion actions are inconsistent. For `DeleteToBeginningOfLine`: - `one «two threeˇ» four` -> ` four` ✅ - `one «ˇtwo three» four` -> ` four` ✅ Great, these have the same results, regardless of selection direction. For `DeleteToEndOfLine`: - `one «two threeˇ» four` -> ` one ` ✅ - `one «ˇtwo three» four` -> `one two three` ❌ Whoops, that second one should be the same, assuming that selection direction again shouldn't matter, as in the case of `DeleteToBeginningOfLine`. The same issue exists for `CutToEndOfLine`: - `The quick «brownˇ» fox` -> `The quick ` ✅ - `The quick «ˇbrown» fox` -> `The quick brown` ❌ I unified the end-of-line actions to match `DeleteToBeginningOfLine`: selection direction no longer matters, so the whole selection is always included. --- - [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 `editor: delete to end of line` to behave consistently regardless of selection direction. - Fixed `editor: cut to end of line` to behave consistently regardless of selection direction. |
||
|
|
19e85945cc |
git: Use version control colors in diff hunk line numbers (#59973)
# Objective When diff hunks are expanded, reading the line numbers can be a little hard due to the contrast between `editor.line_number` and both `editor.diff_hunk.deleted.background` and `editor.diff_hunk.added.background`. In order to make it easier for these line numbers to stand out, the actual `version_control` colors, respective to each diff hunk, can be used to color the line number. ## Solution Luckily, the line numbers color was already dynamically decided on `EditorElement::layout_line_numbers` depending on whether the line had a breakpoint or it was active (part of a selection). The information on whether the row for which the line was being laid out belonged to a diff hunk is also already available on the `Gutter::row_infos`. As such, this change was easy to achieve by simply updating `EditorElement::layout_line_numbers` to take into consideration each row's `RowInfo::diff_status` and mapping each status to a `version_control` color, ignoring the `Modified` variant, as that should never happen. ## Testing Manually tested against a repository that has added, deleted and modified changes. Differences can be seen in the `Showcase` section. ## 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 - [x] Performance impact has been considered and is acceptable ## Showcase <details> <summary>Before</summary> <img width="2744" height="2994" alt="version_control_line_numbers_before" src="https://github.com/user-attachments/assets/75d160a7-e0cc-4c9d-8fdd-4cab5cc6eefb" /> </details> <details> <summary>After</summary> <img width="2744" height="2994" alt="version_control_line_numbers_after" src="https://github.com/user-attachments/assets/887fdb6a-34c6-4331-b750-4d3bea6fcd77" /> </details> --- Release Notes: - Improved line number colors in diff hunks to match the version control colors |
||
|
|
4a0537bc52 |
editor: Fix grammar in comment (#59951)
This is less of an actual fix as more something to test some GitHub stuff with. Release Notes: - N/A |
||
|
|
acd55213d1 |
solo_diff_view: Add git action to open file diff and other improvements (#59752)
# Objective - Polish the solo diff review UI by reducing redundant controls and hiding inactive fold controls in split diff views. ## Solution - Collapse the solo diff header's `Stage File` / `Unstage File` actions into one button that shows the action relevant to the file's current staging state. - Stop reserving fold gutter space and rendering fold crease toggles for editors with companion snapshots, matching the existing LHS split diff behavior. ## Testing - `cargo fmt --check` - `cargo check -p git_ui` ## 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 Before, the solo diff header showed both file-level staging actions at once, and the RHS split diff gutter could show fold arrows that did not work in that view. After this change, the header shows the relevant file-level staging action and split companion panes no longer show inactive fold controls. Release Notes: - Improved solo diff controls by showing the relevant file staging action and hiding inactive fold toggles in split diffs. |
||
|
|
028ebbb43e |
editor: Fix showing completion list with selection does not include the selection (#57405)
Fix the completion list not showing the current selection when you execute `editor: show completions`, the stated reason by comment before is that if you are in a tabstop snippet already it wont give you the correct options. So my change is to just check if you are in such a snippet and if not give the new desired behavior. before: https://github.com/user-attachments/assets/1b2f3594-7361-4636-9ef3-f8c9419faa26 after: https://github.com/user-attachments/assets/1be35582-464a-40f3-b6be-ee74f9961796 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 #57386 Release Notes: - editor: include text from selection in completion list. --------- Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com> |
||
|
|
5bf85b2149 |
text_finder: Send selected word to text finder query (#59766)
## Objective Make text finder match project search and buffer search by pre-populating its query from the active editor according to `seed_search_query_from_cursor`. ## Solution - Seed text finder from the active editor's query suggestion when opening it. - Pass the seeded query into the picker and select the query text so typing replaces it immediately. ## Testing - Manually tested visually by opening text finder from an editor with a selection and with the cursor on text, confirming the query is pre-populated and selected. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Improved text finder to seed its query from the active editor. Co-authored-by: ozacod <ozacod@users.noreply.github.com> Co-authored-by: Yara 🏳️⚧️ <git@yara.blue> |
||
|
|
9079f2fa95 |
Add named bookmark support (#57491)
Adds support for named bookmarks while preserving the existing unnamed bookmark workflow. Main changes: - Keeps `ToggleBookmark` as the quick unnamed bookmark action. - Adds `ToggleNamedBookmark`, which prompts for a bookmark name when adding a new bookmark and removes an existing bookmark on the same line, including unnamed ones. - Adds `EditBookmark` support for renaming existing bookmarks. - Persists bookmark names in workspace storage. - Adds a project bookmarks picker that supports filtering by bookmark name and relative path, with highlighted matches. - Updates bookmark storage and tests to carry bookmark names through serialization, restoration, navigation, and project-level listing. 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 #ISSUE Video toggle: https://github.com/user-attachments/assets/a725b9cf-ac37-4ebc-85cc-ee820452c043 rename: https://github.com/user-attachments/assets/a6d0d0c1-511b-4fb7-ae5d-22c55d1a075f Release Notes: - Add named bookmark support --------- Co-authored-by: Yara 🏳️⚧️ <git@yara.blue> |
||
|
|
9d696a585c |
editor: Fix Python docstring formatting in hover popovers (#59480)
# Objective Fixes #59426 Python (and possible others like JS/TS ) docstrings in hover popovers currently have broken indentation and alignment. This happens because markdown soft breaks (single newlines) are parsed and replaced with spaces by default. This collapses multi-line Python docstrings into single wraps, ruining code example layouts and line-wrapped formatting. ## Solution Configure the `MarkdownStyle` returned by `hover_markdown_style` in `crates/editor/src/hover_popover.rs` to treat soft breaks as hard breaks (`soft_break_as_hard_break: true`). This ensures single newlines in docstrings are preserved and rendered correctly in the hover popover. ## Testing - Verified hover popovers locally on Python methods/classes to confirm that multi-line docstring indentation and alignment are preserved. ## 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 Python and Markdown docstring formatting/alignment in hover popovers. --------- Co-authored-by: Martin Ye <martin@zed.dev> |
||
|
|
6a6fcbebc4 |
Add preview to pickers and make them resizable (#59604)
Overhauls Zed's pickers to make them resizable and give them a preview. Closes #8279 ### Background The most requested Zed feature has the last year has been a [Telescope like search box](https://github.com/zed-industries/zed/issues/8279) [discussion](https://github.com/zed-industries/zed/discussions/22581). To understand why this is so popular we need to understand search can serve thee goals: - Navigation: fuzzy search is faster & easier then clicking in a file tree - Exploration: example, find a function by a word in its doc comment - Collecting: example, getting a list of functions to change The project search which shows results in a multibuffer is the perfect way to operate on a list of items. Navigation and Exploration need a lot of context around each result and offer fast navigation between them. For both of these live searching is also critical. The `telescope UI` is a picker with a preview to the right or below. It's offered in various editors and IDE's most famously Neovim (through the Telescope plugin), IntelliJ (natively), Helix (natively) and of course VScode (plugins) and it's _many_ forks. While having a UI like that for text search (our project search) is most requested the UX pattern is applied widely, from `find_all_references` to `bookmarks`. It enhances most pickers. Note that we have over 50 different picker modals! The community has tried to build something like this for Zed: - https://github.com/zed-industries/zed/pull/44530 - https://github.com/zed-industries/zed/pull/45307 - https://github.com/zed-industries/zed/pull/46478 - https://github.com/zed-industries/zed/pull/43790 These all became huge PR's that we could not merge for various reasons. This is a really hard feature to integrate in Zed! This PR got started as https://github.com/zed-industries/zed/pull/46478 and supercedes that. ### Design - Extend pickers to support an optional preview with minimal changes to the pickers themselves. - Make pickers resizable. - Complement the existing search do not replace it by having both UI's share the underlying search and allow freely switching between them. - Allow extending the preview to things other then files. - Maintain a clean design on all the pickers. ### Heigh level Implementation overview - Adds an `Option<Preview>` to `Picker` - Gives `PickerDelegate` a method to communicate a preview to the Picker - Overhaul the way pickers are drawn to allow for resizing them. Implemented on the `Shape` and `SizeBouds` structs. - Adds a high level way to draw the `footer` and `editor` so we do not need to change much to the pickers. - Adds a new text finder Picker - Adds a way to take a running search from project search and hand it to the text finder Picker and the other way round - Give the file finder a preview ### Next steps A more detailed list and how to help out will be added to the tracking issue for [Pickes with previews](https://github.com/zed-industries/zed/issues/56037) - Add more previews to more pickers! - Enable selectioning multiple items in pickers and performing actions on those - Open selected items in a multibuffer - Add a way to restore the last picker - Make popovers (picker attached to some menu) resizable as well ## 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 TODO (will be done post merge) --- Release Notes: - Added resizing via dragging to all picker modals. - Added a preview to the File finder, the preview can be to the right or below. - Added a Text finder picker with a preview as alternative project search UI. The search is shared and allowes switch between UIs while running. --------- Co-authored-by: ozacod <47009516+ozacod@users.noreply.github.com> Co-authored-by: ozacod <ozacod@users.noreply.github.com> Co-authored-by: Danilo Leal <daniloleal09@gmail.com> |
||
|
|
63c88eced0 |
workspace: Allow user to pick arbitrary parent path as a trustee (#59562)
This allows one to "bless" arbitrary parent path, without making assumptions about the structure of the project storage on users machine. ## Testing - Did you test these changes? If so, how? - Are there any parts that need more testing? - How can other people (reviewers) test your changes? Is there anything specific they need to know? - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? ## 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) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase > This section is optional. If this PR does not include a visual change or does not add a new user-facing feature, you can delete this section. - Help others understand the result of this PR by showcasing your awesome work! - If this PR includes a visual change, consider adding a screenshot, GIF, or video - A before/after comparison is very useful for changes to existing features! While a showcase should aim to be brief and digestible, you can use a toggleable section to save space on longer showcases: <details> <summary>Click to view showcase</summary> My super cool demos here </details> --- Release Notes: - When opening a project for the first time, you can now auto-trust arbitrary parent path; previously Zed offered affordance for auto-trusting a parent of a parent only. |
||
|
|
44487855ad |
editor: Add the git blame toggle to gutter context menu (#59460)
# Objective
- Add a quick way to toggle the **Column Git Blame** gutter directly
from the editor's gutter context menu, so users don't have to move the
cursor to the top-right corner or status bar to enable/disable blame
annotations.
## Solution
- Added a `toggleable_entry("Column Git Blame", ...)` item to the
existing gutter context menu in `crates/editor/src/editor.rs`.
- Selecting it calls `Editor::toggle_git_blame`, which flips
`show_git_blame_gutter` on or off, matching the behavior of the existing
toolbar/status-bar control.
- The entry appears as a toggleable item with the git blame icon,
reflects the current blame state, and reuses the existing
`toggle_git_blame` action/handler so behavior stays consistent with the
rest of the UI.
## Testing
- Added a unit test in `crates/editor/src/editor_tests.rs`
(`test_gutter_context_menu_git_blame_toggle`) that builds the gutter
context menu and verifies selecting the Column Git Blame entry toggles
`show_git_blame_gutter` on and then back off.
- Ran `cargo test -p editor --lib
test_gutter_context_menu_git_blame_toggle` — passes.
- Ran `./script/clippy -p editor` — clean.
- Are there any parts that need more testing? I think No — the change is
a straightforward menu hook into existing state.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
1. Right-click the gutter of any file in the editor.
2. Confirm **Column Git Blame** appears in the menu.
3. Click it — the blame gutter should appear.
4. Open the menu again and click it — the blame gutter should disappear.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test? Tested via unit tests on macOS.
No platform-specific code was added.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments — no unsafe blocks
- [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 — reuses
existing toggle handler, no extra work
## Showcase
> No visual redesign; just a new menu entry.
- Right-click the editor gutter to access the new **Column Git Blame**
toggle.
![Gutter context menu showing Column Git Blame option]
<img width="369" height="248" alt="image"
src="https://github.com/user-attachments/assets/7c83ad91-ae7e-4562-8faf-a17c8b92bdc1"
/>
and not toggled
<img width="409" height="389" alt="image"
src="https://github.com/user-attachments/assets/206a40c0-0933-4720-bc3e-5627786eae43"
/>
---
Release Notes:
- Added a git blame toggle to the gutter's context menu
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
|
||
|
|
76f258b504 |
Refactor editor's select inside/around delimiter actions (#59472)
# Objective Both https://github.com/zed-industries/zed/pull/59005 and https://github.com/zed-industries/zed/pull/53789 introduced actions, at the editor context, which allow to select the content between brackets/delimiters, with the latter also adding an action to include the brackets/delimiters too. Shipping both would mean we end up with three distinct, but similar actions: * `editor: select inside enclosing bracket` * `editor: select inside delimiters` * `editor: select around delimiters` As such, in order to simplify this, the changes in this Pull Request remove the first action, while updating the logic for both `editor: select inside delimiters` and `editor: select around delimiters` in order to support continuous outwards expansion, like `editor: select inside enclosing bracket` supported. ## Solution * Remove `editor::SelectInsideEnclosingBracket` action, it's related method and tests * Updated `editor::selection::Editor::select_delimiters_impl` to use `MultiBufferSnapshot::enclosing_bracket_ranges` instead of `MultiBufferSnapshot::innermost_enclosing_bracket_ranges` so we can traverse all bracket ranges and expand to the next one ## Testing Besides manual testing, a new test case was added specifically for the selection expansion support, namely `editor::editor_tests::test_select_delimiters_expansion`. ## 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 |
||
|
|
afa22f66fe |
editor: Add SelectInsideDelimiters and SelectAroundDelimiters actions (#53789)
## Summary
Adds two new editor actions:
- `editor::SelectInsideDelimiters`: selects the content inside the
nearest enclosing delimiters. Works for `()`, `[]`, `{}`, and string
literals (`""`, `''`, `` ` ` ``).
- `editor::SelectAroundDelimiters`: same as above, but the selection
includes the surrounding delimiters themselves.
This addresses the request in #50042
## Why not `select_larger_syntax_node`?
`SelectLargerSyntaxNode` walks up the AST one level at a time. It's an
incremental, history-aware operation: press repeatedly to expand outward
by syntax tree level. The number of presses needed to reach a specific
delimiter pair depends on the language's AST shape and is unpredictable:
sometimes one press, sometimes three, sometimes overshoot.
`SelectInsideDelimiters` is delimiter-based and stateless: one
invocation jumps directly to the nearest enclosing pair, regardless of
AST nodes. It's the equivalent of Vim's `vi(`, `vi{`, `vi"` text
objects, available without requiring Vim mode. The two actions solve
different problems.
## Implementation
Both actions use
`MultiBufferSnapshot::innermost_enclosing_bracket_ranges`, which already
handles nesting, multi-buffer excerpts, and language-specific bracket
definitions, including languages where quotes are declared as bracket
pairs (Rust, Python, JavaScript, JSON, etc.).
For string-like content where the language does not register quotes as
brackets (notably Markdown inline content), `SelectInsideDelimiters`
additionally falls back to a `syntax_ancestor` check for tree-sitter
node kinds `string_content` and `inline`. This is the same set of node
kinds already used in `select_larger_syntax_node`.
Multi-cursor support comes from `move_offsets_with`.
## Tests
Two new tests in `editor_tests.rs` covering:
- Basic delimiter selection (parens, brackets, braces)
- Innermost detection with nested pairs
- String content selection (TypeScript)
- Selection expansion when starting from an existing selection
- No-op when cursor is not inside any delimiter
## Notes
No default keybindings are provided. Users can bind via their keymap.
Can add defaults if reviewers prefer.
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 #50042
Release Notes:
- Added `editor::SelectInsideDelimiters` and
`editor::SelectAroundDelimiters` actions for selecting content within
the nearest enclosing brackets, braces, parentheses, or quotes.
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
|
||
|
|
2bdbd0ef18 |
editor: Reuse display-map cursors when converting word diffs (#58658)
layout_word_diff_highlights converted each visible word diff to display points via isomorphic_display_point_ranges_for_buffer_range, which walks the inlay/fold/tab/wrap/block trees from the root on every call. With many word diffs on screen this re-walks the same trees repeatedly each frame. Add a DisplayPointConverter that holds a forward-only cursor for each display-map layer (mirroring the cursor reuse already done in BlockMap::sync) so consecutive conversions amortize to near-constant work. New forward cursors BufferOffsetToInlayPointCursor and BlockPointCursor back the inlay and block layers; the fold/tab/wrap layers reuse existing cursors. Each cursor gains a cheap reset() that delegates to the underlying SumTree cursor; the converter resets its cursors if an input range starts before the previous one ended, so it stays correct for overlapping inputs (e.g. the base and buffer word diffs of an inline modified hunk). layout_word_diff_highlights now sorts the visible word diffs and feeds them through a single converter. isomorphic_display_point_ranges_for_buffer_range is reimplemented on top of the converter to keep one code path. Release Notes: - Improved scrolling performance in diff views containing large hunks |
||
|
|
a41ccd1206 |
editor: Speed up multi cursor editing (#58510)
Early draft for #32051 (multi-cursor editing is very slow, and basically hangs at high cursor counts). Opening it early like @Anthony-Eid suggested so we can agree on direction before I go further. ## Root cause Typing with a cursor on every line is mostly O(N)-per-keystroke work spread across a few places. I profiled it with `sample` and per-phase timers around `handle_input`. At 1000 cursors (~24ms before this PR): the post-edit display-map sync is ~11.8ms, the CRDT edit (`apply_local_edit`) ~3.7ms, resolving all N selections plus the per-cursor input loop ~3.5ms, the post-edit selection round-trip ~2.6ms, and `change_selections` plus transact machinery ~2.4ms. The display sync re-runs per-edit `SumTree` work through every layer even when nothing transforms (the plain-text case), and selections get re-resolved through the full display round-trip several times per keystroke. ## What this does - Display fast paths: `InlayMap::sync` (no inlays) and `WrapMap::interpolate` (no soft-wrap) skip the O(edits) transform-tree rebuild and return the passthrough snapshot, gated so a pending inlay splice or existing wraps still take the slow path. - Selection fast path: when nothing collapses buffer content (`!has_folds() && !has_replacement_blocks()`), resolve `Anchor` to `Point` to `Offset` batched and skip the per-selection display round-trip (the `todo(lw)`). - Render and autoscroll resolve only the first/last/newest selections instead of all N per frame. - A parameterized `Multi-cursor input/cursors/{1000,10000,100000}` benchmark. ## Results - Typing (`handle_input`): ~2.1x faster (24ms to 11.7ms at 1k). - Type plus two word-deletes (criterion): 29% faster (89.9 to 63.8ms) at 1k, 37% (959 to 606ms) at 10k. - Post-edit display sync alone: 11.8 to 3.8ms at 1k, 133 to 45ms at 10k. All `editor` (759), `display_map` (67), `multi_buffer` (58), and `text` (36) tests pass, and `./script/clippy` is clean. ## Direction This is ~2x, and I can get the current architecture to roughly 3.5-4.5x with a few more safe changes (hoisting the per-cursor language/editability checks, cheaper snapshot clones). Genuine VS Code numbers (~1-2us/cursor) aren't reachable while the buffer is a CRDT rope of fragments with anchor selections and a 5-layer transform stack. That would need a plain-offset cursor model and/or decoupling display layout from the edit path, which is a bigger effort I'd want to design with you. One thing I'd need your call on: `set_active_selections` sends an `UpdateSelections` collab op every keystroke (building N anchors); can that be debounced to transaction end, or do presence/follow-mode features rely on per-keystroke cursor broadcast? Release Notes: - editor: Improved multi cursor editing performance --------- Co-authored-by: Anthony Eid <anthony@zed.dev> |
||
|
|
0361eff982 |
editor: Refresh active debug line highlight on theme change (#59274)
# Objective Fixes #58736 Pause the debugger on a breakpoint, switch your theme, and the active debug line keeps its old highlight color until you step again or restart Zed. ## Solution The highlight stores a concrete color grabbed from the theme back when `go_to_active_debug_line` ran. A theme switch goes through `theme_changed`, which refreshes brackets, semantic tokens, and outline symbols but never re-applies that highlight, so it stays stale. Re-running `go_to_active_debug_line` from `theme_changed` re-resolves the color against the current theme. ## Testing Added a regression test in `debugger_ui` that stops at a debug line, swaps the theme's `editor.debugger_active_line.background`, and checks the highlight follows. It fails on `main` and passes with the fix. Also tested by hand on Windows: started a debugpy session, paused at a breakpoint, switched themes from the theme selector, and watched the active line recolor live without stepping or restarting. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [ ] 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 Paused at a breakpoint, switching themes from the theme selector. | Before | After | | --- | --- | | https://github.com/user-attachments/assets/653a7e1b-3a99-4316-b1c9-b16ed7a7a8ba | https://github.com/user-attachments/assets/22a4c876-30f2-44c5-aad9-0f860639074c | --- Release Notes: - Fixed the active debug line color not updating when switching themes while the debugger is paused ([#58736](https://github.com/zed-industries/zed/issues/58736)). --------- Co-authored-by: dino <dinojoaocosta@gmail.com> |
||
|
|
a9bba5bf71 |
editor: Show file icon in breadcrumbs when tab bar is hidden (#56267)
[Screencast_20260509_113456.webm](https://github.com/user-attachments/assets/8165246f-6266-4e23-bede-0755e8636a19) Release Notes: - Added file icons to the breadcrumbs when the tab bar is hidden Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
7d516a1a91 |
Anchor sticky scroll to symbol name rows (#56333)
## Context Sticky scroll was anchoring multiline signatures to the first outline context row instead of the row containing the symbol name, which could leave the sticky header showing unhelpful context. This change carries each outline item’s `selection_range` through the tree-sitter, LSP, multi-buffer, and editor paths, then uses that range to choose the sticky header row. This is intended to improve sticky scroll generically, not only for one language. It should help wherever symbol data distinguishes the symbol name from the larger symbol range: - tree-sitter outlines that provide a distinct `@name` capture - LSP document symbols whose `selectionRange` is more precise than `range` The actual improvement is still language-dependent, because it relies on the quality of each language’s outline query or language server symbol metadata. Closes #55587 Manual video of the test : [Screencast from 2026-05-10 14-05-46.webm](https://github.com/user-attachments/assets/40f327d3-cab2-4b85-887b-08b541a102bf) ## How to Review `crates/language/src/outline.rs`, `crates/language/src/buffer.rs`, and `crates/language/src/buffer_tests.rs`: Adds `selection_range` to tree-sitter outline items and verifies that multiline signatures can anchor sticky scroll on the symbol-name row while preserving the existing displayed outline text. `crates/project/src/lsp_store/document_symbols.rs`: Threads LSP document symbol `selection_range` into `OutlineItem`, keeping enriched labels and highlight ranges intact while preserving the distinction between the full symbol range and the selected name range. `crates/multi_buffer/src/multi_buffer.rs`, `crates/editor/src/document_symbols.rs`, `crates/editor/src/editor.rs`, `crates/outline/src/outline.rs`, and `crates/outline_panel/src/outline_panel.rs`: Propagates `selection_range` through the places that remap outline items between buffers, multi-buffers, editors, and outline views. `crates/editor/src/element.rs` and `crates/editor/src/editor_tests.rs`: Uses `selection_range` when computing sticky header rows and adds a regression test covering a multiline signature case from the issue. `crates/language/Cargo.toml` and `Cargo.lock`: Adds `tree-sitter-c` for the language-level regression test fixture. ## 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 sticky scroll anchoring to unhelpful context rows for multiline signatures when symbol metadata provides a more precise name range. |
||
|
|
c3c589168b |
editor: Fix blocks not appearing or lingering when anchored past a soft wrap (#59018)
I came across this while tackling another block map sync issue. When a block is anchored past the point where its line soft wraps, inserting or removing it invalidated the wrong range of rows. Inserting such a block alone could silently not show it, and removing it could leave the block on screen. To reproduce: ```sh SEED=113 ITERATIONS=1 cargo test -p editor test_random_blocks ``` Release Notes: - Fixed inline assistant prompt sometimes not appearing, or lingering after dismissal, when that line was soft-wrapped. |
||
|
|
d5ddf73568 |
editor: Fix splitting brackets inside line comments (#59260)
# Objective Closes #59229 When adding a newline, Zed has a bit of intelligence to do more work than just inserting a `\n`. For comment lines, it simply extends the comment: ```rust Before // This is a comment, ˇThis is another comment After // This is a comment, // ˇThis is another comment ``` And when the cursor is inside bracket pairs, Zed inserts another newline to move the closing bracket to a new line: ```rust Before fn main() {ˇ} After fn main() { ˇ } ``` However, when we have a comment line with the cursor inside a bracket pair, both mechanisms apply, resulting in an unintended state where the closing bracket is kicked out of the comment: ```rust Before // {ˇ} After // { // ˇ } ``` We definitely do not need the bracket extension rule when we are adding a newline inside comments. We want comment lines inside brackets to be split like this: ```rust // { // ˇ} ``` ## Solution - Disable Bracket Extra Newline in Comments: When a line comment delimiter is detected, we override the default `newline_config` to set `extra_line_additional_indent` to `None` . - Avoid Rule Conflicts (Short-circuiting): The three newline customization helpers (line comment, block comment, and list item) are mutually exclusive. I refactored their execution from a sequential list into an `if let Some ... else if let Some` branch. This prevents subsequent rules from having side effects on `newline_config` when a match has already occurred. ## Testing A new test named `test_newline_comments_with_brackets` is introduced in `crates/editor/src/editor_tests.rs`, All tests in the `editor` crate were verified using `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 --- Release Notes: - Fixed an issue where splitting brackets inside line comments inserted an extra line and broke the comment formatting |
||
|
|
a262fbb749 |
file_finder: Adjust list item design (#59164)
Follow up to https://github.com/zed-industries/zed/pull/59072 adjusting some UI design for the file finder that got regressed. Release Notes: - N/A |
||
|
|
70e690effd |
editor: Align split buffer headers with scrollbar layout (#53782)
Clip split buffer headers to the editor's content bounds so they no longer paint into the horizontal scrollbar area. Reuse the editor's computed right margin and horizontal scrollbar visibility instead of reimplementing scrollbar detection in split_editor_view. This keeps split headers aligned when minimap, inline blame, or scrollbar visibility change the available content area. Release Notes: - Fixed horizontal scrollbars not being rendered behind buffer headers in split editors --------- Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com> Co-authored-by: dino <dinojoaocosta@gmail.com> |
||
|
|
423d5b64b5 |
multi_buffer: Don't eagerly clone BufferSnapshot in range_to_buffer_ranges (#59190)
Both cloning and dropping of these has quite a bit of overhead (despite them being snapshots), so avoid where possible, especially in display map syncing Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
fc94e24d79 |
editor: Fix columnar selection alignment on rows with multi-byte chars (#57097)
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](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 #56748 ## Summary Block / column selection (Shift+Ctrl+drag on Linux, Shift+Cmd+drag on macOS) anchored its rectangle on a single byte column applied to every row in the dragged range. On a row whose text contained a multi-byte character before that column (for example `ã`), the same byte offset mapped to a different visual `x`, so the per-row cursor drifted off the column. `select_columns` now anchors the rectangle in `x` pixels via `x_for_display_point` and resolves each row's byte column with `display_column_for_x` — the same pair of helpers already used by vertical cursor motion and Vim VisualBlock. ## Before / After **Before** — cursor on the `ã` row drifts one column to the left: <img width="1068" height="366" alt="image" src="https://github.com/user-attachments/assets/0559ef89-852e-40f3-9d43-f5f44277f99e" /> **After** — rectangle stays visually aligned across rows: <img width="866" height="408" alt="image" src="https://github.com/user-attachments/assets/bce13c1a-1ddb-4fd6-8216-2c0e902f6668" /> ## Test plan - `cargo test -p editor test_columnar_selection_with_multibyte_chars` (new regression test covers the buggy case past the `ã` byte boundary, plus a control case where old and new logic agree). - Manual: Shift+Ctrl+drag down a five-line buffer where row 3 contains `ã` — selection rectangle stays aligned (see screenshots). - Manual: Shift+Alt+drag (`FromSelection` branch) over the same buffer — same result. - Manual: drag past EOL on the `ã` row — each row extends to its own EOL with no panic. Release Notes: - Fixed misalignment of column selection on rows that contain multi-byte characters --------- Co-authored-by: Tom Houlé <tom@tomhoule.com> |
||
|
|
d46ad937fe |
gpui: Add builder API for BoxShadow (#57743)
Replaces the verbose struct-literal construction of `gpui::BoxShadow` with a builder API: - `BoxShadow::new(offset_x, offset_y, color)` takes the required fields, with arguments ordered to match the CSS `box-shadow` property. - `.blur_radius(_)`, `.spread_radius(_)`, and `.inset()` set the optional fields. All in-tree call sites are migrated, including the Tailwind-style `shadow_xs`..`shadow_2xl` helpers in `gpui_macros`, the `ElevationIndex` shadows in `ui`, and various widgets across `editor`, `workspace`, `agent_ui`, and `ai_onboarding`. The fields on `BoxShadow` remain public, so external code using struct-literal construction continues to work. 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) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A |
||
|
|
75cb7330bd |
Fix shadow and gradient artifacts on transparent windows (#58981)
On transparent or blurred window backgrounds, several agent panel surfaces and the multibuffer header rendered drop shadows and fade gradients that have no opaque surface to blend into, so they showed up as dark halos or colored patches: - agent message boxes and the activity bar (drop shadows) - diff-hunk Reject/Keep controls (drop shadow) - the diff review pane (double-painted `editor_background`) - agent thread-list rows, the conversation title edit affordance, and sidebar project headers (fade gradient) - the multibuffer buffer header (`editor_subheader_background` + sticky shadow) These are skipped when `window_background_appearance` isn't `Opaque`. Opaque windows are unchanged; on transparent windows the elements stay delineated by their borders and truncate text with an ellipsis instead of fading. ## Screenshots Before: <img width="1920" height="1200" alt="Screenshot 2026-06-09 at 3 26 26 PM" src="https://github.com/user-attachments/assets/7dc9c76c-7de9-4c50-8719-05957d091706" /> After: <img width="1728" height="1117" alt="Screenshot 2026-06-09 at 3 26 29 PM" src="https://github.com/user-attachments/assets/889a53ad-9f11-41b9-9f51-ffcbd5c37821" /> 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 dark shadow and gradient artifacts in the agent panel and multibuffer headers when using a transparent or blurred window background |
||
|
|
915de507cd |
Add action to select inside enclosing brackets (#59005)
## Summary - add an editor action for selecting the contents of the innermost enclosing bracket pair - register the action without assigning a default key binding - cover cursor, selection, nested bracket, no-op, and multi-cursor cases ## Tests - cargo fmt --check - cargo test -p editor test_select_inside_enclosing_bracket --lib - cargo test -p editor test_move_to_enclosing_bracket --lib Release Notes: - Added action to select the contents enclosed by brackets |
||
|
|
032a1ed498 |
tab_switcher: Middle-truncate long picker filenames (#59072)
## Context This follow-up was inspired by Christopher’s suggestion in the review comment on #58483: long filenames should truncate in the middle so both the beginning and the suffix/extension remain visible. Previously, very long filenames in picker rows could still be clipped or end-truncated, making files with shared prefixes hard to distinguish. This adds middle-truncation support to GPUI text overflow and applies it to filename-focused picker surfaces: the tab switcher and the Ctrl-P file finder. Normal editor tab bar behavior remains unchanged and continues to use the existing title length cap. ## How to Review - **`crates/gpui/src/style.rs`, `crates/gpui/src/styled.rs`, `crates/gpui/src/elements/text.rs`, `crates/gpui/src/text_system/line_wrapper.rs`**: Adds `TextOverflow::TruncateMiddle`, wires it through text layout, and implements middle truncation while preserving valid text runs. Very narrow widths now show the truncation affix instead of falling back to the abruptly clipped original text. - **`crates/ui/src/components/label/label_like.rs`, `crates/ui/src/components/label/label.rs`, `crates/ui/src/components/label/highlighted_label.rs`**: Exposes middle truncation through the shared label components. - **`crates/workspace/src/item.rs`, `crates/editor/src/items.rs`, `crates/workspace/src/pane.rs`, `crates/tab_switcher/src/tab_switcher.rs`**: Adds an explicit tab-content opt-in for middle truncation. The tab switcher enables it, while the normal tab bar and dragged tabs keep their existing behavior. - **`crates/file_finder/src/file_finder.rs`**: Applies middle truncation to Ctrl-P file finder filenames and lets the path shrink from the start so long paths do not hide important filename suffixes. Manual test before change : [Screencast from 2026-06-10 22-59-28.webm](https://github.com/user-attachments/assets/5e032646-a48c-45f2-8fe2-0424507fd576) Manual test after change : [Screencast from 2026-06-10 22-54-48.webm](https://github.com/user-attachments/assets/4b9253da-f169-4ed1-bdd5-ee71dcf49a83) ## 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: - Improved long filename truncation in the tab switcher and file finder so extensions remain visible. |
||
|
|
bf09844984 |
editor: Don't treat scheme-prefixed text as a URL when pasting in Markdown (#59071)
Pasting text that merely starts with a scheme-like prefix (for example a commit message like `editor: Fix crash in project panel`) over a selection in a Markdown buffer would wrap the selection in a Markdown link, because `url::Url::parse` accepts `editor:` as a valid URL scheme. Paste now only treats clipboard text as a URL when the whole text is a single standalone URL, using `linkify` (already a workspace dependency) instead of `url::Url::parse`. Added a regression test covering the scheme-like prefix case. 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 Closes #59070 Release Notes: - Fixed pasting text starting with a scheme-like prefix (such as `editor: ...`) over a selection in a Markdown buffer incorrectly creating a Markdown link. |
||
|
|
38a688b921 |
editor: Expand the gutter width to contain new timestamp format (#59008)
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 Closes #58922 In #57973, a new timestamp format was introduced. For example, for a commit from 13 months ago, the old formatter would output `"1 year ago"`, whereas the new format outputs `"1 year, 1 month ago"`. This new compound format is significantly longer than `"60 minutes ago"`, which is currently used to calculate the maximum width of the blame column here: https://github.com/zed-industries/zed/blob/568abf04f349080d333e8028ce9de432995dc8ab/crates/editor/src/editor.rs#L11376-L11391 As a result, when a blame entry uses the `"{M} years, {N} months ago"` format, its width exceeds the pre-calculated maximum, causing the text to overlap with the line numbers. This PR updates the placeholder string used for the maximum width calculation to `"2 years, 11 months ago"`, ensuring the column is wide enough to accommodate the longest possible compound timestamp. Before: <img width="1407" height="598" alt="before" src="https://github.com/user-attachments/assets/f025f423-9ccc-432d-813c-4b861227a7a1" /> After: <img width="1481" height="594" alt="after" src="https://github.com/user-attachments/assets/c60cbd17-fdd4-471c-a4fc-3c2e7a3ccd5d" /> Release Notes: - Fixed an issue where Git blame text would overlap with line numbers in the gutter. |
||
|
|
6d9ddb96b2 |
Bench app context phase 2 (#58202)
This PR builds out GPUI's benchmark harness so render benchmarks measure
realistic frame costs using GPUI-owned, runtime-gated instrumentation.
It adds measurements for frame draw time, dirty-to-draw latency,
invalidation coalescing, and frame-budget overruns, and runs benchmark
workloads with production-like concurrency.
## How it works
**Frame timings flow through the GPUI profiler.** `Window::draw` emits a
`FrameTiming { window_id, dirty_at, invalidations, draw_start, draw_end
}` event into a global ring buffer in `gpui::profiler`, mirroring the
existing task-timing channel. Collection is runtime-gated by
`profiler::set_frame_trace_enabled` (one relaxed atomic load when
disabled — no `Instant::now` calls in production). `BenchReport` is a
pure listener: it drains events through a cursor-based
`FrameTimingCollector` and builds histograms in the bench layer.
`Window` carries no bench-only cfg fields, and the same event channel
can later feed the miniprofiler UI or an in-app frame-time HUD.
**`BenchDispatcher`** is a multithreaded `PlatformDispatcher` for
benchmarks: background tasks run on a worker pool (same priority queue
as `LinuxDispatcher`, with task-profiler hooks), timers fire in real
time on a dedicated thread, and foreground tasks queue until the bench
thread drains them with a blocking `run_until_idle()`. Unlike
`TestDispatcher`, work executes in parallel in real time, so wall-clock
measurements reflect production concurrency. In-flight accounting is
panic-safe via drop guards.
**`gpui::bench_platform()`** returns a per-process `TestPlatform` backed
by the `BenchDispatcher`, cached in a thread-local so worker threads
persist across Criterion calibration passes. This replaces the earlier
approach of constructing a real platform per invocation, which had
process-global singleton issues, never ran foreground tasks (no run loop
pumped the main queue), and couldn't open windows on headless CI.
**Text shaping** uses `NoopTextSystem`: deterministic across
machines/font installations and CI-portable. Measured cost of this
trade: ~10% of editor draw time vs `MacTextSystem` (Noop still emits one
glyph per character at fixed advances, so downstream layout/paint
structure is preserved).
**GPU coverage (macOS only for now).** `PlatformHeadlessRenderer` gained
`render_scene`, which encodes and submits the scene to Metal against a
cached offscreen target without blocking on completion or reading pixels
back — matching production `present()` CPU cost (`render_scene_to_image`
would overstate it: it waits for the GPU and copies pixels back).
`TestWindow::draw` forwards scenes to the renderer, the real
`MetalAtlas` means glyph/SVG rasterization happens during paint, and
`bench_renderer` presents after each measured update. Platforms without
a headless renderer degrade to discarding the scene.
## Example
```rust
#[gpui::bench]
fn editor_render(cx: &mut BenchAppContext) {
init_context(cx);
let buffer = cx.update(|cx| { /* build a MultiBuffer */ });
let mut window = cx.add_empty_window();
let editor = window.update(|window, cx| {
let editor = window.replace_root(cx, |window, cx| {
let mut editor = Editor::new(EditorMode::full(), buffer, None, window, cx);
editor.set_style(editor::EditorStyle::default(), window, cx);
editor
});
window.focus(&editor.focus_handle(cx), cx);
editor
});
let mut move_down = true;
cx.bench_renderer(editor, move |editor, window, cx| {
if move_down {
editor.move_down(&MoveDown, window, cx);
} else {
editor.move_up(&MoveUp, window, cx);
}
move_down = !move_down;
});
}
```
## Example output (release, M-series)
```
editor_render time: [329.75 µs 330.17 µs 330.69 µs]
GPUI bench report (all observed iterations): editor_render
note: includes Criterion warmup/calibration
window dirty-to-draw:
samples: 31533
mean: 0.321ms
p50: 0.322ms
p90: 0.336ms
p95: 0.342ms
p99: 0.360ms
max: 0.504ms
frame budget overruns total: 0
frame budget overruns max: 0
window draw:
samples: 31533
mean: 0.295ms
p50: 0.295ms
p90: 0.307ms
p95: 0.313ms
p99: 0.330ms
max: 0.455ms
frame budget overruns total: 0
frame budget overruns max: 0
invalidations per frame: mean 5.00, max 5
```
(`invalidations per frame: mean 5.00` is real signal: each `move_down`
notifies the window five times before the draw.)
## Known limitations
- **Draw-per-flush**: the harness draws synchronously when effects flush
rather than coalescing invalidations to a vsync tick, so `dirty-to-draw`
excludes queueing delay, and `frame budget overruns` is a draw-time
budget proxy rather than actual missed presents. A frame-paced mode is
natural follow-up work.
- **GPU submission is measured on macOS only**; other platforms have no
headless renderer yet.
- The GPUI report includes Criterion warmup/calibration samples (noted
in the output); Criterion's `time` is the regression-gating number.
- `run_until_idle` waits for queued, running, and already-due work, but
not for timers that haven't reached their due time — the dispatcher runs
in real time and can't skip ahead like `TestDispatcher`'s virtual clock.
## Future work
- A vsync-like frame-pacing mode (suppress draw-on-flush; tick-driven
draw + present) so dirty-to-draw captures queueing delay
- Record present duration in `FrameTiming` so the report can split draw
vs present
- Benches that scroll through novel content (cold layout caches) and an
agent-panel render bench
- Headless renderers for Windows/Linux
- Move benches into a dedicated crate
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
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
|
||
|
|
ba93284c6a |
Fix miscellaneous typos (#58979)
Some typos I found while reading the code 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 |
||
|
|
7a469725d3 |
Refactor BufferDiff to allow multiple diffs to share the same base text buffer (#58266)
This PR changes how base texts are managed by the `buffer_diff` crate, to enable keeping two diff entities alive that share the same base text buffer entity. Previously, each diff owned its own base text buffer and edited it when calling `BufferDiff::set_snapshot`, so the only way to reuse the same base text between two diffs was to have two independent buffers for it, which is pretty inefficient. After this PR, each diff still has a base text buffer, but `set_snapshot` doesn't edit it. Instead, that responsibility moves into the caller. For updating the base text buffer, this PR also introduces a new pair of APIs, `Buffer::snapshot_with_edits` and `Buffer::fast_forward`, which allow us to move the parsing of the new base text into the background and then install the new syntax tree synchronously on the foreground. The git store uses the low-level APIs `set_snapshot` and `fast_forward` directly, and manages the head text and index text buffers itself (garbage-collecting them when they're no longer needed); this enables adding an `open_staged_diff` API which returns a diff between the managed index buffer and the managed head buffer (the latter is also used for the uncommitted diff's base text). Other downstreams don't need to reuse a base text buffer, and those have been migrated to use the high-level `set_base_text` API, which now calls `set_snapshot` and `fast_forward` internally, with a guard to prevent concurrent updates. Another change worthy of note is that we now always diff the old base text with the new base text to create `snapshot_with_edits`. There are also some incidental bug fixes: - Fixed an issue where a dangling weak unstaged diff could stick around in the git store forever - Restored the `IndexMatchesHead` optimization that had become inoperative in the remote case - Fixed a crash in the multibuffer due to the handling of `BufferDiffEvent::LanguageChanged`, which could cause the multibuffer to have transforms that were inconsistent with the diff base text. 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 ZED-81P Release Notes: - Fixed a rare crash that could occur while using the uncommitted diff. --------- Co-authored-by: Ben Kunkle <ben@zed.dev> Co-authored-by: Anthony Eid <hello@anthonyeid.me> |
||
|
|
714a12f478 |
editor: Register toggle inline values action (#58921)
The `ToggleInlineValues` action was added when inline value hints were introduced, but the action was never registered, so it was not dispatchable: it never appeared in the command palette and any keybinding bound to it had no effect. Follow-up to #28656. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [ ] 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 Closes #58890. Release Notes: - Fixed “editor: toggle inline values” not appearing in the command palette or responding to keybindings |
||
|
|
4405738fab |
editor: Show format selections for cursor ranges (#56571)
Resolves https://github.com/zed-industries/zed/issues/56556 This diff fixes format selections being unavailable when the selection is a cursor range. The action was gated on having at least one non-empty selection, which hid it even when the active formatter supported range formatting. With this change, cursor ranges are included when checking whether the selected buffers support range formatting. This restores the cursor-based `Format Selections` behavior while preserving the existing formatter-capability gate, so the action still stays hidden for formatters that cannot format ranges. https://github.com/user-attachments/assets/fc2ab2b7-1af3-4849-841a-b57a685d748f Release Notes: - Fixed format selections not being available for cursor ranges when the active formatter supports range formatting. Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com> |