Compare commits

...

665 Commits

Author SHA1 Message Date
Anthony Eid
1143de1d06 More shredding 2025-12-12 16:49:17 -05:00
Anthony Eid
99e7aef145 Fix bug where git graph would load from wrong starting point 2025-12-12 16:45:32 -05:00
Anthony Eid
80956e2037 Fix loading delay 2025-12-12 05:02:10 -05:00
Anthony Eid
391f6f1b04 Start work on fetching commit chunks instead of all at once
Co-authored-by: Cole Miller <cole@zed.dev>
2025-12-11 12:28:16 -05:00
Anthony Eid
38bb2ba7da Limit max count of commits 2025-12-11 06:02:07 -05:00
Anthony Eid
4303e8e781 WIP 2025-12-11 05:56:06 -05:00
Anthony Eid
ed1dd89c44 WIP get git graph to be drawn with one canvas draw 2025-12-11 05:39:01 -05:00
Anthony Eid
94526ad28c Use accent colors for git graph 2025-12-11 04:06:40 -05:00
Anthony Eid
ee4cd4d27a More shredding 2025-12-11 03:46:17 -05:00
Anthony Eid
74df6d7db3 More shreding 2025-12-11 03:41:25 -05:00
Anthony Eid
5080697b9b Make git graph a serializable item 2025-12-11 03:39:18 -05:00
Anthony Eid
92affa2bf2 More shreding 2025-12-11 03:15:13 -05:00
Anthony Eid
7479942fd2 More shreddddd 2025-12-11 03:09:26 -05:00
Anthony Eid
8e6f2f5d97 Start the mega shred mhahahahah 2025-12-11 02:41:51 -05:00
Anthony Eid
d74612ce24 UI code clean up 2025-12-11 02:15:49 -05:00
Anthony Eid
ce0f5259bc Remove some tests 2025-12-11 01:58:40 -05:00
Anthony Eid
8e78337ec9 Remove warnings 2025-12-11 01:52:51 -05:00
Anthony Eid
62bcaf41ee Fix bug where initial commit wouldn't be drawned correctly 2025-12-11 01:50:29 -05:00
Anthony Eid
3bb908ce5d Reset files to match original PR
Co-authored-by: pyundev <pyundev@users.noreply.github.com>
2025-12-11 01:44:18 -05:00
Anthony Eid
f86476a480 Remove more unused code 2025-12-11 01:24:57 -05:00
Anthony Eid
a686fc106a Fix wrong commits being filtered out 2025-12-11 01:23:07 -05:00
Anthony Eid
700d3cabac Hook up different backend to git graph ui 2025-12-11 01:20:17 -05:00
Anthony Eid
e8807aaa58 WIP 2025-12-09 14:41:39 -05:00
Anthony Eid
84b787ff32 Take base git graph UI from PR: 44405
https://github.com/zed-industries/zed/pull/44405

Co-authored-by: pyundev <pyundev@users.noreply.github.com>
2025-12-08 17:30:14 -05:00
Anthony Eid
ed6165f450 Have opus generate the init graph building code based off of gitamine 2025-12-08 16:59:46 -05:00
Anthony Eid
efc5c93d9c Initial git graph crate setup 2025-12-08 15:58:46 -05:00
ᴀᴍᴛᴏᴀᴇʀ
b948d8b9e7 git: Improve self-hosted provider support and Bitbucket integration (#42343)
This PR includes several minor modifications and improvements related to
Git hosting providers, covering the following areas:

1. Bitbucket Owner Parsing Fix: Remove the common `scm` prefix from the
remote URL of self-hosted Bitbucket instances to prevent incorrect owner
parsing.
[Reference](a6e3c6fbb2/src/git/remotes/bitbucket-server.ts (L72-L74))
2. Bitbucket Avatars in Blame: Add support for displaying Bitbucket
avatars in the Git blame view.
<img width="2750" height="1994" alt="CleanShot 2025-11-10 at 20 34
40@2x"
src="https://github.com/user-attachments/assets/9e26abdf-7880-4085-b636-a1f99ebeeb97"
/>
3. Self-hosted SourceHut Support: Add support for self-hosted SourceHut
instances.
4. Configuration: Add recently introduced self-hosted Git providers
(Gitea, Forgejo, and SourceHut) to the `git_hosting_providers` setting
option.
<img width="2750" height="1994" alt="CleanShot 2025-11-10 at 20 33
48@2x"
src="https://github.com/user-attachments/assets/44ffc799-182d-4145-9b89-e509bbc08843"
/>


Closes #11043

Release Notes:

- Improved self-hosted git provider support and Bitbucket integration
2025-12-08 13:32:14 -05:00
Floyd Wang
bc17491527 gpui: Revert grid template columns default behavior to align with Tailwind (#44368)
When using the latest version of `GPUI`, I found some grid layout
issues. I discovered #43555 modified the default behavior of grid
template columns. I checked the implementation at
https://tailwindcss.com/docs/grid-template-columns, and it seems our
previous implementation was correct.

If a grid layout is placed inside a flexbox, the layout becomes
unpredictable.

```rust
impl Render for HelloWorld {
    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .flex()
            .size(px(500.0))
            .bg(rgb(0x505050))
            .text_xl()
            .text_color(rgb(0xffffff))
            .child(
                div()
                    .size_full()
                    .gap_1()
                    .grid()
                    .grid_cols(2)
                    .border_1()
                    .border_color(gpui::red())
                    .children((0..10).map(|ix| {
                        div()
                            .w_full()
                            .border_1()
                            .border_color(gpui::green())
                            .child(ix.to_string())
                    })),
            )
    }
}
```

| Before | After |
| - | - |
| <img width="612" height="644" alt="After1"
src="https://github.com/user-attachments/assets/64eaf949-0f38-4f0b-aae7-6637f8f40038"
/> | <img width="612" height="644" alt="Before1"
src="https://github.com/user-attachments/assets/561a508d-29ea-4fd2-bd1e-909ad14b9ee3"
/> |

I also placed the grid layout example inside a flexbox too.

| Before | After |
| - | - |
| <img width="612" height="644" alt="After"
src="https://github.com/user-attachments/assets/fa6f4a2d-21d8-413e-8b66-7bd073e05f87"
/> | <img width="612" height="644" alt="Before"
src="https://github.com/user-attachments/assets/9e0783d1-18e9-470d-b913-0dbe4ba88835"
/> |

I tested the changes from the previous PR, and it seems that setting the
table's parent to `v_flex` is sufficient to achieve a non-full table
width without modifying the grid layout. This was already done in the
previous PR.

I reverted the grid changes, the blue border represents the table width.
cc @RemcoSmitsDev

<img width="1107" height="1000" alt="table"
src="https://github.com/user-attachments/assets/4b7ba2a2-a66a-444d-ad42-d80bc9057cce"
/>

So, I believe we should revert to this implementation to align with
tailwindcss behavior and avoid potential future problems, especially
since the cause of this issue is difficult to pinpoint.

Release Notes:

- N/A
2025-12-08 17:38:10 +01:00
ozzy
f6a6630171 agent_ui: Auto-capture file context on paste (#42982)
Closes #42972


https://github.com/user-attachments/assets/98f2d3dc-5682-4670-b636-fa8ea2495c69

Release Notes:

- Added automatic file context detection when pasting code into the AI
agent panel. Pasted code now displays as collapsible badges showing the
file path and line numbers (e.g., "app/layout.tsx (18-25)").

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-12-08 17:32:04 +01:00
Cameron Mcloughlin
18421845eb nix: Fix nix build failure due to missing protoc (#44412) 2025-12-08 16:08:36 +00:00
Martin Bergo
21439426a0 Add support for Grok 4.1 Fast models in xAI provider (#43419)
Release Notes:

- Added support for Grok 4.1 Fast (reasoning and non-reasoning) models
in the xAI provider, with 2M token context windows and full vision
capabilities.
- Extended 2M token context to existing Grok 4 Fast variants (from 128K)
for consistency with xAI updates.
- Enabled image/vision support for all Grok 4 family models.

Doc:
https://docs.x.ai/docs/models/grok-4-1-fast-reasoning
https://docs.x.ai/docs/models/grok-4-1-fast-non-reasoning
2025-12-08 16:38:34 +01:00
Smit Barmase
044f7b5583 editor: Fix buffer fold focuses first buffer in multi-buffer instead of the toggled one (#44394)
Closes #43870

Regressed in https://github.com/zed-industries/zed/pull/37953

Release Notes:

- Fixed issue where toggling buffer fold focuses first buffer in
multi-buffer instead of the toggled one.
2025-12-08 21:00:00 +05:30
Danilo Leal
12dba5edbe git_ui: Improve the branch picker when in the panel (#44408)
This PR adapts the design for the panel version of the branch picker.


https://github.com/user-attachments/assets/d04b612b-72e8-4bc9-9a19-9d466b9fe696

Release Notes:

- N/A
2025-12-08 15:20:58 +00:00
Lukas Wirth
216a3a60f5 keymap: Fix windows keys for open and open files actions (#44406)
Closes https://github.com/zed-industries/zed/issues/44040
Closes https://github.com/zed-industries/zed/issues/44044
 
Release Notes:

- Fixed incorrect keybindings for `open folder` and `open files` actions
on windows' default keymap
2025-12-08 15:08:44 +00:00
Cameron Mcloughlin
66789607c9 editor: Goto references skip multibuffer if single match (#43026)
Co-authored-by: Agus <agus@zed.dev>
2025-12-08 14:38:19 +00:00
Lee Nussbaum
62c312b35f Update Recent Projects picker to show SSH host (#44349)
**Problem addressed:**

Cannot distinguish an identical path on multiple remote hosts in the
"Recent Projects" picker.

**Related work:**

- Issue #40358 (already closed) identified the issue in the "Expected
Behavior" section for both WSL and SSH remotes.
- PR #40375 implemented WSL distro labels.

**Screenshots:**

Before:
<img width="485" height="98" alt="screenshot-sample-project-before"
src="https://github.com/user-attachments/assets/182d907a-6f11-44aa-8ca5-8114f5551c93"
/>

After:
<img width="481" height="94" alt="screenshot-sample-project-after"
src="https://github.com/user-attachments/assets/5f87ff12-6577-404e-9319-717f84b7d2e7"
/>

**Implementation notes:**

RemoteConnectionOptions::display_name() will be subject to
exhaustiveness checking on RemoteConnectionOption variants.

Keeps the same UI approach as the WSL distro variants.

Release Notes:

- Improved Recent Projects picker: now displays SSH hostname with
remotes.
2025-12-08 11:07:03 -03:00
Ben Brandt
066dd5c9d5 acp: Fix download path for Codex on ARM Windows (#44395)
Both windows paths use .zip, not .tar.gz

Closes #44378

Release Notes:

- acp: Fix codex-acp download path for ARM Windows targets
2025-12-08 14:06:06 +00:00
Lukas Wirth
bdba6fd069 remote(wsl): Make shell and platform discovery more resilient to shell scripts (#44363)
Companion PR to https://github.com/zed-industries/zed/pull/44165

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-08 15:05:50 +01:00
Remco Smits
2260b87ea8 agent_ui: Fix show markdown list checked state (#43567)
Closes #37527

This PR adds support for showing the list state of a list item inside
the agent UI.

**Before**
<img width="643" height="505" alt="Screenshot 2025-11-26 at 16 21 31"
src="https://github.com/user-attachments/assets/30c78022-4096-4fe4-a6cc-db208d03900f"
/>

**After**
<img width="640" height="503" alt="Screenshot 2025-11-26 at 16 41 32"
src="https://github.com/user-attachments/assets/ece14172-79a5-4d5e-a577-4b87db04280f"
/>

Release Notes:
- Agent UI now show the checked state of a list item

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-12-08 14:04:49 +00:00
Kian Kasad
e6214b2b71 extensions: Don't recompile Tree-sitter parsers when up-to-date (#43442)
When installing local "dev" extensions that provide tree-sitter
grammars, compiling the parser can be quite intensive[^anecdote]. This
PR changes the logic to only compile the parser if the WASM object
doesn't exist or the source files are newer than the object (just like
`make(1)` would do).

[^anecdote]: The tree-sitter parser for LLVM IR takes >10 minutes to
compile and uses 20 GB of memory on my laptop.

Release Notes:

- N/A

---------

Co-authored-by: Finn Evers <finn.evers@outlook.de>
2025-12-08 13:48:04 +00:00
Ben Brandt
7ef45914e8 editor: Fix link navigation within editors that don't have a workspace (#44389)
This mostly affects Channel Notes, but due to a change in
https://github.com/zed-industries/zed/pull/43921 we were
short-circuiting before opening links.

I moved the workspace checks back to right before we need them so that
we still follow the same control flow as usual for these editors.

Closes #44207

Release Notes:

- N/A
2025-12-08 13:36:39 +00:00
Mustaque Ahmed
00e6cbc4fc git_ui: Fix tooltip overlaying context menu in git blame (#42764)
Closes #26949


## Summary

1. Split editor references to avoid borrow conflicts in event handlers
2. Check
[has_mouse_context_menu()](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html)
state directly in tooltip conditional instead of caching stale value
3. Restructured context menu deployment to ensure proper sequencing:
hide popover → build menu → deploy menu → notify for re-render

**Screen recording**



https://github.com/user-attachments/assets/8a00f882-1c54-47b0-9211-4f28f8deb867



Release Notes:

- Fixed an issue where the context menu in the Git Blame view would be
frequently overlapped by the commit information tooltip.

---------

Co-authored-by: Finn Evers <finn.evers@outlook.de>
2025-12-08 14:32:06 +01:00
Finn Evers
9e0a4c2a9c terminal_view: Fix casing of popover menu entry (#44377)
This ensures that the casing of this entry aligns with other entries in
the app popover Menus.

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2025-12-08 13:19:54 +00:00
Danilo Leal
a8b61c5ffa ui: Remove support for unnecessary Switch colors (#44388)
This PR removes the default, error, warning, and success color variants
from the `SwitchColor` enum. In the most YAGNI spirit, I think we'll
probably never want to use these colors for the switch, so there's no
reason to support them. And if we ever want to do it, we can re-add
them.

I also took the opportunity to change the default color to be "accent",
which is _already_ what we use for all instances of this component, so
there's no need to have to define it every time. This effectively makes
the enum support only "accent" and "custom", which I think is okay for
now if we ever need an escape hatch before committing to supporting new
values.

Release Notes:

- N/A
2025-12-08 13:16:19 +00:00
Oleksiy Syvokon
d312d59ace Add zeta distill command (#44369)
This PR partially implements a knowledge distillation data pipeline.

`zeta distill` gets a dataset of chronologically ordered commits and
generates synthetic predictions with a teacher model (one-shot Claude
Sonnet).

`zeta distill --batches cache.db` will enable Message Batches API. Under
the first run, this command will collect all LLM requests and upload a
batch of them to Anthropic. On subsequent runs, it will check the batch
status. If ready, it will download the result and put them into the
local cache.


Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-12-08 15:13:22 +02:00
Thomas Wood
b4083ec47b Fix typo in prompt.md (#44326)
"too calls" → "tool calls"

Release Notes:

- N/A
2025-12-08 10:08:15 -03:00
Smit Barmase
e75a7aecd6 languages: Fix Markdown list items are automatically indented erroneously (#44381)
Closes #44223

Regressed in https://github.com/zed-industries/zed/pull/40794 in attempt
to fix https://github.com/zed-industries/zed/issues/40757. This PR
handles both cases and add more tests around it.

Bug is only in Nightly.

Release Notes:

- N/A
2025-12-08 17:49:17 +05:30
Ben Brandt
03cf7ddb53 acp: Update to agent-client-protocol rust sdk v0.9.0 (#44373)
Release Notes:

- N/A
2025-12-08 11:11:05 +00:00
Clay Tercek
9364d39487 Improve TS/TSX/JS syntax highlighting for parameters, types, and punctuation (#43437)
This pull request enhances syntax highlighting for JavaScript,
TypeScript, TSX, and JSDoc by adding more precise rules for parameters,
types, and punctuation.

- Added queries for highlighting parameters (`@variable.parameter`)
- Expanded highlighting for type identifiers, type aliases, interfaces,
classes
- Extended/implemented types to improve distinction between different
type constructs (`@type`, `@type.class`)
- Added highlighting for punctuation in type parameters, unions,
intersections, annotations, index signatures, optional fields, and
predicates (`@punctuation.special`, `@punctuation.bracket`)
- Added highlighting for identifiers in JSDoc comments
(`@variable.jsdoc`)

Release Notes:

- Refined syntax highlighting in JavaScript and TypeScript for better
visual distinction of
  types, parameters, and JSDoc elements
2025-12-08 12:00:38 +01:00
Lukas Wirth
f16913400a title_bar: Fix clicking collaborators on windows not starting a follow (#44364)
Release Notes:

- Fixed left click not allowing to follow in collab title bar on windows
2025-12-08 09:36:55 +00:00
Rémi Kalbe
5bfc0baa4c macos: Reset exception ports for shell-spawned processes (#44193)
## Summary

Follow-up to #40716. This applies the same `reset_exception_ports()` fix
to `set_pre_exec_to_start_new_session()`, which is used by shell
environment capture, terminal spawning, and DAP transport.

### Root Cause

After more debugging, I finally figured out what was causing the issue
on my machine. Here's what was happening:

1. Zed spawns a login shell (zsh) to capture environment variables
2. A pipe is created: reader in Zed, writer mapped to fd 0 in zsh
3. zsh sources `.zshrc` → loads oh-my-zsh → runs poetry plugin
4. Poetry plugin runs `poetry completions zsh &|` in background
5. Poetry inherits fd 0 (the pipe's write end) from zsh
6. zsh finishes `zed --printenv` and exits
7. Poetry still holds fd 0 open
8. Zed's `reader.read_to_end()` blocks waiting for all writers to close
9. Poetry hangs (likely due to inherited crash handler exception ports
interfering with its normal operation)
10. Pipe stays open → Zed stuck → no more processes spawn (including
LSPs)

I confirmed this by killing the hanging `poetry` process, which
immediately unblocked Zed and allowed LSPs to start. However, this
workaround was needed every time I started Zed.

While poetry was the culprit in my case, this can affect any shell
configuration that spawns background processes during initialization
(oh-my-zsh plugins, direnv, asdf, nvm, etc.).

Fixes #36754

## Test plan

- [x] Build with `ZED_GENERATE_MINIDUMPS=true` to force crash handler
initialization
- [x] Verify crash handler logs appear ("spawning crash handler
process", "crash handler registered")
- [x] Confirm LSPs start correctly with shell plugins that spawn
background processes

Release Notes:

- Fixed an issue on macOS where LSPs could fail to start when shell
plugins spawn background processes during environment capture.
2025-12-08 09:26:35 +01:00
Jake Go
d7b99a5b12 gpui: Fix new window cascade positioning (#44358)
Closes [#44354](https://github.com/zed-industries/zed/discussions/44354)

Release Notes:
- Fixed new windows to properly cascade from the active window instead
of opening at the exact same position
2025-12-08 09:11:58 +01:00
Cole Miller
7691cf341c Fix selections when opening excerpt with an existing buffer that has expanded diff hunks (#44360)
Release Notes:

- N/A
2025-12-08 06:12:10 +00:00
Anthony Eid
63cc90cd2c debugger: Fix stack frame filter not persisting between sessions (#44352)
This bug was caused by using two separate keys for writing/reading from
the KVP database. The bug didn't show up in my debug build because there
was an old entry of a valid key.

I added an integration test for this feature to prevent future
regressions as well.

Release Notes:

- debugger: Fix a bug where the stack frame filter state wouldn't
persist between sessions
2025-12-08 01:43:30 +00:00
Remco Smits
d1e45e27de debugger: Fix UI would not update when you select the Current State option (#44340)
This PR fixes that the `Current State` option inside the history
dropdown does not updating the UI. This was because we didn't send the
`SessionEvent::HistoricSnapshotSelected` event in the reset case. This
was just a mistake.

**After**


https://github.com/user-attachments/assets/6df5f990-fd66-4c6b-9633-f85b422fb95a

cc @Anthony-Eid

Release Notes:

- N/A
2025-12-07 16:59:38 -05:00
Kunall Banerjee
9da0d40694 docs: Point to the right URL for Regal LSP (#44318)
Release Notes:

- N/A
2025-12-07 05:29:58 +00:00
Kunall Banerjee
9f344f093e docs: Point to the right URL for Astro LSP (#44314)
The original URL points to a deprecated repo.

Release Notes:

- N/A
2025-12-06 19:14:13 -05:00
Remco Smits
ef76f07b1e debugger: Make historic snapshot button a dropdown menu (#44307)
This allows users to select any snapshot in the debugger history feature
and go back to the active session snapshot.

We also change variable names to use hsitoric snapshot instead of
history and move the snapshot icon to the back of the debugger top
control strip.


https://github.com/user-attachments/assets/805de8d0-30c1-4719-8af7-2d47e1df1da4

Release Notes:

- N/A

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-12-06 21:08:33 +00:00
Remco Smits
4577e1bf8f debugger: Get stack frame list working with historic snapshot feature (#44303)
This PR fixes an issue where the stack frame list would not update when
viewing a historic snapshot.
We now also show the right active debug line based on the currently
selected history.


https://github.com/user-attachments/assets/baccd078-23ed-4db3-9959-f83dc2be8309

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-12-06 15:34:19 -05:00
Remco Smits
a574ae8779 debugger: Start work on adding session snapshot feature (#44298)
This PR adds the basic logic for a feature that allows you to visit any
stopped information back in time. We will follow up with PRs to improve
this and actually add UI for it so the UX is better.


https://github.com/user-attachments/assets/42d8a5b3-8ab8-471a-bdd0-f579662eadd6


Edit Anthony:

We feature flagged this so external users won't be able to access this
until the feature is polished

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-12-06 19:31:08 +00:00
Kirill Bulatov
16666f5357 Use single languages::{rust_lang, markdown_lang} in tests across the codebase (#44282)
This allows referencing proper queries and keeping the tests up-to-date.

Release Notes:

- N/A
2025-12-06 18:49:21 +00:00
Ben Kunkle
b2e35b5f99 zlog: Fix dynamic mod path filtering (#44296)
Closes #ISSUE

Release Notes:

- Linux: cleaned up noisy logs from `zbus`
2025-12-06 17:56:49 +00:00
John Tur
9e33243015 Fix unregistration logic for pull diagnostics (#44294)
Even if `workspace_diagnostics_refresh_tasks` is empty, registrations
which didn't advertise support for workspace diagnostics may still
exist.

Release Notes:

- N/A
2025-12-06 11:31:05 -05:00
Danilo Leal
a0848daab4 agent ui: Fix clicks on the notification sometimes not being triggered (#44280)
Closes https://github.com/zed-industries/zed/issues/43292

We were seeing clicks on the "View Panel" and "Dismiss" buttons
sometimes not being triggered. I believe this was happening because the
overall parent also had an on_click, which due to this being a popup
window, was causing conflicts with the buttons' on click handlers. This
should hopefully fix that issue.

Release Notes:

- agent: Fixed an issue where clicking on the agent notification buttons
would sometimes not trigger their actions.
2025-12-06 12:43:37 +00:00
David Kleingeld
d72746773f Put tracy dependency behind feature tracy (#44277)
It broke CI, now it no longer does 🎉 Proper fix followes after the
weekend.

Release Notes:

- N/A
2025-12-06 14:08:01 +02:00
Danilo Leal
0565992d7a project picker: Improve tooltip on secondary actions (#44264)
This PR adds the keybinding for the "open in project window" button on
the project picker as well as makes the tooltip for the content bit on
the active list item only show up for the content container.


https://github.com/user-attachments/assets/42944cf7-e4e7-4bf8-8695-48df8b3a35eb


Release Notes:

- N/A
2025-12-06 09:06:51 -03:00
Danilo Leal
e1d8c1a6a1 Improve visual alignment on the inline assistant (#44265)
Just making all of the elements in the inline assistant more vertically
centered.

<img width="500" height="1938" alt="Screenshot 2025-12-06 at 12  02@2x"
src="https://github.com/user-attachments/assets/7f9627ac-4f2d-4f93-9a7e-31c5a01c32d1"
/>

Release Notes:

- N/A
2025-12-06 09:06:43 -03:00
Agus Zubiaga
f08fd732a7 Add experimental mercury edit prediction provider (#44256)
Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-12-06 10:08:44 +00:00
Haojian Wu
51b7d06a27 Fix a typo: to -> two (#44272)
Release Notes:

- N/A
2025-12-06 09:35:18 +02:00
Cole Miller
66c7bdf037 git: For conflicted files, set project diff excerpts using conflicts only (#44263)
It's just distracting having excerpts for all the successfully merged
hunks.

Release Notes:

- git: The project diff now focuses on merge conflicts for files that
have them.
2025-12-06 02:20:14 +00:00
Cole Miller
363fbbf0d4 git: Fix excerpt ranges in the commit view (#44261)
Release Notes:

- N/A
2025-12-06 02:05:34 +00:00
Serophots
9860884217 gpui: Make length helpers into const functions (#44259)
Make gpui's `rems()`, `phi()`, `auto()` length related helpers into
const functions.

I can't see why these functions aren't already const except that it
must've been overlooked when they were written?

In my project I had need for rems() to be const, and I thought I'd do
phi() and auto() whilst I was in the neighbourhood

Release Notes:

- N/A
2025-12-06 01:08:43 +00:00
Conrad Irwin
4cef8eb47b Fix persistence for single-file worktrees (#44257)
We were just deleting them before

Co-Authored-By: Matthew Chisolm <mchisolm0@gmail.com>

Closes #ISSUE

Release Notes:

- Fixed restoring window location for single-file worktrees

Co-authored-by: Matthew Chisolm <mchisolm0@gmail.com>
2025-12-05 23:55:05 +00:00
Oleksii (Alexey) Orlenko
e5f87735d3 markdown_preview: Remove unnecessary vec allocation (#44238)
Instead of allocating a one-element vec on the heap, we can just use an
array here (since `Editor::edit` accepts anything that implements
`IntoIterator`).

I haven't checked if there are more instances that can be simplified,
just accidentally stumbled upon this when working on something else in
the markdown preview crate.

Release Notes:

- N/A
2025-12-05 23:27:21 +01:00
Mayank Verma
f4b8b0f471 settings: Fix inconsistent terminal font weight step size (#44243)
Closes #44242

Release Notes:

- Fixed inconsistent terminal font weight step size in settings
2025-12-05 19:24:59 -03:00
Michael Benfield
5cd30e5106 inline assistant: Use tools and remove insertion mode (#44248)
Co-authored by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>

Release Notes:

- N/A
2025-12-05 13:28:29 -08:00
Kirill Bulatov
a350438a21 Specify a schema to use when dealing with JSONC files (#44250)
Follow-up of https://github.com/zed-industries/zed/pull/43854
Closes https://github.com/zed-industries/zed/issues/40970

Seems that json language server does not distinguish between JSONC and
JSON files in runtime, but there is a static schema, which accepts globs
in its `fileMatch` fields.

Use all glob overrides and file suffixes for JSONC inside those match
fields, and provide a grammar for such matches, which accepts trailing
commas.

Release Notes:

- Improved JSONC trailing comma handling
2025-12-05 20:26:42 +00:00
Danilo Leal
bd6ca841ad git_ui: Improve the branch picker UI (#44217)
Follow up to https://github.com/zed-industries/zed/pull/42819 and
https://github.com/zed-industries/zed/pull/44206.

- Make this picker feel more consistent with other similar pickers
(namely, the project picker)
- Move actions to the footer and toggle them conditionally
- Only show the "Create" and "Create New From: {default}" when we're
selecting the "Create" list item _or_ when that item is the only
visible. This means I'm changing here the state transition to only
change to `NewBranch/NewRemote` if we only have those items available.
- Reuse more UI code and use components when available (e.g.,
`ListHeader`)
- Remove secondary actions from the list item

Next step (in another PR), will be refine the same picker in the
smaller, panel version.


https://github.com/user-attachments/assets/fe72ac06-c1df-4829-a8a4-df8a9222672f

Release Notes:

- N/A
2025-12-05 17:17:50 -03:00
Bennet Bo Fenner
f9cea5af29 Fix project not getting dropped after closing window (#44237) 2025-12-05 19:53:53 +01:00
Danilo Leal
3bb6c2546a git_ui: Fix history view label truncation (#44218)
There's still a weird problem happening where the labels (and the label
on the tab, too, for what is worth) flicker as the file history view
gets smaller. I suspect that problem is related to something
else—potentially the truncation algorithm or focus management—so I'm not
solving it here.

<img width="500" height="1948" alt="Screenshot 2025-12-05 at 11  24@2x"
src="https://github.com/user-attachments/assets/25715725-e2cb-475a-bdab-f506bb75475f"
/>

Release Notes:

- N/A
2025-12-05 15:46:28 -03:00
Lukas Wirth
37b0cdf94b multi_buffer: Remap excerpt ids to latest excerpt in excerpt fetching (#44229)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Co-authored by: Cole Miller <cole@zed.dev>
2025-12-05 18:20:29 +00:00
Dino
d76dd86272 tab_switcher: Add documentation for tab switcher (#44189)
Release Notes:

- Added documentation for Tab Switcher
2025-12-05 18:18:51 +00:00
David Kleingeld
b558be7ec6 adds tracing for instrumenting non-async functions (#44147)
Tracing code is not included in normal release builds
Documents how to use them in our performance docs
Only the maps and cursors are instrumented atm

# Compile times:
current main: fresh release build (cargo clean then build --release)
377.34 secs
current main: fresh debug build (cargo clean then build )
89.31 secs

tracing tracy: fresh release build (cargo clean then build --release)
374.84 secs
tracing tracy: fresh debug build (cargo clean then build )
88.95 secs

tracing tracy: fresh release build with timings (cargo clean then build
--release --features tracing)
375.77 secs
tracing tracy: fresh debug build with timings (cargo clean then build
--features tracing)
90.03 secs


Release Notes:

- N/A

---------

Co-authored-by: localcc <work@localcc.cc>
2025-12-05 17:23:06 +00:00
Agus Zubiaga
07fe8e9bb1 remoting: Proxy configuration docs (#44225)
Adds an explicit section about how to configure proxies when remoting.

Release Notes:

- N/A
2025-12-05 13:47:29 -03:00
Ben Brandt
b776178b52 agent_ui: Fix mention and slash command menu not appearing with show_completions_on_input set to false (#44222)
Addresses a regression introduced by
https://github.com/zed-industries/zed/pull/44021 that caused @mentions
and slash commands to stop working if you set
`show_completions_on_input: false` in your settings.

In this case, we should always show these menus, otherwise the features
won't work at all.

Release Notes:

- N/A
2025-12-05 15:50:32 +00:00
Dino
1d0aef6b22 Ensure font features are applied to styled text (#44219)
- Replace `gpui::styled::Styled.font_family()` calls with
`gpui::styled::Styled.font()` when laying out inline diagnostics and
inline blame, to ensure that the font's features are also used, and
not just the font feature.
- Update both `editor::hover_popover::hover_markdown_style` and
`editor::hover_popover::diagnostics_markdown_style` to ensure that
both the UI and Buffer font features are used in both markdown and
diagnostics popover.

Closes #44209 

Release Notes:

- Fixed font feature application for inline git blame, inline
diagnostics, markdown popovers and diagnostics popovers
2025-12-05 15:24:07 +00:00
Agus Zubiaga
c7ef3025e4 remoting: Server download connect timeout (#44216)
Sometimes machines are configured to drop outbound packets (rather than
reject connections). In these cases, curl/wget just hang causing our
download step to never complete. This PR adds a timeout of 10s for the
connection (not the whole download), so that in situations like this we
can fallback to our client-side download eventually.

Related to but doesn't fully fix:
https://github.com/zed-industries/zed/issues/43694 and
https://github.com/zed-industries/zed/issues/43718

Release Notes:

- remote: Add 10s connect timeout for server download
2025-12-05 16:16:46 +01:00
Agus Zubiaga
822fc7ef16 remote: Use last line of uname and shell output (#44165)
We have seen cases (see
https://github.com/zed-industries/zed/issues/43694) where the user's
shell initialization script includes text that ends up in the output of
the commands we use to detect the platform and shell of the remote. This
solution isn't perfect, but it should address the issue in most
situations since both commands should only output one line.

Release Notes:

- remote: Improve resiliency when initialization scripts output text
2025-12-05 14:04:01 +01:00
Anthony Eid
126d708fa1 git: Fix branch picker creating new branches with refs/head/ prefixed on the branch name (#44206)
The bug was introduced in this recent PR:
https://github.com/zed-industries/zed/pull/42819. Since it's still in
nightly, there is no need for release notes.

I also polished the feature a bit by:
- Ensuring branch names are always a single line so the branch picker's
uniform list uses the correct element height.
- Adding tooltip text for the filter remotes button.
- Fixing the create branch from default icon showing up for non-new
branch entries.

Release Notes:

- N/A
2025-12-05 12:59:13 +00:00
Lukas Wirth
a5ab5c7d5d gpui: Document the leak detector (#44208)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-05 12:35:05 +00:00
Piotr Osiewicz
35da6d000a debugger: Fix evaluate selection running two evaluations & failing for Python and go (#44205)
Evaluate selection now acts as if the text was typed verbatim into the
console.

Closes ##33526

Release Notes:

- debugger: Fixed "evaluate selection" not behaving as if the
highlighted text was not typed verbatim into the console.
2025-12-05 11:08:04 +00:00
Max Brunsfeld
d6241b17d3 Fix infinite loop in assemble_excerpts (#44195)
Also, expand the number of identifiers fetched.

Release Notes:

- N/A
2025-12-05 06:51:26 +00:00
Max Brunsfeld
42583c1141 Reorganize edit prediction code and remove old experiments (#44187)
Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-12-04 15:56:57 -08:00
Max Brunsfeld
76167109db Add experimental LSP-based context retrieval system for edit prediction (#44036)
To do

* [x] Default to no context retrieval. Allow opting in to LSP-based
retrieval via a setting (for users in `zeta2` feature flag)
* [x] Feed this context to models when enabled
* [x] Make the zeta2 context view work well with LSP retrieval
* [x] Add a UI for the setting (for feature-flagged users)
* [x] Ensure Zeta CLI `context` command is usable

---

* [ ] Filter out LSP definitions that are too large / entire files (e.g.
modules)
* [ ] Introduce timeouts
* [ ] Test with other LSPs
* [ ] Figure out hangs

Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-12-04 12:48:39 -08:00
Ian Chamberlain
cd8679e81a Allow trailing commas in builtin JSONC schemas (#43854)
The JSON language server looks for a top-level `allowTrailingCommas`
flag to decide whether it should warn for trailing commas. Since the
JSONC parser for these builtin files can handles trailing commas, adding
this flag to the schema also prevents a warning for those commas.

I don't think there's an issue that is only for this specific issue, but
it relates to *many* existing / older issues:
- #18509
- #17487
- #40970
- #18509
- #21303

Release Notes:

- Suppress warning for trailing commas in builtin JSON files
(`settings.json`, `keymap.json`, etc.)
2025-12-04 15:37:32 -05:00
Danilo Leal
43f977c6b9 terminal view: Use tooltip element for the tab tooltip (#44169)
Just recently realized we don't need this custom component for it given
we now have `Tooltip::element`. UI result is exactly the same; nothing
changes.

Release Notes:

- N/A
2025-12-04 16:48:03 -03:00
Danilo Leal
bdb8caa42e git_ui: Fix indent guides not showing for file buffers in the commit view (#44166)
Follow up to https://github.com/zed-industries/zed/pull/44162 where my
strategy for not displaying the indent guides only in the commit message
was wrong given I ended up... disabling indent guides for all the
buffers. This PR adds a new method to the editor where we can disable it
for a specific buffer ID following the pattern of
`disable_header_for_buffer`.

Release Notes:

- N/A
2025-12-04 16:47:27 -03:00
vipex
9ae77ec3c9 markdown: Don't adjust indentation when inserting with multiple cursors (#40794)
Closes #40757

## Summary

This PR addresses an issue where Zed incorrectly adjusts the indentation
of Markdown lists when inserting text using multiple cursors. Currently:

- Editing individual lines with a single cursor behaves correctly (no
unwanted indentation changes).
- Using multiple cursors, Zed automatically adjusts the indentation,
unlike VS Code, which preserves the existing formatting.

## Tasks
- [x] Implement a new test to verify correct Markdown indentation
behavior with multiple cursors.
- [x] Apply the fix to prevent Zed from adjusting indentation when
inserting text on multiple cursors.

------------------------

Release Notes:

- Fixed an issue where inserting text with multiple cursors inside a
nested Markdown list would cause it to lose its indentation.

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-12-05 00:18:06 +05:30
Cole Miller
d5ed9d3e3a git: Don't call git2::Repository::find_remote for every blamed buffer (#44107)
We already store the remote URLs for `origin` and `upstream` in the
`RepositorySnapshot`, so just use that data. Follow-up to #44092.

Release Notes:

- N/A
2025-12-04 13:25:30 -05:00
Liffindra Angga Zaaldian
74a1b5d14d Update PHP language server docs (#44001)
Reformat document structure like other language docs, improve
information flow, add missing requirements, and fix typos.

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2025-12-04 19:04:06 +01:00
Lukas Wirth
07af011eb4 worktree: Fix git ignored directories dropping their contents when they are refreshed (#44143)
Closes https://github.com/zed-industries/zed/issues/38653

Release Notes:

- Fixed git ignored directories appearing as empty when their content
changes on windows

Co-authored by: Smit Barmase <smit@zed.dev>
2025-12-04 18:14:10 +01:00
Danilo Leal
c357dc25fc git_ui: Clean up the commit view UI (#44162) 2025-12-04 13:44:48 -03:00
Lukas Wirth
93bc6616c6 editor: Improve performance of update_visible_edit_prediction (#44161)
One half of https://github.com/zed-industries/zed/issues/42861

This basically reduces the main thread work for large enough json (and
other) files from multiple milliseconds (15ms was observed in that test
case) down to microseconds (100ms here).

Release Notes:

- Improved cursor movement performance when edit predictions are enabled
2025-12-04 15:41:48 +00:00
Lukas Wirth
a33e881906 remote: Recognize WSL interop to open browser for codex web login (#44136)
Closes #41521

Release Notes:

- Fixed codex web login not working on wsl remotes if no browser is
installed

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-12-04 14:42:26 +00:00
Agus Zubiaga
c978db8626 Fix background scanner deadlock (#44109)
Fixes a deadlock in the background scanner that occurs on single-core
Linux devices. This happens because the background scanner would `block`
on a background thread waiting for a future, but on single-core Linux
devices there would be no other thread to pick it up. This mostly
affects SSH remoting use cases where it's common for servers to have 1
vCPU.

Closes #43884 
Closes #43809

Release Notes:

- Fix SSH remoting hang when connecting to 1 vCPU servers
2025-12-04 11:30:16 -03:00
Rawand Ahmed Shaswar
2dad46c5c0 gpui: Fix division by zero when chars/sec = 0 on Wayland (#44151)
Closes #44148

the existing rate == 0 check inside the timer callback already handles
disabling repeat - it just drops the timer immediately. So the fix
prevents the crash while preserving correct behavior. 

Release Notes:

- Linux (Wayland): Fixed a crash that could occur when
`characters_per_second` was zero
2025-12-04 11:26:17 -03:00
Coenen Benjamin
4c51fffbb5 Add support for git remotes (#42819)
Follow up of #42486 
Closes #26559



https://github.com/user-attachments/assets/e2f54dda-a78b-4d9b-a910-16d51f98a111



Release Notes:

- Added support for git remotes

---------

Signed-off-by: Benjamin <5719034+bnjjj@users.noreply.github.com>
2025-12-04 14:23:36 +01:00
Piotr Osiewicz
0d80b452fb python: Improve sorting order of toolchains to give higher precedence to project-local virtual environments that are within current subproject (#44141)
Closes #44090

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>

Release Notes:

- python: Improved sorting order of toolchains in monorepos with
multiple local virtual environments.
- python: Fixed toolchain selector not having an active toolchain
selected on open.

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Co-authored-by: Smit <smit@zed.dev>
2025-12-04 12:33:13 +00:00
John Gibb
bad6bde03a Use buffer language when formatting with Prettier (#43368)
Set `prettier_parser` explicitly if the file extension for the buffer
does not match a known one for the current language

Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-12-04 12:07:40 +00:00
Piotr Osiewicz
4ec2d04ad9 search: Fix sort order not being maintained in presence of open buffers (#44135)
In project search UI code we were seeing an issue where "Go to next
match" would act up and behave weirdly. It would not wrap at times.
Stuff would be weird, yo. It turned out that match ranges reported by
core project search were sometimes out of sync with the state of the
multi-buffer. As in, the sort order of
`search::ProjectSearch::match_ranges` would not match up with
multi-buffer's sort order. This is ~because multi-buffers maintain their
own sort order.

What happened within project search is that we were skipping straight
from stage 1 (filtering paths) to stage 3 via an internal channel and in
the process we've dropped the channel used to maintain result sorting.
This made is so that, given 2 files to scan:
- project/file1.rs <- not open, has to go through stage2 (FS scan)
- project/file2.rs <- open, goes straight from stage1 (path filtering)
  to stage3 (finding all matches) We would report matches for
  project/file2.rs first, because we would notice that there's an
  existing language::Buffer for it. However, we should wait for
  project/file1.rs status to be reported first before we kick off
  project/file2.rs

The fix is to use the sorting channel instead of an internal one, as
that keeps the sorting worker "in the loop" about the state of the
world.

Closes #43672

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>

Release Notes:

- Fixed "Select next match" in project search results misbehaving when
some of the buffers within the search results were open before search
was ran.
- Fixed project search results being scrolled to the last file active
prior to running the search.

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Co-authored-by: Smit <smit@zed.dev>
2025-12-04 12:21:02 +01:00
Shardul Vaidya
0f0017dc8e bedrock: Support global endpoints and new regional endpoints (#44103)
Closes #43598

Release Notes:

- bedrock: Added opt-in `allow_global` which enables global endpoints
- bedrock: Updated cross-region-inference endpoint and model list
- bedrock: Fixed Opus 4.5 access on Bedrock, now only accessible through the `allow_global` setting
2025-12-04 12:14:31 +01:00
Agus Zubiaga
9db0d66251 linux: Spawn at least two background threads (#44110)
Related to https://github.com/zed-industries/zed/pull/44109,
https://github.com/zed-industries/zed/issues/43884,
https://github.com/zed-industries/zed/issues/43809.

In the Linux dispatcher, we create one background thread per CPU, but
when a single core is available, having a single background thread
significantly hinders the perceived performance of Zed. This is
particularly helpful when SSH remoting to low-resource servers.

We may want to bump this to more than two threads actually, but I wanted
to be conservative, and this seems to make a big difference already.

Release Notes:

- N/A
2025-12-04 10:40:51 +00:00
Aero
b07389d9f3 macos: Add missing file access entitlements (#43609)
Adds `com.apple.security.files.user-selected.read-write` and
`com.apple.security.files.downloads.read-write` to zed.entitlements.

This resolves an issue where the integrated terminal could not access
external drives or user-selected files on macOS, even when "Full Disk
Access" was granted. These entitlements are required for the application
to properly inherit file access permissions.

Release Notes:

- Resolves an issue where the integrated terminal could not access
external drives or user-selected files on macOS.
2025-12-04 12:38:10 +02:00
Kirill Bulatov
db2e26f67b Re-colorize the brackets when the theme changes (#44130)
Closes https://github.com/zed-industries/zed/issues/44127

Release Notes:

- Fixed brackets not re-colorizing on theme change
2025-12-04 10:21:37 +00:00
John Tur
391c92b07a Reduce priority of Windows thread pool work items (#44121)
`WorkItemPriority::High` will enqueue the work items to threads with
higher-than-normal priority. If the work items are very intensive, this
can cause the system to become unresponsive. It's not clear what this
gets us, so let's avoid the responsiveness issue by deleting this.

Release Notes:

- N/A
2025-12-04 07:45:36 +00:00
Conrad Irwin
1e4d80a21f Update fancy-regex (#44120)
Fancy regex has a max backtracking limit which defaults to 1,000,000
backtracks. This avoids spinning the CPU forever in the case that a
match is taking a long time (though does mean that some matches may be
missed).

Unfortunately the verison we depended on causes an infinite loop when
the backtracking limit is hit
(https://github.com/fancy-regex/fancy-regex/issues/137), so we got the
worse of both worlds: matches were missed *and* we spun the CPU forever.

Updating fixes this.

Excitingly regex may gain support for lookarounds
(https://github.com/rust-lang/regex/pull/1315), which will make
fancy-regex much less load bearing.

Closes #43821

Release Notes:

- Fix a bug where search regexes with look-around or backreferences
could hang
  the CPU. They will now abort after a certain number of match attempts.
2025-12-04 07:29:31 +00:00
Joseph T. Lyons
f90d9d26a5 Prefer to disable options over hiding (git panel entry context menu) (#44102)
When adding the File History option here, I used the pattern to hide the
option, since that's what another option was already doing here, but I
see other menus in the git panel (`...`) that use disabling over hiding,
which is what I think is a nicer experience (allows you to learn of
actions, the full range of actions is always visible, don't have to
worry about how multiple hidden items might interact in various
configurations, etc).

<img width="336" height="241" alt="SCR-20251203-pnpy"
src="https://github.com/user-attachments/assets/0da90b9a-c230-4ce3-87b9-553ffb83604f"
/>

<img width="332" height="265" alt="SCR-20251203-pobg"
src="https://github.com/user-attachments/assets/5da95c7d-faa9-4f0f-a069-f1d099f952b9"
/>


In general, I think it would be good to move to being more consistent
with disabling over hiding - there are other places in the app that are
hiding - some might be valid, but others might just choices made on a
whim.

Release Notes:

- N/A
2025-12-03 22:56:51 +00:00
Andrew Farkas
40a611bf34 tab_switcher: Subscribe to workspace events instead of pane events (#44101)
Closes #43171

Previously the tab switcher only subscribed to events from a single pane
so closing tabs in other panes wouldn't cause the tab switcher to
update. This PR changes that so the tab switcher subscribes to the whole
workspace and thus updates when tabs in other panes are closed.

It also modifies the work in #44006 to sync selected index across the
whole workspace instead of just the original pane in the case of the
all-panes tab switcher.

Release Notes:

- Fixed all-panes tab switcher not updating in response to changes in
other panes
2025-12-03 22:49:44 +00:00
Smit Barmase
8ad3a150c8 editor: Add active match highlight for buffer and project search (#44098)
Closes #28617

<img width="400" alt="image"
src="https://github.com/user-attachments/assets/b1c2880c-5744-4bed-a687-5c5e7aa7fef5"
/>

Release Notes:

- Improved visibility of the currently active match when browsing
results in buffer or project search.

---------

Co-authored-by: DarkMatter-999 <darkmatter999official@gmail.com>
2025-12-04 03:55:04 +05:30
Andrew Farkas
87976e91cf Add more preview tab settings and fix janky behavior (#43921)
Closes #41495

Known issues:
- File path links always open as non-preview tabs. Fixing this is not
technically too difficult but requires more invasive changes and so
should be done in a future PR.

Release Notes:

- Fixed strange behavior when reopening closed preview tabs
- Overhauled preview tabs settings:
- Added setting `preview_tabs.enable_preview_from_project_panel`
(default `true`)
- Kept setting `preview_tabs.enable_preview_from_file_finder` (default
`false`)
- Added setting `preview_tabs.enable_preview_from_multibuffer` (default
`true`)
- Added setting
`preview_tabs.enable_preview_multibuffer_from_code_navigation` (default
`false`)
- Added setting `preview_tabs.enable_preview_file_from_code_navigation`
(default `true`)
- Renamed setting `preview_tabs.enable_preview_from_code_navigation` to
`preview_tabs.enable_keep_preview_on_code_navigation` (default `false`)

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Co-authored-by: Cole Miller <cole@zed.dev>
2025-12-03 21:56:39 +00:00
Michael Benfield
290a1550aa ai: Add an eval for the inline assistant (#43291)
Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-12-03 20:32:25 +00:00
feeiyu
92dcfdef76 Fix circular reference issue around PopoverMenu again (#44084)
Follow up to https://github.com/zed-industries/zed/pull/42351

Release Notes:

- N/A
2025-12-03 21:34:01 +02:00
Cole Miller
4ef8433396 Run git2::Repository::find_remote in the background (#44092)
We were seeing this hog the main thread.

Release Notes:

- N/A

---------

Co-authored-by: cameron <cameron.studdstreet@gmail.com>
2025-12-03 19:33:40 +00:00
John Tur
a51e975b81 Improve support for multiple registrations of textDocument/diagnostic (#43703)
Closes https://github.com/zed-industries/zed/issues/41935

The registration ID responsible for generating each diagnostic is now
tracked. This allows us to replace only the diagnostics from the same
registration ID when a pull diagnostics report is applied.

Additionally, various deficiencies in our support for pull diagnostics
have been fixed:
- Document pulls are issued for all open buffers, not just the edited
one. A shorter debounce is used for the edited buffer. Workspace
diagnostics are also now ignored for open buffers.
- Tracking of `lastResultId` is improved.
- Stored pull diagnostics are discarded when the corresponding buffer is
closed.

Release Notes:

- Improved compatibility with language servers that use the "pull
diagnostics" feature of Language Server Protocol.

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-12-03 20:47:43 +02:00
Conrad Irwin
493cfadb42 Revert "http_client: Add integrity checks for GitHub binaries using digest checks (#43737)" (#44086)
This reverts commit 05764e8af7.

Internally we've seen a much higher incidence of macOS code-signing
failing on
the download rust analyzer than we did before this change.

It's unclear why this would be a problem, but we want to try reverting
to see if that fixes it.

Release Notes:

- Reverted a change that seemed to cause problems with code-signing on
rust-analyzer
2025-12-03 11:40:47 -07:00
Mayank Verma
0818cedded editor: Fix blame hover not working when inline git blame is disabled (#42992)
Closes #42936

Release Notes:

- Fixed editor blame hover not working when inline git blame is disabled

Here's the before/after:


https://github.com/user-attachments/assets/a3875011-4a27-45b3-b638-3e146c06f1fe
2025-12-03 12:25:31 -05:00
Dino
6b46a71dd0 tab_switcher: Fix bug where selected index after closing tab did not match pane's active item (#44006)
Whenever an item is removed using the Tab Switcher, the list of matches
is automatically updated, which can lead to the order of the elements
being updated and changing in comparison to what the user was previously
seeing. Unfortunately this can lead to a situation where the selected
index, since it wasn't being updated, would end up in a different item
than the one that was actually active in the pane.

This Pull Request updates the handling of the `PaneEvent::RemovedItem`
event so that the `TabSwitcherDelegate.selected_index` field is
automatically updated to match the pane's new active item.

Seeing as this is being updated, the
`test_close_preserves_selected_position` test is also removed, as it no
longer makes sense with the current implementation. I believe a better
user experience would be to actually not update the order of the
matches, simply removing the ones that no longer exist, and keep the
selected index position, but will tackle that in a different Pull
Request.

Closes #44005 

Release Notes:

- Fixed a bug with the tab switcher where, after closing a tab, the
selected entry would not match the pane's active item
2025-12-03 17:12:04 +00:00
Ramon
575ea49aad Fix yank around paragraph missing newline (#43583)
Use `MotionKind::LineWise` in both
`vim::normal::change::Vim.change_object` and
`vim::normal::yank::Vim.yank_object` when dealing with objects that
target `Mode::VisualLine`, for example, paragraphs. This fixes an issue
where yanking and changing paragraphs would not include the trailing
newline character.

Closes #28804

Release Notes:

- Fixed linewise text object operations (`yap`, `cap`, etc.) omitting
trailing blank line in vim mode

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>
2025-12-03 17:11:53 +00:00
Xipeng Jin
85ccd7c98b Fix not able to navigate to files in git commit multibuffer (#42558)
Closes #40851

Release Notes:

- Fixed: Commit diff multibuffers now open real project files whenever
possible, restoring navigation and annotations inside those excerpts.

---------

Co-authored-by: Anthony Eid <anthony@zed.dev>
2025-12-03 11:59:56 -05:00
Vitaly Slobodin
b168679c18 language: Remove old unused HTML/ERB language ID (#44081)
The `HTML/ERB` language was renamed
to `HTML+ERB` in https://github.com/zed-industries/zed/pull/40000 We can
remove the old name safely now.

Release Notes:

- N/A
2025-12-03 17:34:49 +01:00
Jeff Brennan
621ac16e35 go: Fix language injections (#43775)
Closes #43730


## Summary
This modifies the existing injections.scm file for go by adding more
specific prefix queries and *_content nodes to the existing
`raw_string_literal` and `interpreted_string_literal` sections

<details><summary>This PR</summary>

<img width="567" height="784" alt="image"
src="https://github.com/user-attachments/assets/bfe8c64e-1dc2-470c-9f85-2c664a6c5a15"
/>
<img width="383" height="909" alt="image"
src="https://github.com/user-attachments/assets/9349af8c-22d3-4c9b-a435-a73719f17ba3"
/>
<img width="572" height="800" alt="image"
src="https://github.com/user-attachments/assets/939ada3b-1440-443b-8492-0eb61a7ee90f"
/>

</details>

<details><summary>Current Release (0.214.7)</summary>

<img width="569" height="777" alt="image"
src="https://github.com/user-attachments/assets/e6fffe77-e4c6-48e3-9c6d-a140298225c5"
/>
<img width="381" height="896" alt="image"
src="https://github.com/user-attachments/assets/bf107950-c33a-4603-90d3-2304bef0a4af"
/>
<img width="574" height="798" alt="image"
src="https://github.com/user-attachments/assets/2c5e43e5-f101-4722-8f58-6b176ba950ca"
/>

</details>

<details><summary>Code</summary>

```go
func test_sql() {
	// const assignment
	const _ = /* sql */ "SELECT * FROM users"
	const _ = /* sql */ `SELECT id, name FROM products`

	// var assignment
	var _ = /* sql */ `SELECT id, name FROM products`
	var _ = /* sql */ "SELECT id, name FROM products"

	// := assignment
	test := /* sql */ "SELECT * FROM users"
	test2 := /* sql */ `SELECT * FROM users`
	println(test)
	println(test2)

	// = assignment
	_ = /* sql */ "SELECT * FROM users WHERE id = 1"
	_ = /* sql */ `SELECT * FROM users WHERE id = 1`

	// literal elements
	_ = testStruct{Field: /* sql */ "SELECT * FROM users"}
	_ = testStruct{Field: /* sql */ `SELECT * FROM users`}

	testFunc(/* sql */ "SELECT * FROM users")
	testFunc(/* sql */ `SELECT * FROM users`)

	const backtickString = /* sql */ `SELECT * FROM users;`
	const quotedString = /* sql */ "SELECT * FROM users;"

	const backtickStringNoHighlight = `SELECT * FROM users;`
	const quotedStringNoHighlight = "SELECT * FROM users;"
}

func test_yaml() {
	// const assignment
	const _ = /* yaml */ `
settings:
  enabled: true
  port: 8080
`

	// := assignment
	test := /* yaml */ `
settings:
  enabled: true
  port: 8080
`

	println(test)

	// = assignment
	_ = /* yaml */ `
settings:
  enabled: true
  port: 8080
`

	// literal elements in a struct
	_ = testStruct{Field: /* yaml */ `
settings:
  test: 1234
  port: 8080
`}

	// function argument
	testFunc(/* yaml */ `
settings:
  enabled: true
  port: 8080
`)
}

func test_css() {
	// const assignment
	const _ = /* css */ "body { margin: 0; }"
	const _ = /* css */ `body { margin: 0; }`

	const cssCodes = /* css */ `
h1 {
  color: #333;
}
`

	// := assignment
	test := /* css */ "body { margin: 0; }"
	println(test)

	// = assignment
	_ = /* css */ "body { margin: 0; }"
	_ = /* css */ `body { margin: 0; }`

	// literal elements
	_ = testStruct{Field: /* css */ "body { margin: 0; }"}
	_ = testStruct{Field: /* css */ `body { margin: 0; }`}

	testFunc(/* css */ "body { margin: 0; }")
	testFunc(/* css */ `body { margin: 0; }`)

	const backtickString = /* css */ `body { margin: 0; }`
	const quotedString = /* css */ "body { margin: 0; }"

	const backtickStringNoHighlight = `body { margin: 0; }`
	const quotedStringNoHighlight = "body { margin: 0; }"
}
```

</details>

Release Notes:

- Greatly improved the quality of comment-directed language injections
in Go
2025-12-03 10:32:51 -06:00
Arthur Schurhaus
c248a956e0 markdown: Fix rendering of inline HTML <code> tags (#43513)
Added support for rendering HTML `<code> `tags inside Markdown content.
Previously, these tags were ignored by the renderer and displayed as raw
text (inside LSP hover documentation).

Closes: #43166 

Release Notes:
- Fixed styling of `<code>` HTML tags in Markdown popovers.

Before:
<img width="445" height="145" alt="image"
src="https://github.com/user-attachments/assets/67c4f864-1fa7-46a9-bb25-8b07a335355d"
/>
After:
<img width="699" height="257" alt="image"
src="https://github.com/user-attachments/assets/8d784a75-28be-43cd-80b4-3aad8babb65b"
/>
2025-12-03 16:58:51 +01:00
Remco Smits
7e177c496c markdown_preview: Fix markdown tables taking up the full width of the parent element (#43555)
Closes #39152

This PR fixes an issue where we would render Markdown tables full width
based on their container size. We now render tables based on their
content min size, meaning you are still allowed to make the table render
as it was before by making the columns `w_full`.

I had to change the `div()` to `v_flex().items_start()` because this
introduced a weird displaying behavior of the outside table border,
because the grid container was not shrinking due to It was always taking
up the full width of their container.

**Before**
<img width="1273" height="800" alt="Screenshot 2025-11-26 at 14 37 19"
src="https://github.com/user-attachments/assets/2e152021-8679-48c2-b7bd-1c02768c0253"
/>

**After**
<img width="1273" height="797" alt="Screenshot 2025-11-26 at 14 56 12"
src="https://github.com/user-attachments/assets/4459d20e-8c3b-487b-a215-c95ee5c1fc8e"
/>

**Code example**

```markdown
|   Name   |   Age   |   Occupation   |
|:--------:|:-------:|:--------------:|
| Alice    |  28     | Engineer       |
| Bob      |  34     | Designer       |
| Carol    |  25     | Developer      |


| Syntax      | Description |
| ----------- | ----------- |
| Header      | Title       |
| Paragraph   | Text        |


| City           | Population (approx.) | Known For                          |
|----------------|----------------------|------------------------------------|
| New York       | 8,500,000            | Statue of Liberty, Wall Street     |
| Los Angeles    | 4,000,000            | Hollywood, film industry           |
| Chicago        | 2,700,000            | Architecture, deep-dish pizza      |
| Houston        | 2,300,000            | NASA, energy industry              |
| Miami          | 470,000              | Beaches, Latin culture             |
| San Francisco  | 800,000              | Golden Gate Bridge, Silicon Valley |
| Las Vegas      | 650,000              | Casinos, nightlife                 |


<table>
    <caption>Table Caption</caption>
  <thead>
    <tr>
    <th>ID asjkfjaslkf jalksjflksajflka jlksdla k</th>
    <th>Name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>Chris</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Dennis</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Sarah</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Karen</td>
    </tr>
  </tbody>
</table>
```

cc @bennetbo

Release Notes:

- Markdown Preview: Markdown tables scale now based on their content
size
2025-12-03 16:49:40 +01:00
Joseph T. Lyons
e39dd2af67 Bump Zed to v0.217 (#44080)
Release Notes:

- N/A
2025-12-03 15:45:45 +00:00
Finn Evers
904d90bee7 extension_ci: Run tests on pushes to main (#44079)
This seems sensible to do - it already was the case prior but
indirectly, lets rather be explicit about this.

Release Notes:

- N/A

Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-12-03 15:13:15 +00:00
Xiaobo Liu
1e09cbfefa workspace: Scope tab tooltip to tab content only (#44076)
Release Notes:

- Fixed scope tab tooltip to tab content only

Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
2025-12-03 12:08:49 -03:00
Finn Evers
8ca2571367 extension_ci: Do not trigger version bump on workflow file changes (#44077)
Release Notes:

- N/A

Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-12-03 15:05:15 +00:00
Agus Zubiaga
95a553ea94 Do not report rejected sweep predictions to cloud (#44075)
Release Notes:

- N/A

Co-authored-by: MrSubidubi <dev@bahn.sh>
2025-12-03 14:26:40 +00:00
Joseph T. Lyons
bf878e9a95 Remove unnecessary variable redeclaration (#44074)
Release Notes:

- N/A
2025-12-03 13:55:58 +00:00
Alexander Andreev
a688239113 python: Fix autocomplete sorting (#44050)
Closes:
#38727 (Python autocompletion being sorted alphabetically)
<img
src="https://github.com/user-attachments/assets/8208d511-f4a4-41f9-8550-3b24370bf776"
width="400">

<img
src="https://github.com/user-attachments/assets/e7c99d7a-b61e-463b-b0f1-36cd279b3887"
width="400">


Release Notes:
- Improve sort order of pyright/basedpyright code completions
2025-12-03 13:51:18 +01:00
Lukas Wirth
4e8f6ddae9 git: Fix unwrap in git2::Index::get_path (#44059)
Fixes ZED-1VR

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-03 11:56:16 +00:00
Ben Brandt
0f67f08795 Update to ACP SDK v0.8.0 (#44063)
Uses the latest version of the SDK + schema crate. A bit painful because
we needed to move to `#[non_exhaustive]` on all of these structs/enums,
but will be much easier going forward.

Also, since we depend on unstable features, I am pinning the version so
we don't accidentally introduce compilation errors from other update
cycles.

Release Notes:

- N/A
2025-12-03 11:24:08 +00:00
Ben Brandt
fe6fa1bbdc Revert "acp: Add a timeout when initializing an ACP agent so the user isn't waiting forever" (#44066)
Reverts zed-industries/zed#43663
2025-12-03 11:11:23 +00:00
Lukas Wirth
50d0f29624 languages: Fix python run module task failing on windows (#44064)
Fixes #40155

Release Notes:

- Fixed python's run module task not working on windows platforms

Co-authored by: Smit Barmase <smit@zed.dev>
2025-12-03 10:29:18 +00:00
lipcut
9857fd233d Make highlighting of C preprocessing directive same as C++ (#44043)
Small fix for consistency between C and C++ highlighting. Related to
https://github.com/zed-industries/zed/issues/9461

Release Notes:

- Change syntax highlighting for preprocessing directive in C so it can
be configured with `keyword.directive` instead of being treated as other
`keyword`. The behavior should be like the C++ one now.

<img width="953" height="343" alt="圖片"
src="https://github.com/user-attachments/assets/6b45d2cc-323d-44ba-995f-d77996757669"
/>
2025-12-03 09:30:54 +01:00
Kirill Bulatov
ad51017f20 Properly filter out the greedy bracket pairs (#44022)
Follow-up of https://github.com/zed-industries/zed/pull/43607

Release Notes:

- N/A
2025-12-03 08:27:55 +00:00
Joseph T. Lyons
2bf47879de Hide "File History" for untracked files in Git Panel context menu (#44035) 2025-12-02 20:47:01 -05:00
Rani Malach
65b4e9b10a extensions_ui: Add upsell banners for integrated extensions (#43872)
Add informational banners for extensions that have been integrated into
Zed core:
- Basedpyright (Python language server)
- Ruff (Python linter)
- Ty (Python language server)

These banners appear when users search for these extensions, informing
them that the functionality is now built-in and linking to relevant
documentation.

The banners trigger when:
- Users search by extension ID (e.g., 'id:ruff')
- Users search using relevant keywords (e.g., 'basedpyright', 'pyright',
'ruff', 'ty')

Supersedes #43844

Closes #43837

Release Notes:

- Added banners to the extensions page when searching for Basedpyright,
Ruff, or Ty, indicating that these features are now built-in.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-12-02 18:44:08 -05:00
Marshall Bowers
98dec9246e zed: Promote comment to a doc comment (#44031)
This PR promotes a line comment above a variant member to a doc comment,
so that the docs show up on hover.

Release Notes:

- N/A
2025-12-02 23:28:30 +00:00
Joseph T. Lyons
39536cae83 docs: Add Conda package to Linux community-maintained packages list (#44029)
Release Notes:

- N/A
2025-12-02 22:44:22 +00:00
Mikhail Pertsev
b4e1d86a16 git: Use UI font in commit and blame popovers (#43975)
Closes #30353

Release Notes:

- Fixed: Hover tooltips in git commit and blame popovers now
consistently use the UI font
2025-12-02 19:44:03 -03:00
Agus Zubiaga
8a12ecf849 commit view: Display message within editor (#44024)
#42441 moved the commit message out of the multi-buffer editor into its
own header element which looks nicer, but unfortunately can make the
view become unusable when the commit message is too long since it
doesn't scroll with the diff.

This PR maintains the metadata in its own element, but moves the commit
message back to the editor so the user can scroll past it. This does
mean that we lose markdown rendering for now, but we think this is a
good solution for the moment.


https://github.com/user-attachments/assets/d67cf22e-1a79-451a-932a-cdc8a65e43de

Release Notes:

- N/A

---------

Co-authored-by: cameron <cameron.studdstreet@gmail.com>
2025-12-02 22:30:43 +00:00
Danilo Leal
22bf449b9e settings_ui: Fix some non-title case settings items (#44026)
Release Notes:

- N/A
2025-12-02 22:30:18 +00:00
Dino
bcf9142bbc Update tree-sitter-bash to 0.25.1 (#44009)
With the merging and publishing of
https://github.com/tree-sitter/tree-sitter-bash/pull/311 , we can now go
ahead and update the version of `tree-sitter-bash` that Zed relies on to
the latest version.

Closes #42091

Release Notes:

- Improved grammar for "Shell Script"
2025-12-02 22:29:48 +00:00
Marshall Bowers
a2d57fc7b6 zed_extension_api: Fork new version of extension API (#44025)
This PR forks a new version of the `zed_extension_api` in preparation
for new changes.

We're jumping from v0.6.0 to v0.8.0 for the WIT because we released
v0.7.0 of the `zed_extension_api` without any WIT changes (it probably
should have been v0.6.1, instead).

Release Notes:

- N/A
2025-12-02 22:26:40 +00:00
Andrew Farkas
96a917091a Apply show_completions_on_input: false to word & snippet completions (#44021)
Closes #43408

Previously, we checked the setting inside `is_completion_trigger()`,
which only affects LSP completions. This was ok because user-defined
snippets were tacked onto LSP completions. Then #42122 and #42398 made
snippet completions their own thing, similar to word completions,
surfacing #43408. This PR moves the settings check into
`open_or_update_completions_menu()` so it applies to all completions.

Release Notes:

- Fixed setting `show_completions_on_input: false` so that it affects
word and user-defined snippet completions as well as LSP completions
2025-12-02 21:33:41 +00:00
John Tur
a2ddb0f1cb Fix "busy" cursor appearing on startup (#44019)
Closes https://github.com/zed-industries/zed/issues/43910

Release Notes:

- N/A
2025-12-02 16:15:18 -05:00
Pranav Joglekar
23e5477a4c vim: Move to opening html tag from / in closing tag (#42513)
Closes #41582

Release Notes:

- Improves the '%' vim motion for html by moving the cursor to the
opening tag when its positioned on the `/` ( slash ) of the closing tag
2025-12-02 12:58:20 -08:00
David Kleingeld
4e043cd56b Add git team to git in REVIEWERS.conl (#41841)
Release Notes:

- N/A
2025-12-02 20:40:26 +00:00
Joseph T. Lyons
d283338885 Add "File History" option to Git Panel entry context menu (#44016)
Sublime Merge:

<img width="403" height="313" alt="image"
src="https://github.com/user-attachments/assets/3ebf3c20-324f-4453-88fb-ad93da546f92"
/>

Fork: 

<img width="347" height="474" alt="image"
src="https://github.com/user-attachments/assets/bcec8920-9415-4ffe-9f17-8ee3e764c5bb"
/>

Zed:

<img width="362" height="316" alt="image"
src="https://github.com/user-attachments/assets/06e577cb-f897-45e0-b4b3-59a2c28b3342"
/>


Release Notes:

- Added a "File History" option to Git Panel entry context menu
2025-12-02 20:39:29 +00:00
Danilo Leal
b1af02ca71 debugger: Improve step icons (#44017)
Closes https://github.com/zed-industries/zed/issues/38475

<img width="500" height="820" alt="Screenshot 2025-12-02 at 5  24@2x"
src="https://github.com/user-attachments/assets/802e4fd7-0d6d-4cc2-a77a-17df8c268fc7"
/>

Release Notes:

- N/A
2025-12-02 20:38:18 +00:00
Agus Zubiaga
59b5de5532 Update typos to 1.40.0 (#43739)
Noticed we had a few typos that weren't caught by the version we were
using

Release Notes:

- N/A
2025-12-02 18:47:25 +00:00
Agus Zubiaga
efa98a12fd Pin cargo-about to 0.8.2 (#44012)
cargo-about@0.8.3 doesn't seem to like our license identifiers. Pinning
to 0.8.2 for now.

Release Notes:

- N/A
2025-12-02 17:54:08 +00:00
Kirill Bulatov
7bea1ba555 Run commands if completion items require so (#44008)
Abide the LSP better and actually run commands if completion items
request those:


https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItem
```
/**
   * An optional command that is executed *after* inserting this completion.
   * *Note* that additional modifications to the current document should be
   * described with the additionalTextEdits-property.
   */
   command?: [Command](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#command);
```

Release Notes:

- N/A
2025-12-02 19:38:01 +02:00
Novus Nota
7c95834b7b languages: Add injections to highlight script blocks of actions/github-script as JavaScript (#43771)
Hello, this is my first time contributing here! The issue creation
button noted that "feature request"-esque items should go to
discussions, so with this PR, I'm closing that discussion and not an
issue. I hope that's ok :)

---

Closes https://github.com/zed-industries/zed/discussions/43769

Preview:

<img width="500" alt="image"
src="https://github.com/user-attachments/assets/00b8d475-d452-42e9-934b-ee5dbe48586e"
/><p/>

Release Notes:

- Added JavaScript highlighting via YAML `injections.scm` for script
blocks of `actions/github-script`
2025-12-02 17:30:09 +00:00
Agus Zubiaga
3d58738548 collab: Add action to copy room id (#44004)
Adds a `collab: copy room id` action which copies the live kit room name
and session ID to the clipboard.

Release Notes:

- N/A
2025-12-02 16:27:36 +00:00
Agus Zubiaga
2db237aa52 Limit edit prediction reject batches to max (#43965)
We currently attempt to flush all rejected predictions at once even if
we have accumulated more than
`MAX_EDIT_PREDICTION_REJECTIONS_PER_REQUEST`. Instead, we will now flush
as many as possible, and then keep the rest for the next batch.


Release Notes:

- N/A
2025-12-02 13:22:16 -03:00
Lukas Wirth
305e73ebbb gpui(windows): Move interior mutability down into fields (#44002)
Windows applications tend to be fairly re-entrant which does not work
well with our current top-level `RefCell` approach. We have worked
around a bunch of panics in the past due to this, but ultimately this
will re-occur (and still does according to sentry) in the future. So
this PR moves all interior mutability down to the fields.

Fixes ZED-1HM
Fixes ZED-3SH
Fixes ZED-1YV
Fixes ZED-29S
Fixes ZED-29X
Fixes ZED-369
Fixes ZED-20W

Release Notes:

- Fixed panics on windows caused by unexpected re-entrancy for interior
mutability
2025-12-02 16:02:47 +00:00
Lukas Wirth
ec6e7b84b8 gpui(windows): Fix top resize edge being only 1px tall (#43995)
Closes https://github.com/zed-industries/zed/issues/41693

Release Notes:

- Fixed an issue on windows where the resize area on the title bar is
only 1px tall
2025-12-02 13:45:51 +00:00
Shuhei Kadowaki
4f5cc0a24b lsp: Send client process ID in LSP initialize request (#43988)
Per the [LSP
specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initializeParams),
the client should send its process ID to the language server so the
server can monitor the client process and shut itself down if the client
exits unexpectedly.

Release Notes:

- N/A
2025-12-02 14:04:28 +02:00
Lukas Wirth
a2f69cd5bd remote: Finer grained --exec attempts (#43987)
Closes https://github.com/zed-industries/zed/issues/43328

Instead of trying to guess whether we can `--exec`, this now
restructures things to only attempt to use that flag where its
necessary. We only need to `--exec` when we are interested in the shell
output after all.

Release Notes:

- Fixed wsl remoting not working with some nixOS setups
2025-12-02 12:50:25 +01:00
Lukas Wirth
6a097298b0 terminal_view: Fix close tab button tooltip showing wrong keybinding on windows (#43981)
Closes https://github.com/zed-industries/zed/issues/43882

Release Notes:

- Fixed wrong button tooltips being shown for terminal pane on windows
2025-12-02 11:33:00 +00:00
Lukas Wirth
0df86e406a windows: Fix more vscode keybindings (#43983)
Closes https://github.com/zed-industries/zed/issues/43151 Closes
https://github.com/zed-industries/zed/issues/42022

Looking at other mappings, it seems like this should've been an alt
keybind after all

Release Notes:

- Fixed toggling focus to project panel via keybinding on windows not
always working
2025-12-02 11:31:34 +00:00
Bhuminjay Soni
a74aac88c9 Increase askpass timeout for git operations (#42946)
Closes #29903

Release Notes:

- Increased timeout from 17->300 & improved the error message.

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <bhuminjaysoni@gmail.com>
2025-12-02 12:18:13 +01:00
John Tur
e5105ccdbe Don't halt processing from WM_SETCURSOR (#43982)
Release Notes:

- N/A
2025-12-02 05:57:20 -05:00
Lukas Wirth
876b258088 zed: Fix unnecessary rebuilds of the main binary on windows (#43979)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-02 10:15:30 +00:00
Arjun Bajaj
19aba43f3e Add Tailwind CSS support for Gleam (#43968)
Currently, Zed does not provide suggestions and validations for Gleam,
as it is only available for languages specified in `tailwind.rs`. This
pull-request adds Gleam to that list of languages.

After this, if Tailwind is configured to work with Gleam, suggestions
and validation appear correctly.

Even after this change, Tailwind will not be able to detect and give
suggestions in Gleam directly. Below is the config required for Tailwind
classes to be detected in all Gleam strings.


<details><summary>Zed Config for Tailwind detection in Gleam</summary>
<p>

```
{
  "languages": {
    "Gleam": {
      "language_servers": [
        "gleam",
        "tailwindcss-language-server"
      ]
    }
  },
  "lsp": {
    "tailwindcss-language-server": {
      "settings": {
        "experimental": {
          "classRegex": [
            "\"([^\"]*)\""
          ]
        }
      }
    }
  }
}
```

The `classRegex` will match all Gleam strings, making it work seamlessly
with Lustre templates and plain string literals.

</p>
</details> 

Release Notes:

- Added support for Tailwind suggestions and validations for the [Gleam
programming language](https://gleam.run/).
2025-12-02 03:43:31 -05:00
Lukas Wirth
8d09610748 git_ui: Fix utf8 panic in compress_commit_diff (#43972)
Fixes ZED-3QG

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-02 08:31:44 +00:00
Lukas Wirth
5b6663ef97 terminal: Try to fix flaky macOS test (#43971)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-02 08:21:18 +00:00
Josh Piasecki
f445f22fe6 Add an action listener to workspace for ActivatePreviousItem and ActivateNextItem (#42588)
Release Notes:

- pane::ActivatePreviousItem and pane::ActivateNextItem now toggle the
most recent pane when called from a dock panel


a couple months ago i posted a work around that used `SendKeystrokes` to
cycle through pane items when focused on a dock.
#35253

this pr would add this functionality to the these actions by default.
i implemented this by adding an action listener to the workspace level.

------
if the current context is a dock that does not hold a pane
it retrieves the most recent pane from `activation_history` and
activates the next item on that pane instead.

- `"Pane > Editor"`
cycles through the current pane like normal
- `"Dock > Pane > Terminal"`
also cycles through the pane items like normal
- `"Dock > (Any Child that is not a child of Pane)"`
cycles through the items of the most recent pane.

this is the standard behavior in VS Code i believe.

in the video below you can see the actions cycling through the editor
like normal when focus is on the editor.
then you can see the editor continue to cycle when the focus is on the
project panel.
and that the focus stays on the project panel.
and you can see the action cycle the terminal items when the focus is
moved to the terminal


https://github.com/user-attachments/assets/999ab740-d2fa-4d00-9e53-f7605217e6ac

the only thing i noticed is that for this to work the keybindings must
be set above `Pane`
so they have to be set globally or on workspace. otherwise they do not
match in the context
2025-12-01 22:01:11 -07:00
Connor Tsui
6216af9b5a Allow dynamic set_theme based on Appearance (#42812)
Tracking Issue (does not close):
https://github.com/zed-industries/zed/issues/35552

This is somewhat of a blocker for
https://github.com/zed-industries/zed/pull/40035 (but also the current
behavior doesn't really make sense).

The current behavior of `ThemeSelectorDelegate::set_theme` (the theme
selector menu) is to simply set the in-memory settings to `Static`,
regardless of if it is currently `Dynamic`. The reason this doesn't
matter now is that the `theme::set_theme` function that updates the
user's settings file _will_ make this check, so dynamic settings stay
dynamic in `settings.json`, but not in memory.

But this is also sort of strange, because `theme::set_theme` will set
the setting of whatever the old appearance was to the new theme name. In
other words, if I am currently on a light mode theme and I change my
theme to a dark mode theme using the theme selector, the `light` field
of `theme` in `settings.json` is set to a dark mode theme!

_I think this is because displaying the new theme in the theme selector
does not update the global context, so
`ThemeSettings::get_global(cx).theme.name(appearance).0` returns the
original theme appearance, not the new one._

---

This PR makes `ThemeSelectorDelegate::set_theme` keep the current
`ThemeSelection`, as well as changes the behavior of the
`theme::set_theme` call to always choose the correct setting to update.

One edge case that might be slightly strange now is that if the user has
specified the mode as `System`, this will now override that with the
appearance of the new theme. I think this is fine, as otherwise a user
might set a dark theme and nothing will change because the
`ThemeAppearanceMode` is set to `light` or `system` (where `system` is
also light).

I also have an `unreachable!` in there that I'm pretty sure is true but
I don't really know how to formally prove that...

Release Notes:

- N/A *or* Added/Fixed/Improved ...

---------

Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
2025-12-01 20:52:57 -07:00
Anthony Eid
464c0be2b7 git: Add word diff highlighting (#43269)
This PR adds word/character diff for expanded diff hunks that have both
a deleted and added section, as well as a setting `word_diff_enabled` to
enable/disable word diffs per language.

- `word_diff_enabled`: Defaults to true. Whether or not expanded diff
hunks will show word diff highlights when they're able to.

### Preview
<img width="1502" height="430" alt="image"
src="https://github.com/user-attachments/assets/1a8d5b71-449e-44cd-bc87-d6b65bfca545"
/>

### Architecture

I had three architecture goals I wanted to have when adding word diff
support:

- Caching: We should only calculate word diffs once and save the result.
This is because calculating word diffs can be expensive, and Zed should
always be responsive.
- Don't block the main thread: Word diffs should be computed in the
background to prevent hanging Zed.
- Lazy calculation: We should calculate word diffs for buffers that are
not visible to a user.

To accomplish the three goals, word diffs are computed as a part of
`BufferDiff` diff hunk processing because it happens on a background
thread, is cached until the file is edited, and is only refreshed for
open buffers.

My original implementation calculated word diffs every frame in the
Editor element. This had the benefit of lazy evaluation because it only
calculated visible frames, but it didn't have caching for the
calculations, and the code wasn't organized. Because the hunk
calculations would happen in two separate places instead of just
`BufferDiff`. Finally, it always happened on the main thread because it
was during the `EditorElement` layout phase.

I used Zed's
[`diff_internal`](02b2aa6c50/crates/language/src/text_diff.rs (L230-L267))
as a starting place for word diff calculations because it uses
`Imara_diff` behind the scenes and already has language-specific
support.

#### Future Improvements

In the future, we could add `AST` based word diff highlights, e.g.
https://github.com/zed-industries/zed/pull/43691.

Release Notes:

- git: Show word diff highlight in expanded diff hunks with less than 5
lines.
- git: Add `word_diff_enabled` as a language setting that defaults to
true.

---------

Co-authored-by: David Kleingeld <davidsk@zed.dev>
Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: cameron <cameron.studdstreet@gmail.com>
Co-authored-by: Lukas Wirth <lukas@zed.dev>
2025-12-01 22:36:30 -05:00
Moritz Fröhlich
2df5993eb0 workspace: Add ctrl-w x support for vim (#42792)
Adds support for the vim CTRL-W x keybinding, which swaps the active
pane with the next adjacent one, prioritizing column over row and next
over previous. Upon swap, the pane which was swapped with is activated
(this is the vim behavior).

See also
ca6a260ef1/runtime/doc/windows.txt (L514C1-L521C24)

Release Notes:

- Added ctrl-w x keybinding in Vim mode, which swaps the active window
with the next adjacent one (aligning with Vim behavior)

**Vim behavior**


https://github.com/user-attachments/assets/435a8b52-5d1c-4d4b-964e-4f0f3c9aca31


https://github.com/user-attachments/assets/7aa40014-1eac-4cce-858f-516cd06d13f6

**Zed behavior**


https://github.com/user-attachments/assets/2431e860-4e11-45c6-a3f2-08f1a9b610c1


https://github.com/user-attachments/assets/30432d9d-5db1-4650-af30-232b1340229c

Note: There is a discrepancy where in Vim, if vertical and horizontal
splits are mixed, swapping from a column with a single window does not
work (see the vertical video), whilst in Zed it does. However, I don't
see a good reason as to why this should not be supported and would argue
that it makes more sense to keep the clear priority swap behavior,
instead of adding a workaround to supports such cases.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-12-02 03:36:06 +00:00
Hans
04e92fb2d2 vim: Fix :s command ignoring case sensitivity settings (#42457)
Closes #36260 

This PR fixes the vim :s Command Ignores Case Sensitivity Settings

Release Notes:

- N/A
2025-12-01 20:22:22 -07:00
Conrad Irwin
e27590432f Actually show settings errors on app load (#43268)
They were previously hidden by the global settings file succeeding to
parse :face-palm:

Release Notes:

- N/A
2025-12-01 19:53:36 -07:00
Cole Miller
a675eb1667 Fix regression in closing project diff (#43964)
Follow-up to #43586--when the diff is not split, just render the primary
editor. Otherwise the child `Pane` intercepts `CloseActiveItem`. (This
is still a bug for the actual split diff, but just fixing the
user-visible regression for now.)

Release Notes:

- N/A
2025-12-02 02:32:36 +00:00
Agus Zubiaga
b27ad98520 zeta: Put back 15s reject debounce (#43958)
Release Notes:

- N/A
2025-12-01 23:57:09 +00:00
Finn Evers
9c4e16088c extension_ci: Use more robust bash syntax for bump_extension_version (#43955)
Also does some more cleanup here and there.

Release Notes:

- N/A
2025-12-01 23:38:05 +00:00
Finn Evers
34a2bfd6b7 ci: Supply github_token to bufbuild_setup_action (#43957)
This should hopefully resolve errors like
https://github.com/zed-industries/zed/actions/runs/19839788214/job/56845583441

Release Notes:

- N/A
2025-12-01 23:37:33 +00:00
Finn Evers
99d8d34d48 Fix failing CI on main (#43956)
Release Notes:

- N/A
2025-12-01 23:27:27 +00:00
Finn Evers
bd79edee71 extension_ci: Improve behavior when no Rust is present (#43953)
Release Notes:

- N/A
2025-12-01 22:58:15 +00:00
Agus Zubiaga
0bb1c6ad3e Return json value instead of empty string from accept/reject endpoints 2025-12-01 19:53:06 -03:00
Clément Lap
fd146757cf Add Doxygen injection into C and C++ comments (#43581)
Release Notes:

- C/C++ files now support Doxygen grammars (if a Doxygen extension is installed).
2025-12-01 23:32:23 +01:00
mikeHag
6eb9f9add7 Debug a test annotated with the ignore attribute if the test name partially matches another test (#43110)
Related: #42574
If an integration test is annotated with the ignore attribute, allow the
"debug: Test" option of the debug scenario or Code Action to run with
the "--include-ignored" and "--exact" arguments. Inclusion of "--exact"
covers the case where more that one test shares a base name. For
example, consider two tests named "test_no_ace_in_middle_of_straight"
and "test_no_ace_in_middle_of_straight_flush." Without the "--exact"
argument both tests would run if a user attempts to debug
"test_no_ace_in_middle_of_straight".

Release Notes:

- Improved "debug test" experience in Rust with ignored tests.
2025-12-01 23:28:37 +01:00
Katie Geer
3d3d124e01 docs: Migrate from VS Code (#43438)
Add guide for users coming from VS Code

Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-12-01 14:08:07 -08:00
Kirill Bulatov
de392cda39 Fix bracket colorization not working in file-less files with a proper language (#43947)
Follow-up of https://github.com/zed-industries/zed/pull/43172

Release Notes:

- Fixed bracket colorization in file-less files with a proper language
2025-12-01 21:53:54 +00:00
Piotr Osiewicz
33513292af script: Fix upload-nightly versioning (#43933)
Release Notes:

- N/A
2025-12-01 22:38:58 +01:00
Finn Evers
1535e95066 ci: Always run nextest for extensions (#43945)
This makes rolling this out across extensions a far bit easier and also
safer, because we don't have to manually set `run_tests` for every
extension (and never have to consider this when updating these).

Release Notes:

- N/A
2025-12-01 21:20:47 +00:00
Agus Zubiaga
26f77032a2 edit prediction: Do not attempt to gather context for non-zeta2 models (#43943)
We were running the LLM-based context gathering for zeta1 and sweep
which don't use it.

Release Notes:

- N/A
2025-12-01 21:14:00 +00:00
Agus Zubiaga
efff602909 zeta: Smaller reject batches (#43942)
We were allowing the client to build up to
`MAX_EDIT_PREDICTION_REJECTIONS_PER_REQUEST`. We'll now attempt to flush
the rejections when we reach half max.

Release Notes:

- N/A
2025-12-01 21:10:13 +00:00
Danilo Leal
58c9cbae40 git_ui: Clean up file history view design (#43941)
Mostly cleaning up the UI code. The UI looks fairly the same just a bit
more polished, with proper colors and spacing. Also added a scrollbar in
there. Next step would be to make it keyboard navigable.

<img width="500" height="1948" alt="Screenshot 2025-12-01 at 5  38@2x"
src="https://github.com/user-attachments/assets/c266b97c-4a79-4a0e-8e78-2f1ed1ba495f"
/>

Release Notes:

- N/A
2025-12-01 20:56:34 +00:00
Finn Evers
7881551dda ci: Request GitHub token for proper repository (#43940)
Release Notes:

- N/A
2025-12-01 21:30:51 +01:00
Ben Kunkle
ff6bd7d82e sweep: Add UI for setting Sweep API token in system keychain (#43502)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-01 14:36:49 -05:00
Finn Evers
bfb876c782 Improve extension CI concurrency (#43935)
While this does work for PRs and such, it does not work with main...
Hence, moving the token a few chars to the right to fix this issue.

Release Notes:

- N/A
2025-12-01 19:29:20 +00:00
Joseph T. Lyons
64b432e4ac Update crash report template (#43934)
- Updates tone to match bug template
- Removes the "current vs expected" behavior section
- It doesn't feel very useful. Users expect Zed to not crash, regardless
of what they are doing.

Release Notes:

- N/A
2025-12-01 19:05:10 +00:00
Richard Feldman
33ecb0a68f Clarify how outlining works in read_file_tool description (#43929)
<img width="698" height="218" alt="Screenshot 2025-12-01 at 1 27 02 PM"
src="https://github.com/user-attachments/assets/a5d9e121-4e68-40d0-a346-4dd39e77233b"
/>

Closes #419

Release Notes:

- Revise tool call description for read file tool to explain outlining
behavior
2025-12-01 14:00:18 -05:00
Danilo Leal
7b7ddbd1e8 Adjust edit prediction upsell copy and animation (#43931)
Release Notes:

- N/A
2025-12-01 18:45:45 +00:00
Finn Evers
ed81ef0442 ci: Add extension workflow concurrency rules (#43930)
Release Notes:

- N/A
2025-12-01 18:42:46 +00:00
Finn Evers
88fffae9dd Fix extension CI workflow disclaimer (#43926)
Release Notes:

- N/A
2025-12-01 18:22:52 +00:00
Lukas Wirth
61ae59708d text: Downgrade some more offset panics to error logs (#43925)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-01 18:04:01 +00:00
Finn Evers
c8166abbcb Add more workflows for extension repositories (#43924)
This PR adds workflows to be used for CD in extension reposiories in the
`zed-extensions` organization and updates some of the existing ones with
minor improvemts.

Release Notes:

- N/A
2025-12-01 19:00:54 +01:00
Lukas Wirth
628c52a96a buffer: Keep the shorter language setting names for the common operation (#43915)
cc
https://github.com/zed-industries/zed/pull/43888#issuecomment-3597265087

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-01 18:55:33 +01:00
Lukas Wirth
465536644c git: Push process spawns to the background threads (#43918)
Release Notes:

- Improved performance of multibuffers by spawning git blame processes
on the background threads
2025-12-01 16:42:48 +00:00
Richard Feldman
da3bab18fe Unit eval GPT-5 and Gemini 3 Pro (#43916)
Follow-up to #43907

Release Notes:

- N/A
2025-12-01 11:41:15 -05:00
Kirill Bulatov
89841d034d Suppress a backtrace in extension error logging (#43917)
One less redundant backtrace in the logs:

<img width="2032" height="1161" alt="backtrace"
src="https://github.com/user-attachments/assets/f03192b4-1b9c-4fa1-809d-9e826452f711"
/>

Release Notes:

- N/A
2025-12-01 16:20:10 +00:00
Richard Feldman
7aa610e24f Run the unit evals cron in a matrix (#43907)
For now, just using Sonnet 4.5 and Opus 4.5 - I'll make a separate PR
for non-Anthropic models, in case they introduce new failures.

Release Notes:

- N/A
2025-12-01 11:03:00 -05:00
Joseph T. Lyons
26ef93ffeb Remove "Other [Staff Only]" GitHub Issue template (#43913)
Release Notes:

- N/A
2025-12-01 15:52:51 +00:00
Mark Polonsky
60312a3b04 Fix attach to process in Go debugger (#43898)
Release Notes:

- Fixed the behavior of the attach to process feature for the Golang
debugger.

**Step to reprorduce:**
1. Build and run golang binary
2. In Zed open debugger
3. Go to Attach tab
4. Select the binary you just built from the list
**Actual result:** 
```
Tried to launch debugger with: {
  "request": "attach",
  "mode": "debug",
  "processId": 74702,
  "stopOnEntry": false,
  "cwd": "/path/to/the/current/working/directory"
}
error: Failed to attach: invalid debug configuration - unsupported 'mode' attribute "debug" 
```

**Expected result:** The debugger can attach to the process.


According to the [Delve
documentation](https://github.com/go-delve/delve/tree/master/Documentation/api/dap#launch-and-attach-configurations)
`attach` request supports `local` and `remote` values only
2025-12-01 10:50:28 -05:00
Joseph T. Lyons
d9b3523459 Update community champions list (#43911)
Release Notes:

- N/A
2025-12-01 15:47:21 +00:00
Danilo Leal
1106f77246 Improve icon component preview (#43906)
Release Notes:

- N/A
2025-12-01 15:20:49 +00:00
Agus Zubiaga
8561943095 rust: Nicer snippet completions (#43891)
At some point, rust-analyzer started including the snippet expression
for some completions in the description field, but that can look like a
bug:

<img width="1570" height="578" alt="CleanShot 2025-11-27 at 20 39 49@2x"
src="https://github.com/user-attachments/assets/5a87c9fe-c0a8-472f-8d83-3bc9e9e00bbc"></img>


In these cases, we will now inline the tab stops as an ellipsis
character:

<img width="1544" height="428" alt="CleanShot 2025-12-01 at 10 01 18@2x"
src="https://github.com/user-attachments/assets/4c550891-4545-47cd-a295-a5eb07e78e92"></img>

You may also notice that we now syntax highlight the pattern closer to
what it looks like after accepted.

Alternatively, when the tab stop isn't just one position, it gets
highlighted as a selection since that's what it would do when accepted:

<img width="1558" height="314" alt="CleanShot 2025-12-01 at 10 04 37@2x"
src="https://github.com/user-attachments/assets/ce630ab2-da22-4072-a996-7b71ba21637d"
/>


Release Notes:

- rust: Display completion tab stops inline rather than as a raw LSP
snippet expression
2025-12-01 12:19:00 -03:00
Liffindra Angga Zaaldian
a996fa4320 Docs README.md small fixes (#43904)
Correct punctuation marks, style keywords in Markdown so it rendered
correctly (e.g. HTML tags, paths), capitalize abbreviations (HTML, YAML,
ASCII), fix typos for consistency (e.g. mdBook).

Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-12-01 14:57:00 +00:00
Lukas Wirth
1d6b5e72a1 REVIEWERS.conl: Add me to a couple more areas (#43903)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-01 14:51:35 +00:00
Floyd Wang
5b0f936041 ui: Refactor dashed divider with path builder (#43879)
Replace the original div implementation with path builder.

| Before | After |
| - | - |
| <img width="1182" height="887" alt="before1"
src="https://github.com/user-attachments/assets/3d9fb3ce-35c8-46fb-925c-e24974dedc4b"
/><img width="1182" height="887" alt="before2"
src="https://github.com/user-attachments/assets/137fd952-7db0-49fe-b502-971b22ae3c2a"
/> | <img width="1182" height="887" alt="after1"
src="https://github.com/user-attachments/assets/8e2b6070-4bd4-4b1a-86cf-64516e165e5b"
/><img width="1182" height="887" alt="after2"
src="https://github.com/user-attachments/assets/cf67701c-2359-4ff1-9a7c-76fcf11f2781"
/> |

Release Notes:

- N/A
2025-12-01 11:51:13 -03:00
Adam Huganir
b3478e87f1 agent_ui: JSON encode command path along with other elements in MCPcontext modal (#42693)
Closes #42692

Release Notes:

- Fixed command path not being correctly encoded in context server
config modal
2025-12-01 15:36:29 +01:00
Lukas Wirth
de6855fa6e gpui(windows): Fix apps not quitting if they overwhelm the foreground thread with tasks (#43896)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-01 14:23:12 +00:00
Kunall Banerjee
776c853756 Rename issue templates (#43893)
Closes #43892.

Release Notes:

- N/A
2025-12-01 09:03:22 -05:00
Oleksiy Syvokon
95f512af9b Add zeta score command (#43894)
A small CLI helper to test the metric

Release Notes:

- N/A
2025-12-01 14:01:50 +00:00
Lukas Wirth
9af6e82e65 language: Only block the foreground on buffer reparsing when necessary (#43888)
Gist is we only need to block the foreground thread for reparsing if
immediate language changes are useful to the user. That is usually only
the case when they edit the buffer

Release Notes:

- Improved performance of large project searches and project diffs

Co-authored by: David Kleingeld <david@zed.dev>
2025-12-01 14:57:15 +01:00
José Olórtegui
3969109aa3 Add file icon for the Odin programming language (#43855)
Release Notes:

- Added file icon for the [Odin programming
language](https://odin-lang.org/).

<img width="617" height="288" alt="CleanShot 2025-11-30 at 22 37 00"
src="https://github.com/user-attachments/assets/2389b90a-2fec-43bf-838f-1441f08b724a"
/>
2025-12-01 10:46:59 -03:00
ozzy
05c2028068 Add file history view (#42441)
Closes #16827

Release Notes:

- Added: File history view accessible via right-click context menu on
files in the editor or project panel. Shows commit history for the
selected file with author, timestamp, and commit message. Clicking a
commit opens a diff view filtered to show only changes for that specific
file.

<img width="1293" height="834" alt="Screenshot 2025-11-11 at 16 31 32"
src="https://github.com/user-attachments/assets/3780d21b-a719-40b3-955c-d928c45a47cc"
/>
<img width="1283" height="836" alt="Screenshot 2025-11-11 at 16 31 24"
src="https://github.com/user-attachments/assets/1dc4e56b-b225-4ffa-a2af-c5dcfb2efaa0"
/>

---------

Co-authored-by: cameron <cameron.studdstreet@gmail.com>
2025-12-01 13:25:33 +00:00
Lukas Wirth
747dc23138 util: Move PathMatcher over to RelPath (#43881)
Closes https://github.com/zed-industries/zed/issues/40688 Closes
https://github.com/zed-industries/zed/issues/41242

Release Notes:

- Fixed project search not working correctly on remotes where the host
and remote path styles differ
2025-12-01 12:57:51 +00:00
Lukas Wirth
aa0e19feca gpui(windows): Reset foreground time budget when hitting the timeout limit (#43886)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-01 12:00:48 +00:00
Lukas Wirth
bb859a85d5 gpui(windows): Only poll PAINT messages twice in foreground task timeout (#43883)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-01 11:30:14 +00:00
Lukas Wirth
b9c5900fb0 terminal_view: Fix selection columns not being clamped correctly for rendering (#43876)
Before:


https://github.com/user-attachments/assets/3be4d451-81a6-430b-bc36-d91f4cd44c9b

After:


https://github.com/user-attachments/assets/6d64bf0c-5bd1-45be-b9d8-20118e5a25e6



Release Notes:

- Fixed rendered selections in the terminal view not being clamped to
the line start/ends correctly
2025-12-01 08:59:33 +00:00
Lukas Wirth
4e3aa0b1b6 zed: Fix session ID mismatching for workspace and telemetry (#43659)
I don't think this caused any problems for us but you never know ...

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-12-01 09:12:31 +01:00
Tom Planche
05764e8af7 http_client: Add integrity checks for GitHub binaries using digest checks (#43737)
Generalizes the digest verification logic from `rust-analyzer` and
`clangd` into a reusable helper function in
`http_client::github_download`.

This removes ~100 lines of duplicated code across the two language
adapters and makes it easier for other language servers to adopt digest
verification in the future.

Closes #35201

Release Notes:

- N/A
2025-12-01 09:00:27 +01:00
Ian Chamberlain
db86febc0c direnv: Allow disabling env integration entirely (#43764)
Relates to #35759, but maybe doesn't entirely fix it? I think it will
improve the situation, at least.
Also provides a workaround for the issue described in
https://github.com/zed-industries/zed/issues/40094#issuecomment-3559808526
for users of WSL + `nix-direnv`.

Rationale: there are cases where automatic direnv integration is not
always desirable, but Zed currently has no way of opting out of this
integration besides `direnv revoke` (which is often not desirable).

This PR provides such an opt-out for users who run into problems with
the existing direnv integration methods. Some reasons why disabling
might be useful:
- Security concerns about auto-loading `.envrc` (arguably, `direnv
revoke` should cover this most of the time)
- As in #35759, for users who use different shells/envs for
interactive/non-interactive cases and want to manually control the
environment Zed uses
- As in #40094, to workaround OS limits on environment variable /
command-line parameter size


Release Notes:

- Added the ability to disable direnv integration entirely
2025-12-01 08:58:30 +01:00
Mikko Perttunen
d1d419b209 gpui: Further fix extraction of font runs from text runs (#43856)
After #39928, if a font's weight changes between text runs without other
decoration changing, the earlier weight continues to be used for the
subsequent run(s).

PR #40840 fixes this in shape_text, but similar code exists also in
layout_text. The latter is used for text in the editor view itself, so
the issue continues to appear when using a highlighting theme with
varied font weights.

Fix the issue by applying the same fix in layout_text.

Closes #42297

Release Notes:

- Fixed incorrect font weights in editor view when using a highlighting
theme with varying font weights
2025-12-01 08:54:47 +01:00
Cole Miller
2e00f40c54 Basic side-by-side diff implementation (#43586)
Release Notes:

- N/A

---------

Co-authored-by: cameron <cameron.studdstreet@gmail.com>
Co-authored-by: Cameron <cameron@zed.dev>
2025-11-30 22:45:01 -05:00
Danilo Leal
ca6e64d451 docs: Fix bug on table of contents (#43840)
We were previously always highlighting the last header in the table of
contents as active even if you were at the top of the page. This is now
fixed, where upon loading the page, no header is highlighted, and as you
scroll, we highlight the top-most heading one by one.

Release Notes:

- N/A
2025-11-30 16:11:20 +00:00
Kunall Banerjee
0079044653 Add @yeskunall to REVIEWERS.conl (#43823)
Release Notes:

- N/A
2025-11-30 02:18:02 +00:00
John Tur
450cd3d42b Respect vertical placement offset for glyphs (#43812)
Before:
<img width="41" height="45" alt="image"
src="https://github.com/user-attachments/assets/0f8b1d0e-b0cf-483c-aa2d-77aadd90503c"
/>

After:
<img width="34" height="50" alt="image"
src="https://github.com/user-attachments/assets/fa3eb842-2d1b-49b1-9c37-ebe6e11b37eb"
/>



Release Notes:

- N/A
2025-11-29 17:04:04 -05:00
Danilo Leal
dd9cc90de9 docs: Add more design polish (#43802)
Fixing the theme toggle popover, search results design, and
sidebar/table of contents responsiveness.

Release Notes:

- N/A
2025-11-29 12:22:45 -03:00
Aero
8abf1d35be agent_ui: Fix delete icon event in history panel (#43796)
Summary

Fixed the thread deletion issue by:

1. Click handler conflict between the trash icon and the main ListItem
2. Added `cx.stop_propagation()` to prevent the click event from
bubbling up to the parent ListItem's click handler
3. Followed the established `cx.stop_propagation()` pattern used
throughout the codebase

Now when you click the trash icon to delete a thread:
-  The thread deletion happens immediately on the first click
-  No race condition between deletion and activation
-  No double-clicking required

The fix is minimal, follows established patterns, and addresses the
exact root cause of the issue.

- Stop event propagation in thread removal handler

Release Notes:

- agent: Improved delete thread action in the history view by preventing
it from also triggering the thread activation.
2025-11-29 14:20:15 +00:00
Xipeng Jin
e75e137a49 Fix the misalignment issue with Inline Assist (#43768)
Easy path to fix the issue #38755

<img width="787" height="157" alt="Screenshot 2025-11-28 at 9 39 55 PM"
src="https://github.com/user-attachments/assets/4943f668-c2d7-4c35-8fa7-a6fea839a99f"
/>

Release Notes:

- agent: Fixed the UI misalignment between old and new lines from the
inline assist.
2025-11-29 11:15:19 -03:00
Kirill Bulatov
9c593f32c8 proto: Bump to v0.2.3 (#43791)
Release Notes:

- N/A

---------

Co-authored-by: Finn Evers <finn@zed.dev>
2025-11-29 13:59:18 +00:00
Dario
e5ce7cb19a extensions: Pass protobuf-language-server settings and initialization_options to the LSP (#43787)
pass protobuf-language-server settings and initialization_options to the
protobuf-language-server

Closes #43786

Release Notes:

- N/A
2025-11-29 10:19:23 +00:00
Jakub Konka
f326854495 buffer_diff: Fix git gutter incorrectly showing for ignored files (#43776)
Closes #43734
Closes #43698

Release Notes:

- Fixed git gutter incorrectly showing up for ignored files.
2025-11-29 08:08:51 +01:00
Danilo Leal
200a4a5c78 docs: Improve table of content responsiveness (#43766)
Release Notes:

- N/A
2025-11-29 00:54:34 +00:00
Danilo Leal
be8605bb10 agent_ui: Make thread loading state clearer (#43765)
Closes https://github.com/zed-industries/zed/issues/43721

This PR makes the loading state clearer by disabling the message editor
while the agent is loading as well as pulsating the icon.

Release Notes:

- agent: Made the thread loading state clearer in the agent panel.
2025-11-29 00:52:44 +00:00
Danilo Leal
63eb3ea7e0 docs: Customize view transition and other layout shift improvements (#43763)
Follow up to https://github.com/zed-industries/zed/pull/43762.

Yet another shot at making the navigation between pages flicker less.

Release Notes:

- N/A
2025-11-28 23:05:58 +00:00
Danilo Leal
34b453cee6 docs: Reduce load flicker (#43762)
Follow up to https://github.com/zed-industries/zed/pull/43758.

This PR uses view transition animations to reduce the page flickering
when navigating between one and the other. Pretty cool CSS-only
solution.

Release Notes:

- N/A
2025-11-28 22:41:33 +00:00
Danilo Leal
b11f22bb9a Fix button in multibuffer header overflowing (#43761)
Closes https://github.com/zed-industries/zed/issues/43726

Note that the truncation strategy on the file path is not yet perfect.
I'm just applying the regular method from the label component, but we
should wrap path text from the start rather than from the end. Some time
in the future!

<img width="500" height="712" alt="Screenshot 2025-11-28 at 7  17@2x"
src="https://github.com/user-attachments/assets/6ebc618a-4b4a-42fd-b5b7-39fec3ae5335"
/>

Release Notes:

- Fixed a bug where the "Open File" button would overflow in the
multibuffer header if the file path was too long.
2025-11-28 22:29:07 +00:00
Danilo Leal
6040c0c00a agent_ui: Fix activity bar when plan and edited files list is long (#43759)
Closes https://github.com/zed-industries/zed/issues/43728

Release Notes:

- agent: Fixed a bug where the plan and edit files list would consume
the whole space of the thread if they were too long. They're now capped
by a max-height and scrollable.
2025-11-28 22:01:02 +00:00
Danilo Leal
e8a3368226 docs: Reduce layout shift when navigating between pages (#43758)
This is still not perfect, but it reduces the shift that happens when
navigating between pages that have and don't have the table of contents.
It also tries to reduce the theme flicker that happens by moving its
loading to an earlier moment.

Release Notes:

- N/A
2025-11-28 18:36:47 -03:00
procr1337
d4c0b87fb2 agent: Clarify include_pattern parameter usage for grep tool (#41225)
Adds clarifying examples and information about the `include_pattern`
parameter for the grep tool, analogous to the `path` parameter of the
read and list tools and others.

The lack of clarity led to unexpected agent behavior described in
#41189. This change is confirmed to improve the precision of Claude
Sonnet 4.5 when searching for code in an empty conversation (without it,
it leaves out the root directory in the query).

Closes #41189.

```
Release Notes:

- Clarify grep tool description to improve agent precision when using it with the `include_pattern` parameter
```
2025-11-28 15:51:17 -05:00
Junseong Park
6404939427 google_ai: Update Gemini models (#43117)
Closes #43040

Release Notes:

- Remove the end-of-support Gemini 1.5 model from the options.
- Remove the older Gemini 2.0 model from the options.
- Please let me know if you think it's better to keep it, as it is still
a usable model.
- Update the incorrect amounts for some input/output tokens.
- Update the default model to Gemini 2.5 Flash-Lite.
- Rename variant `Gemini3ProPreview` to `Gemini3Pro`

When this PR is merged, users will be able to select the following
Gemini models.

- 2.5 Flash
- 2.5 Flash-Lite
- 2.5 Pro
- 3 Pro
2025-11-28 15:48:33 -05:00
Danilo Leal
557d39332c docs: Add sidebar title collapse functionality and design facelift (#43754)
This PR adds the ability to collapse section in the docs sidebar (which
are persistent until you close the tab), and some design facelift to the
docs, which makes its design close to the site as well as polishing up
many elements and interactions (like moving the search to a modal and
making the table of content visible in smaller breakpoints).

<img width="600" height="2270" alt="Screenshot 2025-11-28 at 5  26@2x"
src="https://github.com/user-attachments/assets/3a8606c6-f74f-4bd2-84c8-d7a67ff97564"
/>

Release Notes:

- N/A
2025-11-28 17:36:50 -03:00
David Baldwin
1cc3a4cc9c settings ui: Add tab_bar.show_tab_bar_buttons to settings UI (#43746)
Release Notes:

- Added "Show Tab Bar Buttons" setting to Settings UI to control
visibility of New, Split Pane, and Zoom buttons in the tab bar.


<img width="899" height="750" alt="Screenshot 2025-11-28 at 12 37 08 PM"
src="https://github.com/user-attachments/assets/9894da7b-9702-4b37-9adb-ae0e43c87edf"
/>
2025-11-28 17:35:12 -03:00
Joseph T. Lyons
f6ab73033a Allow opening settings.json file from Settings UI with keybinding (#43747)
Release Notes:

- Added a keybinding to open the `settings.json` file when the Settings
UI is in focus: `cmd-,` (macOS) / `ctrl-,` (Windows / Linux).
- A tooltip was added to the `Edit in settings.json` button to show this
keybinding.
2025-11-28 18:34:52 +00:00
Lena
6c98003ffb Tweak the issue template for bugs (#43719)
- shorten some phrases to (hopefully) retain attention better
- move the required field up to not bore people with scrolling
- call it a bug and not an issue to further emphasize we want feature
requests in Discussions where the product managers are
- remove the default from the WSL question since not everyone does
WSL or even Windows

Release Notes:

- N/A

---------

Co-authored-by: Miguel Raz Guzmán Macedo <miguel@zed.dev>
Co-authored-by: Kunall Banerjee <hey@kimchiii.space>
2025-11-28 16:46:42 +01:00
Agus Zubiaga
7b4e050dd8 Use test db for command palette under vim tests (#43731) 2025-11-28 14:16:44 +00:00
Smit Barmase
7c967b8d1a language: Allow support for ESLint working directories (#43677)
Closes #9648 #9755

Release Notes:

- Added way to configure ESLint's working directories in settings. For
example:
`{"lsp":{"eslint":{"settings":{"workingDirectories":["./client","./server"]}}}}`

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2025-11-28 19:40:23 +05:30
Matthew Chisolm
478bcea6d3 docs: Add configuration explanation in git section (#43637)
Closes https://github.com/zed-industries/zed/issues/33441

Release Notes:

- Added configuration documentation for git-commit settings including preferred-line-length in the git panel

---------

Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
2025-11-28 13:26:54 +00:00
Mayank Verma
c18481ed13 git: Add UI for deleting branches (#42703)
Closes #42641

Release Notes:

- Added UI for deleting Git branches

---------

Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
2025-11-28 12:59:54 +00:00
Mayank Verma
87e3d6e014 git: Check push config before falling back to branch remote (#41700)
Closes #26649

Release Notes:

- Improved git support for remotes handling. Git will now check pushRemote and pushDefault configurations before
falling back to branch remote.

---------

Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
2025-11-28 11:47:21 +00:00
Lena
d877e562ab Add a script to label untriaged GH issues (#43711)
Release Notes:

- N/A
2025-11-28 10:24:59 +01:00
Lukas Wirth
b89bcbead6 editor: Speed up colorize_brackets slightly in large multibuffers (#43713)
There is still room for improvement here as `anchor(s)_in_excerpt` is
generally a bad API here due to it reseeking the entire excerpt tree
from the start on every call which we don't really need. But this at
least cuts the seeks down by a factor of 4 for now.

Release Notes:

- Improved performance of bracket colorization in large multibuffers
2025-11-28 09:16:59 +00:00
Lukas Wirth
8f8a92ccf0 editor: Reduce amount of sumtree traversals in header_and_footer_blocks (#43709)
Introduces new "mapping point cursors" for the different display map
layers allowing one to map multiple points in increasing order more
efficiently than using the one shot operations.

This is used in the `BlockMap::sync` for `header_and_footer_blocks`
which spends a significant time in sumtree traversal due to repeatedly
transforming points between the different layers. This effectively turns
the complexity of those operations from quadratic in the number of
excerpts to linear, as we only go through the respective sumtrees once
instead of restarting from the start over and over again.

Release Notes:

- Improved performance for editors of large multibuffers with many
different files
2025-11-28 09:06:59 +01:00
Lukas Wirth
fc213f1c26 git_ui: Introduce explicit yield points for project diff (#43706)
These are long running foreground tasks that tend to not have pending
await points, meaning once we start executing them they will never
reschedule themselves, starving the foreground thread.

Release Notes:

- Improved git project diff responsiveness
2025-11-28 07:46:09 +00:00
Maokaman1
f856a3ca89 askpass: Use double quotes for script name in helper command (#43689)
Replace single quotes with double quotes when referencing the askpass
script name in the helper command. The Windows command processor
(cmd.exe) requires double quotes for proper string handling, as single
quotes are treated as literal characters.

Error I get when trying to open remote project:
<img width="396" height="390" alt="image"
src="https://github.com/user-attachments/assets/1538ee10-8efc-4f80-a867-b367908091b6"
/>

```
Zed Nightly 0.216.0 
c2281779af
0.216.0+nightly.1965.c2281779af56bd52c829ccd31aae4eb82b682ebc
```

Release Notes:

- N/A
2025-11-28 08:18:19 +01:00
Danilo Leal
65e224c551 agent panel: Improve keybindings on Windows (#43692)
Closes https://github.com/zed-industries/zed/issues/43439

Release Notes:

- agent: Fixed keybinding for allowing a command to run in the agent
panel on Windows.
2025-11-27 22:31:48 +00:00
Libon
b17b903c57 Enhance color logic for phantom elements to improve visual distinction based on collision status (#42710)
This change modifies the color assignment for phantom elements in the
editor. If a phantom element collides with existing elements, it now
uses a custom color based on the theme's debugger accent with reduced
opacity. Otherwise, it defaults to the hint color.

BEFORE:
![20251114-0939-17
9919633](https://github.com/user-attachments/assets/4ec56f03-4708-4b35-b1ab-abdfd41c763e)

AFTER:
![20251114-0942-38
8596606](https://github.com/user-attachments/assets/6e9a98f1-a34d-4676-9dc6-b3e2b9f967bc)


Release Notes:

- Enhanced color logic for phantom elements to improve visual
distinction based on collision status.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-11-27 21:32:43 +00:00
Agus Zubiaga
77656a4091 sweep: Set use_bytes flag (#43687)
Without this new flag, the Sweep API will actually produce a combination
of UTF-16 and char indices, leading to incorrect edit positions.

Release Notes:

- N/A
2025-11-27 18:29:42 -03:00
Jakub Konka
05b999bb7c git: Move git-blame outside of git job queue (#43565)
We restructured `struct Repository` a bit so that it now stores a shared
task of `enum RepositoryState`. This way, we can now re-use it outside
of the git job queue when spawning a git job a standalone bg task. An
example here would be `git-blame` that does not require any file locks
and is not susceptible to git job ordering.

As a result of this change, loading (and modifying) a file that contains
a huge git history will no longer block other git operations on the repo
such as staging/unstaging/committing.

Release Notes:

- Improved overall git experience when loading buffers with massive git
history where they would block other git jobs from running (such as
staging/unstaging/commiting). Now, git-blame will run separately from
the git job queue on the side and the buffer with blame hints when
finished thus unblocking other git operations.

Co-authored-by: Cole Miller <cole@zed.dev>
2025-11-27 21:07:26 +00:00
Danilo Leal
d099ea048e docs: Improve content about text threads vs. threads (#43688)
Release Notes:

- N/A
2025-11-27 20:49:05 +00:00
Scott Churchley
518ea716ee agent_ui: Truncate file path in context completions (#42682)
Closes https://github.com/zed-industries/zed/issues/38753

Followed some prior art here: [a similar calculation
](e80b490ac0/crates/file_finder/src/file_finder.rs (L1105))
in `file_finder.rs`

Release Notes:

- improved visibility of long path names in context completions by
truncating on the left when space is insufficient to render the full
path

Before:
<img width="544" height="251" alt="Screenshot of overflowing file paths"
src="https://github.com/user-attachments/assets/8440710d-3f59-4273-92bb-3db85022b57c"
/>

After: 
<img width="544" height="251" alt="Screenshot of truncated file paths"
src="https://github.com/user-attachments/assets/e268ba87-7979-4bc3-8d08-9198b4baea02"
/>
2025-11-27 17:26:00 -03:00
Remco Smits
20b584398e checkbox: Fix showing cursor pointer for edge of the checkbox when it's disabled (#43577)
This PR fixes that you saw the cursor pointer for disabled checkbox.
This is only for the edge of the checkbox component, because we didn't
check for the disabled case.

**Before**


https://github.com/user-attachments/assets/cfebad01-533b-4515-b8d9-4bcb839eaec4

**After**


https://github.com/user-attachments/assets/969600de-081b-42df-a288-ca3db5758d12

Release Notes:

- N/A
2025-11-27 17:12:03 -03:00
Floyd Wang
28dde14a33 ui: Fix custom size preview example of vector component (#43633)
In the vector component custom size preview example, the image will
overflow the content box.

| Before | After |
| - | - |
| <img width="1235" height="1042" alt="Before"
src="https://github.com/user-attachments/assets/1c4ff772-2578-41b2-94e8-9e2d625484f8"
/> | <img width="1234" height="1044" alt="After"
src="https://github.com/user-attachments/assets/19d6ab1a-d169-417e-b550-1e422358288e"
/> |

Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-11-27 20:09:19 +00:00
Floyd Wang
f10afd1059 component_preview: Pin the filter input at the top (#43636)
When I scroll the page after selecting a component, the filter input
disappears. It would be more helpful if it stayed fixed at the top.

## Before


https://github.com/user-attachments/assets/e26031f1-d6e7-4ae2-a148-88f4c548a5cf

## After


https://github.com/user-attachments/assets/f120355c-74d3-4724-9ffc-71fb7e3a4586

Release Notes:

- N/A
2025-11-27 17:02:03 -03:00
Danilo Leal
45285ee345 settings ui: Fix debugger step granularity setting (#43686)
Closes https://github.com/zed-industries/zed/issues/43549

Release Notes:

- N/A
2025-11-27 19:38:52 +00:00
Xiaobo Liu
fa070c50e5 editor: Show "Toggle Excerpt Unfold" tooltip when buffer is fold (#43626)
Release Notes:

- N/A

---------

Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-11-27 19:33:48 +00:00
Xiaobo Liu
d97d4f3949 ui: Remove early return after painting existing menu (#43631)
Release Notes:

- Fixed right-click context menu UX issues

Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
2025-11-27 16:19:54 -03:00
Lukas Wirth
64633bade4 gpui(windows): Prioritize system messages when exceeding main thread task budget (#43682)
Release Notes:

- Improved responsiveness on windows when there is a lot of tasks
running in the foreground
2025-11-27 19:51:19 +01:00
Bennet Bo Fenner
d82be97963 acp: Support using @mentions after typing slash command (#43681)
Release Notes:

- acp: Allow using @mentions after typing slash command
2025-11-27 17:47:12 +00:00
Lukas Wirth
aa899f6d78 gpui: Give windows message loop processing chances when we overload the main thread with tasks (#43678)
This reduces hangs on windows when we have many tasks queued up on the
main thread that yield a lot.

Release Notes:

- Reduced hangs on windows in some situations
2025-11-27 17:14:42 +00:00
Danilo Leal
5f0212de5f Better promote edit prediction when signed out (#43665)
Introducing this little popover here that's aimed at better
communicating what Zed's built-in edit prediction feature is and how
much people can get of it for free by purely just signing in.

<img width="600" height="1914" alt="Screenshot 2025-11-27 at 9  50@2x"
src="https://github.com/user-attachments/assets/a7013292-f662-4cae-9a6f-0e69a4a4fa1d"
/>

Release Notes:

- N/A
2025-11-27 13:10:55 -03:00
Piotr Osiewicz
c2281779af auto_update: Tentatively prevent auto-update loop on Nightly again (#43670)
Release Notes:

- N/A
2025-11-27 14:08:43 +00:00
Piotr Osiewicz
02fbafcda6 release_version: Do not use prerelease field (#43669)
- **release_channel: Do not use prerelease channel for build id**
Prerelease channel specifiers always compare as less than to
non-prerelease, which led to 2 auto-update bugs fixed in
https://github.com/zed-industries/zed/pull/43595 and
https://github.com/zed-industries/zed/pull/43611.
We'll use a dot-delimited build specifiers in form:
release-channel.build_number.sha1 instead
- **auto_update: Do not display full build metadata in update
notification**

Release Notes:

- N/A
2025-11-27 13:46:43 +00:00
Ben Brandt
007d648f5e acp: Add a timeout when initializing an ACP agent so the user isn't waiting forever (#43663)
Sometimes we are unable to receive messages at all from an agent. This
puts on upper bound on the `initialize` call so we can at least give a
message to the user that something is wrong here.

30s might feel like too long, but I wanted to avoid some false positives
in case there was something an agent needed to do at startup. This will
still communicate to the user at some point that something is wrong,
rather than leave them waiting forever with no signal that something is
going wrong.

Release Notes:

- agent: Show an error message to the user if we are unable to
initialize an ACP agent in a reasonable amount of time.
2025-11-27 13:01:41 +00:00
Lena
bbdbfe3430 Auto-label new bugs/crashes with 'needs triage' (#43658)
Release Notes:

- N/A
2025-11-27 12:15:57 +01:00
Dino
ab96155d6a buffer_search: Fix replace buttons not working if search bar is not focused (#43569)
Update the way that both
`search::buffer_search::BufferSearchBar.replace_next` and
`search::buffer_search::BufferSearchBar.replace_all` are registered as
listeners, so that we don't require the replacement editor to be focused
in order for these listeners to be active, only requiring the
replacement mode to be active in the buffer search bar.

This means that, even if the user is focused on the buffer editor, if
the "Replace Next Match" or "Replace All Matches" buttons are clicked,
the replacement will be performed.

Closes #42471 

Release Notes:

- Fixed issue with buffer search bar where the replacement buttons
("Replace Next Match" & "Replace All Matches") wouldn't work if search
bar was not focused
2025-11-27 11:00:38 +00:00
Lukas Wirth
8e04706c4d editor: Fix panic in wrap_map (#43650)
Fixes ZED-3P9

We only clamped the end which for a completely wrong input could cause
us to construct a reversed range which will end up underflowing later
on.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-27 09:29:27 +00:00
Oleksiy Syvokon
99d7b2fa1d zeta2: Compute diff-aware chrF metric (#43485)
Zeta evals now include a character n-gram metric adapted for multi-edit diffs (“delta chrF”). It works as follows:

1. Reconstruct the original, golden (expected), and actual texts from unified diffs.
   - "original": the text before any edits
   - "golden": the text after applying the expected edits
   - "actual": the text after applying the actual edits

2. Compute n-gram count deltas between original→golden and original→actual.
   - n-grams are computed as in chrF (max n=6, whitespace ignored).

3. Compare these deltas to assess how well the actual edits match the expected edits.
   - As in standard chrF, classify n-grams as true positives, false positives, and false negatives, and report the F-beta score with beta=2.

Release Notes:

- N/A
2025-11-27 11:10:35 +02:00
Kirill Bulatov
91400e7489 Do not color html tags and other "long" "bracket pairs" (#43644)
Closes https://github.com/zed-industries/zed/issues/43621
Follow-up of https://github.com/zed-industries/zed/pull/43172

Release Notes:

- (Preview only) Fixed html tags incorrectly colorized
2025-11-27 08:37:34 +00:00
Piotr Osiewicz
82b768258f remote: Do not include prerelease and build meta in asset queries (#43611)
Closes #43580

Release Notes:

- (Preview only) Fixed failures to fetch remoting server (needed to run
remoting).
2025-11-26 23:55:11 +00:00
Finn Evers
8c355b5eee Improve extension_bump workflow (#43612)
This extends the extension CI workflow to create a tag once the version
is bumped on main.

Release Notes:

- N/A
2025-11-26 23:39:18 +00:00
Kirill Bulatov
54309f4a48 Account for greedy tree-sitter bracket matches (#43607)
Current approach is to colorize brackets based on their depth, which was
broken for markdown:

<img width="388" height="50" alt="image"
src="https://github.com/user-attachments/assets/bd8b6c2f-5a26-4d6b-a301-88675bf05920"
/>

Markdown grammar, for bracket queries
00e93bfa11/crates/languages/src/markdown/brackets.scm (L1-L8)

and markdown document `[LLM-powered features](./ai/overview.md), [bring
and configure your own API
keys](./ai/llm-providers.md#use-your-own-keys)`, matches first bracket
(offset 0) with two different ones:

* `[LLM-powered features]`

* `[LLM-powered features](./ai/overview.md), [bring and configure your
own API keys]`

which mix and add different color markers.

Now, in case multiple pairs exist for the same first bracket, Zed will
only colorize the shortest one:
<img width="373" height="33" alt="image"
src="https://github.com/user-attachments/assets/04b3f7af-8927-4a8b-8f52-de8b5bb063ac"
/>


Release Notes:

- Fixed bracket colorization mixing colors in markdown files
2025-11-26 23:22:13 +00:00
Finn Evers
958f1098b7 editor: Consider experimental theme overrides for colorized bracket invalidation (#43602)
Release Notes:

- Fixed a small issue where bracket colors would not be immediately
updated if `experimental_theme_overrides.accents` was changed.
2025-11-26 22:51:11 +00:00
Piotr Osiewicz
c366627642 auto-update: Fix auto-update loop with non-nightly channels (#43595)
There are 3 factors:
1. The Preview channel endpoint does not propagate versions with build
identifier (which we oh-so-conveniently store in pre-release field of
semver).
2. Preview build, once fetched, sees it's version *with* build
identifier (as that's baked into the binary).
3. Auto update logic treats versions with pre-release version as less
than versions without pre-release version.

This in turn makes any Preview client see itself as versioned like
0.214.4-123-asdf1234455311, whereas the latest version on the endpoint
is 0.214.4. Thus, the endpoint version is always more recent than the
client version, causing an update loop.

The fix is to ignore build identifier when comparing versions of
non-nightly channels. This should still let us introduce changes to
auto-update behavior in minor releases in the future.

Closes #43584

Release Notes:

- (Preview only): Fixed an update loop with latest Preview update.
2025-11-26 21:42:03 +00:00
Ole Jørgen Brønner
ae649c66ed Make key repeat rate on Wayland more precise (2) (#43589)
CLOSES  #39042

This is a reopening of #34985+. 

_Original descrioption_:

In Wayland, the client implement key repeat themself. In Zed this is
ultimately handled by the gpui crate by inserting a timer source into
the event loop which repeat itself if the key is still held down [1].

But it seems the processing of the repeated key event happen
synchronously inside the timer source handler, meaning the effective
rate become slightly lower (since the repeated timer is scheduled using
the 1/rate as delay).

I measured the event processing time on my laptop and it's typically
around 3ms, but sometimes spiking at 10ms. At low key repeat rates this
is probably not _very_ noticeable. I see the default in Zed is set to a
(measly) 16/s, but I assume most systems will use something closer to
25, which is a 40ms delay. So ~3ms is around 7.5% of the delay. At
higher rate the discrepancy become worse of course.

I can visible notice the spikes, and doing some crude stopwatch
measurements using gedit as a reference I can reproduce around 5-10%
slower rates in Zed.

IMO this is significant enough to warrant improving, especially since
some people can get quite used the repeat rate and might feel something
being "off" in Zed.

~~The suggested fix simply subtract the processing time from the next
delay timer.~~


[1] 32df726f3b/crates/gpui/src/platform/linux/wayland/client.rs (L1355)

Release Notes:

- Improved Wayland (Linux) key repeat rate precision
2025-11-26 22:16:50 +01:00
Agus Zubiaga
36a3b41f53 edit prediction: Request trigger (#43588)
Adds a `trigger` field to the zeta1/zeta2 prediction requests so that we
can distinguish between editor, diagnostic, and zeta-cli requests.

Release Notes:

- N/A
2025-11-26 20:34:29 +00:00
Agus Zubiaga
f89e5308e3 edit prediction: Report early-rejected predictions and fix cancel bug (#43585)
Many prediction requests end up being rejected early without ever being
set as the current prediction. Before this change, those cases weren’t
reported as rejections because the `request_prediction_with_*` functions
simply returned `Ok(None)`.

With this update, whenever we get a successful response from the
provider, we will return at least the `id`, allowing it to be properly
reported. The request now also includes a “reject reason,” since the
different variants carry distinct implications for prediction quality.

All of these scenarios are now covered by tests. While adding them, I
also found and fixed a bug where some cancelled predictions were
incorrectly being set as the current one.

Release Notes:

- N/A

---------

Co-authored-by: MrSubidubi <dev@bahn.sh>
2025-11-26 20:15:05 +00:00
Dino
61a414df77 Fix language server renaming when parent directory does not exist (#43499)
Update the `fs::RenameOptions` used by
`project::lsp_store::LocalLspStore.deserialize_workspace_edit` in order
to always set `create_parents` to `true`. Doing this ensures that we'll
always create the folders for the new file path provided by the language
server instead of failing to handle the request in case the parent

- Introduce `create_parents` field to `fs::RenameOptions`
- Update `fs::RealFs.rename` to ensure that the `create_parents` option
is respected

Closes #41820 

Release Notes:

- Fixed a bug where using language server's file renaming actions could
fail if the parent directory of the new file did not exist
2025-11-26 19:34:03 +00:00
Miguel Raz Guzmán Macedo
233b976441 Add WSL Linux choice and settings.json prompt for GitHub issue template (#43479)
Release Notes:

- N/A

---------

Co-authored-by: Kunall Banerjee <hey@kimchiii.space>
2025-11-26 11:57:27 -06:00
Piotr Osiewicz
6b92c1a47b workspace: Fix broken main build after #43518 (#43570)
*cough* merge queue *cough*

Release Notes:

- N/A
2025-11-26 17:21:22 +00:00
Jason Lee
1a23115773 gpui: Unify track_scroll method to receive a reference type (#43518)
Release Notes:

- N/A

This PR to change the `track_scroll` method to receive a reference type
like the
[Div#track_scroll](https://docs.rs/gpui/latest/gpui/trait.StatefulInteractiveElement.html#method.track_scroll),
[Div#track_focus](https://docs.rs/gpui/latest/gpui/trait.InteractiveElement.html#method.track_focus).


```diff
- .track_scroll(self.scroll_handle.clone())
+ .track_scroll(&self.scroll_handle)

- .vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
+ .vertical_scrollbar_for(&self.scroll_handle, window, cx)
```


56a2f9cfcf/crates/gpui/src/elements/div.rs (L1088-L1093)


56a2f9cfcf/crates/gpui/src/elements/div.rs (L613-L620)
2025-11-26 18:03:42 +01:00
Cole Miller
757c043171 Fix git features not working when a Windows host collaborates with a unix guest (#43515)
We were using `std::path::Path::strip_prefix` to determine which
repository an absolute path belongs to, which doesn't work when the
paths are Windows-style but the code is running on unix. Replace it with
a platform-agnostic implementation of `strip_prefix`.

Release Notes:

- Fixed git features not working when a Windows host collaborates with a
unix guest
2025-11-26 16:56:34 +00:00
Marshall Bowers
57e1bb8106 collab: Add zed-zippy[bot] to the GET /contributor endpoint (#43568)
This PR adds the `zed-zippy[bot]` user to the `GET /contributor`
endpoint so that it passes the CLA check.

Release Notes:

- N/A
2025-11-26 16:53:05 +00:00
Finn Evers
5403e74bbd Add callable workflow to bump the version of an extension (#43566)
This adds an intial workflow file that can be pulled in to create a bump
commit for an extension version in an extension repository.

Release Notes:

- N/A
2025-11-26 16:51:50 +00:00
Mayank Verma
0713ddcabc editor: Fix vertical scroll margin not accounting for file header height (#43521)
Closes #43178

Release Notes:

- Fixed vertical scroll margin not accounting for file header height

Here's the before/after:

With `{ "vertical_scroll_margin": 0 }` in `~/.config/zed/settings.json`


https://github.com/user-attachments/assets/418c6d7f-de0f-4da6-a038-69927b1b8b88
2025-11-26 16:06:02 +00:00
Joseph T. Lyons
6fbbc89904 Bump Zed to v0.216 (#43564)
Release Notes:

- N/A
2025-11-26 15:59:13 +00:00
Remco Smits
8aa53612fd agent_ui: Add support for deleting thread history (#43370)
This PR adds support for deleting your entire thread history. This is
inspired by a Zed user from the meetup in Amsterdam, he was missing this
feature.

**Demo**


https://github.com/user-attachments/assets/5a195007-1094-4ec6-902a-1b83db5ec508

Release Notes:

- AI: Add support for deleting your entire thread history

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-11-26 15:55:12 +00:00
David Kleingeld
6a311cad11 Detail how to add symbols to samply's output (#43472)
Release Notes:

- N/A
2025-11-26 15:46:17 +00:00
Peter Tripp
51e97d343d languages: Recognize .clangd as YAML (#43557)
Follow-up to: https://github.com/zed-industries/zed/pull/43469

Thanks @WeetHet for [the idea]([WeetHet](https://github.com/WeetHet)).

Release Notes:

- Added support for identifying. .clangd files as YAML by default
2025-11-26 15:55:23 +01:00
Lukas Wirth
c36b12f3b2 settings_ui: Pick a more reasonable minimum window size (#43556)
Closes https://github.com/zed-industries/zed/issues/41903

Release Notes:

- Fixed settings ui being forced larger than small screens
2025-11-26 14:32:25 +00:00
Finn Evers
7c724c0f10 editor: Do not show scroll thumb if page fits (#43548)
Follow-up to https://github.com/zed-industries/zed/pull/39367

Release Notes:

- Fixed a small issue where a scrollbar would sometimes show in the
editor although the content fix exactly on screen.
2025-11-26 13:11:47 +00:00
Lukas Wirth
1e6a05d0d8 askpass: Quote askpass script in askpass helper command (#43542)
Closes #40276

Release Notes:

- Fixed askpass execution failing on windows sometimes
2025-11-26 12:17:57 +00:00
Lukas Wirth
b9af6645e3 gpui: Return None for non-existing credentials in read_credentials on windows (#43540)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-26 12:01:01 +00:00
Lukas Wirth
c2cb76b026 rope: Turn ChunkSlice::slice panics into error logs (#43538)
While logically not really correct, its better than tearing down the
application until we figure out the root cause here

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-26 12:43:30 +01:00
ihavecoke
684a58fc84 Implement vertical scrolling for extended keymap load error information (#42542)
This PR fix an issue where, if an error occurs while loading the keymap
file during application startup, an excessively long error message would
be truncated and not fully displayed.

Before:

<img width="1567" height="1056" alt="before"
src="https://github.com/user-attachments/assets/ab80c204-b642-4e8f-aaf5-eae070ab01a2"
/>


After:

<img width="1567" height="1056" alt="image"
src="https://github.com/user-attachments/assets/1b2ff2ce-3796-41d5-b145-dc7d69f04f11"
/>


Release Notes:

- N/A
2025-11-26 10:09:26 +01:00
Floyd Wang
9150346a43 outline_panel: Fix the panel frequent flickering during search (#43530)
The outline panel flickers when searching or when the file content
changes. This happens because an empty UI appears during the search
process, but it only lasts for a few milliseconds, so we can safely
ignore it.

## Before

https://github.com/user-attachments/assets/9b409827-75ee-4a45-864a-58f0ca43191f

## After

https://github.com/user-attachments/assets/b6d48143-1f1a-4811-8754-0a679428eec2

Release Notes:

- N/A
2025-11-26 09:03:52 +00:00
Bhuminjay Soni
425d4c73f3 git: Use correct file mode when staging (#41900)
Closes #28667

Release Notes:

- Fixed git not preserving file mode when committing. Now if an input file is executable it will be preserved when committed with Zed.

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <bhuminjaysoni@gmail.com>
Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
2025-11-26 09:01:20 +00:00
Lukas Wirth
00e93bfa11 shell: Correctly identifiy powershell shells on windows (#43526)
Release Notes:

- Fixed zed only finding pwsh but not powershell on windows
2025-11-26 08:00:46 +00:00
Oscar Vargas Torres
9d8b5077b4 zeta: Avoid logging an error for not having SWEEP_AI_TOKEN (#43504)
Closes #43503 

Release Notes:

- Fixes ERROR No SWEEP_AI_TOKEN environment variable set

Co-authored-by: oscarvarto <oscarvarto@users.noreply.github.com>
2025-11-26 07:48:06 +01:00
qystishere
3072133e59 Improve bash detection on Windows (#43455)
I have git installed via [scoop](https://scoop.sh). The current
implementation finds `git.exe` in scoop's shims folder and then tries to
find `bash.exe` relative to it.

For example, `git.exe` (shim) is located at:
```
C:\Users\<username>\scoop\shims\git.exe
```

And the code tries to find `bash.exe` at:
```
C:\Users\<username>\scoop\shims\..\bin\bash.exe
```
which doesn't exist.

This PR changes the logic to first check if `bash.exe` is available in
PATH (using `which::which`), and only falls back to the git-relative
path if that fails.
2025-11-26 07:45:50 +01:00
Anthony Eid
56a2f9cfcf Revert "git: Make the version_control.{deleted/added} colors more accessible" (#43512)
Reverts zed-industries/zed#43475

The colors ended up being too dark. Zed adds an opacity to the
highlights.


e13e93063c/crates/editor/src/element.rs (L9195-L9200)


Reverting to avoid having the colors go out in preview will fix shortly
after.
2025-11-26 03:58:29 +00:00
Anthony Eid
88ef5b137f terminal: Update search match highlights on resize (#43507)
The fix for this is emitting a wake-up event to tell the terminal to
recalculate its search highlights on resize.

Release Notes:

- terminal: Fix bug where search match highlights wouldn't update their
position when resizing the terminal.
2025-11-26 03:45:54 +00:00
Max Brunsfeld
e13e93063c Avoid continuing zeta requests that are cancelled before their throttle (#43505)
Release Notes:

- N/A
2025-11-25 17:33:10 -08:00
Peter Tripp
98e369285b languages: Recognize .clang-format as YAML (#43469)
Clang-Format uses uses a YAML config file format.

Use YAML language by default for `.clang-format` and `_clang-format`
filenames.
([source](https://clang.llvm.org/docs/ClangFormatStyleOptions.html))
Add `#yaml-language-server: $schema` to `.clang-format` example in C
language docs.

Release Notes:

- Added support for identifying. `.clang-format` files as YAML by
default
2025-11-26 01:31:52 +02:00
John Tur
6548eb74f1 Upgrade python-environment-tools (#43496)
Fixes https://github.com/zed-industries/zed/issues/42554
Fixes https://github.com/zed-industries/zed/issues/43383

Release Notes:

- python: Added support for detecting uv workspaces as toolchains.
- windows: Fixed console windows sometimes appearing when opening Python
files.
2025-11-25 18:05:59 -05:00
Mikayla Maki
53eb35f5b2 Add GPT 5.1 to Zed BYOK (#43492)
Release Notes:

- Added support for OpenAI's GPT 5.1 model to BYOK
2025-11-25 14:17:27 -08:00
Joseph T. Lyons
877763b960 More tweaks to collaboration docs (#43494)
Release Notes:

- N/A
2025-11-25 22:08:39 +00:00
Joseph T. Lyons
d490443286 Refresh collaboration docs (#43489)
Most of the features for collab were previously listed in the section
that was written for private calls. Most of this PR is moving that
content over to the channel documentation and adapting it slightly.
Private calls have similar collaboration, so we can just point back to
the channels doc in that section and keep it pretty thin / DRY.

Release Notes:

- N/A
2025-11-25 16:06:46 -05:00
Agus Zubiaga
1f9d5ef684 Always display terminal cursor when blinking is disabled (#43487)
Fixes an issue where the terminal cursor wouldn't always be displayed in
the default `blink: "terminal_controlled"` mode unless the terminal
requested cursor blinking.

Release Notes:

- N/A
2025-11-25 19:49:16 +00:00
Peter Tripp
83f0a3fd13 Redact sensitive environment variables in LSP Logs: Server Info (#43480)
Follow-up to: 
- https://github.com/zed-industries/zed/pull/43436
- https://github.com/zed-industries/zed/pull/42831

The changes in #42831 resulted in a regression where environment
variables in the Server Info view were no longer redact. The changes in
#43436 were insufficient as I was still seeing sensitive values in
Nightly e6fe95b4f2 (which includes
#43436).

CC: @SomeoneToIgnore (Hi! 👋 Thanks for keeping this redaction
functionality alive)

Release Notes:

- N/A
2025-11-25 21:00:31 +02:00
Ben Kunkle
7ecbf8cf60 zeta2: Remove expected context from evals (#43430)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-25 13:44:04 -05:00
Max Brunsfeld
fb0fcd86fd Add missing update of last_prediction_refresh (#43483)
Fixes a regression introduced in
https://github.com/zed-industries/zed/pull/43284 where edit predictions
stopped being throttled at all 😬

Release Notes:

- N/A

Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-11-25 10:43:46 -08:00
Max Brunsfeld
36708c910a Separate experimental edit prediction jumps feature from the Sweep AI prediction provider (#43481)
Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-11-25 10:36:45 -08:00
Smit Barmase
388fda2292 editor: Fix package version completion partial accept and improve sorting (#43473)
Closes #41723

This PR fixes an issue with accepting partial semver completions by
including `.` in the completion query. This makes the editor treat the
entire version string as the query, instead of breaking segment at last
`.` .

This PR also adds a test for sorting semver completions. The actual
sorting fix is handled in the `package-version-server` by having it
provide `sort_text`. More:
https://github.com/zed-industries/package-version-server/pull/10

<img width="600" alt="image"
src="https://github.com/user-attachments/assets/7657912f-c6da-4e05-956b-1c044918304f"
/>

Release Notes:

- Fixed an issue where accepting a completion for a semver version in
package.json would append the suggestion to the existing text instead of
replacing it.
- Improved the sorting of semver completions in package.json so the
latest versions appear at the top.
2025-11-25 23:27:11 +05:30
Bennet Bo Fenner
94f9b85859 acp: Only pass enabled MCP servers to agent (#43467)
Release Notes:

- Fix an issue where ACP agents would start MCP servers that were
disabled in Zed
2025-11-25 18:29:45 +01:00
Anthony Eid
1c072017a4 git: Make the version_control.{deleted/added} colors more accessible (#43475)
The new colors are easier to tell apart for people that are colorblind

cc: @mattermill 

## One Dark
### Before
<img width="723" height="212" alt="Screenshot 2025-11-25 at 12 13 14 PM"
src="https://github.com/user-attachments/assets/cea67b08-5662-4afa-8119-dbfcef53ada7"
/>

### After
<img width="711" height="109" alt="Screenshot 2025-11-25 at 12 14 16 PM"
src="https://github.com/user-attachments/assets/a42d88ea-1a85-4f48-8f5e-b9bedf321c62"
/>

## One Light
### Before
<img width="724" height="219" alt="Screenshot 2025-11-25 at 12 15 13 PM"
src="https://github.com/user-attachments/assets/c0176b8c-12bf-451c-8a2c-a2efd15463d1"
/>
### After
<img width="723" height="209" alt="Screenshot 2025-11-25 at 12 15 45 PM"
src="https://github.com/user-attachments/assets/b8858a11-29e2-4309-b1a6-c734f89f6d5e"
/>

Release Notes:

- N/A
2025-11-25 17:29:26 +00:00
Richard Feldman
8a992703a7 Add Gemini 3 support to Copilot (#43096)
Closes #43024

Release Notes:

- Add support for Gemini 3 to Copilot
2025-11-25 12:15:55 -05:00
Joseph T. Lyons
2053fea0a7 Add collaboration redirects (#43471)
Redirect:

https://zed.dev/docs/collaboration ->
https://zed.dev/docs/collaboration/overview
https://zed.dev/docs/channels ->
https://zed.dev/docs/collaboration/channels

Release Notes:

- N/A
2025-11-25 11:28:33 -05:00
Jakub Konka
552bc02783 git: Bring back auto-commit suggestions (#43470)
This got accidentally regressed in
https://github.com/zed-industries/zed/pull/42149.

Release Notes:

- Fixed displaying auto-commit suggestions for single staged entries.
2025-11-25 16:26:44 +00:00
Lukas Wirth
fafe1afa61 multi_buffer: Remove redundant buffer id field (#43459)
It is easy for us to get the two fields out of sync causing weird
problems, there is no reason to have both here so.

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Co-authored by: Antonio Scandurra <antonio@zed.dev>
2025-11-25 17:13:16 +01:00
Bennet Bo Fenner
ab80ef1845 mcp: Fix source property showing up as undefined in settings (#43417)
Follow up to #39021.

<img width="576" height="141" alt="image"
src="https://github.com/user-attachments/assets/c89885a4-e664-4614-9bb0-86442dff34ee"
/>

- Add migration to remove `source` tag because `ContextServerSettings`
is now untagged
- Fix typos in context server modal
- PR seems to have removed the `test_action_namespaces` test, which I
brought back in this PR

Release Notes:

- Fixed an issue where the `source` property of MCP settings would show
up as unrecognised
2025-11-25 16:03:21 +00:00
Joseph T. Lyons
9cae39449a Restructure collaboration docs (#43464)
Overview
    - Channels
    - Private calls

---

Up next would be to 

- [ ] Update any zed.dev links to point to items in this structure
- [ ] Update content in these docs (would prefer to do that in a
separate PR from this one)

Release Notes:

- N/A
2025-11-25 10:53:47 -05:00
Jason Lee
f58de21068 miniprofiler_ui: Improve MiniProfiler to use uniform list (#43457)
Release Notes:

- N/A

---

- Apply uniform_list for timing list for performance.
- Add paddings for window.
- Add space to `ms`, before: `100ms` after `100 ms`.

## Before 

<img width="1392" height="860" alt="image"
src="https://github.com/user-attachments/assets/9706a96f-7093-4d4f-832f-306948a9b17b"
/>

## After 

<img width="1392" height="864" alt="image"
src="https://github.com/user-attachments/assets/38df1b71-15e7-4101-b0c9-ecdcdb7752d7"
/>
2025-11-25 16:08:49 +01:00
David Kleingeld
1cbb49864c document how to do flamecharts in an easy way (#43461)
Release Notes:

- N/A
2025-11-25 15:01:38 +00:00
Lukas Wirth
f8965317c3 multi_buffer: Fix up some anchor checks (#43454)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-25 13:41:19 +00:00
David Kleingeld
a359a5a1f2 Add performance doc (#43265)
Release Notes:

- N/A
2025-11-25 13:49:27 +01:00
Piotr Osiewicz
7651854bbd ci: Do not show output of failed tests at the end too (#43449)
This reverts #39643, effectively

For the record, @SomeoneToIgnore found it quite cumbersome to scroll
through logs just to see which tests have failed. I kinda see the
argument. At the same time, I wish nextest could do both: it could
aggregate logs of failed tests and then print out the summary.

Release Notes:

- N/A
2025-11-25 09:22:00 +00:00
AidanV
5139cc2bfb helix: Fix Vim::NextWordEnd off-by-one in HelixSelect (#43234)
Closes #43209
Closes #38121

Starting on the first character.
Running `v e` before changes: 
<img width="410" height="162" alt="image"
src="https://github.com/user-attachments/assets/ee13fa29-826c-45c0-9ea0-a598cc8e781a"
/>

Running `v e` after changes:
<img width="483" height="166" alt="image"
src="https://github.com/user-attachments/assets/24791a07-97df-47cd-9ef2-171522adb796"
/>

Change Notes:

- Added helix selection sanitation code that directly mirrors the code
in the Vim
[`visual_motion`](b6728c080c/crates/vim/src/visual.rs (L237))
method. I kept the comments from the Vim section that explains its
purpose.
- The above change converted the problem from fixing `v e` to fixing `v
w`. Since `w` is treated differently in Helix than in Vim (i.e. `w` in
Vim goes to the first character of a word and `w` in Helix goes to the
character before a word. Commented
[here](b6728c080c/crates/vim/src/helix.rs (L132))),
the code treats `w` in `HelixSelect` as a motion that differs from the
Vim motion in the same way that the function
[`helix_move_cursor`](b6728c080c/crates/vim/src/helix.rs (L353))
separates these behaviors.
- Added a regression test

Release Notes:

- Fixes bug where `Vim::NextWordEnd` in `HelixSelect` would not select
whole word.
2025-11-25 10:20:01 +01:00
Piotr Osiewicz
c0e85481b0 lsp: Fix potential double didClose notification when renaming a file (#43448)
Closes #42709

Release Notes:

- N/A
2025-11-25 09:11:43 +00:00
Kirill Bulatov
e6fe95b4f2 Only show ssh logs when toggled (#43445)
Same as in collab projects.

Release Notes:

- N/A
2025-11-25 08:25:49 +00:00
Kirill Bulatov
303c23cf1e Fix first window open not focusing the modals (#43180)
Closes https://github.com/zed-industries/zed/issues/4357
Closes https://github.com/zed-industries/zed/issues/41278

Release Notes:

- Fixed modals not getting focus on window reopen

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-11-25 07:34:23 +00:00
Ole Jørgen Brønner
0e2041dd41 multi_buffer: Fix editor::ExpandExcerpts failing when cursor is at excerpt start (#42324)
The bug is easily verified by:

1. open any multi-buffer
2. place the cursor at the beginning of an excerpt
3. run the editor::ExpandExcerpts / editor: expand excerpts action
4. The excerpt is not expanded

Since the `buffer_ids_for_range` function basically did the same and had
even been changed the same way earlier I DRYed these functions as well.

Note: I'm a rust novice, so keep an extra eye on rust technicalities
when reviewing :)

---

Release Notes:

- Fix editor: expand excerpts failing when cursor is at excerpt start

---------

Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
2025-11-25 08:21:18 +01:00
Max Brunsfeld
9122dd2d70 Combine zeta and zeta2 edit prediction providers (#43284)
We've realized that a lot of the logic within an
`EditPredictionProvider` is not specific to a particular edit prediction
model / service. Rather, it is just the generic state management
required to perform edit predictions at all in Zed. We want to move to a
setup where there's one "built-in" edit prediction provider in Zed,
which can be pointed at different edit prediction models. The only logic
that is different for different models is how we construct the prompt,
send the request, and parse the output.

This PR also changes the behavior of the staff-only `zeta2` feature flag
so that in only gates your *ability* to use Zeta2, but you can still use
your local settings file to choose between different edit prediction
models/services: zeta1, zeta2, and sweep.

This PR also makes zeta1's outcome reporting and prediction-rating
features work with all prediction models, not just zeta1.

To do:
* [x] remove duplicated logic around sending cloud requests between
zeta1 and zeta2
* [x] port the outcome reporting logic from zeta to zeta2.
* [x] get the "rate completions" modal working with all EP models
   * [x] display edit prediction diff
   * [x] show edit history events
* [x] remove the original `zeta` crate.

Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-11-24 22:17:48 -08:00
Kirill Bulatov
17d7988ad4 Redact environment variables in server info view (#43436)
Follow-up of https://github.com/zed-industries/zed/pull/42831

Release Notes:

- N/A
2025-11-24 22:05:16 +00:00
Julia Ryan
8fd2e2164c Fix remote project snippet duplication (#43429)
Closes #43311

Release Notes:

- N/A

---------

Co-authored-by: John Tur <john-tur@outlook.com>
2025-11-24 21:54:18 +00:00
Kirill Bulatov
e499f157dd Keep single default PHP language server (#43432)
9a119b18ee/extension.toml
provides 3 language servers for `php`, so `...` will always include all
3 if those are not excluded or included explicitly.

Change the configs and docs so, that only one php language server is
used.

Release Notes:

- N/A
2025-11-24 23:46:55 +02:00
Julia Ryan
f75e7582e6 Fix zed cli in NixOS WSL instances (#43433)
This fixes running `zed <path>` inside nixos wsl instances. We're
copying the approach used elsewhere which is to try using `--exec`
first, and if that fails use an actual shell which should cover the
nixos case because it only puts binaries on your PATH inside the
`/etc/profile` script which is sourced on shell startup.

Release Notes:

- N/A

---------

Co-authored-by: John Tur <john-tur@outlook.com>
2025-11-24 15:46:13 -06:00
Mayank Verma
9e69ac889c editor: Fix copy file actions not working in remote environments (#43362)
Closes #42500

Release Notes:

- Fixed all three editor actions not working in remote environments
  - `editor: copy file name`
  - `editor: copy file location`
  - `editor: copy file name without extension`

Here's the before/after:




https://github.com/user-attachments/assets/bfb03e99-2e1a-47a2-bd26-280180154fe3
2025-11-24 21:45:12 +00:00
Lennart
769464762a vim: Fix cursor shape after deactivation (#42834)
Update the `Vim.deactivate` method to ensure that the cursor shape is
reset to the one available in the user's settings, in the `cursor_shape`
setting, instead of simply defaulting to `CursorShape::Bar`.

In order to test this behavior, the `Editor.cursor_shape` method was
also introduced.

Release Notes:

- Fixed the cursor shape reset in vim mode deactivation, ensuring that
the user's `cursor_shape` setting is used

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>
2025-11-24 21:31:20 +00:00
Mayank Verma
342eba6f22 project: Send LSP metadata to remote ServerInfo (#42831)
Closes #39582

Release Notes:

- Added LSP metadata to remote ServerInfo

Here's the before/after:


https://github.com/user-attachments/assets/1057faa5-82af-4975-abad-5e10e139fac1

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2025-11-24 23:16:35 +02:00
Mikayla Maki
bd2c1027fa Add support for Opus 4.5 (#43425)
Adds support for Opus 4.5
- [x] BYOK
- [x] Amazon Bedrock

Release Notes:

- Added support for Opus 4.5

Co-authored-by: Richard Feldman <oss@rtfeldman.com>
2025-11-24 20:01:43 +00:00
localcc
d295ff4f04 Improve Windows path canonicalization (#43423)
Path canonicalization on windows will now favor keeping the drive letter
intact when canonicalizing paths. This helps some lsps with mapped
network drive compatibility.

Closes #41336 

Release Notes:

- N/A
2025-11-24 20:48:16 +01:00
morgankrey
7ce4f2ae62 Opus 4.5 and Gemini 3 to docs (#43424)
Add Opus 4.5 and Gemini 3 to docs

Release Notes:

- N/A
2025-11-24 13:38:14 -06:00
Kunall Banerjee
092250b4fa Rework and consolidate issue templates (#43403)
We’re reworking our triage process and in doing so, reworking our issue
templates is worth looking into. We have multiple issue templates, for
arbitrary categories, and not enough enforcement. The plan is to
consolidate the issue templates (maybe all into one) and drop the
others.

Release Notes:

- N/A
2025-11-24 14:12:54 -05:00
Yeoh Joer
b577f8a5ea Passthrough env to npm subcommands when using the system node runtime (#43102)
Closes #39448
Closes #37866

This PR expands the env-clearing fix from #42587 to include the
SystemNodeRuntime, which covers Node.js installations managed by Mise.
When running under the system runtime, npm subcommands were still
launched with a cleared environment, preventing variables such as
MISE_DATA_DIR from reaching the shim or the mise binary itself. As a
result, Mise finds the npm binary in the default MISE_DATA_DIR,
consistent with the behavior described in
https://github.com/zed-industries/zed/issues/39448#issuecomment-3433644569.

This change ensures that environment variables are passed through for
npm subcommands when using the system Node runtime, restoring expected
behavior for Mise-managed Node installations. This also fixes cases
where envs are used by npm itself.

Release Notes:

- Enable environment passthrough for npm subcommands
2025-11-24 11:08:45 -08:00
Danilo Leal
4329a817aa ui: Update ThreadItem component design (#43421)
Release Notes:

- N/A
2025-11-24 18:31:13 +00:00
Richard Feldman
6631d8be4e Fix Gemini 3 on OpenRouter (#43416)
Release Notes:

- Gemini 3 now works on OpenRouter in the Agent Panel
2025-11-24 13:24:26 -05:00
Agus Zubiaga
a7fff59136 Add each panel to the workspace as soon as it's ready (#43414)
We'll now add panels to the workspace as soon as they're ready rather
than waiting for all the rest to complete. We should strive to make all
panels fast, but given that their load tasks are fallible and do IO,
this approach seems more resilient.

Additionally, we'll now start loading the agent panel at the same time
as the rest.

Release Notes:

- workspace: Add panels as soon as they are ready
2025-11-24 14:41:40 -03:00
AidanV
4a36f67f94 vim: Fix bug where d . . freezes the editor (#42145)
This bug seems to be caused by pushing an operator (i.e. `d`) followed
by a repeat (i.e. `.`) so the recording includes the push operator and
the repeat. When this is repeated (i.e. `.`) it causes an infinite loop.

This change fixes this bug by pushing a ClearOperator action if there is
an ongoing recording when repeat is called.

Release Notes:

- Fixed bug where pressing `d . .` in Vim mode would freeze the editor.

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>
2025-11-24 16:55:19 +00:00
HuaGu-Dragon
47e8946581 Attempt to fix go to the end of the line when using helix mode (#41575)
Closes #41550

Release Notes:

- Fixed `<g-l>` behavior in helix mode which will now correctly go to the last charactor of the line.
- Fixed not switching to helix normal mode when in default vim context and pressing escape.

---------

Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
2025-11-24 17:32:30 +01:00
Oleksiy Syvokon
ea7568ceb3 zeta2: Support experimental 1120-seedcoder model (#43411)
1. Introduce a common `PromptFormatter` trait
2. Let models define their generation params.
3. Add support for the experimental 1120-seedcoder prompt format


Release Notes:

- N/A
2025-11-24 16:27:11 +00:00
Kirill Bulatov
e6b42a2be2 Use a proper name for highlights.scm (#43412)
Release Notes:

- N/A
2025-11-24 16:15:38 +00:00
Piotr Osiewicz
7bbc65ea71 auto_updater: Fix upload-nightly.ps1 and auto-update check (#43404)
Release Notes:

- N/A
2025-11-24 16:39:17 +01:00
Danilo Leal
d6c550c838 debugger_ui: Add button to close the panel when docked to bottom (#43409)
This PR adds a button to close the panel when it is docked to the
bottom. Effectively, the button triggers the same `ToggleBottomDock`
action that clicking on the button that opened the panel triggers, but I
think having it there just makes it extra obvious how to close it, which
is beneficial.

As a bonus, also fixed the panel controls container height when it is
docked to the sides, so it perfectly aligns with the panel tabbar
height.

| Perfectly Aligned Header | Close Button |
|--------|--------|
| <img width="2620" height="2010" alt="Screenshot 2025-11-24 at 12  01
2@2x"
src="https://github.com/user-attachments/assets/08a50858-1b50-4ebd-af7a-c5dae32cf4f6"
/> | <img width="2620" height="2010" alt="Screenshot 2025-11-24 at 12 
01@2x"
src="https://github.com/user-attachments/assets/17a6eee0-9934-4949-8741-fffd5b106e95"
/> |

Release Notes:

- N/A
2025-11-24 12:28:23 -03:00
Danilo Leal
eff592c447 agent_ui: Refine "reject"/"keep" behavior when regenerating previous prompts (#43347)
Closes https://github.com/zed-industries/zed/issues/42753

Consider the following flow: you submit prompt A. Prompt A generates
some edits. You don't click on either "reject" or "keep"; they stay in a
pending state. You then submit prompt B, but before the agent outputs
any response, you click to edit prompt B, thus submitting a
regeneration.

Before this PR, the above flow would make the edits originated from
prompt A to be auto-rejected. This feels very incorrect and can surprise
users when they see that the edits that were pending got rejected. It
feels more correct to only auto-reject changes if you're regenerating
the prompt that directly generated those edits in the first place. Then,
it also feels more correct to assume that if there was a follow-up
prompt after some edits were made, those edits were passively
"accepted".

So, this is what this PR is doing. Consider the following flow to get a
picture of the behavior change:
- You submit prompt A. 
- Prompt A generates some edits. 
- You don't click on either "reject" or "keep"; they're pending. 
- You then submit prompt B, but before the agents outputs anything, you
click to edit prompt B, submitting a regeneration.
- Now, edits from prompt A will be auto-kept.

Release Notes:

- agent: Improved the "reject"/"keep" behavior when regenerating older
prompts by auto-keeping pending edits that don't originate from the
prompt to-be-regenerated.
2025-11-24 11:13:38 -03:00
Vasyl Protsiv
138286f3b1 sum_tree: Make SumTree::append run in logarithmic time (#43349)
The `SumTree::append` method is slow when appending large trees to small
trees. The reason is this code here:

f57f4cd360/crates/sum_tree/src/sum_tree.rs (L628-L630)

`append` is called recursively until `self` and `other` have the same
height, effectively making this code `O(log^2 n)` in the number of
leaves of `other` tree in the worst case.

There are no algorithmic reasons why appending large trees must be this
much slower.

This PR proves it by providing implementation of `append` that works in
logarithmic time regardless if `self` is smaller or larger than `other`.

The helper method `append_large` has the symmetric logic to
`push_tree_recursive` but moves the (unlikely) case of merging
underflowing node in a separate helper function to reduce stack usage. I
am a bit unsure about some implementation choices made in
`push_tree_recursive` and would like to discuss some of these later, but
at the moment I didn't change anything there and tried to follow the
same logic in `append_large`.

We might also consider adding `push_front`/`prepend` methods to
`SumTree`.

I did not find a good benchmark that covers this case so I added a new
one to rope benchmarks.

<details>
<summary>cargo bench (compared to current main)</summary>

```
     Running benches\rope_benchmark.rs (D:\zed\target\release\deps\rope_benchmark-59c669d2895cd2c4.exe)
Gnuplot not found, using plotters backend
push/4096               time:   [195.67 µs 195.75 µs 195.86 µs]
                        thrpt:  [19.944 MiB/s 19.955 MiB/s 19.964 MiB/s]
                 change:
                        time:   [+0.2162% +0.3040% +0.4057%] (p = 0.00 < 0.05)
                        thrpt:  [-0.4040% -0.3030% -0.2157%]
                        Change within noise threshold.
Found 14 outliers among 100 measurements (14.00%)
  2 (2.00%) low mild
  6 (6.00%) high mild
  6 (6.00%) high severe
Benchmarking push/65536: Warming up for 3.0000 s
Warning: Unable to complete 100 samples in 5.0s. You may wish to increase target time to 7.8s, enable flat sampling, or reduce sample count to 50.
push/65536              time:   [1.4431 ms 1.4485 ms 1.4546 ms]
                        thrpt:  [42.966 MiB/s 43.147 MiB/s 43.310 MiB/s]
                 change:
                        time:   [-3.2257% -1.2013% +0.6431%] (p = 0.27 > 0.05)
                        thrpt:  [-0.6390% +1.2159% +3.3332%]
                        No change in performance detected.
Found 11 outliers among 100 measurements (11.00%)
  1 (1.00%) low mild
  5 (5.00%) high mild
  5 (5.00%) high severe

append/4096             time:   [15.107 µs 15.128 µs 15.149 µs]
                        thrpt:  [257.86 MiB/s 258.22 MiB/s 258.58 MiB/s]
                 change:
                        time:   [+0.9650% +1.5256% +1.9057%] (p = 0.00 < 0.05)
                        thrpt:  [-1.8701% -1.5026% -0.9557%]
                        Change within noise threshold.
Found 2 outliers among 100 measurements (2.00%)
  1 (1.00%) low mild
  1 (1.00%) high severe
append/65536            time:   [1.2870 µs 1.4496 µs 1.6484 µs]
                        thrpt:  [37.028 GiB/s 42.106 GiB/s 47.425 GiB/s]
                 change:
                        time:   [-28.699% -16.073% -0.3133%] (p = 0.04 < 0.05)
                        thrpt:  [+0.3142% +19.151% +40.250%]
                        Change within noise threshold.
Found 17 outliers among 100 measurements (17.00%)
  1 (1.00%) high mild
  16 (16.00%) high severe

slice/4096              time:   [30.580 µs 30.611 µs 30.639 µs]
                        thrpt:  [127.49 MiB/s 127.61 MiB/s 127.74 MiB/s]
                 change:
                        time:   [-2.2958% -0.9674% -0.1835%] (p = 0.08 > 0.05)
                        thrpt:  [+0.1838% +0.9769% +2.3498%]
                        No change in performance detected.
slice/65536             time:   [614.86 µs 795.04 µs 1.0293 ms]
                        thrpt:  [60.723 MiB/s 78.613 MiB/s 101.65 MiB/s]
                 change:
                        time:   [-12.714% +7.2092% +30.676%] (p = 0.52 > 0.05)
                        thrpt:  [-23.475% -6.7244% +14.566%]
                        No change in performance detected.
Found 14 outliers among 100 measurements (14.00%)
  14 (14.00%) high severe

bytes_in_range/4096     time:   [3.3298 µs 3.3416 µs 3.3563 µs]
                        thrpt:  [1.1366 GiB/s 1.1416 GiB/s 1.1456 GiB/s]
                 change:
                        time:   [+2.0652% +3.0667% +4.3765%] (p = 0.00 < 0.05)
                        thrpt:  [-4.1930% -2.9754% -2.0234%]
                        Performance has regressed.
Found 2 outliers among 100 measurements (2.00%)
  2 (2.00%) high severe
bytes_in_range/65536    time:   [80.640 µs 80.825 µs 81.024 µs]
                        thrpt:  [771.38 MiB/s 773.28 MiB/s 775.05 MiB/s]
                 change:
                        time:   [-0.6566% +1.0994% +2.9691%] (p = 0.27 > 0.05)
                        thrpt:  [-2.8835% -1.0875% +0.6609%]
                        No change in performance detected.
Found 10 outliers among 100 measurements (10.00%)
  2 (2.00%) high mild
  8 (8.00%) high severe

chars/4096              time:   [763.17 ns 763.68 ns 764.36 ns]
                        thrpt:  [4.9907 GiB/s 4.9952 GiB/s 4.9985 GiB/s]
                 change:
                        time:   [-2.1138% -0.7973% +0.1096%] (p = 0.18 > 0.05)
                        thrpt:  [-0.1095% +0.8037% +2.1595%]
                        No change in performance detected.
Found 10 outliers among 100 measurements (10.00%)
  1 (1.00%) low severe
  6 (6.00%) low mild
  3 (3.00%) high severe
chars/65536             time:   [12.479 µs 12.503 µs 12.529 µs]
                        thrpt:  [4.8714 GiB/s 4.8817 GiB/s 4.8910 GiB/s]
                 change:
                        time:   [-2.4451% -1.0638% +0.6633%] (p = 0.16 > 0.05)
                        thrpt:  [-0.6589% +1.0753% +2.5063%]
                        No change in performance detected.
Found 11 outliers among 100 measurements (11.00%)
  4 (4.00%) high mild
  7 (7.00%) high severe

clip_point/4096         time:   [63.148 µs 63.182 µs 63.229 µs]
                        thrpt:  [61.779 MiB/s 61.825 MiB/s 61.859 MiB/s]
                 change:
                        time:   [+1.0107% +2.1329% +4.2849%] (p = 0.02 < 0.05)
                        thrpt:  [-4.1088% -2.0883% -1.0006%]
                        Performance has regressed.
Found 5 outliers among 100 measurements (5.00%)
  4 (4.00%) high mild
  1 (1.00%) high severe
Benchmarking clip_point/65536: Warming up for 3.0000 s
Warning: Unable to complete 100 samples in 5.0s. You may wish to increase target time to 7.8s, enable flat sampling, or reduce sample count to 50.
clip_point/65536        time:   [1.2578 ms 1.2593 ms 1.2608 ms]
                        thrpt:  [49.573 MiB/s 49.631 MiB/s 49.690 MiB/s]
                 change:
                        time:   [+0.4881% +0.8942% +1.3488%] (p = 0.00 < 0.05)
                        thrpt:  [-1.3308% -0.8863% -0.4857%]
                        Change within noise threshold.
Found 15 outliers among 100 measurements (15.00%)
  1 (1.00%) high mild
  14 (14.00%) high severe

point_to_offset/4096    time:   [16.211 µs 16.235 µs 16.257 µs]
                        thrpt:  [240.28 MiB/s 240.61 MiB/s 240.97 MiB/s]
                 change:
                        time:   [-1.4913% +0.1685% +2.2662%] (p = 0.89 > 0.05)
                        thrpt:  [-2.2159% -0.1682% +1.5139%]
                        No change in performance detected.
Found 2 outliers among 100 measurements (2.00%)
  1 (1.00%) high mild
  1 (1.00%) high severe
point_to_offset/65536   time:   [360.06 µs 360.58 µs 361.16 µs]
                        thrpt:  [173.05 MiB/s 173.33 MiB/s 173.58 MiB/s]
                 change:
                        time:   [+0.0939% +0.8792% +1.8751%] (p = 0.06 > 0.05)
                        thrpt:  [-1.8406% -0.8715% -0.0938%]
                        No change in performance detected.
Found 10 outliers among 100 measurements (10.00%)
  3 (3.00%) high mild
  7 (7.00%) high severe

cursor/4096             time:   [19.266 µs 19.282 µs 19.302 µs]
                        thrpt:  [202.38 MiB/s 202.58 MiB/s 202.75 MiB/s]
                 change:
                        time:   [+1.2457% +2.2477% +2.8702%] (p = 0.00 < 0.05)
                        thrpt:  [-2.7901% -2.1983% -1.2304%]
                        Performance has regressed.
Found 4 outliers among 100 measurements (4.00%)
  2 (2.00%) high mild
  2 (2.00%) high severe
cursor/65536            time:   [467.63 µs 468.36 µs 469.14 µs]
                        thrpt:  [133.22 MiB/s 133.44 MiB/s 133.65 MiB/s]
                 change:
                        time:   [-0.2019% +1.3419% +2.8915%] (p = 0.10 > 0.05)
                        thrpt:  [-2.8103% -1.3241% +0.2023%]
                        No change in performance detected.
Found 12 outliers among 100 measurements (12.00%)
  3 (3.00%) high mild
  9 (9.00%) high severe

append many/small to large
                        time:   [37.419 ms 37.656 ms 37.929 ms]
                        thrpt:  [321.84 MiB/s 324.17 MiB/s 326.22 MiB/s]
                 change:
                        time:   [+0.8113% +1.7361% +2.6538%] (p = 0.00 < 0.05)
                        thrpt:  [-2.5852% -1.7065% -0.8047%]
                        Change within noise threshold.
Found 9 outliers among 100 measurements (9.00%)
  9 (9.00%) high severe
append many/large to small
                        time:   [51.289 ms 51.437 ms 51.614 ms]
                        thrpt:  [236.50 MiB/s 237.32 MiB/s 238.00 MiB/s]
                 change:
                        time:   [-87.518% -87.479% -87.438%] (p = 0.00 < 0.05)
                        thrpt:  [+696.08% +698.66% +701.13%]
                        Performance has improved.
Found 13 outliers among 100 measurements (13.00%)
  4 (4.00%) high mild
  9 (9.00%) high severe
```
</details>

Release Notes:

- sum_tree: Make SumTree::append run in logarithmic time
2025-11-24 14:49:00 +01:00
Lukas Wirth
f6f8fc1229 gpui: Do not panic when GetMonitorInfoW fails (#43397)
Fixes ZED-29R

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-24 13:35:24 +00:00
Piotr Osiewicz
2d55c088cc releases: Add build number to Nightly builds (#42990)
- **Remove semantic_version crate and use semver instead**
- **Update upload-nightly**


Release Notes:

- N/A

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-11-24 13:34:04 +01:00
Lukas Wirth
a0fa5d57c1 proto: Fix cloned errors losing all context (#43393)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-24 12:13:20 +00:00
Kunall Banerjee
f8729f6ea0 docs: Better wording for terminal.working_directory setting (#43388)
Initially this was just going to be a minor docs fix, but then I
wondered if we could improve the copy in the editor as well.

Release Notes:

- N/A
2025-11-24 11:24:05 +00:00
Lukas Wirth
f7772af197 util: Fix invalid powershell redirection syntax used in uni shell env capture (#43390)
Closes  https://github.com/zed-industries/zed/issues/42869

Release Notes:

- Fixed shell env sourcing not working with powershell on unix systems
2025-11-24 11:11:45 +00:00
Binlogo
2f46e6a43c http_client: Support GITHUB_TOKEN env to auth GitHub requests (#42623)
Closes #33903

Release Notes:

- Ensured Zed reuses `GITHUB_TOKEN` env variable when querying GitHub

---

Before fixing:

-  The `crates-lsp` extension request captured:
```
curl 'https://api.github.com/repos/MathiasPius/crates-lsp/releases' \
-H 'accept: */*' \
-H 'user-agent: Zed/0.212.3 (macos; aarch64)' \
-H 'host: api.github.com' \
```

-  `crates-lsp` extension error: 
```
Language server crates-lsp:

from extension "Crates LSP" version 0.2.0: status error 403, response: "{\"message\":\"API rate limit exceeded for x.x.x.x. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)\",\"documentation_url\":\"https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting\"}\n"
```

After fixing:

```
export GITHUB_TOKEN=$(gh auth token)
cargo run
```

-  The `crates-lsp` extension request captured:
```
curl 'https://api.github.com/repos/MathiasPius/crates-lsp/releases' \
-H 'authorization: Bearer gho_Nt*****************2KXLw2' \
-H 'accept: */*' \
-H 'user-agent: Zed/0.214.0 (macos; aarch64)' \
-H 'host: api.github.com' \
```

The API rate limitation is resolved.

---

This isn't a perfect solution, but it enables users to avoid the noise.
2025-11-24 12:51:45 +02:00
Oscar Villavicencio
d333535e76 docs: Document git_hosting_providers for self-hosted Git instances (#43278)
Closes #38433

Document how to register self-hosted GitHub/GitLab/Bitbucket instances
via git_hosting_providers setting so permalinks and issue links resolve.

Release Notes:

- Added documentation on how to register self-hosted
GitHub/GitLab/Bitbucket instances via the `git_hosting_providers`
setting. This ensures permalinks and issue links can be resolved for
these instances.
2025-11-24 11:32:44 +01:00
shaik-zeeshan
4b04be6020 Fix gutter hover breakpoint not updating when switching the tabs (#43163)
Closes #42073

fixes hover breakpoint not disappearing from a tab when tabs are
switched


https://github.com/user-attachments/assets/43096d2a-cc5b-46c4-b903-5bc8c33305c5


Release Notes:

- N/A

---------

Co-authored-by: Finn Evers <finn.evers@outlook.de>
2025-11-24 10:27:42 +00:00
mg
fc11ecfa2b Add Windows path for extensions (#42645)
### Description

The `installing-extensions.md` guide was missing the directory path for
the Windows platform. It currently only lists the paths for macOS and
Linux. This PR adds the correct path for Windows users
(`%LOCALAPPDATA%\zed\extensions`).

Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2025-11-24 09:46:15 +00:00
Lukas Wirth
3281b9077f agent: Fix utf8 panic in outline (#43141)
Fixes ZED-3F3

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-24 09:43:20 +00:00
Benjamin Jurk
194f6c9f95 Treat .h++ files as C++ (#42802)
Release Notes:

- `.h++` files are now treated as C++.
2025-11-24 11:36:04 +02:00
Lukas Wirth
99277a427f miniprofiler_ui: Copy path to clipboard on click (#43280)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-24 10:33:03 +01:00
Ulysse Buonomo
48e113a90e cli: Allow opening non-existent paths (#43250)
Changes are made to `parse_path_with_position`:
we try to get the canonical, existing parts of
a path, then append the non-existing parts.

Closes #4441

Release Notes:

- Added the possibility to open a non-existing path using `zed` CLI
  ```
  zed path/to/non/existing/file.txt
  ```

Co-authored-by: Syed Sadiq Ali <sadiqonemail@gmail.com>
2025-11-24 11:30:19 +02:00
Danilo Leal
06f8e35597 agent_ui: Make thread markdown editable (#43377)
This PR makes the thread markdown editable. This refers to the "open
thread as markdown" feature, where you previously could only read. One
benefit of this move is that it makes a bit more obvious that you can
`cmd-s` to save the markdown, allowing you to store the content of a
given thread. You could already do this before, but due to it being
editable now, you see the tab with a dirty indicator, which communicates
that better.

Release Notes:

- agent: Made the thread markdown editable.
2025-11-24 00:12:04 -03:00
Danilo Leal
07b6686411 docs: Improve edit prediction page (#43379)
This PR improves the edit prediction page particularly by adding
information about pricing and plans, which wasn't at all mentioned here
before, _and_ by including a section with a keybinding example
demonstrating how to always use just `tab` to always accept edit
predictions.

Release Notes:

- N/A
2025-11-24 00:11:46 -03:00
Danilo Leal
dbcfb48198 Add mouse-based affordance to open a recent project in new window (#43373)
Closes https://github.com/zed-industries/zed/issues/31796

<img width="500" height="1034" alt="Screenshot 2025-11-23 at 7  39 2@2x"
src="https://github.com/user-attachments/assets/bd516359-328f-44aa-9130-33f9567df805"
/>

Release Notes:

- N/A
2025-11-23 19:40:33 -03:00
Ben Kunkle
34a2e1d56b settings_ui: Don't show sh as default shell on windows (#43276)
Closes #ISSUE

Release Notes:

- Fixed an issue in the settings UI where changing the terminal shell
would set the default shell to `sh` on Windows
2025-11-23 16:52:50 -05:00
Bennet Bo Fenner
da143c5527 Fix inline assist panic (#43364)
Fixes a panic that was introduced in #42633. Repro steps:
1. Open the inline assistant and mention a file in the prompt
2. Run the inline assistant
3. Remove the mention and insert a different one
4. 💥

This would happen because the mention set still had a reference to the
old editor, because we create a new one in `PromptEditor::unlink`.

Also removes the unused
`crates/agent_ui/src/context_picker/completion_provider.rs` file, which
was not removed by mistake in the previous PR.

Release Notes:

- N/A
2025-11-23 18:26:07 +01:00
Mayank Verma
1f03fc62db editor: Fix tab tooltips not showing file path for remote files (#43359)
Closes #42344

Release Notes:

- Fixed editor tab tooltips not showing file path for remote files

Here's the before/after, tested both local and remote:


https://github.com/user-attachments/assets/2768a0f8-e35b-4eff-aa95-d0decb51ec78
2025-11-23 17:35:43 +02:00
Lukas Wirth
06e03a41aa terminal_view: Reuse editor's blink manager (#43351)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-23 12:24:01 +00:00
John Tur
41c61900d1 Fix labels for GitHub issue templates (#43348)
Release Notes:

- N/A
2025-11-23 03:21:43 -05:00
Danilo Leal
f57f4cd360 agent_ui: Display footer for model selector when in Zed agent (#43294)
This PR adds back the footer with the "Configure" button in the model
selector but only when the seeing it from the Zed agent (or inline
assistant/text threads). I had removed it a while back because seeing
the "Configure" button, which takes you to the agent panel settings
view, when clicking from an external agent didn't make much sense, given
there's nothing model-wise you can configure from Zed (at least yet) for
an external agent.

This also makes the button in the footer a bit nicer by making it full
screen and displaying a keybinding, so that you can easily do the whole
"trigger model selector → go to settings view" all with the keyboard.

<img width="400" height="870" alt="Screenshot 2025-11-21 at 10  38@2x"
src="https://github.com/user-attachments/assets/c14f2acf-b793-4bc1-ac53-8a8a53b219e6"
/>

Release Notes:

- N/A
2025-11-23 00:33:18 -03:00
Danilo Leal
d9498b4b55 debugger_ui: Improve some elements of the UI (#43344)
- In the launch tab of the new session mode, I've switched it to use the
`InputField` component instead given that had all that we needed
already. Allows for removing a good chunk of editor-related code
- Also in the launch tab, added support for keyboard navigation between
all of the elements there (dropdown, inputs, and switch component)
- Added some simple an empty state treatment for the breakpoint column
when there are none set


https://github.com/user-attachments/assets/a441aa8a-360b-4e38-839f-786315a8a235

Release Notes:

- debugger: Made the input elements within the launch tab in the new
session modal keyboard navigable˙.
2025-11-22 23:28:34 -03:00
Danilo Leal
7a5851e155 ui: Remove CheckboxWithLabel and improve Switch and Checkbox (#43343)
This PR finally removes the `CheckboxWithLabel` component, which is not
fully needed given the `Checkbox` can take a `label` method. Then, took
advantage of the opportunity to add more methods with regards to label
customization (position, size, and color) in both the `Checkbox` and
`Switch` components.

Release Notes:

- N/A
2025-11-22 22:26:26 -03:00
warrenjokinen
5b23a4ad7b docs: Fix minor typo in docker.md (#43334)
Updated wording (added a missing word) for reporting issues in
Dockerfile extension documentation.

Closes #ISSUE N/A

Release Notes:

- N/A
2025-11-22 22:11:41 +02:00
Liffindra Angga Zaaldian
10eba0bd5f Update JavaScript default language server (#43316)
As stated in [TypeScript Language Server
documentation](https://zed.dev/docs/languages/typescript#language-servers),
JavaScript uses `vtsls` as the default language server.

Release Notes:

- N/A
2025-11-22 12:21:56 +02:00
Marco Mihai Condrache
ab0527b390 gpui: Fix documentation of window methods (#43315)
Closes #43313 

Release Notes:

- N/A

Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
2025-11-22 12:21:01 +02:00
Julia Ryan
bfe141ea79 Fix wsl path parsing (#43295)
Closes #40286

Release Notes:

- N/A

---------

Co-authored-by: John Tur <john-tur@outlook.com>
2025-11-22 02:23:52 +00:00
Cole Miller
4376eb8217 Disable flaky test_git_status_postprocessing test (#43293)
Release Notes:

- N/A
2025-11-22 01:45:01 +00:00
Lukas Wirth
8e2c0c3a0c askpass: Fix double command ampersand in powershell script (#43289)
Fixes https://github.com/zed-industries/zed/issues/42618 /
https://github.com/zed-industries/zed/issues/43109

Release Notes:

- N/A
2025-11-22 00:46:53 +00:00
Mikayla Maki
de58a496ef Fix a bug where Anthropic completions would not work on nightly (#43287)
Follow up to: https://github.com/zed-industries/zed/pull/43185/files

Release Notes:

- N/A

Co-authored-by: Michael <mbenfield@zed.dev>
2025-11-22 00:10:26 +00:00
Jakub Konka
d07193cdf2 git: Handle git pre-commit hooks separately (#43285)
We now run git pre-commit hooks before we commit. This ensures we don't
run into timeout issues with askpass delegate and report invalid error
to the user.

Closes #43157

Release Notes:

- Fixed long running pre-commit hooks causing committing from Zed to
fail.

Co-authored-by: Cole Miller <cole@zed.dev>
2025-11-22 00:33:32 +01:00
Conrad Irwin
279b76d440 Retry sentry uploads (#43267)
We see internal server errors occasionally; and it's very annoying to
have to re-run the entire step

Release Notes:

- N/A
2025-11-21 13:29:08 -07:00
Be
dfa102c5ae Add setting for enabling server-side decorations (#39250)
Previously, this was controllable via the undocumented
ZED_WINDOW_DECORATIONS environment variable (added in #13866). Using an
environment variable for this is inconvenient because it requires users
to set that environment variable somehow before starting Zed, such as in
the .desktop file or persistently in their shell. Controlling this via a
Zed setting is more convenient.

This does not modify the design of the titlebar in any way. It only
moves the existing option from an environment variable to a Zed setting.

Fixes #14165

Client-side decorations (default):
<img width="3840" height="2160" alt="image"
src="https://github.com/user-attachments/assets/525feb92-2f60-47d3-b0ca-47c98770fa8c"
/>


Server-side decorations in KDE Plasma:
<img width="3840" height="2160" alt="image"
src="https://github.com/user-attachments/assets/7379c7c8-e5e3-47ba-a3ea-4191fec9434d"
/>

Release Notes:

- Changed option for Wayland server-side decorations from an environment
variable to settings.json field

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-11-21 19:56:00 +00:00
Be
a04b3d80c8 gpui: Fall back to client-side decorations on Wayland if SSD not supported (#39313)
It is optional for Wayland servers to support server-side decorations.
In particular, GNOME chooses to not implement SSD
(https://gitlab.gnome.org/GNOME/mutter/-/issues/217). So, even if the
application requests SSD, it must draw client-side decorations unless
the application receives a response from the server confirming the
request for SSD.

Before, when the user requested SSD for Zed, but the Wayland server did
not support it, there were no server-side decorations (window titlebar)
drawn, but Zed did not draw the window minimize, maximize, and close
buttons either. This fixes Zed so it always draws the window control
buttons if the Wayland server does not support SSD.

Before on GNOME Wayland with SSD requested:
<img width="3840" height="2160" alt="image"
src="https://github.com/user-attachments/assets/68a6d853-623d-401f-8e7f-21d4dea00543"
/>

After on GNOME Wayland with SSD requested:
<img width="3840" height="2160" alt="image"
src="https://github.com/user-attachments/assets/b258ae8b-fe0e-4ba2-a541-ef6f2c38f788"
/>


Release Notes:

- Fixed window control buttons not showing in GNOME Wayland when SSD
requested
2025-11-21 19:55:44 +00:00
Dave Waggoner
e76b485de3 terminal: New settings for path hyperlink regexes (#40305)
Closes:
- #12338
- #40202 

1. Adds two new settings which allow customizing the set of regexes used
to identify path hyperlinks in terminal
1. Fixes path hyperlinks for paths containing unicode emoji and
punctuation, for example, `mojo.🔥`
1. Fixes path hyperlinks for Windows verbatim paths, for example,
`\\?\C:\Over\here.rs`.
1. Improves path hyperlink performance, especially for terminals with a
lot of content
1. Replaces existing custom hard-coded default path hyperlink parsing
logic with a set of customizable default regexes

## New settings

(from default.json)

### terminal.path_hyperlink_regexes

Regexes used to identify paths for hyperlink navigation. Supports
optional named capture
groups `path`, `line`, `column`, and `link`. If none of these are
present, the entire match
is the hyperlink target. If `path` is present, it is the hyperlink
target, along with `line`
and `column` if present. `link` may be used to customize what text in
terminal is part of the
hyperlink. If `link` is not present, the text of the entire match is
used. If `line` and
`column` are not present, the default built-in line and column suffix
processing is used
which parses `line:column` and `(line,column)` variants. The default
value handles Python
diagnostics and common path, line, column syntaxes. This can be extended
or replaced to
handle specific scenarios. For example, to enable support for
hyperlinking paths which
contain spaces in rust output,
```
[
  "\\s+(-->|:::|at) (?<link>(?<path>.+?))(:$|$)",
  "\\s+(Compiling|Checking|Documenting) [^(]+\\((?<link>(?<path>.+))\\)"
],
```
could be used. Processing stops at the first regex with a match, even if
no link is
produced which is the case when the cursor is not over the hyperlinked
text. For best
performance it is recommended to order regexes from most common to least
common. For
readability and documentation, each regex may be an array of strings
which are collected
into one multi-line regex string for use in terminal path hyperlink
detection.

### terminal.path_hyperlink_timeout_ms
Timeout for hover and Cmd-click path hyperlink discovery in
milliseconds. Specifying a
timeout of `0` will disable path hyperlinking in terminal.

## Performance

This PR fixes terminal to only search the hovered line for hyperlinks
and adds a benchmark. Before this fix, hyperlink detection grows
linearly with terminal content, with this fix it is proportional only to
the hovered line. The gains come from replacing
`visible_regex_match_iter`, which searched all visible lines, with code
that only searches the line hovered on (including if the line is
wrapped).

Local benchmark timings (terminal with 500 lines of content):

||main|this PR|Δ|
|-|-|-:|-|
| cargo_hyperlink_benchmark | 1.4 ms | 13 µs | -99.0% |
| rust_hyperlink_benchmark | 1.2 ms | 11 µs | -99.1% |
| ls_hyperlink_benchmark | 1.3 ms | 7 µs |  -99.5% |

Release Notes:

- terminal: New settings to allow customizing the set of regexes used to
identify path hyperlinks in terminal
- terminal: Fixed terminal path hyperlinks for paths containing unicode
punctuation and emoji, e.g. mojo.🔥
- terminal: Fixed path hyperlinks for Windows verbatim paths, for
example, `\\?\C:\Over\here.rs`
- terminal: Improved terminal hyperlink performance, especially for
terminals with a lot of content visible
2025-11-21 14:01:06 -05:00
Joseph T. Lyons
0492255d7b Make community champions public (#43271)
Release Notes:

- N/A
2025-11-21 18:30:02 +00:00
Agus Zubiaga
6b9c2b0363 zeta2: Improve jump outside UI (#43262)
Still a prototype UI but a bit more noticeable :) 

Release Notes:

- N/A
2025-11-21 17:22:49 +00:00
Bennet Bo Fenner
f0820ae8e4 agent_ui: Remove context strip from inline assistant (#42633)
TODO
- [x] Implement PromptEditor::paste
- [x] Fix creases on unlink
- [x] PromptCompletionProviderDelegate::supports_images
- [ ] Fix highlighting in completion menu

Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-11-21 18:10:30 +01:00
Luke Naylor
5a9b810aef markdown: Add LaTeX syntax highlighting injection (#41110)
Closes [#30264](https://github.com/zed-industries/zed/issues/30264)

Small addition based on
[nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter/blob/main/runtime/queries/markdown_inline/injections.scm)

<img width="1122" height="356" alt="Screenshot From 2025-10-24 15-47-58"
src="https://github.com/user-attachments/assets/33e7387d-a299-4921-9db8-622d2657bec1"
/>

This does require the LaTeX extension to be installed.

Release Notes:

- Added LaTeX highlighting for inline and display equations in Markdown when the LaTeX extension is installed

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-11-21 16:57:56 +00:00
Agus Zubiaga
4fb671f4eb zeta2: Predict at next diagnostic location (#43257)
When no predictions are available for the current buffer, we will now
attempt to predict at the closest diagnostic from the cursor location
that wasn't included in the last prediction request. This enables a
commonly desired kind of far-away jump without requiring explicit model
support.

Release Notes:

- N/A
2025-11-21 16:39:08 +00:00
Lukas Wirth
a3cbe1a554 crashes: Print panic message to logs (#43159)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-21 17:31:57 +01:00
Conrad Irwin
9b823616dd Fix install linux (#43205)
Closes: #42726

Release Notes:

- Fix ./script/install-linux for installing a development version of Zed
on Linux
2025-11-21 09:12:19 -07:00
Smit Barmase
3c69e5c46b Revert "gpui: Convert macOS clipboard file URLs to paths for paste" (#43254)
Reverts zed-industries/zed#36848

Turns out this broke copying a screenshot from apps like CleanShot X and
then pasting it over. We should land this again after taking a look at
those cases. Pasting screenshots from the native macOS screenshot
functionality works though.

cc @seantimm 

Release Notes:

- Fixed issue where copying a screenshot from apps like CleanShot X into
Agent Panel didn't work as expected.
2025-11-21 16:11:19 +00:00
Conrad Irwin
2ac13b9489 Fallible Settings (#42938)
Also tidies up error notifications so that in the case of syntax errors
we don't see noise about the migration failing as well.

Release Notes:

- Invalid values in settings files will no longer prevent the rest of
the file from being parsed.
2025-11-21 08:28:17 -07:00
Lukas Wirth
a8d7f06b47 Revert "util: Check whether discovered powershell is actually executable" (#43247)
Reverts zed-industries/zed#43044
Closes https://github.com/zed-industries/zed/issues/43224

This slows down startup on windows significantly

Release Notes:

- Fixed slow startup on Windows
2025-11-21 14:48:41 +00:00
Smit Barmase
28e1c15e90 agent_ui: Fix sent agent prompt getting lost after authentication (#43245)
Closes #42379

Release Notes:

- Fixed issue where a sent agent message is not restored after
successful authentication.
2025-11-21 20:03:11 +05:30
Danilo Leal
0ee7271e48 Allow onboarding pages to be zoomed in/out (#43244)
We were just missing adding keybindings for these.

Release Notes:

- onboarding: The onboarding pages can now be zoomed in/out with the
same keybindings you'd use to zoom in/out a regular buffer.
2025-11-21 11:22:51 -03:00
Kunall Banerjee
d6a5566619 docs: Point to the right URL for Gemini CLI (#43239)
Point to the right URL for Gemini CLI.

Release Notes:

- N/A

---

💖
2025-11-21 07:23:43 -05:00
Kunall Banerjee
ea85f905f1 docs: Fix small typo in docs for Snippets (#43238)
Happened to notice this typo while going through the docs.

Release Notes:

- N/A

---

💖
2025-11-21 06:56:47 -05:00
Lukas Wirth
1ce58a88cc zed: Allocate more rayon threads depending on available parallelism (#43235)
While zed itself is not a heavy user of rayon, wasmtime is, especially
for compilation. This change is similar to the rayon default but we
halve the number of threads still so we don't spawn too many threads
overall.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-21 10:33:38 +00:00
Lukas Wirth
a30887f03b Fix some panics (#43233)
Fixes ZED-2NP
Fixes ZED-3DP
Fixes ZED-3EV

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-21 11:08:21 +01:00
jtaub
92b6e8eb6e Jetbrains keymap updates (#42848)
Closes https://github.com/zed-industries/zed/issues/14639

## Release Notes:

Various improvements to the Jetbrains keymap. Added various missing
keyboard shortcuts that I use on a daily basis in Jetbrains, and changed
a few which were present in the keymap but mapped to the wrong behavior.

### Added:
- Added various missing keybindings for Jetbrains keymap
  - `ctrl-n` → `project_symbols::Toggle`
  - `ctrl-alt-n` → `file_finder::Toggle` (open project files)
  - `ctrl-~` → `git::Branch`
  - `ctrl-\` → `assistant::InlineAssist`
  - `ctrl-space` → `editor::ShowCompletions`
  - `ctrl-q` → `editor::Hover`
  - `ctrl-p` → `editor::ShowSignatureHelp`
  - `ctrl-f5` → `task::Rerun`
  - `shift-f9` → `debugger::Start`
  - `shift-f10` → `task::Spawn`
- Added macOS equivalents for all of the above, however I only have a
Linux machine so I have not tested the mac bindings. The binding are
generally the same except `ctrl → cmd` with few exceptions.
  - `cmd-j` → `editor::Hover`

### Fixed:
- Several incorrectly mapped keybindings for the Jetbrains keymap
- `ctrl-alt-s` → `editor::OpenSettings` (was `editor::OpenSettingsFile`)
- `ctrl-alt-b` → `editor::GoToImplementation` (was
`editor::GoToDefinitionSplit`)
  - `alt-left` → `pane::ActivatePreviousItem`
  - `alt-right` → `pane::ActivateNextItem`
- `ctrl-k` now opens the Git panel. I believe this was commented out
because of a bug where focus is not given to the commit message text
box, but imo the current behavior of not doing anything at all feels
more confusing/frustrating to a Jetbrains user (projecting a little
here, happy to revert).
2025-11-21 11:04:43 +01:00
Andrew Farkas
0a6cb6117b Fix connect.host setting being ignored by debugpy (#43190)
Closes #42727

Unfortunately we can only support IPv4 addresses right now because
`TcpArguments` only supports an IPv4 address. I'm not sure how difficult
it would be to lift this limitation.

Release Notes:

- Fixed `connect.host` setting being ignored by debugpy

Co-authored-by: Cole Miller <cole@zed.dev>
2025-11-21 09:20:15 +00:00
Smit Barmase
e2f6422b3e language: Move language server update to background when it takes too long (#43164)
Closes https://github.com/zed-industries/zed/issues/42360

If updating a language server takes longer than 10 seconds, we now fall
back to launching the currently installed version (if exists) and
continue downloading the update in the background.

Release Notes:

- Improved language server updates for slow connection, now Zed launches
existing server if the update is taking too long.

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2025-11-21 14:19:55 +05:30
cacaosteve
bb514c158e macOS: Enumerate GPUs first; prefer low-power non-removable; fall back to system default (#38164)
Problem: Some macOS environments report no devices via
MTLCopyAllDevices, causing startup failure with “unable to access a
compatible graphics device,” especially on Apple Silicon.
Change: Prefer MTLCreateSystemDefaultDevice
(metal::Device::system_default()) first. If None, enumerate devices and
select a non‑removable, low‑power device by preference.
Why this works: On Apple Silicon the system default is the unified GPU;
on Intel, the fallback keeps a stable policy and avoids accidentally
picking removable/high‑power devices.
Impact: Fixes startup on affected ASi systems; improves selection
consistency on Intel multi‑GPU. Behavior unchanged where
system_default() succeeds.
Risk: Low. Aligns with Apple’s recommended selection path. Still fails
early with a clearer message if no Metal devices exist.

Closes #37689.

Release Notes:
- Fixed: Startup failure on some Apple Silicon machines when Metal
device enumeration returned no devices by falling back to the system
default device.

---------

Co-authored-by: 张小白 <364772080@qq.com>
Co-authored-by: Kate <work@localcc.cc>
2025-11-21 00:25:37 -05:00
Conrad Irwin
550442e100 Disable fsevents tests (#43218)
They're flakier than phyllo dough, and not nearly as delicious

Release Notes:

- N/A
2025-11-20 22:17:50 -07:00
Xipeng Jin
b3ebcef5c6 gpui: Only time out multi-stroke bindings when current prefix matches (#42659)
Part One for Resolving #10910

### Summary
Typing prefix (partial keybinding) will behave like Vim. No timeout
until you either finish the sequence or hit Escape, while ambiguous
sequences still auto-resolve after 1s.

### Description
This follow-up tweaks the which-key system first part groundwork so our
timeout behavior matches Vim’s expectations. Then we can implement the
UI part in the next step (reference latest comments in
https://github.com/zed-industries/zed/pull/34798)
- `DispatchResult` now reports when the current keystrokes are already a
complete binding in the active context stack (`pending_has_binding`). We
only start the 1s flush timer in that case. Pure prefixes or sequences
that only match in other contexts—stay pending indefinitely, so
leader-style combos like `space f g` no longer evaporate after a second.
- `Window::dispatch_key_event` cancels any prior timer before scheduling
a new one and only spawns the background flush task when
`pending_has_binding` is true. If there’s no matching binding, we keep
the pending keystrokes and rely on an explicit Escape or more typing to
resolve them.

Release Notes:

- Fixed multi-stroke keybindings so only ambiguous prefixes auto-trigger
after 1 s; unmatched prefixes now stay pending until canceled, matching
Vim-style leader behavior.
2025-11-20 19:42:56 -07:00
Conrad Irwin
2b9eeb9a30 Disable keychain timeout in bundle-mac (#43204)
Attempt to reduce the number of times bundle-mac fails to notorize by
disabling
keychain's auto-lock timeout

Release Notes:

- N/A
2025-11-21 02:42:49 +00:00
Max Brunsfeld
07d98981e8 Make the edit prediction status bar menu work correctly when using sweep (#43203)
Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-11-20 23:59:02 +00:00
Ben Kunkle
8bbd101dcd ci: Run check_docs when code changes (#43188)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-20 17:30:21 -05:00
Marshall Bowers
dbdc501c89 Fix casing in comments in default.json (#43201)
This PR fixes the casing of the operating system names in the
language-specific sections of `default.json`.

This files serves as documentation for users (since it can be viewed
through `zed: open default settings`), so we should make sure it is
tidy.

Release Notes:

- N/A
2025-11-20 22:17:52 +00:00
Mikayla Maki
898c133906 Simplify error management in stream_completion (#43035)
This PR simplifies error and event handling by removing the
`Ok(LanguageModelCompletionEvent::Status(CompletionRequestStatus::Failed)))`
state from the stream returned by `LanguageModel::stream_completion()`,
by changing it into an `Err(LanguageModelCompletionError)`. This was
done by collapsing the valid `CompletionRequestStatus` values into
`LanguageModelCompletionEvent`.

Release Notes:

- N/A

---------

Co-authored-by: Michael Benfield <mbenfield@zed.dev>
2025-11-20 22:16:07 +00:00
Michael Benfield
659169f06d Add codegen_ranges function in inline_assistant.rs (#43186)
Just a simple refactor.

Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
2025-11-20 21:57:43 +00:00
Danilo Leal
361fcc5c90 Make search field in panels be at the top (#43200)
This mostly affects the collab and outline panels for now. It has always
been a bit weird that the search field was at the bottom of the panel,
even more so because in both cases, you can _arrow down_ to start
navigating the list with your keyboard. So, with the search at the
bottom, you'd arrow down and get to the top of the list, which was very
strange. Now, with it at the top, it not only looks better but it is
also more generally consistent with other surfaces in the app, like
pickers, the settings UI, rules library, etc. Most search fields are
always at the top.

<img width="800" height="1830" alt="image"
src="https://github.com/user-attachments/assets/3e2c3b8f-5907-4d83-8804-b3fc77342103"
/>

Release Notes:

- N/A
2025-11-20 18:57:22 -03:00
Danilo Leal
9667d7882a extensions_ui: Improve error message when extensions fail to load (#43197)
<img width="500" height="1902" alt="Screenshot 2025-11-20 at 6  12@2x"
src="https://github.com/user-attachments/assets/daa5b020-17c8-4398-a64a-d691c566d6e7"
/>

Release Notes:

- extensions UI: Improved the feedback message for when extensions are
not being displayed due to a fetch error caused by lack of connection.
2025-11-20 18:28:11 -03:00
Danilo Leal
6adb0f4d03 agent_ui: Improve UI for the feedback container (#43195)
Improves a previously weird wrapping and simplify the UI by adding the
meta text inside the tooltip itself.


https://github.com/user-attachments/assets/9896d4a2-6954-4e61-9b77-864db8f2542a

Release Notes:

- N/A
2025-11-20 18:18:30 -03:00
Danilo Leal
a332b79189 ui: Add DiffStat component (#43192)
Release Notes:

- N/A
2025-11-20 18:18:08 -03:00
Xiaobo Liu
b41eb3cdaf windows: Fix maximized window size when DPI scale changes (#40053)
The WM_DPICHANGED suggested RECT is calculated for non-maximized
windows. When a maximized window's DPI changes, we now query the
monitor's work area directly to ensure the window correctly fills the
entire screen.

For non-maximized windows, the original behavior using the
system-suggested RECT is preserved.

Release Notes:

- windows: Fixed maximized window size when DPI scale changes

Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
2025-11-20 20:34:17 +00:00
Andrew Farkas
6899448812 Remove prompt-caching-2024-07-31 beta header for Anthropic AI (#43185)
Closes #42715

Release Notes:

- Remove `prompt-caching-2024-07-31` beta header for Anthropic AI

Co-authored-by: Cole Miller <cole@zed.dev>
2025-11-20 15:16:09 -05:00
Lukas Wirth
28ef7455f0 gpui: #[inline] some trivial functions (#43189)
These appear in a lot of stacktraces (especially on windows) despite
them being plain forwarding calls.

Also removes some intermediate calls within gpui that will only turn
into more unnecessary compiler work.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-20 19:54:47 +00:00
Kirill Bulatov
7e341bcf94 Support bracket colorization (rainbow brackets) (#43172)
Deals with https://github.com/zed-industries/zed/issues/5259

Highlights brackets with different colors based on their depth.
Uses existing tree-sitter queries from brackets.scm to find brackets,
uses theme's accents to color them.


https://github.com/user-attachments/assets/cc5f3aba-22fa-446d-9af7-ba6e772029da

1. Adds `colorize_brackets` language setting that allows, per language
or globally for all languages, to configure whether Zed should color the
brackets for a particular language.

Disabled for all languages by default.

2. Any given language can opt-out a certain bracket pair by amending the
brackets.scm like `("\"" @open "\"" @close) ` -> `(("\"" @open "\""
@close) (#set! rainbow.exclude))`

3. Brackets are using colors from theme accents, which can be overridden
as

```jsonc
"theme_overrides": {
  "One Dark": {
    "accents": ["#ff69b4", "#7fff00", "#ff1493", "#00ffff", "#ff8c00", "#9400d3"]
  }
},
```

Release Notes:

- Added bracket colorization (rainbow brackets) support. Use
`colorize_brackets` language setting to enable.

---------

Co-authored-by: MrSubidubi <dev@bahn.sh>
Co-authored-by: Lukas Wirth <lukas@zed.dev>
Co-authored-by: MrSubidubi <finn@zed.dev>
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-11-20 19:47:39 +00:00
Lukas Wirth
e6e5ccbf10 ui: Render fallback icon for avatars that failed to load (#43183)
Before we were simply not rendering anything which could lead to some
very surprising situations when joining channels ...

Now it will look like so
<img width="147" height="50" alt="image"
src="https://github.com/user-attachments/assets/13069de8-3dc0-45e1-b562-3fe81507dd87"
/>

Release Notes:

- Improved rendering of avatars that failed to load by rendering a
fallback icon
2025-11-20 19:30:34 +00:00
Andrew Farkas
d6d967f443 Re-resolve anchor before applying AI inline assist edits (#43103)
Closes #39088

Release Notes:

- Fixed AI assistant edits being scrambled when file was modified while
it was open

--

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-11-20 18:40:27 +00:00
Adrian
18f14a6ebf vim: Fix paste action for visual modes (#43031)
Closes #41810 

Release Notes:

- Fixed paste not working correctly in vim visual modes
2025-11-20 11:12:57 -07:00
Piotr Osiewicz
58fe19d55e project search: Skip loading of gitignored paths when their descendants will never match an inclusion/exclusion query (#42968)
Co-authored-by: dino <dinojoaocosta@gmail.com>

Related-to: #38799

Release Notes:

- Improved project search performance with "Also search files ignored by
configuration" combined with file inclusion/exclusion queries.

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>
2025-11-20 18:44:55 +01:00
Bennet Bo Fenner
2a40dcfd77 acp: Support specifying settings for extensions (#43177)
This allows you to specify default_model and default_mode for ACP
extensions, e.g.
```
"auggie": {
  "default_model": "gpt-5",
  "default_mode": "default",
  "type": "extension"
},
```

Release Notes:

- Added support for specifying settings for ACP extensions
(`default_mode`, `default_model`)
2025-11-20 17:12:00 +00:00
Smit Barmase
a5c3267b3e extensions: Add - as linked edit character for HTML (#43179)
Closes https://github.com/zed-industries/zed/issues/43060

Release Notes:

- Fixed issue where typing in custom HTML tag would not complete
subsequent end tag for `-` character.

Co-authored-by: Kunall Banerjee <hey@kimchiii.space>
2025-11-20 17:05:24 +00:00
Dino
5ef6402d64 editor: Ensure all menus and popups are dismissed (#43169)
While investigating a bug report that, in Helix mode, pressing the
`escape` key would only hide the signature help popup and not the
completions menu, when `auto_signature_help` was enabled, it was noted
that the `editor::Editor.dismiss_menus_and_popups` method was not
dismissing all possible menus and popups and was, instead, stopping as
soon as a single menu or popup was dismissed.

From the name of the method it appears that we actually want to dismiss
all so this commit updates it as such, ensuring that the bug reported is
also fixed.

Closes #42499 

Release Notes:

- Fixed issue with popups and menus not being dismissed while using
`auto_signature_help` in Helix Mode
2025-11-20 16:25:09 +00:00
Danilo Leal
ba93a5d62f ui: Remove Badge component (#43168)
We're not using it anywhere anymore, so I think we can clean it up now.
This was a somewhat specific component we did for the sake of
Onboarding, but it ended up being removed.

Release Notes:

- N/A
2025-11-20 12:45:49 -03:00
Danilo Leal
73568fc454 ui: Add ThreadItem component (#43167)
Release Notes:

- N/A
2025-11-20 12:45:26 -03:00
Anthony Eid
56401fc99c debugger: Allow users to include PickProcessId in debug tasks and resolve Pid (#42913)
Closes #33286

This PR adds support for Zed's `$ZED_PICK_PID` command in debug
configurations, which allows users to select a process to attach to at
debug time. When this variable is present in a debug configuration, Zed
automatically opens a process picker modal.

Follow up for this will be integrating this variable in the task system
instead of just the debug configuration system.

Release Notes:

- Added `$ZED_PICK_PID` variable for debug configurations, allowing
users to select which process to attach the debugger to at runtime

---------

Co-authored-by: Remco Smits <djsmits12@gmail.com>
2025-11-20 10:12:59 -05:00
Vinh Tran
e033829ef2 Fix diff highlights (#38384)
Per
https://github.com/zed-industries/zed/discussions/23371#discussioncomment-13533635,
the issue is not new and I don't know how to solve the problem more
holistically yet.

All of the native themes don't have spec for `@diff.plus` and
`@diff.minus` leaving addition and deletion not being highlighted. For
diff file, the most valuable highlighting comes from exactly what we're
missing. Hence, I think this is worth fixing.

Perhaps, the ideal fix would be standardizing and documenting captures
such as `@diff.plus` and `@diff.minus` on
https://zed.dev/docs/extensions/languages#syntax-highlighting for theme
writers to adopt. But the existing list of captures seems to be
language-agnostic so I'm not sure if that's the best way forward.

Per
https://github.com/the-mikedavis/tree-sitter-diff/pull/18#issuecomment-2569785346,
`tree-sitter-diff`'s author prefers using `@keyword` and `@string` so
that `tree-sitter highlight` can work out of the box. So it seems to be
an ok choice for Zed.

Another approach is just adding `@diff.plus` and `@diff.minus` to the
native themes. Let me know if I should pursue this instead.

Before
<img width="668" height="328" alt="Screenshot 2025-09-18 at 11 16 14 AM"
src="https://github.com/user-attachments/assets/d9a5b3b5-b9ef-4e74-883f-831630fb431e"
/>

After
<img width="1011" height="404" alt="Screenshot 2025-09-18 at 12 11
15 PM"
src="https://github.com/user-attachments/assets/9cf453c0-30df-4d17-99e9-f2297865f12a"
/>
<img width="915" height="448" alt="Screenshot 2025-09-18 at 12 12 14 PM"
src="https://github.com/user-attachments/assets/9e7438a6-9009-4136-b841-1f8e1356bc9b"
/>



Closes https://github.com/zed-industries/extensions/issues/490


Release Notes:
- Fixed highlighting for addition and deletion for diff language

---------

Co-authored-by: MrSubidubi <finn@zed.dev>
2025-11-20 15:52:15 +01:00
Finn Evers
61f512af03 Move protobuf action to default linux runner (#43085)
Release Notes:

- N/A

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-11-20 12:48:39 +00:00
Arun Chapagain
dd5482a899 docs: Update developing extension docs for updating specific submodule (#42548)
Release Notes:

- N/A
2025-11-20 13:33:13 +01:00
Bhuminjay Soni
9094eb811b git: Compress diff for commit message generation (#42835)
This PR compresses diff capped at 20000 bytes by:
- Truncation of all lines to 256 chars
- Iteratively removing last hunks from each file until size <= 20000
bytes.


Closes #34486

Release Notes:

- Improved: Compress large diffs for commit message generation (thanks
@11happy)

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Co-authored-by: Oleksiy Syvokon <oleksiy@zed.dev>
2025-11-20 11:30:34 +00:00
Lukas Wirth
29f9853978 svg_preview: Remove unnecessary dependency on editor (#43147)
Editor is a choke point in our compilation graph while also being a very
common crate that is being edited. So reducing things that depend on it
will generally improve compilation times for us.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-20 12:18:50 +01:00
Aaron Saunders
1e45c99c80 Improve readability of files in the git changes panel (#41857)
Closes _unknown_

<img width="1212" height="463" alt="image"
src="https://github.com/user-attachments/assets/ec00fcf0-7eb9-4291-b1e2-66e014dc30ac"
/>


This PR places the file_name before the file_path so that when the panel
is slim it is still usable, mirrors the behaviour of the file picker
(cmd+P)

Release Notes:
-  Improved readability of files in the git changes panel
2025-11-20 06:14:46 -05:00
Danilo Leal
28f50977cf agent_ui: Add support for setting a model as the default for external agents (#43122)
This PR builds on top of the `default_mode` feature where it was
possible to set an external agent mode as the default if you held a
modifier while clicking on the desired option. Now, if you want to have,
for example, Haiku as your default Claude Code model, you can do that.
This feature adds parity between external agents and Zed's built-in one,
which already supported this feature for a little while.

Note: This still doesn't work with external agents installed from
extensions. At the moment, this is limited to Claude Code, Codex, and
Gemini—the ones we include out of the box.

Release Notes:

- agent: Added the ability to set a model as the default for a given
built-in external agent (Claude Code, Codex CLI, or Gemini CLI).
2025-11-20 11:00:01 +01:00
Lukas Wirth
95cb467cd9 multi_buffer: Remove redundant TypedOffset/TypedPoint (#43139)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-20 09:31:18 +00:00
Miguel Raz Guzmán Macedo
681a56506b project_panel: Add CollapseAllEntries keybinding (#43112)
Motivated by user feature requests

* https://github.com/zed-industries/zed/issues/6880
* https://discord.com/channels/869392257814519848/1439453067119562793

In analogy with VSCode functionality, we're adding a keybinding to the
project panel.

This is particularly for useful for large monorepos.

Release Notes:

- Keybinding added for `CollapseAllEntries` when in the `ProjectPanel`.

Co-authored-by: mikayla <mikayla@zed.dev>
2025-11-20 14:30:13 +05:30
Lukas Wirth
5052a460b4 vim: Fix increment panicking due to invalid utf8 offsets (#43101)
Fixes ZED-3ER

Release Notes:

- Fixed a panic when using vim increment on a multibyte character
2025-11-20 07:12:50 +00:00
Danilo Leal
66382acd52 ui: Remove outdated/unused component stories (#43118)
This PR removes basically all of the component stories, with the
exception of the context menu, which is a bit more intricate to set up.
All of the component that won't have a story after this PR will have an
entry in the Component Preview, which serves basically the same purpose.

Release Notes:

- N/A
2025-11-20 01:52:13 -03:00
Danilo Leal
ba596267d8 ui: Remove the ToggleButton component (#43115)
This PR removes the old `ToggleButton` component, replacing it with the
newer `ToggleButtonGroup` component in the couple of places that used to
use it. Ended up also adding a few more methods to the newer toggle
button group so the UI for the extensions page and the debugger main
picker didn't get visually impacted much. Then, as I was already in the
extensions page, decided to bake in some reasonably small UI
improvements to it as well.

Release Notes:

- N/A
2025-11-20 01:28:25 -03:00
Finn Evers
1fab43d467 Allow styling the container of markdown elements (#43107)
Closes #43033

Release Notes:

- FIxed an issue where the padding on info popovers would overlay text
when the content was scrollable.
2025-11-19 23:40:54 +00:00
Ben Kunkle
f2f40a5099 zeta2: Merge Sweep and Zeta2 Providers (#43097)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-11-19 15:40:06 -08:00
Piotr Osiewicz
c70f2d16ad lsp_button: Do not surface language servers from different windows in current workspace (#42733)
This led to a problem where we'd have a zombie entries in LSP dropdown
because they were treated as if they originated from an unknown
worktree.

Closes #42077

Release Notes:

- Fixed LSP status list containing zombie entries for LSPs in other
windows

---------

Co-authored-by: HactarCE <6060305+HactarCE@users.noreply.github.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-11-19 22:40:17 +00:00
Lukas Wirth
c98b2d6944 multi_buffer: Typed MultiBufferOffset (#42707)
This PR introduces a new `MultiBufferOffset` new type wrapping size. The
goal of this is to make it clear at the type level when we are
interacting with offsets of a multi buffer versus offsets of a language
/ text buffer. This improves readability of things quite a bit by making
it clear what kind of offsets one is working with while also reducing
accidental bugs by using the wrong kin of offset for the wrong API.

This PR also uncovered two minor bugs due to that.

Does not yet introduce the MultiBufferPoint equivalent, that is for a
follow up PR.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-19 22:00:58 +00:00
Cole Miller
5e21457f21 Fix panic in the git panel when toggling sort_by_path (#43074)
We call `entry_by_path` on the `bulk_staging` anchor entry at the
beginning of `update_visible_entries`, but in the codepath where
`sort_by_path` is toggled on or off, we clear entries without clearing
`bulk_staging` or counts, causing that `entry_by_path` to do an out of
bounds index. Fixed by clearing `bulk_staging` as well.

Release Notes:

- N/A
2025-11-19 16:53:40 -05:00
Conrad Irwin
f312215e93 Potentially make zip test less flakey (#43099)
Authored-By: Claude

Release Notes:

- N/A
2025-11-19 14:50:32 -07:00
Jakub Konka
08692bb108 git: Clear pending ops for remote repos (#43098)
Release Notes:

- N/A
2025-11-19 22:47:51 +01:00
Max Brunsfeld
09e02a483a Allow running zeta evals against sweep (#43039)
This PR restructures the subcommands in `zeta-cli`, so that the
prediction engine (currently `zeta1` vs `zeta2`) is no longer the
highest order subcommand. Instead, there is just one layer of
subcommands: `eval`, `predict`, `context`, etc. Within these commands,
there are flags for using `zeta1`, `zeta2`, and now `sweep`.

Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
Co-authored-by: Agus <agus@zed.dev>
2025-11-19 16:09:19 -05:00
David Kleingeld
68b87fc308 Use fixed calloop (#43081)
Calloop (used by our linux executor) was running all futures regardless
of how long they take. Unfortunaly some of our futures are rather busy
and take a while (>10ms).

Running all of them froze the editor for multiple seconds or even
minutes when opening a large project diff (git reset HEAD~2000 in
chromium for example).

Closes #ISSUE

Release Notes:

- N/A

---------

Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
2025-11-19 22:06:13 +01:00
Agus Zubiaga
ec220dcc05 sweep: Coalesce edits based on line distance rather than time (#43006)
Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-11-19 20:32:14 +00:00
Joseph T. Lyons
b6c8c3f3d9 Remove migrated scripts (#43095)
These scripts have been migrated to:
https://github.com/zed-industries/release_notes

Release Notes:

- N/A
2025-11-19 20:28:40 +00:00
Smit Barmase
dccddf6f66 project_panel: Remove cmd-opt-. binding for hiding hidden files (#43091)
Lots of folks were accidentally clicking this. Even though it’s the
default in macOS Finder, it’s a good idea to not have it as the default
for us.

Release Notes:

- Removed the default `cmd-opt-.` binding for toggling hidden files in
the Project Panel so it’s harder to hide them by accident.
2025-11-20 00:38:29 +05:30
Andrew Farkas
27cb01f4af Fix Helix mode search & selection (#42928)
This PR redoes the desired behavior changes of #41583 (reverted in
#42892) but less invasively

Closes #41125
Closes #41164

Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-11-19 18:30:55 +00:00
Andrew Farkas
829be71061 Fix invalid Unicode in terms & conditions (#42906)
Closes #40210

Previously attempted in #40423 and #42756. Third time's the charm?

Release Notes:

- Fixed encoding error in terms & conditions displayed when installing
2025-11-19 13:00:35 -05:00
Finn Evers
2a2f5a9c7a Add callable workflow for extension repositories (#43082)
This starts the work on a workflow that can be invoked in extension CI
to test changes on extension repositories.

Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-11-19 17:47:34 +00:00
Kirill Bulatov
97b429953e gpui: Do not render ligatures between different styled text runs (#43080)
An attempt to re-land https://github.com/zed-industries/zed/pull/41043
Part of https://github.com/zed-industries/zed/issues/5259 (as `>>>`
forms a ligature that we need to break into differently colored tokens)

Before:

<img width="301" height="86" alt="image"
src="https://github.com/user-attachments/assets/e710391a-b8ad-4343-8344-c86fc5cb86b6"
/>

and


https://github.com/user-attachments/assets/ae77ba64-ca50-4b5d-9ee4-a7d46fcaeb34


After:
<img width="1254" height="302" alt="image"
src="https://github.com/user-attachments/assets/7fd5dba5-d798-4153-acf2-e38a1cb712ae"
/>


When certain combination of characters forms a ligature, it takes the
color of the first character.
Even though the runs are split already by color and other properties,
the underlying font system merges the runs together.

Attempts to modify color and other, unrelated to font size, parameters,
did not help on macOS, hence a somewhat odd approach was taken: runs get
interleaved font sizes: normal and "normal + a tiny bit more".
This is the only option that helped splitting the ligatures, and seems
to render fine.

Release Notes:

- Fixed ligatures forming between different text kinds

---------

Co-authored-by: Lukas Wirth <lukas@zed.dev>
2025-11-19 19:37:22 +02:00
John Tur
404ee53812 Fix Windows bundling (#43083)
The updated package from
https://github.com/zed-industries/zed/pull/43066 changed the paths of
these files in the nupkg.

Release Notes:

- N/A
2025-11-19 12:25:03 -05:00
localcc
17c30565fc Fix extension auto-install on first setup (#43078)
Release Notes:

- N/A
2025-11-19 16:41:39 +00:00
Lena
f05eef58c4 Stop the buggy stalebot for now (#43076)
Delay the stalebot runs until the end of the year since it's currently
broken and leaves unhelpful comments on all the issues, including feature requests. Bad
bot. Allegedly this bug will soon be gone
https://github.com/actions/stale/issues/1302 but it's too much work
protecting issues from the bot until then.

Release Notes:

- N/A
2025-11-19 17:09:28 +01:00
Joseph T. Lyons
52716bacef Bump Zed to v0.215 (#43075)
Release Notes:

- N/A
2025-11-19 16:04:35 +00:00
Smit Barmase
79be5cbfe2 editor: Fix prepaint recursion when updating stale sizes (#42896)
The bug is in the `place_near` logic, specifically the
`!row_block_types.contains_key(&(row - 1))` check. The problem isn’t
really that condition itself, but it’s that it relies on
`row_block_types`, which does not take into account that upon block
resizes, subsequent block start row moves up/down. Since `place_near`
depends on this incorrect map, it ends up causing incorrect resize syncs
to the block map, which then triggers more bad recursive calls. The
reason it worked till now in most of the cases is that recursive resizes
eventually lead to stabilizing it.

Before `place_near`, we never touched `row_block_types` during the first
prepaint pass because we knew it was based on outdated heights. Once all
heights are finalized, using it is fine.

The fix is to make sure `row_block_types` is accurate from the very
first prepaint pass by keeping an offset whenever a block shrinks or
expands. Now ideally it should take only one subsequent prepaint. But
due to shrinking, new custom/diagnostics blocks might come into the view
from below, which needs further prepaint calls for resolving. Right now,
tests pass after 2 subsequent prepaint calls. Just to be safe, we have
set it to 5.

<img width="500" alt="image"
src="https://github.com/user-attachments/assets/da3d32ff-5972-46d9-8597-b438e162552b"
/>

Release Notes:

- Fix issue where sometimes Zed used to experience freeze while working
with inline diagnostics.
2025-11-19 21:02:31 +05:30
Jakub Konka
a42676b6bb git: Put pending ops container out of snapshot (#43061)
This also fixes staging checkbox flickering.

Release Notes:

- Fixed staging checkbox flickering sporadically in the Git panel.
2025-11-19 14:56:10 +00:00
Ben Kunkle
39f8aefa8c zeta2: Improve context retrieval (#43014)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Co-authored-by: Agus <agus@zed.dev>
Co-authored-by: Max <max@zed.dev>
2025-11-19 14:44:58 +00:00
Lukas Wirth
1c1dfba7e3 windows: Bundle freshers conpty.dll builds (#43066)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-19 15:11:37 +01:00
Finn Evers
2c4fd24d37 gpui: Restore last window close behavior on macOS (#43058)
Follow-up to https://github.com/zed-industries/zed/pull/42391

Release Notes:

- Fixed an issue where Zed did not respect the `on_last_window_closed`
setting on macOS
2025-11-19 13:22:29 +00:00
Lukas Wirth
3125e78904 windows: Bundle new conpty.dll/OpenConsole.exe and use it for local builds on x86_64 (#43059)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-19 13:12:57 +00:00
Vasyl Protsiv
74d61aad7f util: Fix zip extraction (#42714)
I was trying to use Zed for Rust debugging on windows, but was getting
this warning in debugger console: "Could not initialize Python
interpreter - some features will be unavailable (e.g. debug
visualizers)."
As the warning suggests this led to bad debugging experience where the
variables were not visualized properly in the "Variables" panel.

After some investigation I found that the problem is that Zed silently
failed to extract all files from the debug adapter package
(https://github.com/vadimcn/codelldb/releases/download/v1.11.8/codelldb-win32-x64.vsix).
Particularly `python-lldb` folder was missing, which caused the warning.
The error occurred here:

cf7c64d77f/crates/util/src/archive.rs (L47)
And then gets ignored here:

cf7c64d77f/crates/dap/src/adapters.rs (L323-L326)
The simple fix is to update `async_zip` crate to version 0.0.18 where
this issue appears to be fixed. I also added logging instead of silently
ignoring the error, as I believe that would have helped to catch it
earlier.

To reproduce the original issue you can try to follow these steps:
0. (Optional) Remove/rename old codelldb adapter at
`%localappdata%\Zed\debug_adapters\CodeLLDB`. Restart Zed.
1. Create a simple Rust project. Make sure you use gnu toolchain (target
`x86_64-pc-windows-gnu`)
```rust
fn world() -> String {
    "world".into()
}

fn main() {
    let w = world();
    println!("hello {}", w);
}
```

2. Put a breakpoint on line 7 (`println`)
3. In the command palette choose "debugger: start" and then select "run
*crate name*"

Screenshot before the fix:

<img width="893" height="411" alt="image"
src="https://github.com/user-attachments/assets/78097690-b55e-4989-bfa4-20452560f9fc"
/>


<details>
<summary>Console before the fix</summary>

```
Checking latest version of CodeLLDB...
Downloading from https://github.com/vadimcn/codelldb/releases/download/v1.11.8/codelldb-win32-x64.vsix...
Download complete
Could not initialize Python interpreter - some features will be unavailable (e.g. debug visualizers).
Console is in 'commands' mode, prefix expressions with '?'.
warning: (x86_64) D:\repro\target\x86_64-pc-windows-gnu\debug\repro.exe unable to locate separate debug file (dwo, dwp). Debugging will be degraded.
Launching: D:\repro\target\x86_64-pc-windows-gnu\debug\repro.exe
Launched process 13836 from 'D:\repro\target\x86_64-pc-windows-gnu\debug\repro.exe'
error: repro.exe [0x0000000000002074]: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x000000000000001a) attribute, but range extraction failed (invalid range list offset 0x1a), please file a bug and attach the file at the start of this error message
error: repro.exe [0x000000000000208c]: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x0000000000000025) attribute, but range extraction failed (invalid range list offset 0x25), please file a bug and attach the file at the start of this error message
error: repro.exe [0x00000000000020af]: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x0000000000000030) attribute, but range extraction failed (invalid range list offset 0x30), please file a bug and attach the file at the start of this error message
error: repro.exe [0x00000000000020c4]: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x000000000000003b) attribute, but range extraction failed (invalid range list offset 0x3b), please file a bug and attach the file at the start of this error message
error: repro.exe [0x00000000000020fc]: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x0000000000000046) attribute, but range extraction failed (invalid range list offset 0x46), please file a bug and attach the file at the start of this error message
error: repro.exe [0x0000000000002130]: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x0000000000000046) attribute, but range extraction failed (invalid range list offset 0x46), please file a bug and attach the file at the start of this error message
> ? w
< {...}
```
</details>

Screenshot after the fix:
<img width="634" height="295" alt="image"
src="https://github.com/user-attachments/assets/67e36a64-97d2-406c-9216-7ac5b01f4101"
/>

<details>
<summary>Console after the fix</summary>

```
Checking latest version of CodeLLDB...
Downloading from https://github.com/vadimcn/codelldb/releases/download/v1.11.8/codelldb-win32-x64.vsix...
Download complete
Console is in 'commands' mode, prefix expressions with '?'.
Loading Rust formatters from C:\Users\Vasyl\.rustup\toolchains\1.91.1-x86_64-pc-windows-msvc\lib/rustlib/etc
warning: (x86_64) D:\repro\target\x86_64-pc-windows-gnu\debug\repro.exe unable to locate separate debug file (dwo, dwp). Debugging will be degraded.
Launching: D:\repro\target\x86_64-pc-windows-gnu\debug\repro.exe
Launched process 10364 from 'D:\repro\target\x86_64-pc-windows-gnu\debug\repro.exe'
error: repro.exe [0x0000000000002074]: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x000000000000001a) attribute, but range extraction failed (invalid range list offset 0x1a), please file a bug and attach the file at the start of this error message
error: repro.exe [0x000000000000208c]: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x0000000000000025) attribute, but range extraction failed (invalid range list offset 0x25), please file a bug and attach the file at the start of this error message
error: repro.exe [0x00000000000020af]: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x0000000000000030) attribute, but range extraction failed (invalid range list offset 0x30), please file a bug and attach the file at the start of this error message
error: repro.exe [0x00000000000020c4]: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x000000000000003b) attribute, but range extraction failed (invalid range list offset 0x3b), please file a bug and attach the file at the start of this error message
error: repro.exe [0x00000000000020fc]: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x0000000000000046) attribute, but range extraction failed (invalid range list offset 0x46), please file a bug and attach the file at the start of this error message
error: repro.exe [0x0000000000002130]: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x0000000000000046) attribute, but range extraction failed (invalid range list offset 0x46), please file a bug and attach the file at the start of this error message
> ? w
< "world"
```
</details>

This fixes #33753

Release Notes:

- util: Fixed archive::extract_zip failing to extract some archives
2025-11-19 12:34:41 +01:00
Piotr Osiewicz
40dd4e2270 zeta: Add stats about context lines from patch that were retrieved during context retrieval (#43053)
A.K.A: Eval: Expect lines necessary to uniquely target every change in
"Expected Patch" to be included as context

Release Notes:

- N/A
2025-11-19 11:25:53 +00:00
xdBronch
9feb260216 lsp: Support deprecated completion item tag and advertise capability (#43000)
Release Notes:

- N/A
2025-11-19 12:19:58 +01:00
Lukas Wirth
5ccbe945a6 util: Check whether discovered powershell is actually executable (#43044)
Closes https://github.com/zed-industries/zed/issues/42944

The powershell we discovered might be in a directory with higher
permission requirements which will cause us to fail using it.

Release Notes:

- Fixed powershell discovery disregarding admin requirements
2025-11-19 09:26:49 +00:00
Bennet Bo Fenner
a910c594d6 agent_ui: Add mode_id to telemetry (#43045)
Release Notes:

- N/A
2025-11-19 08:49:56 +00:00
Mikayla Maki
19d2532cf8 Update google_ai.rs (#43034)
Release Notes:

- N/A
2025-11-19 05:41:24 +00:00
Cole Miller
785b81aa3a Revert "Fix track file renames in git panel (#42352)" (#43030)
This reverts commit b0a7defd09.

It looks like this doesn't interact correctly with the project diff or
with staging, let's revert and reland with bugs fixed.

Release Notes:

- N/A
2025-11-19 03:56:45 +00:00
Ben Heimberg
24c1617e74 git_ui: Dismiss pickers only on active window (#41320)
Small QOL improvement for branch picker to only dismiss when focus lost
in active window.
This can benefit those who need to switch windows mid branch creation to
fetch correct jira ticket number or etc.

Added `window.is_active_window()` guard in `picker.rs` -> `cancel` event

Release Notes:
- (Let's Git Together) Fixed a behavior where pickers would
automatically close upon the window becoming inactive.
2025-11-18 21:57:30 -05:00
Julia Ryan
1e2f15a3d7 Disable phpactor by default on windows (#43011)
We install phpactor by default, but on windows it doesn't work out of
the box (see
[here](https://github.com/phpactor/phpactor/discussions/2579) for
details). For now we'll default to using intelephense, but in the future
we'd like to switch back if phpactor lands windows support given that
it's open source.

Release Notes:

- N/A
2025-11-18 16:38:19 -08:00
Martin Bergo
7c0663b825 google_ai: Add gemini-3-pro-preview model (#43015)
Release Notes:

- Added the newly released Gemini 3 Pro Preview Model


https://docs.cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/3-pro
2025-11-18 23:51:32 +00:00
Lukas Wirth
94a43dc73a extension_host: Fix IS_WASM_THREAD being set for wrong threads (#43005)
https://github.com/zed-industries/zed/pull/40883 implemented this
incorrectly. It was marking a random background thread as a wasm thread
(whatever thread picked up the wasm epoch timer background task),
instead of marking the threads that actually run the wasm extension.

This has two implications:
1. it didn't prevent extension panics from tearing down as planned
2. Worse, it actually made us hide legit panics in sentry for one of our
background workers.

Now 2 still technically applies for all tokio threads after this, but we
basically only use these for wasm extensions in the main zed binary.

Release Notes:

- Fixed extension panics crashing Zed on Linux
2025-11-18 23:49:22 +00:00
Ben Kunkle
e8e0707256 zeta2: Improve queries parsing (#43012)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...

---------

Co-authored-by: Agus <agus@zed.dev>
Co-authored-by: Max <max@zed.dev>
2025-11-18 23:46:29 +00:00
Tom Zaspel
d7c340c739 docs: Add documenation for OpenTofu support (#42448)
Closes -

Release Notes:

- N/A

Signed-off-by: Tom Zaspel <40226087+tzabbi@users.noreply.github.com>
2025-11-18 18:40:09 -05:00
Julia Ryan
16b24e892e Increase error verbosity (#43013)
Closes #42288

This will actually print the parsing error that prevented the vscode
settings file from being loaded which should make it easier for users to
self help when they have an invalid config.

Release Notes:

- N/A
2025-11-18 23:25:12 +00:00
Barani S
917148c5ce gpui: Use DWM API for backdrop effects and add Mica/Mica Alt support (#41842)
This PR updates window background rendering to use the **official DWM
backdrop API** (`DwmSetWindowAttribute`) instead of the legacy
`SetWindowCompositionAttribute`.
It also adds **Mica** and **Mica Alt** options to
`WindowBackgroundAppearance` for native Windows 11 effects.

### Motivation

Enables modern, stable, and GPU-accelerated backdrops consistent with
Windows 11’s Fluent Design.
Removes reliance on undocumented APIs while maintaining backward
compatibility with older Windows versions.

### Changes

* Added `MicaBackdrop` and `MicaAltBackdrop` variants.
* Switched to DWM API for applying backdrop effects.
* Verified fallback behavior on Windows 10.

### Release Notes:

- Added `WindowBackgroundAppearance::MicaBackdrop` and
`WindowBackgroundAppearance::MicaAltBackdrop` for Windows 11 Mica and
Mica Alt window backdrops.

### Screenshots

- `WindowBackgroundAppearance::Blurred`
<img width="553" height="354" alt="image"
src="https://github.com/user-attachments/assets/57c9c25d-9412-4141-94b5-00000cc0b1ec"
/>

- `WindowBackgroundAppearance::MicaBackdrop`
<img width="553" height="354" alt="image"
src="https://github.com/user-attachments/assets/019f541c-3335-4c9e-b026-71f5a1786534"
/>

- `WindowBackgroundAppearance::MicaAltBackdrop`
<img width="553" height="354" alt="image"
src="https://github.com/user-attachments/assets/5128d600-c94d-4c89-b81a-8b842fe1337a"
/>

---------

Co-authored-by: John Tur <john-tur@outlook.com>
2025-11-18 18:20:32 -05:00
Piotr Osiewicz
951132fc13 chore: Fix build graph - again (#42999)
11.3s -> 10.0s for silly stuff like extracting actions from crates.
project panel still depends on git_ui though..

Release Notes:

- N/A
2025-11-18 19:20:34 +01:00
Ben Kunkle
bf0dd4057c zeta2: Make new_text/old_text parsing more robust (#42997)
Closes #ISSUE

The model often uses the wrong closing tag, or has spaces around the
closing tag name. This PR makes it so that opening tags are treated as
authoritative and any closing tag with the name `new_text` `old_text` or
`edits` is accepted based on depth. This has the additional benefit that
the parsing is more robust with contents that contain `new_text`
`old_text` or `edits. I.e. the following test passes

```rust
    #[test]
    fn test_extract_xml_edits_with_conflicting_content() {
        let input = indoc! {r#"
            <edits path="component.tsx">
            <old_text>
            <new_text></new_text>
            </old_text>
            <new_text>
            <old_text></old_text>
            </new_text>
            </edits>
        "#};

        let result = extract_xml_replacements(input).unwrap();
        assert_eq!(result.file_path, "component.tsx");
        assert_eq!(result.replacements.len(), 1);
        assert_eq!(result.replacements[0].0, "<new_text></new_text>");
        assert_eq!(result.replacements[0].1, "<old_text></old_text>");
    }
```

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-18 12:36:37 -05:00
Conrad Irwin
3c4ca3f372 Remove settings::Maybe (#42933)
It's unclear how this would ever be useful

cc @probably-neb

Release Notes:

- N/A
2025-11-18 10:23:16 -07:00
Artur Shirokov
03132921c7 Add HTTP transport support for MCP servers (#39021)
### What this solves

This PR adds support for HTTP and SSE (Server-Sent Events) transports to
Zed's context server implementation, enabling communication with remote
MCP servers. Currently, Zed only supports local MCP servers via stdio
transport. This limitation prevents users from:

- Connecting to cloud-hosted MCP servers
- Using MCP servers running in containers or on remote machines
- Leveraging MCP servers that are designed to work over HTTP/SSE

### Why it's important

The MCP (Model Context Protocol) specification includes HTTP/SSE as
standard transport options, and many MCP server implementations are
being built with these transports in mind. Without this support, Zed
users are limited to a subset of the MCP ecosystem. This is particularly
important for:

- Enterprise users who need to connect to centralized MCP services
- Developers working with MCP servers that require network isolation
- Users wanting to leverage cloud-based context providers (e.g.,
knowledge bases, API integrations)

### Implementation approach

The implementation follows Zed's existing architectural patterns:

- **Transports**: Added `HttpTransport` and `SseTransport` to the
`context_server` crate, built on top of the existing `http_client` crate
- **Async handling**: Uses `gpui::spawn` for network operations instead
of introducing a new Tokio runtime
- **Settings**: Extended `ContextServerSettings` enum with a `Remote`
variant to support URL-based configuration
- **UI**: Updated the agent configuration UI with an "Add Remote Server"
option and dedicated modal for remote server management

### Changes included

- [x] HTTP transport implementation with request/response handling
- [x] SSE transport for server-sent events streaming
- [x] `build_transport` function to construct appropriate transport
based on URL scheme
- [x] Settings system updates to support remote server configuration
- [x] UI updates for adding/editing remote servers
- [x] Unit tests using `FakeHttpClient` for both transports
- [x] Integration tests (WIP)
- [x] Documentation updates (WIP)

### Testing

- Unit tests for both `HttpTransport` and `SseTransport` using mocked
HTTP client
- Manual testing with example MCP servers over HTTP/SSE
- Settings validation and UI interaction testing

### Screenshots/Recordings

[TODO: Add screenshots of the new "Add Remote Server" UI and
configuration modal]

### Example configuration

Users can now configure remote MCP servers in their `settings.json`:

```json
{
  "context_servers": {
    "my-remote-server": {
      "enabled": true,
      "url": "http://localhost:3000/mcp"
    }
  }
}
```

### AI assistance disclosure

I used AI to help with:

- Understanding the MCP protocol specification and how HTTP/SSE
transports should work
- Reviewing Zed's existing patterns for async operations and suggesting
consistent approaches
- Generating boilerplate for test cases
- Debugging SSE streaming issues

All code has been manually reviewed, tested, and adapted to fit Zed's
architecture. The core logic, architectural decisions, and integration
with Zed's systems were done with human understanding of the codebase.
AI was primarily used as a reference tool and for getting unstuck on
specific technical issues.

Release notes:
* You can now configure MCP Servers that connect over HTTP in your
settings file. These are not yet available in the extensions API.
  ```
  {
    "context_servers": {
      "my-remote-server": {
        "enabled": true,
        "url": "http://localhost:3000/mcp"
      }
    }
  }
  ```

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-11-18 16:39:08 +00:00
Richard Feldman
c0fadae881 Thought signatures (#42915)
Implement Gemini API's [thought
signatures](https://ai.google.dev/gemini-api/docs/thinking#signatures)

Release Notes:

- Added thought signatures for Gemini tool calls
2025-11-18 10:41:19 -05:00
Agus Zubiaga
1c66c3991d Enable sweep flag for staff (#42987)
Release Notes:

- N/A
2025-11-18 15:39:27 +00:00
Agus Zubiaga
7e591a7e9a Fix sweep icon spacing (#42986)
Release Notes:

- N/A
2025-11-18 15:33:03 +00:00
Danilo Leal
c44d93745a agent_ui: Improve the modal to add LLM providers (#42983)
Closes https://github.com/zed-industries/zed/issues/42807

This PR makes the modal to add LLM providers a bit better to interact
with:

1. Added a scrollbar
2. Made the inputs navigable with tab
3. Added some responsiveness to ensure it resizes on shorter windows


https://github.com/user-attachments/assets/758ea5f0-6bcc-4a2b-87ea-114982f37caf

Release Notes:

- agent: Improved the modal to add LLM providers by making it responsive
and keyboard navigable.
2025-11-18 12:28:14 -03:00
Lukas Wirth
b4e4e0d3ac remote: Fix up incorrect logs (#42979)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-18 15:14:52 +00:00
Lukas Wirth
097024d46f util: Use process spawn helpers in more places (#42976)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-18 14:31:39 +00:00
Ben Brandt
f1c2afdee0 Update codex docs to include configuration for third-party providers (#42973)
Release Notes:

- N/A
2025-11-18 13:50:59 +00:00
Jakub Konka
ea120dfe18 Revert "git: Remove JobStatus from PendingOp in favour of in-flight p… (#42970)
…runing (#42955)"

This reverts commit 696fdd8fed.

Release Notes:

- N/A
2025-11-18 13:30:40 +00:00
Lukas Wirth
d2988ffc77 vim: Fix snapshot out of bounds indexing (#42969)
Fixes ZED-38X

Release Notes:

- N/A
2025-11-18 13:02:40 +00:00
Engin Açıkgöz
f17d2c92b6 terminal_view: Fix terminal opening in root directory when editing single file corktree (#42953)
Fixes #42945

## Problem
When opening a single file via command line (e.g., `zed
~/Downloads/file.txt`), the terminal panel was opening in the root
directory (/) instead of the file's directory.

## Root Cause
The code only checked for active project directory, which returns None
when a single file is opened. Additionally, file worktrees weren't
handling parent directory lookup.

## Solution
Added fallback logic to use the first project directory when there's no
active entry, and made file worktrees return their parent directory
instead of None.

## Testing
- All existing tests pass
- Added test coverage for file worktree scenarios
- Manually tested with `zed ~/Downloads/file.txt` - terminal now opens
in correct directory

This improves the user experience for users who frequently open single
files from the command line.

## Release Notes

- Fixed terminal opening in root directory when editing single files
from the command line
2025-11-18 13:37:48 +01:00
Antonio Scandurra
c1d9dc369c Try reducing flakiness of fs-event tests by bumping timeout to 4s on CI (#42960)
Release Notes:

- N/A
2025-11-18 11:00:02 +00:00
Jakub Konka
696fdd8fed git: Remove JobStatus from PendingOp in favour of in-flight pruning (#42955)
The idea is that we only store running (`!self.finished`) or finished
(`self.finished`) pending ops, while everything else (skipped, errored)
jobs are pruned out immediately. We don't really need them in the grand
scheme of things anyway.

Release Notes:

- N/A
2025-11-18 10:22:34 +00:00
Lena
980f8bff2a Add a github issue label to shoo the stalebot away (#42950)
Labeling an issue with "never stale" will keep the stalebot away; the
bot can get annoying in some situations otherwise.

Release Notes:

- N/A
2025-11-18 10:15:09 +01:00
Kirill Bulatov
2a3bcbfe0f Properly check chunk version on lsp store update (#42951)
Release Notes:

- N/A

Co-authored-by: Lukas Wirth <lukas@zed.dev>
2025-11-18 09:13:32 +00:00
aleanon
5225a84aff For and await highlighting rust (#42924)
Closes #42922

Release Notes:

- Fixed Correctly highlighting the 'for' keyword in Rust as
keyword.control only in for loops.
- Fixed Highlighting the 'await' keyword in Rust as keyword.control
2025-11-18 09:11:36 +01:00
Max Brunsfeld
5c70f8391f Fix panic when using sweep AI without token env var (#42940)
Release Notes:

- N/A
2025-11-17 22:23:14 -08:00
Danilo Leal
10efbd5eb4 agent_ui: Show the "new thread" keybinding for the currently active agent (#42939)
This PR's goal is to improve discoverability of how Zed "remembers" the
currently selected agent when hitting `cmd-n` (or `ctrl-n`). Hitting
that binding starts a new thread with whatever agent is currently
selected.

In the example below, I am in a Claude Code thread and if I hit `cmd-n`,
a new, fresh CC thread will be started:

<img width="500" height="822" alt="Screenshot 2025-11-18 at 1  13@2x"
src="https://github.com/user-attachments/assets/d3acd1aa-459d-4078-9b62-bbac3b8c1600"
/>


Release Notes:

- agent: Improved discoverability of the `cmd-n` keybinding to create a
new thread with the currently selected agent.
2025-11-18 01:53:37 -03:00
Agus Zubiaga
0386f240a9 Add experimental Sweep edit prediction provider (#42927)
Only for staff

Release Notes:

- N/A

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-11-18 01:00:26 +00:00
Conrad Irwin
a39ba03bcc Use metrics-id for sentry user id when we have it (#42931)
This should make it easier to correlate Sentry reports with user reports
and
github issues (for users who have diagnostics enabled)

Release Notes:

- N/A
2025-11-17 23:44:59 +00:00
Lukas Wirth
2c7bcfcb7b multi_buffer: Work around another panic bug in path_key (#42920)
Fixes ZED-346 for now until I find the time to dig into this bug
properly

Release Notes:

- Fixed a panic in the diagnostics pane
2025-11-17 22:38:48 +00:00
Lukas Wirth
6bea23e990 text: Temporarily remove assert_char_boundary panics (#42919)
As discussed in the first responders meeting. We have collected a lot of
backtraces from these, but it's not quite clear yet what causes this.
Removing these should ideally make things a bit more stable even if we
may run into panics later one when the faulty anchor is used still.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-17 22:20:45 +00:00
Julia Ryan
98da1ea169 Fix remote extension syncing (#42918)
Closes #40906
Closes #39729

SFTP uploads weren't quoting the install directory which was causing
extension syncing to fail. We were also only running `install_extension`
once per remote-connection instead of once per project (thx @feeiyu for
pointing this out) so extension weren't being loaded in subsequently
opened remote projects.

Release Notes:

- N/A

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-11-17 16:13:58 -06:00
Danilo Leal
98a83b47e6 agent_ui: Make input fields in Bedrock settings keyboard navigable (#42916)
Closes https://github.com/zed-industries/zed/issues/36587

This PR enables jumping from one input to the other, in the Bedrock
settings section, with tab.

Release Notes:

- N/A
2025-11-17 19:13:15 -03:00
Danilo Leal
5f356d04ff agent_ui: Fix model name label truncation (#42921)
Closes https://github.com/zed-industries/zed/issues/32739

Release Notes:

- agent: Fixed an issue where the label for model names wouldn't use all
the available space in the model picker.
2025-11-17 19:12:26 -03:00
Marshall Bowers
73d3f9611e collab: Add external_id column to billing_customers table (#42923)
This PR adds an `external_id` column to the `billing_customers` table.

Release Notes:

- N/A
2025-11-17 22:12:00 +00:00
Finn Evers
d9cfc2c883 Fix formatting in various files (#42917)
This fixes various issues where rustfmt failed to format code due to too
long strings, most of which I stumbled across over the last week and
some additonal ones I searched for whilst fixing the others.

Release Notes:

- N/A
2025-11-17 21:48:09 +00:00
Dino
ee420d530e vim: Change approach to fixing vim's temporary mode bug (#42894)
The `Vim.exit_temporary_normal` method had been updated
(https://github.com/zed-industries/zed/pull/42742) to expect and
`Option<&Motion>` that would then be used to determine whether to move
the cursor right in case the motion was `Some(EndOfLine { ..})`.
Unfortunately this meant that all callers now had to provide this
argument, even if just `None`.

After merging those changes I remember that we could probably play
around with `clip_at_line_ends` so this commit removes those intial
changes in favor of updating the `vim::normal::Vim.move_cursor` method
so that, if vim is in temporary mode and `EndOfLine` is used, it
disables clipping at line ends so that the newline character can be
selected.

Closes [#42278](https://github.com/zed-industries/zed/issues/42278)

Release Notes:

- N/A
2025-11-17 21:34:37 +00:00
Miguel Raz Guzmán Macedo
d801d0950e Add @miguelraz to reviewers and support sections (#42904)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-17 15:22:34 -06:00
Lukas Wirth
3f25d36b3c agent_ui: Fix text pasting no longer working (#42914)
Regressed in https://github.com/zed-industries/zed/pull/42908
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-17 21:04:50 +00:00
Joseph T. Lyons
f015368586 Update top-ranking issues script (#42911)
- Added Windows category
- Removed unused import
- Fixed a type error reported by `ty`

Release Notes:

- N/A
2025-11-17 15:20:22 -05:00
Ben Kunkle
4bf3b9d62e zeta2: Output bucketed_analysis.md (#42890)
Closes #ISSUE

Makes it so that a file named `bucketed_analysis.md` is written to the
runs directory after an eval is ran with > 1 repetitions. This file
buckets the predictions made by the model by comparing the edits made so
that seeing how many times different failure modes were encountered
becomes much easier.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-17 15:17:39 -05:00
Lukas Wirth
599a217ea5 workspace: Fix logging of errors in prompt_err (#42908)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-17 20:39:50 +01:00
ozzy
b0a7defd09 Fix track file renames in git panel (#42352)
Closes #30549

Release Notes:

- Fixed: Git renames now properly show as renamed files in the git panel
instead of appearing as deleted + untracked files
<img width="351" height="132" alt="Screenshot 2025-11-10 at 17 39 44"
src="https://github.com/user-attachments/assets/80e9c286-1abd-4498-a7d5-bd21633e6597"
/>
<img width="500" height="95" alt="Screenshot 2025-11-10 at 17 39 55"
src="https://github.com/user-attachments/assets/e4c59796-df3a-4d12-96f4-e6706b13a32f"
/>
2025-11-17 13:25:51 -06:00
Davis Vaughan
57e3bcfcf8 Revise R documentation - about Air in particular (#42755)
Returning the favor from @rgbkrk in
https://github.com/posit-dev/air/pull/445

I noticed the R docs around Air are a bit incorrect / out of date. I'll
make a few more comments inline. Feel free to take over for any other
edits.

Release Notes:

- Improved R language support documentation
2025-11-17 11:53:42 -07:00
Oleksiy Syvokon
b2f561165f zeta2: Support qwen3-minimal prompt format (#42902)
This prompt is for a fine-tuned model. It has the following changes,
compared to `minimal`:
- No instructions at all, except for one sentence at the beginning of
the prompt.
- Output is a simplified unified diff -- hunk headers have no line
counts (e.g., `@@ -20 +20 @@`)
- Qwen's FIM tokens are used where possible (`<|file_sep|>`,
`<|fim_prefix|>`, `<|fim_suffix|>`, etc.)

To evaluate this model:
```
ZED_ZETA2_MODEL=zeta2-exp [usual zeta-cli eval params ...]  --prompt-format minimal-qwen
```

This will point to the most recent Baseten deployment of zeta2-exp
(which may change in the future, so the prompt-format may get out of
sync).

Release Notes:

- N/A
2025-11-17 20:36:05 +02:00
localcc
fd1494c31a Fix remote server completions not being queried from all LSP servers (#42723)
Closes #41294

Release Notes:

- Fixed remote LSPs not being queried
2025-11-17 18:07:49 +00:00
Danilo Leal
faa1136651 agent_ui: Don't create a new terminal when hitting the new thread binding from the terminal (#42898)
Closes https://github.com/zed-industries/zed/issues/32701

Release Notes:

- agent: Fixed a bug where hitting the `NewThread` keybinding when
focused inside a terminal within the agent panel would create a new
terminal tab instead of a new thread.
2025-11-17 15:05:38 -03:00
Conrad Irwin
6bf5e92a25 Revert "Keep selection in SwitchToHelixNormalMode (#41583)" (#42892)
Closes #ISSUE

Release Notes:

- Fixes vim "go to definition" making a selection
2025-11-17 11:01:34 -07:00
Lukas Wirth
46ad6c0bbb ci: Remove remaining nextest compiles (#42630)
Follow up to https://github.com/zed-industries/zed/pull/42556

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-17 17:59:48 +00:00
Lukas Wirth
671500de1b agent_ui: Fix images copied from win explorer not being pastable (#42858)
Closes https://github.com/zed-industries/zed/issues/41505

A bit adhoc but it gets the job done for now

Release Notes:

- Fixed images copied from windows explorer not being pastable in the
agent panel
2025-11-17 17:58:22 +00:00
Kirill Bulatov
0519c645fb Deduplicate inlays when getting those from multiple language servers (#42899)
Part of https://github.com/zed-industries/zed/issues/42671

Release Notes:

- Deduplicate inlay hints from different language servers
2025-11-17 17:53:05 +00:00
Richard Feldman
23872b0523 Fix stale edits (#42895)
Closes #34069

<img width="532" height="880" alt="Screenshot 2025-11-17 at 11 14 19 AM"
src="https://github.com/user-attachments/assets/abc50c32-d54d-4310-a6e6-83008db7ed81"
/>

<img width="525" height="863" alt="Screenshot 2025-11-17 at 12 22 50 PM"
src="https://github.com/user-attachments/assets/15a69792-c2c7-4727-add9-c1f9baa5e665"
/>

Release Notes:

- Agent file edits now error if the file has changed since last read
(allowing the agent to read changes and avoid overwriting changes made
outside Zed)
2025-11-17 12:23:18 -05:00
Richard Feldman
4b050b651a Support Agent Servers on remoting (#42683)
<img width="348" height="359" alt="Screenshot 2025-11-13 at 6 53 39 PM"
src="https://github.com/user-attachments/assets/6fe75796-8ceb-4f98-9d35-005c90417fd4"
/>

Also added support for per-target env vars to Agent Server Extensions

Closes https://github.com/zed-industries/zed/issues/42291

Release Notes:

- Per-target env vars are now supported on Agent Server Extensions
- Agent Server Extensions are now available when doing SSH remoting

---------

Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-11-17 10:48:14 -05:00
Danilo Leal
bb46bc167a settings_ui: Add "Edit in settings.json" button to subpage header (#42886)
Closes https://github.com/zed-industries/zed/issues/42094

This will make it consistent with the regular/main page. Also ended up
fixing a bug along the way where this button wouldn't work for subpage
items.

Release Notes:

- settings ui: Fixed a bug where the "Edit in settings.json" wouldn't
work for subpages like all the Language pages.
2025-11-17 11:58:43 -03:00
Oleksiy Syvokon
b274f80dd9 zeta2: Print average length of prompts and outputs (#42885)
Release Notes:

- N/A
2025-11-17 16:56:58 +02:00
Danilo Leal
d77ab99ab1 keymap_editor: Make "toggle exact match mode" the default for binding search (#42883)
I think having the "exact mode" turned on by default is usually what
users will expect when searching for a specific keybinding. When it's
turned off, it's very odd to search for a super common binding like
"command-enter" and get no results. That happens because without that
mode, we're trying to match for subsequent matches, which I'm betting
it's an edge case. Hopefully, this change will make the keymap editor
feel more like it works well.

I'm also adding the toggle icon button inside the keystroke input for
consistency with the project search input.

Making this change very inspired by [Sam Rose's
feedback](https://bsky.app/profile/samwho.dev/post/3m5juszqyd22w).

Release Notes:

- keymap editor: Made the "toggle exact match mode" the default
keystroke search mode so that whatever you search for matches exactly to
results.
2025-11-17 11:43:15 -03:00
Finn Evers
97792f7fb9 Prefer loading extension.toml before extension.json (#42884)
Closes #42406

The issue for the fish-extension is that a `extension.json` is still
present next to a `extension.toml`, although the former is deprecated.

We should prefer the `extension.toml` if it is present and only fall
back to the `extension.json` if needed. This PR tackles this.

Release Notes:

- N/A
2025-11-17 14:29:50 +00:00
tidely
9bebf314e0 http_client: Remove unused HttpClient::type_name method (#42803)
Closes #ISSUE

Remove unused method `HttpClient::type_name`. Looking at the PR from a
year ago when it was added, it was never actually used for anything and
seems like a prototyping artifact.

Other misc changes for the `http_client` crate include:

- Use `derive_more::Deref` for `HttpClientWithUrl` (already used for
`HttpClientWithProxy`)
- Move `http_client::proxy()` higher up in the trait definition. (It was
in between methods that have default implementations)

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-17 15:28:22 +01:00
Danilo Leal
4092e81ada keymap_editor: Adjust some items of the UI (#42876)
- Only showing the "Create" menu item in the right-click context menu
for actions that _do not_ contain a binding already assigned to them
- Only show the "Clear Input" icon button in the keystroke modal when
the input is focused/in recording mode
- Add a subtle hover style to the table rows just to make it easier to
navigate

Release Notes:

- N/A
2025-11-17 10:59:46 -03:00
Kirill Bulatov
e0b64773d9 Properly sanitize out inlay hints from remote hosts (#42878)
Part of https://github.com/zed-industries/zed/issues/42671

Release Notes:

- Fixed remote hosts causing duplicate hints to be displayed
2025-11-17 15:53:18 +02:00
Piotr Osiewicz
f1bebd79d1 zeta2: Add skip-prediction flag to eval CLI (#42872)
Release Notes:

- N/A
2025-11-17 13:37:51 +00:00
Lukas Wirth
a66a539a09 Reduce macro burden for rust-analyzer (#42871)
This enables optimizations for our own proc-macros as well as some heavy
hitters. Additionally this gates the `derive_inspector_reflection` to be
skipped for rust-analyzer as it currently slows down rust-analyzer way
too much

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-17 12:31:00 +00:00
Lucas Parry
a2d3e3baf9 project_panel: Add sort mode (#40160)
Closes #4533 (partly at least)

Release Notes:

- Added `project_panel.sort_mode` option to control explorer file sort
(directories first, mixed, files first)

 ## Summary

Adds three sorting modes for the project panel to give users more
control over how files and directories are displayed:

- **`directories_first`** (default): Current behaviour - directories
grouped before files
- **`mixed`**: Files and directories sorted together alphabetically
- **`files_first`**: filed grouped before directories

 ## Motivation

Users coming from different editors and file managers have different
expectations for file sorting. Some prefer directories grouped at the
top (traditional), while others prefer the macOS Finder-style mixed
sorting where "Apple1/", "apple2.tsx" and "Apple3/" appear
alphabetically mixed together.


 ### Screenshots

New sort options in settings:
<img width="515" height="160" alt="image"
src="https://github.com/user-attachments/assets/8f4e6668-6989-4881-a9bd-ed1f4f0beb40"
/>


Directories first | Mixed | Files first
-------------|-----|-----
<img width="328" height="888" alt="image"
src="https://github.com/user-attachments/assets/308e5c7a-6e6a-46ba-813d-6e268222925c"
/> | <img width="327" height="891" alt="image"
src="https://github.com/user-attachments/assets/8274d8ca-b60f-456e-be36-e35a3259483c"
/> | <img width="328" height="890" alt="image"
src="https://github.com/user-attachments/assets/3c3b1332-cf08-4eaf-9bed-527c00b41529"
/>


### Agent usage

Copilot-cli/claude-code/codex-cli helped out a lot. I'm not from a rust
background, but really wanted this solved, and it gave me a chance to
play with some of the coding agents I'm not permitted to use for work
stuff

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-11-17 17:52:46 +05:30
Serophots
175162af4f project_panel: Fix preview tabs disabling focusing files after just one click in project panel (#42836)
Closes #41484

With preview tabs disabled, when you click once on a file in the project
panel, rather than focusing on that file, zed will incorrectly focus on
the text editor panel. This means if you click on a file to focus it,
then follow up with a keybind like backspace to delete that file, it
doesn't delete that file because the backspace goes through to the text
editor instead.

Incorrect behaviour seen here:


https://github.com/user-attachments/assets/8c2dea90-bd90-4507-8ba6-344be348f151



Release Notes:

- Fixed improper UI focus behaviour in the project panel when preview
tabs are disabled
2025-11-17 16:53:12 +05:30
Dino
cdcc068906 vim: Fix temporary mode exit on end of line (#42742)
When using the end of line motion ($) while in temporary mode, the
cursor would be placed in insert mode just before the last character
instead of after, just like in NeoVim.

This happens because `EndOfLine` kind of assumes that we're in `Normal`
mode and simply places the cursor in the last character instead of the
newline character.

This commit moves the cursor one position to the right when exiting
temporary mode and the motion used was `Motion::EndOfLine`

- Update `vim::normal::Vim.exit_temporary_normal` to now accept a
`Option<&Motion>` argument, in case callers want this new logic to
potentially be applied

Closes #42278 

Release Notes:

- Fixed temporary mode exit when using `$` to move to the end of the
line
2025-11-17 11:14:49 +00:00
Mayank Verma
86484aaded languages: Clean up invalid init calls after recent API changes (#42866)
Related to https://github.com/zed-industries/zed/pull/41670

Release Notes:

- Cleaned up invalid init calls after recent API changes in
https://github.com/zed-industries/zed/pull/42238
2025-11-17 10:29:29 +00:00
Mayank Verma
d32934a893 languages: Fix indentation for if/else statements in C/C++ without braces (#41670)
Closes #41179

Release Notes:

- Fixed indentation for if/else statements in C/C++ without braces
2025-11-17 10:05:54 +01:00
warrenjokinen
b463266fa1 Remove mention of Fireside Hacks (#42853)
Fireside Hack events are no longer being held.

Closes #ISSUE

Release Notes:

- N/A
2025-11-16 23:26:38 -05:00
Agus Zubiaga
b0525a26a6 Report automatically discarded zeta predictions (#42761)
We weren't reporting predictions that were generated but never made it
out of the provider, such as predictions that failed to interpolate, and
those that are cancelled because another request completes before it.

Release Notes:

- N/A

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-11-16 11:51:13 -08:00
Smit Barmase
1683052e6c editor: Fix MoveToEnclosingBracket and unmatched forward/backward Vim motions in Markdown code blocks (#42813)
We now correctly use bracket ranges from the deepest syntax layer when
finding enclosing brackets.

Release Notes:

- Fixed an issue where `MoveToEnclosingBracket` didn’t work correctly
inside Markdown code blocks.
- Fixed an issue where unmatched forward/backward Vim motions didn’t
work correctly inside Markdown code blocks.

---------

Co-authored-by: MuskanPaliwal <muskan10112002@gmail.com>
2025-11-15 23:57:49 +05:30
Alvaro Parker
07cc87b288 Fix wild install script (#42747)
Use
[`command`](https://www.gnu.org/software/bash/manual/bash.html#index-command)
instead of `which` to check if `wild` is installed.

Using `which` will result in an error being printed to stdout: 

```bash
./script/install-wild
which: invalid option -- 's'
/usr/local/bin/wild
Warning: existing wild 0.6.0 found at /usr/local/bin/wild. Skipping installation.
```

Release Notes:

- N/A
2025-11-15 15:15:37 +01:00
Danilo Leal
1277f328c4 docs: Improve custom keybinding for external agent example (#42776)
Follow up to https://github.com/zed-industries/zed/pull/42772 adding
some comments to improve clarity.

Release Notes:

- N/A
2025-11-14 23:08:46 +00:00
Danilo Leal
b3097cfc8a docs: Add section about keybinding for external agent threads (#42772)
Release Notes:

- N/A
2025-11-14 19:54:52 -03:00
Ivan Pasquariello
305206fd48 Make drag and double click enabled on the whole title bar on macOS (#41839)
Closes #4947

Taken inspiration from @tasuren implementation, plus the addition for
the double click enabled on the whole title bar too to
maximizes/restores the window.

I was not able to test the application on Linux, no need to test on
Windows since the feature is enabled by the OS.

Release Notes:

- Fixed title bar not fully draggable on macOS
- Fixed not being able to maximizes/restores the window with double
click on the whole title bar on macOS
2025-11-14 22:46:35 +00:00
Ben Kunkle
c387203ac8 zeta2: Prediction prompt engineering (#42758)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...

---------

Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Michael Sloan <mgsloan@gmail.com>
2025-11-14 16:50:55 -05:00
Danilo Leal
a260ba6428 agent_ui: Simplify labels in new thread menu (#42746)
Drop the "new", it's simpler! 😆 

| Before | After |
|--------|--------|
| <img width="800" height="932" alt="Screenshot 2025-11-14 at 2  48@2x"
src="https://github.com/user-attachments/assets/efa67d57-9b5c-4eef-8dc7-f36c8e6a4a90"
/> | <img width="800" height="772" alt="Screenshot 2025-11-14 at 2 
47@2x"
src="https://github.com/user-attachments/assets/042d2a0b-24b4-4ad5-8411-82e0eafb993f"
/> |




Release Notes:

- N/A
2025-11-14 18:02:09 +00:00
Lukas Wirth
a8e0de37ac gpui: Fix crashes when losing devices while resizing on windows (#42740)
Fixes ZED-1HC

Release Notes:

- Fixed Zed panicking when moving Zed windows over different screens
associated with different gpu devices on windows
2025-11-14 17:51:26 +00:00
Danilo Leal
a1a599dac5 collab_ui: Fix search matching in the panel (#42743)
Release Notes:

- collab: Fixed a regression where search matches wouldn't expand the
parent channel if that happened to be collapsed.
2025-11-14 14:45:58 -03:00
Smit Barmase
524b97d729 project_panel: Fix autoscroll and filename editor focus race condition (#42739)
Closes https://github.com/zed-industries/zed/issues/40867

Since the recent changes in
[https://github.com/zed-industries/zed/pull/38881](https://github.com/zed-industries/zed/pull/38881),
the filename editor is sometimes not focused after duplicating a file or
creating a new one, and similarly, autoscroll sometimes didn’t work. It
turns out that multiple calls to `update_visible_entries_task` cancel
the existing task, which might contain information about whether we need
to focus the filename editor and autoscroll after the task ends. To fix
this, we now carry that information forward to the next task that
overwrites it, so that when the latest task ends, we can use that
information to do the right thing.

Release Notes:

- Fixed an issue in the Project Panel where duplicating or creating an
entry sometimes didn’t focus the rename editing field.
2025-11-14 21:56:48 +05:30
Ben Kunkle
8772727034 zeta2: Improve zeta old text matching (#42580)
This PR improves Zeta2's matching of `old_text`/`new_text` pairs, using
similar code to what we use in the edit agent. For right now, we've
duplicated the code, as opposed to trying to generalize it.

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Michael <michael@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Agus <agus@zed.dev>
2025-11-14 11:18:16 -05:00
Dino
aaa116d129 languages: Fix command used for Go subtests (#42734)
The command used to run go subtests was breaking if the test contained
square brackets, for example:

```
go test . -v -run ^TestInventoryCheckout$/^\[test\]_test_checkout$
```

After a bit of testing it appears that the best way to actually resolve
this in a way supported by `go test` is to wrap this command in quotes.
As such, this commit updates the command to, considering the example
above:

```
go test . -v -run '^TestInventoryCheckout$/^\[test\]_test_checkout$'
```

We also tested escape the square brackets, using `\\\[` instead of `\[`,
but that would lead to a more complex change, so we opted for the
simpler solution of wrapping the command in quotes.

Closes #42347 

Release Notes:

- Fixed command used to run Go subtests to ensure that escaped
characters don't lead to a failure in finding tests to run
2025-11-14 16:04:54 +00:00
Danilo Leal
c1096d8b63 agent_ui: Render error descriptions as markdown in thread view callouts (#42732)
This PR makes the description in the callout that display general errors
in the agent panel be rendered as markdown. This allow us to pass URLs
to these error strings that will be clickable, improving the overall
interaction with them. Here's an example:

<img width="500" height="396" alt="Screenshot 2025-11-14 at 11  43@2x"
src="https://github.com/user-attachments/assets/f4fc629a-6314-4da1-8c19-b60e1a09653b"
/>

Release Notes:

- agent: Improved the interaction with errors by allowing links to be
clickable.
2025-11-14 12:12:47 -03:00
Josh Piasecki
092071a2f0 git_ui: Allow opening a file with the diff hunks expanded (#40616)
So i just discovered `editor::ExpandAllDiffHunks`

I have been really missing the ability to look at changes NOT in a multi
buffer so i was very pleased to finally figure out that this is already
possible in Zed.

i have seen alot of discussion/issues requesting this feature so i think
it is safe to say i'm not the only one that is not aware it exists.

i think the wording in the docs could better communicate what this
feature actually is, however, i think an even better way to show users
that this feature exists would be to just put it in front of them.

In the `GitPanel`:
- `menu::Confirm` opens the project diff
- `menu::SecondaryConfirm` opens the selected file in a new editor.

I think it would be REALLY nice if opening a file with
`SecondaryConfirm` opened the file with the diff hunks already expanded
and scrolled the editor to the first hunk.

ideally i see this being toggle-able in settings something like
`GitPanel - Open File with Diffs Expanded` or something. so the user
could turn this off if they preferred.

I tried creating a new keybinding using the new `actions::Sequence`
it was something like:
```json
{
  "context": "GitPanel && ChangesList",
  "bindings": {
    "cmd-enter" : [ "actions::Sequence", ["menu:SecondaryConfirm", "editor::ToggleFocus", "editor::ExpandAllDiffHunks", "editor::GoToHunk"]]
  }
}
```
but the action sequence does not work. i think because opening the file
is an async task.

i have a first attempt here, of just trying to get the diff hunks to
expand after opening the file.
i tried to copy and paste the logic/structure as best i could from the
confirm method in file_finder.rs:1432

it compiles, but it does not work, and i do not have enough experience
in rust or in this project to figure out anything further.

if anyone was interested in working on this with me i would enjoy
learning more and i think this would be a nice way to showcase this
tool!
2025-11-14 08:47:46 -05:00
Oleksiy Syvokon
723f9b1371 zeta2: Add minimal prompt for fine-tuned models (#42691)
1. Add `--prompt-format=minimal` that matches single-sentence
instructions used in fine-tuned models (specifically, in `1028-*` and
`1029-*` models)

2. Use separate configs for agentic context search model and edit
prediction model. This is useful when running a fine-tuned EP model, but
we still want to run vanilla model for context retrieval.

3. `zeta2-exp` is a symlink to the same-named Baseten deployment. This
model can be redeployed and updated without having to update the
deployment id.

4. Print scores as a compact table

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <piotr@zed.dev>
2025-11-14 13:08:54 +00:00
Jakub Konka
37523b0007 git_panel: Fix buffer header checkbox not showing partially staged files (#42718)
Release Notes:

- Fixed buffer header controls (staging checkbox) not showing partially
staged files
2025-11-14 12:55:04 +00:00
Jakub Konka
b4167caaf1 git_panel: Fix StageAll/UnstageAll not working when panel not in focus (#42708)
Release Notes:

- Fixed "Stage All"/"Unstage All" buttons from not working when git
panel is not in focus
2025-11-14 10:42:32 +01:00
Smit Barmase
020f518231 project_panel: Add tests for cross worktree drag-and-drop (#42704)
Add missing tests for cross worktree drag-and-drop:

- file -> directory
- file -> file (drops into parent directory)
- whole directory move
- multi selection move

Release Notes:

- N/A
2025-11-14 13:31:44 +05:30
morgankrey
ead4f26b52 Update docs for Gemini ZDR (#42697)
Closes #ISSUE

Release Notes:

- N/A
2025-11-14 00:22:20 -06:00
Josh Piasecki
3de3a369f5 editor: Add diffs_expanded to key context when diff hunks are expanded (#40617)
including a new identifier on the Editor key context will allow for some
more flexibility when creating keybindings.

for example i would like to be able to set the following:
```json
{
  "context": "Editor",
  "bindings": {
    "pageup": ["editor::MovePageUp", { "center_cursor": true }],
    "pagedown": ["editor::MovePageDown", { "center_cursor": true }],
  }
},
{
  "context": "Editor && diffs_expanded",
  "bindings": {
    "pageup": "editor::GoToPrevHunk",
    "pagedown": "editor::GoToHunk",
  }
},
```

<img width="1392" height="1167" alt="Screenshot 2025-10-18 at 23 51 46"
src="https://github.com/user-attachments/assets/cf4e262e-97e7-4dd9-bbda-cd272770f1ac"
/>


very open to suggestions for the name. that's the best i could come up
with.

the action *IS* called `editor::ExpandAllDiffHunks` so this seems
fitting.

the identifier is included if *any* diff hunk is visible, even if some
of them have been closed using `editor::ToggleSelectedDiffHunk`


Release Notes:

- The Editor key context now includes 'diffs_expanded' when diff changes
are visible
2025-11-14 03:33:53 +00:00
Xipeng Jin
28a0b82618 git_panel: Fix FocusChanges does nothing with no entries (#42553)
Closes #31155

Release Notes:

- Ensure `git_panel::FocusChanges` bypasses the panel’s `Focusable`
logic and directly focuses the `ChangesList` handle so the command works
even when the repository has no entries.
- Keep the `Focusable` behavior from the commit 45b126a (which routes
empty panels to the commit editor) by handling this special-case action
rather than regressing the default focus experience.
2025-11-13 21:59:39 -05:00
Mayank Verma
e2c95a8d84 git: Continue parsing other branches when refs have missing fields (#42523)
Closes #34684

Release Notes:

- (Let's Git Together) Fixed Git panel not showing any branches when
repository contains refs with missing fields
2025-11-13 21:16:38 -05:00
Anthony Eid
3da4d3aac3 settings_ui: Make open project settings action open settings UI (#42669)
This PR makes the `OpenProjectSettings` action open the settings UI in
project settings mode for the first visible worktree, instead of opening
the file. It also adds a `OpenProjectSettingsFile` action that maintains
the old behavior.

Finally, this PR partially fixes a bug where the settings UI won't load
project settings when the settings window is loaded before opening a
project/workspace. This happens because the global `app_state` isn't
correct in the `Subscription` that refreshes the available setting files
to open. The bug is still present in some cases, but it's out of scope
for this PR.

Release Notes:

- settings ui: Project Settings action now opens settings UI instead of
a file
2025-11-13 20:06:09 -05:00
Conrad Irwin
6f99eeffa8 Don't try and delete ./target/. (#42680)
Release Notes:

- N/A
2025-11-13 16:39:35 -07:00
Julia Ryan
15ab96af6b Add windows nightly update banner (#42576)
Hopefully this will nudge some of the beta users who were on nightly to
get on the official stable builds now that they're out.

Release Notes:

- N/A
2025-11-13 15:33:33 -08:00
Marshall Bowers
e80b490ac0 client: Clear plan and usage information when signing out (#42678)
This PR makes it so we clear the user's plan and usage information when
they sign out.

Release Notes:

- Signing out will now clear the local cache containing the plan and
usage information.
2025-11-13 23:13:27 +00:00
Jakub Konka
3c577ba019 git_panel: Fix Stage All/Unstage All ignoring partially staged files (#42677)
Release Notes:

- Fix "Stage All"/"Unstage All" not affecting partially staged files
2025-11-13 23:57:05 +01:00
Danilo Leal
e1d295a6b4 markdown: Improve table display (#42674)
Closes https://github.com/zed-industries/zed/issues/36330
Closes https://github.com/zed-industries/zed/issues/35460

This PR improves how we display markdown tables by relying on grids
rather than flexbox. Given this makes text inside each cell wrap, I
ended up removing the `table_overflow_x_scroll` method, as it was 1)
used only in the agent panel, and 2) arguably not the best approach as a
whole, because as soon as you need to scroll a table, you probably need
more elements to make it be really great.

One thing I'm slightly unsatisfied with, though, is the border
situation. I added a half pixel border to the cell so they all sum up to
1px, but there are cases where there's a tiny space between rows and I
don't quite know where that's coming from and how it happens. But I
think it's a reasonable improvement overall.

<img width="500" height="1248" alt="Screenshot 2025-11-13 at 7  05@2x"
src="https://github.com/user-attachments/assets/182b2235-efeb-4a61-ada2-98262967355d"
/>

Release Notes:

- agent: Improved table rendering in the agent panel, ensuring cell text
wraps, not going off-screen.
2025-11-13 19:36:16 -03:00
AidanV
84f24e4b62 vim: Add :<range>w <filename> command (#41256)
Release Notes:

- Adds support for `:[range]w {file}`
  - This writes the lines in the range to the specified
- Adds support for `:[range]w`
  - This replaces the current file with the selected lines
2025-11-13 13:27:08 -07:00
Abul Hossain Khan
03fad4b951 workspace: Fix pinned tab causing resize loop on adjacent tab (#41884)
Closes #41467 

My first PR in Zed, any guidance or tips are appreciated.

This fixes the flickering/resize loop that occurred on the tab
immediately to the right of a pinned tab.

Removed the conditional border on the pinned tabs container. The border
was a visual indicator to show when unpinned tabs were scrolled, but it
wasn't essential and was causing the layout thrashing.

Release Notes:

- Fixed

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-11-14 01:52:57 +05:30
Kevin Rubio
c626e770a0 outline_panel: Remove toggle expanded behavior from OpenSelectedEntry (#42214)
Fixed outline panel space key behavior by removing duplicate toggle call

The `open_selected_entry` function in `outline_panel.rs` was incorrectly
calling `self.toggle_expanded(&selected_entry, window, cx)` in addition
to its primary logic, causing the space key to both open/close entries
AND toggle their expanded state. Removed the redundant `toggle_expanded`
call to achieve the intended behavior.

Closes #41711

Release Notes:

- Fixed issue with the outline panel where pressing space would cause an
open selected entry to collapse and cause a closed selected entry to
open.

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-11-14 01:07:22 +05:30
Lionel Henry
fa0c7500c1 Update runtimed to fix compatibility issue with the Ark kernel (#40889)
Closes #40888

This updates runtimed to the latest version, which handles the
"starting" variant of `execution_state`. It actually handles a bunch of
other variants that are not documented in the protocol (see
https://jupyter-client.readthedocs.io/en/stable/messaging.html#kernel-status),
like "starting", "terminating", etc. I added implementations for these
variants as well.

Release Notes:

- Fixed issue that prevented the Ark kernel from working in Zed
(#40888).

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-11-13 19:35:45 +00:00
Richard Feldman
e91be9e98e Fix ACP CLI login via remote (#42647)
Release Notes:

- Fixed logging into Gemini CLI and Claude Code when remoting and
authenticating via CLI

Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
2025-11-13 19:13:09 +01:00
Rafael Lüder
46eb9e5223 Update scale factor and drawable size when macOS window changes screen (#38269)
Summary

Fixes UI scaling issue that occurs when starting Zed after disconnecting
an external monitor on macOS. The window's scale factor and drawable
size are now properly updated when the window changes screens.

Problem Description

When an external monitor is disconnected and Zed is started with only
the built-in screen active, the UI scale becomes incorrect. This happens
because:

1. macOS triggers the `window_did_change_screen` callback when a window
moves between displays (including when displays are disconnected)
2. The existing implementation only restarted the display link but
didn't update the window's scale factor or drawable size
3. This left the window with stale scaling information from the previous
display configuration

Root Cause

The `window_did_change_screen` callback in
`crates/gpui/src/platform/mac/window.rs` was missing the logic to update
the window's scale factor and drawable size when moving between screens.
This logic was only present in the `view_did_change_backing_properties
callback`, which isn't triggered when external monitors are
disconnected.

Solution

- Extracted common logic: Created a new `update_window_scale_factor()`
function that encapsulates the scale factor and drawable size update
logic
- Added scale factor update to screen change: Modified
`window_did_change_screen` to call this function after restarting the
display link
- Refactored existing code: Updated `view_did_change_backing_properties`
to use the new shared function, reducing code duplication

The fix ensures that whenever a window changes screens (due to monitor
disconnect, reconnect, or manual movement), the scale factor, drawable
size, and renderer state are properly synchronized.

Testing

-  Verified that UI scaling remains correct after disconnecting
external monitor
-  Confirmed that reconnecting external monitor works properly
-  Tested that manual window movement between displays updates scaling
correctly
-  No regressions observed in normal window operations

To verity my fix worked I had to copy my preview workspace over my dev
workspace, once I had done this I could reproduce the issue on main
consistently. After switching to the branch with this fix the issue was
resolved.

The fix is similar to what was done on
https://github.com/zed-industries/zed/pull/35686 (Windows)

Closes #37245 #38229

Release Notes:

- Fixed: Update scale factor and drawable size when macOS window changes
screen

---------

Co-authored-by: Kate <work@localcc.cc>
2025-11-13 16:51:13 +00:00
Conrad Irwin
cb7bd5fe19 Include source PR number in cherry-picks (#42642)
Release Notes:

- N/A
2025-11-13 16:06:26 +00:00
Ben Kunkle
b900ac2ac7 ci: Fix script/clear-target-dir-if-larger-than post #41652 (#42640)
Closes #ISSUE

The namespace runners mount the `target` directory to the cache drive,
so `rm -rf target` would fail with `Device Busy`. Instead we now do `rm
-rf target/* target/.*` to remove all files (including hidden files)
from the `target` directory, without removing the target directory
itself

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-13 10:58:59 -05:00
Dino
b709996ec6 editor: Fix pane's tab buttons flicker on right-click (#42549)
Whenever right-click was used on the editor, the pane's tab buttons
would flicker, which was confirmed to happen because of the following
check:

```
self.focus_handle.contains_focused(window, cx)
    || self
        .active_item()
        .is_some_and(|item| {
            item.item_focus_handle(cx).contains_focused(window, cx)
        })
```

This check was returning `false` right after right-clicking but
returning `true` right after. When digging into it a little bit more,
this appears to be happening because the editor's `MouseContextMenu`
relies on `ContextMenu` which is rendered in a deferred fashion but
`MouseContextMenu` updates the window's focus to it instantaneously.

Since the `ContextMenu` is rendered in a deferred fashion, its focus
handle is not yet a descendant of the editor (pane's active item) focus
handle, so the `contains_focused(window, cx)` call would return `false`,
with it returning `true` after the menu was rendered.

This commit updates the `MouseContextMenu::new` function to leverage
`cx.on_next_frame` and ensure that the focus is only moved to the
`ContextMenu` 2 frames later, ensuring that by the time the focus is
moved, the `ContextMenu`'s focus handle is a descendant of the editor's.

Closes #41771 

Release Notes:

- Fixed pane's tab buttons flickering when using right-click on the
editor
2025-11-13 15:57:26 +00:00
Smit Barmase
b6972d70a5 editor: Fix panic when calculating jump data for buffer header (#42639)
Just on nightly.

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
2025-11-13 15:48:05 +00:00
kitt
ec1664f61a zed: Enable line wrapping for cli help (#42496)
This enables clap's [wrap-help] feature and sets max_term_width to wrap
after 100 columns (the value clap is planning to default to in clap-v5).

This commit also adds blank lines which cause clap to split longer doc
comments into separate help (displayed for `-h`) and long_help
(displayed for `--help`) messages, as per [doc-processing].

[wrap-help]:
https://docs.rs/clap/4.5.49/clap/_features/index.html#optional-features
[doc-processing]:
https://docs.rs/clap/4.5.49/clap/_derive/index.html#pre-processing

![before: some lines of help text stretch across the whole screen.
after: all lines are wrapped at 100 columns, and some manual linebreaks
are preserved where it makes sense (in particular, when listing the
user-data-dir locations on each
platform)](https://github.com/user-attachments/assets/359067b4-5ffb-4fe3-80bd-5e1062986417)


Release Notes:

- N/A
2025-11-13 10:46:51 -05:00
Agus Zubiaga
c2c5fceb5b zeta eval: Allow no headings under "Expected Context" (#42638)
Release Notes:

- N/A
2025-11-13 15:43:22 +00:00
Richard Feldman
eadc2301e0 Fetch the unit eval commit before checking it out (#42636)
Release Notes:

- N/A
2025-11-13 15:21:53 +00:00
Richard Feldman
b500470391 Disabled agent commands (#42579)
Closes #31346

Release Notes:

- Agent commands no longer show up in the command palette when `agent`
is disabled. Same for edit predictions.
2025-11-13 10:10:02 -05:00
Oleksiy Syvokon
55e4258147 agent: Workaround for Sonnet inserting </parameter> tag (#42634)
Release Notes:

- N/A
2025-11-13 15:09:16 +00:00
Agus Zubiaga
8467a1b08b zeta eval: Improve output (#42629)
Hides the aggregated scores if only one example/repetition ran. It also
fixes an issue with the expected context scoring.

Release Notes:

- N/A
2025-11-13 14:47:48 +00:00
Tim McLean
fb90b12073 Add retry support for OpenAI-compatible LLM providers (#37891)
Automatically retry the agent's LLM completion requests when the
provider returns 429 Too Many Requests. Uses the Retry-After header to
determine the retry delay if it is available.

Many providers are frequently overloaded or have low rate limits. These
providers are essentially unusable without automatic retries.

Tested with Cerebras configured via openai_compatible.

Related: #31531 

Release Notes:

- Added automatic retries for OpenAI-compatible LLM providers

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-11-13 14:15:46 +00:00
Mayank Verma
92e64f9cf0 settings: Add tilde expansion support for LSP binary path (#41715)
Closes #38227

Release Notes:

- Added tilde expansion support for LSP binary path in `settings.json`
2025-11-13 09:14:18 -05:00
Remco Smits
f318bb5fd7 markdown: Add support for HTML href elements (#42265)
This PR adds support for `HTML` href elements. It also refactored the
way we stored the regions, this was done because otherwise I had to add
2 extra arguments to each `HTML` parser method. It's now also more
inline with how we have done it for the highlights.

**Small note**: the markdown parser only supports HTML href tags inside
a paragraph tag. So adding them as a root node will result in just
showing the inner text. This is a limitation of the markdown parser we
use itself.

**Before**
<img width="935" height="174" alt="Screenshot 2025-11-08 at 15 40 28"
src="https://github.com/user-attachments/assets/42172222-ed49-4a4b-8957-a46330e54c69"
/>

**After**
<img width="1026" height="180" alt="Screenshot 2025-11-08 at 15 29 55"
src="https://github.com/user-attachments/assets/9e139c2d-d43a-4952-8d1f-15eb92966241"
/>

**Example code**
```markdown
<p>asd <a href="https://example.com">Link Text</a> more text</p>
<p><a href="https://example.com">Link Text</a></p>

[Duck Duck Go](https://duckduckgo.com)
```

**TODO**:
- [x] Add tests

cc @bennetbo

Release Notes:

- Markdown Preview: Add support for `HTML` href elements.

---------

Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
2025-11-13 15:12:17 +01:00
Piotr Osiewicz
430b55405a search: New recent old search implementation (#40835)
This is an in-progress work on changing how task scheduler affects
performance of project search. Instead of relying on tasks being
executed at a discretion of the task scheduler, we want to experiment
with having a set of "agents" that prioritize driving in-progress
project search matches to completion over pushing the whole thing to
completion. This should hopefully significantly improve throughput &
latency of project search.

This PR has been reverted previously in #40831.

Release Notes:
- Improved project search performance in local projects.

---------

Co-authored-by: Smit Barmase <smit@zed.dev>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-11-13 14:56:40 +01:00
Lukas Wirth
27f700e2b2 askpass: Quote paths in generated askpass script (#42622)
Closes https://github.com/zed-industries/zed/issues/42618

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-13 14:37:47 +01:00
Smit Barmase
b5633f5bc7 editor: Improve multi-buffer header filename click to jump to the latest selection from that buffer - take 2 (#42613)
Relands https://github.com/zed-industries/zed/pull/42480

Release Notes:

- Clicking the multi-buffer header file name or the "Open file" button
now jumps to the most recent selection in that buffer, if one exists.

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2025-11-13 17:14:33 +05:30
R.Amogh
b9ce52dc95 agent_ui: Fix scrolling in context server configuration modal (#42502)
## Summary

Fixes #42342

When installing a dev extension with long installation instructions, the
configuration modal would overflow and users couldn't scroll to see the
full content or interact with buttons at the bottom.

## Solution

This PR adds a `ScrollHandle` to the `ConfigureContextServerModal` and
passes it to the `Modal` component, enabling the built-in modal
scrolling capability. This ensures all content remains accessible
regardless of length.

## Changes

- Added `ScrollHandle` import to the ui imports
- Added `scroll_handle: ScrollHandle` field to
`ConfigureContextServerModal` struct
- Initialize `scroll_handle` with `ScrollHandle::new()` when creating
the modal
- Pass the scroll handle to `Modal::new()` instead of `None`

## Testing

- Built the changes locally
- Tested with extensions that have long installation instructions
- Verified scrolling works and all content is accessible
- Confirmed no regression for extensions with short descriptions

Release Notes:

- Fixed scrolling issue in extension configuration modal when
installation instructions overflow the viewport

---------

Co-authored-by: Finn Evers <finn.evers@outlook.de>
2025-11-13 12:41:38 +01:00
mikeHag
34a7cfb2e5 Update cargo.rs to allow debugging of integration test annotated with the ignore attribute (#42574)
Address #40429

If an integration test is annotated with the ignore attribute, allow the
"debug: Test" option of the debug scenario or Code Action to run with
"--include-ignored"

Closes #40429

Release Notes:

- N/A
2025-11-13 12:31:23 +01:00
Kirill Bulatov
99016e3a85 Update outdated dependencies (#42611)
New rustc starts to output a few warnings, fix them by updating the
corresponding packages.

<details>
  <summary>Incompatibility notes</summary>
    
  ```
The following warnings were discovered during the build. These warnings
are an
indication that the packages contain code that will become an error in a
future release of Rust. These warnings typically cover changes to close
soundness problems, unintended or undocumented behavior, or critical
problems
that cannot be fixed in a backwards-compatible fashion, and are not
expected
to be in wide use.

Each warning should contain a link for more information on what the
warning
means and how to resolve it.


To solve this problem, you can try the following approaches:


- Some affected dependencies have newer versions available.
You may want to consider updating them to a newer version to see if the
issue has been fixed.

num-bigint-dig v0.8.4 has the following newer versions available: 0.8.5,
0.9.0, 0.9.1

- If the issue is not solved by updating the dependencies, a fix has to
be
implemented by those dependencies. You can help with that by notifying
the
maintainers of this problem (e.g. by creating a bug report) or by
proposing a
fix to the maintainers (e.g. by creating a pull request):

  - num-bigint-dig@0.8.4
  - Repository: https://github.com/dignifiedquire/num-bigint
- Detailed warning command: `cargo report future-incompatibilities --id
1 --package num-bigint-dig@0.8.4`

- If waiting for an upstream fix is not an option, you can use the
`[patch]`
section in `Cargo.toml` to use your own version of the dependency. For
more
information, see:

https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section

The package `num-bigint-dig v0.8.4` currently triggers the following
future incompatibility lints:
> warning: macro `vec` is private
> -->
/Users/someonetoignore/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-bigint-dig-0.8.4/src/biguint.rs:490:22
>     |
> 490 |         BigUint::new(vec![1])
>     |                      ^^^
>     |
> = warning: this was previously accepted by the compiler but is being
phased out; it will become a hard error in a future release!
> = note: for more information, see issue #120192
<https://github.com/rust-lang/rust/issues/120192>
> 
> warning: macro `vec` is private
> -->
/Users/someonetoignore/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-bigint-dig-0.8.4/src/biguint.rs:2005:9
>      |
> 2005 |         vec![0]
>      |         ^^^
>      |
> = warning: this was previously accepted by the compiler but is being
phased out; it will become a hard error in a future release!
> = note: for more information, see issue #120192
<https://github.com/rust-lang/rust/issues/120192>
> 
> warning: macro `vec` is private
> -->
/Users/someonetoignore/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-bigint-dig-0.8.4/src/biguint.rs:2027:16
>      |
> 2027 |         return vec![b'0'];
>      |                ^^^
>      |
> = warning: this was previously accepted by the compiler but is being
phased out; it will become a hard error in a future release!
> = note: for more information, see issue #120192
<https://github.com/rust-lang/rust/issues/120192>
> 
> warning: macro `vec` is private
> -->
/Users/someonetoignore/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-bigint-dig-0.8.4/src/biguint.rs:2313:13
>      |
> 2313 |             vec![0]
>      |             ^^^
>      |
> = warning: this was previously accepted by the compiler but is being
phased out; it will become a hard error in a future release!
> = note: for more information, see issue #120192
<https://github.com/rust-lang/rust/issues/120192>
> 
> warning: macro `vec` is private
> -->
/Users/someonetoignore/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-bigint-dig-0.8.4/src/prime.rs:138:22
>     |
> 138 |     let mut moduli = vec![BigUint::zero(); prime_limit];
>     |                      ^^^
>     |
> = warning: this was previously accepted by the compiler but is being
phased out; it will become a hard error in a future release!
> = note: for more information, see issue #120192
<https://github.com/rust-lang/rust/issues/120192>
> 
> warning: macro `vec` is private
> -->
/Users/someonetoignore/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/num-bigint-dig-0.8.4/src/bigrand.rs:319:25
>     |
> 319 |         let mut bytes = vec![0u8; bytes_len];
>     |                         ^^^
>     |
> = warning: this was previously accepted by the compiler but is being
phased out; it will become a hard error in a future release!
> = note: for more information, see issue #120192
<https://github.com/rust-lang/rust/issues/120192>
> 

  ```
  
</details>

Release Notes:

- N/A
2025-11-13 10:35:16 +00:00
Lukas Wirth
dea3c8c949 remote: More nushell fixes (#42608)
Closes https://github.com/zed-industries/zed/issues/42594

Release Notes:

- Fixed remote server installation failing with nutshell
2025-11-13 09:53:31 +00:00
Lukas Wirth
7eac6d242c diagnostics: Workaround weird panic in update_path_excerpts (#42602)
Fixes ZED-36P

Patching this over for now until I can figure out the cause of this

Release Notes:

- Fixed panic in diagnostics pane
2025-11-13 09:13:54 +00:00
Lukas Wirth
b92b28314f Replace {floor/ceil}_char_boundary polyfills with std (#42599)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-13 08:11:18 +00:00
AidanV
1fc0642de1 vim: Make each vim repeat its own transaction (#41735)
Release Notes:

- Pressing `u` after multiple `.` in rapid succession will now only undo
the latest repeat instead of all repeats.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-11-13 06:46:14 +00:00
Conrad Irwin
045ac6d1b6 Release failure visibility (#42572)
Closes #ISSUE

Release Notes:

- N/A
2025-11-12 23:11:09 -07:00
Sean Hagstrom
1936f16c62 editor: Use a single newline between each copied line from a multi-cursor selection (#41204)
Closes #40923

Release Notes:

- Fixed the amount of newlines between copied lines from a multi-cursor
selection of multiple full-line copies.

---


https://github.com/user-attachments/assets/ab7474d6-0e49-4c29-9700-7692cd019cef
2025-11-12 22:58:13 -07:00
Conrad Irwin
b32559f07d Avoid re-creating releases when re-running workflows (#42573)
Closes #ISSUE

Release Notes:

- N/A
2025-11-12 21:50:15 -07:00
Julia Ryan
28adedf1fa Disable env clearing for npm subcommands (#42587)
Fixes #39448

Several node version managers such as [volta](https://volta.sh) use thin
wrappers that locate the "real" node/npm binary with an env var that
points at their install root. When it finds this, it prepends the
correct directory to PATH, otherwise it'll check a hardcoded default
location and prepend that to PATH if it exists.

We were clearing env for npm subcommands, which meant that volta and co.
failed to locate the install root, and because they were installed via
scoop they don't use the default install path either so it simply
doesn't prepend anything to PATH (winget on the other hand installs
volta to the right place, which is why it worked when using that instead
of scoop to install volta @IllusionaryX).

So volta's npm wrapper executes a subcommand `npm`, but when that
doesn't prepend a different directory to PATH the first `npm` found in
PATH is that same wrapper itself, which horrifyingly causes itself to
re-exec continuously. I think they might have some logic to try to
prevent this using, you'll never guess, another env var that they set
whenever a volta wrapper execs something. Of course since we clear the
env that var also fails to propagate.

Removing env clearing (but keeping the prepending of npm path from your
settings) fixes these issues.

Release Notes:

- Fixed issues with scoop installations of mise/volta

Co-authored-by: John Tur <john-tur@outlook.com>
2025-11-12 22:03:59 -06:00
Max Brunsfeld
c9e231043a Report discarded zeta predictions and indicate whether they were shown (#42403)
Release Notes:

- N/A

---------

Co-authored-by: Michael Sloan <mgsloan@gmail.com>
Co-authored-by: Ben Kunkle <ben@zed.dev>
Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-11-12 16:41:04 -08:00
Richard Feldman
ede3b1dae6 Allow running concurrent unit evals (#42578)
Right now only one unit eval GitHub Action can be run at a time. This
permits them to run concurrently.

Release Notes:

- N/A
2025-11-12 22:04:38 +00:00
Agus Zubiaga
b0700a4625 zeta eval: --repeat flag (#42569)
Adds a `--repeat` flag to the zeta eval that runs each example as many
times as specified. Also makes the output nicer in a few ways.

Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
Co-authored-by: Michael <michael@zed.dev>
2025-11-12 16:58:22 -05:00
Michael Sloan
f2a1eb9963 Make check-licenses script check that AGPL crates are not included in release binaries (#42571)
See discussion in #24657. Recalled that I had a stashed change for this,
so polished it up

Release Notes:

- N/A
2025-11-12 21:58:12 +00:00
Andrew Farkas
0c1ca2a45a Improve pane: reopen closed item to not reopen closed tabs (#42568)
Closes #42134

Release Notes:

- Improved `pane: reopen closed item` to not reopen closed tabs.
2025-11-12 21:08:41 +00:00
Conrad Irwin
8fd8b989a6 Use powershell for winget job steps (#42565)
Co-Authored-By: Claude

Release Notes:

- N/A
2025-11-12 13:41:20 -07:00
Lucas Parry
fd837b348f project_panel: Make natural sort ordering consistent with other apps (#41080)
The existing sorting approach when faced with `Dir1`, `dir2`, `Dir3`,
would only get as far as comparing the stems without numbers (`dir` and
`Dir`), and then the lowercase-first tie breaker in that function would
determine that `dir2` should come first, resulting in an undesirable
order of `dir2`, `Dir1`, `Dir3`.

This patch defers tie-breaking until it's determined that there's no
other difference in the strings outside of case to order on, at which
point we tie-break to provide a stable sort.

Natural number sorting is still preserved, and mixing different cases
alphabetically (as opposed to all lowercase alpha, followed by all
uppercase alpha) is preserved.

Closes #41080


Release Notes:

- Fixed: ProjectPanel sorting bug

Screenshots:

Before | After
----|---
<img width="237" height="325" alt="image"
src="https://github.com/user-attachments/assets/6e92e8c0-2172-4a8f-a058-484749da047b"
/> | <img width="239" height="325" alt="image"
src="https://github.com/user-attachments/assets/874ad29f-7238-4bfc-b89b-fd64f9b8889a"
/>

I'm having trouble reasoning through what was previously going wrong
with `docs` in the before screenshot, but it also seems to now appear
alphabetically where you'd expect it with this patch

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-11-13 02:04:40 +05:30
Piotr Osiewicz
6b239c3a9a Bump Rust to 1.91.1 (#42561)
Release Notes:

- N/A

---------

Co-authored-by: Julia Ryan <juliaryan3.14@gmail.com>
2025-11-12 20:27:04 +00:00
Piotr Osiewicz
73e5df6445 ci: Install pre-built cargo nextest instead of rolling our own (#42556)
Closes #ISSUE

Release Notes:

- N/A
2025-11-12 20:05:40 +00:00
KyleBarton
b403c199df Add additional comment for context in Tyepscript highlights (#42564)
This adds additional comments which were left out from #42494 by
accident. Namely, it describes why we have additional custom
highlighting in `highlights.scm` for the Typescript grammar.

Release Notes:

- N/A
2025-11-12 19:59:10 +00:00
Konstantinos Lyrakis
cb4067723b Fix typo (#42559)
Fixed a typo in the docs

Release Notes:

- N/A
2025-11-12 21:07:34 +02:00
Ben Kunkle
1c625f8783 Fix JSON Schema documentation for code_actions_on_format (#42128)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-11-12 18:33:02 +00:00
KyleBarton
4adec27a3d Implement pretty TypeScript errors (#42494)
Closes #7844

This change uses tree-sitter highlights as a method of showing
typescript errors prettily, keeping regex as simple as possible:

<img width="832" height="446" alt="Screenshot 2025-11-11 at 3 40 24 PM"
src="https://github.com/user-attachments/assets/0b3b6cf1-4d4d-4398-b89b-ef5ec0df87ec"
/>

It covers three main areas:

1. Diagnostics

Diagnostics are now rendered with language-aware typescript, by
providing the project's language registry.

2. Vtsls

The LSP provider for typescript now implements the
`diagnostic_message_to_markdown` function in the `LspAdapter` trait, so
as to provide Diagnostics with \`\`\`typescript...\`\`\`-style code
blocks for any selection of typescript longer than one word. In the
single-word case, it simply wraps with \`\`

3. Typescript's `highlights.scm`

`vtsls` doesn't provide strictly valid typescript in much of its
messaging. Rather, it returns a message with snippets of typescript
values which are invalid. Tree-sitter was not properly highlighting
these snippets because it was expecting key-value formats. For instance:
```
type foo = { foo: string; bar: string; baz: number[] }
```
is valid, whereas simply
```
{ foo: string; bar: string; baz: number[] }
```
is not.

Therefore, highlights.scm needed to be adjusted in order to
pattern-match on literal values that might be returned from the vtsls
diagnostics messages. This was done by a) identifying arrow functions on
their own, and b) augmenting the `statment_block` pattern matching in
order to match on values which were clearly object literals.

This approach may not be exhaustive - I'm happy to work on any
additional cases we might identify from `vtsls` here - but hopefully
demonstrates an extensible approach to making these messages look nice,
without taking on the technical burden of extensive regex.

Release Notes:

- Show pretty TypeScript errors with language-aware Markdown.
2025-11-12 10:32:46 -08:00
Remco Smits
e8daab15ab debugger: Fix prevent creating breakpoints inside breakpoint editor (#42475)
Closes #38057

This PR fixes that you can no longer create breakpoints inside the
breakpoint editor in code called `BreakpointPromptEditor`. As you can
see, inside the after video, there is no breakpoint editor created
anymore.

**Before**


https://github.com/user-attachments/assets/c4e02684-ac40-4176-bd19-f8f08e831dde

**After**


https://github.com/user-attachments/assets/f5b1176f-9545-4629-be12-05c64697a3de

Release Notes:

- Debugger: Prevent breakpoints from being created inside the breakpoint
editor
2025-11-12 18:18:10 +00:00
Ben Kunkle
6501b0c311 zeta eval: Improve determinism and debugging ergonomics (#42478)
- Improves the determinism of the search step for better cache
reusability
- Adds a `--cache force` mode that refuses to make any requests or
searches that aren't cached
- The structure of the `zeta-*` directories under `target` has been
rethought for convenience

Release Notes:

- N/A

---------

Co-authored-by: Agus <agus@zed.dev>
2025-11-12 18:16:13 +00:00
Ben Kunkle
6c0069ca98 zeta2: Improve error reporting and eval purity (#42470)
Closes #ISSUE

Improves error reporting for various failure modes of zeta2, including
failing to parse the `<old_text>`/`<new_text>` pattern, and the contents
of `<old_text>` failing to match.

Additionally, makes it so that evals are checked out into a worktree
with the _repo_ name instead of the _example_ name, in order to make
sure that the eval name has no influence on the models prediction. The
repo name worktrees are still namespaced by the example name like
`{example_name}/{repo_name}` to ensure evals pointing to the same repo
do not conflict.

Release Notes:

- N/A *or* Added/Fixed/Improved ...

---------

Co-authored-by: Agus <agus@zed.dev>
2025-11-12 12:52:11 -05:00
Conrad Irwin
c8930e07a3 Allow multiple parked threads in tests (#42551)
Closes #ISSUE

Release Notes:

- N/A

Co-Authored-By: Piotr <piotr@zed.dev>
2025-11-12 10:29:31 -07:00
Richard Feldman
ab352f669e Gracefully handle @mention-ing large files with no outlines (#42543)
Closes #32098

Release Notes:

- In the Agent panel, when `@mention`-ing large files with no outline,
their first 1KB is now added to context
2025-11-12 16:55:25 +00:00
Finn Evers
e79188261b fs: Fix wrong watcher trace log on Linux (#42544)
Follow-up to #40200

Release Notes:

- N/A
2025-11-12 16:26:53 +00:00
Marshall Bowers
ab62739605 collab: Remove unused methods from User model (#42536)
This PR removes some unused methods from the `User` model.

Release Notes:

- N/A
2025-11-12 15:38:16 +00:00
Marco Mihai Condrache
cfbde91833 terminal: Add setting for scroll multiplier (#39463)
Closes #5130

Release Notes:

- Added setting option for scroll multiplier of the terminal

---------

Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Co-authored-by: MrSubidubi <finn@zed.dev>
2025-11-12 16:38:06 +01:00
Vasyl Protsiv
80b32ddaad gpui: Add 'Nearest' scrolling strategy to 'UniformList' (#41844)
This PR introduces `Nearest` scrolling strategy to `UniformList`. This
is now used in completions menu and the picker to choose the appropriate
scrolling strategy depending on movement direction. Previously,
selecting the next element after the last visible item caused the menu
to scroll with `ScrollStrategy::Top`, which scrolled the whole page and
placed the next element at the top. This behavior is inconsistent,
because using `ScrollStrategy::Top` when moving up only scrolls one
element, not the whole page.


https://github.com/user-attachments/assets/ccfb238f-8f76-4a18-a18d-bbcb63340c5a

The solution is to introduce the `Nearest` scrolling strategy which will
internally choose the scrolling strategy depending on whether the new
selected item is below or above currently visible items. This ensures a
single-item scroll regardless of movement direction.


https://github.com/user-attachments/assets/8502efb8-e2c0-4ab1-bd8d-93103841a9c4


I also noticed that some functions in the file have different logic
depending on `y_flipped`. This appears related to reversing the order of
elements in the list when the completion menu appears above the cursor.
This was a feature suggested in #11200 and implemented in #23446. It
looks like this feature was reverted in #27765 and there currently seem
to be no way to have `y_flipped` to be set to `true`.

My understanding is that the opposite scroll strategy should be used if
`y_flipped`, but since there is no way to enable this feature to test it
and I don't know if the feature is ever going to be reintroduced I
decided not to include it in this PR.


Release Notes:

- gpui: Add 'Nearest' scrolling strategy to 'UniformList'
2025-11-12 16:37:14 +01:00
Joseph T. Lyons
53652cdb3f Bump Zed to v0.214 (#42539)
Release Notes:

- N/A
2025-11-12 15:36:28 +00:00
997 changed files with 68585 additions and 48817 deletions

View File

@@ -1,59 +0,0 @@
name: Bug Report (AI)
description: Zed Agent Panel Bugs
type: "Bug"
labels: ["ai"]
title: "AI: <a short description of the AI Related bug>"
body:
- type: textarea
attributes:
label: Summary
description: Describe the bug with a one line summary, and provide detailed reproduction steps
value: |
<!-- Please insert a one line summary of the issue below -->
SUMMARY_SENTENCE_HERE
### Description
<!-- Describe with sufficient detail to reproduce from a clean Zed install. -->
Steps to trigger the problem:
1.
2.
3.
**Expected Behavior**:
**Actual Behavior**:
### Model Provider Details
- Provider: (Anthropic via ZedPro, Anthropic via API key, Copilot Chat, Mistral, OpenAI, etc)
- Model Name:
- Mode: (Agent Panel, Inline Assistant, Terminal Assistant or Text Threads)
- Other Details (MCPs, other settings, etc):
validations:
required: true
- type: textarea
id: environment
attributes:
label: Zed Version and System Specs
description: 'Open Zed, and in the command palette select "zed: copy system specs into clipboard"'
placeholder: |
Output of "zed: copy system specs into clipboard"
validations:
required: true
- type: textarea
attributes:
label: If applicable, attach your `Zed.log` file to this issue.
description: |
From the command palette, run `zed: open log` to see the last 1000 lines.
Or run `zed: reveal log in file manager` to reveal the log file itself.
value: |
<details><summary>Zed.log</summary>
<!-- Paste your log inside the code block. -->
```log
```
</details>
validations:
required: false

View File

@@ -1,53 +0,0 @@
name: Bug Report (Debugger)
description: Zed Debugger-Related Bugs
type: "Bug"
labels: ["debugger"]
title: "Debugger: <a short description of the Debugger bug>"
body:
- type: textarea
attributes:
label: Summary
description: Describe the bug with a one line summary, and provide detailed reproduction steps
value: |
<!-- Please insert a one line summary of the issue below -->
SUMMARY_SENTENCE_HERE
### Description
<!-- Describe with sufficient detail to reproduce from a clean Zed install. -->
Steps to trigger the problem:
1.
2.
3.
**Expected Behavior**:
**Actual Behavior**:
validations:
required: true
- type: textarea
id: environment
attributes:
label: Zed Version and System Specs
description: 'Open Zed, and in the command palette select "zed: copy system specs into clipboard"'
placeholder: |
Output of "zed: copy system specs into clipboard"
validations:
required: true
- type: textarea
attributes:
label: If applicable, attach your `Zed.log` file to this issue.
description: |
From the command palette, run `zed: open log` to see the last 1000 lines.
Or run `zed: reveal log in file manager` to reveal the log file itself.
value: |
<details><summary>Zed.log</summary>
<!-- Paste your log inside the code block. -->
```log
```
</details>
validations:
required: false

View File

@@ -1,53 +0,0 @@
name: Bug Report (Git)
description: Zed Git Related Bugs
type: "Bug"
labels: ["git"]
title: "Git: <a short description of the Git bug>"
body:
- type: textarea
attributes:
label: Summary
description: Describe the bug with a one-line summary, and provide detailed reproduction steps
value: |
<!-- Please insert a one-line summary of the issue below -->
SUMMARY_SENTENCE_HERE
### Description
<!-- Describe with sufficient detail to reproduce from a clean Zed install. -->
Steps to trigger the problem:
1.
2.
3.
**Expected Behavior**:
**Actual Behavior**:
validations:
required: true
- type: textarea
id: environment
attributes:
label: Zed Version and System Specs
description: 'Open Zed, and in the command palette select "zed: copy system specs into clipboard"'
placeholder: |
Output of "zed: copy system specs into clipboard"
validations:
required: true
- type: textarea
attributes:
label: If applicable, attach your `Zed.log` file to this issue.
description: |
From the command palette, run `zed: open log` to see the last 1000 lines.
Or run `zed: reveal log in file manager` to reveal the log file itself.
value: |
<details><summary>Zed.log</summary>
<!-- Paste your log inside the code block. -->
```log
```
</details>
validations:
required: false

View File

@@ -1,53 +0,0 @@
name: Bug Report (Windows)
description: Zed Windows Related Bugs
type: "Bug"
labels: ["windows"]
title: "Windows: <a short description of the Windows bug>"
body:
- type: textarea
attributes:
label: Summary
description: Describe the bug with a one-line summary, and provide detailed reproduction steps
value: |
<!-- Please insert a one-line summary of the issue below -->
SUMMARY_SENTENCE_HERE
### Description
<!-- Describe with sufficient detail to reproduce from a clean Zed install. -->
Steps to trigger the problem:
1.
2.
3.
**Expected Behavior**:
**Actual Behavior**:
validations:
required: true
- type: textarea
id: environment
attributes:
label: Zed Version and System Specs
description: 'Open Zed, and in the command palette select "zed: copy system specs into clipboard"'
placeholder: |
Output of "zed: copy system specs into clipboard"
validations:
required: true
- type: textarea
attributes:
label: If applicable, attach your `Zed.log` file to this issue.
description: |
From the command palette, run `zed: open log` to see the last 1000 lines.
Or run `zed: reveal log in file manager` to reveal the log file itself.
value: |
<details><summary>Zed.log</summary>
<!-- Paste your log inside the code block. -->
```log
```
</details>
validations:
required: false

View File

@@ -1,67 +1,53 @@
name: Bug Report (Other)
description: |
Something else is broken in Zed (exclude crashing).
type: "Bug"
name: Report a bug
description: Report a problem with Zed.
type: Bug
labels: "state:needs triage"
body:
- type: markdown
attributes:
value: |
Is this bug already reported? Upvote to get it noticed faster. [Here's the search](https://github.com/zed-industries/zed/issues). Upvote means giving it a :+1: reaction.
Feature request? Please open in [discussions](https://github.com/zed-industries/zed/discussions/new/choose) instead.
Just have a question or need support? Welcome to [Discord Support Forums](https://discord.com/invite/zedindustries).
- type: textarea
attributes:
label: Summary
description: Provide a one sentence summary and detailed reproduction steps
value: |
<!-- Begin your issue with a one sentence summary -->
SUMMARY_SENTENCE_HERE
### Description
<!-- Describe with sufficient detail to reproduce from a clean Zed install.
- Any code must be sufficient to reproduce (include context!)
- Include code as text, not just as a screenshot.
- Issues with insufficient detail may be summarily closed.
-->
DESCRIPTION_HERE
Steps to reproduce:
1.
2.
3.
4.
**Expected Behavior**:
**Actual Behavior**:
<!-- Before Submitting, did you:
1. Include settings.json, keymap.json, .editorconfig if relevant?
2. Check your Zed.log for relevant errors? (please include!)
3. Click Preview to ensure everything looks right?
4. Hide videos, large images and logs in ``` inside collapsible blocks:
<details><summary>click to expand</summary>
```json
```
</details>
-->
label: Reproduction steps
description: A step-by-step description of how to reproduce the bug from a **clean Zed install**. The more context you provide, the easier it is to find and fix the problem fast.
placeholder: |
1. Start Zed
2. Click X
validations:
required: true
- type: textarea
attributes:
label: Current vs. Expected behavior
description: |
Current behavior (screenshots, videos, etc. are appreciated), vs. what you expected the behavior to be.
placeholder: |
Current behavior: <screenshot with an arrow> The icon is blue. Expected behavior: The icon should be red because this is what the setting is documented to do.
validations:
required: true
- type: textarea
id: environment
attributes:
label: Zed Version and System Specs
label: Zed version and system specs
description: |
Open Zed, from the command palette select "zed: copy system specs into clipboard"
Open the command palette in Zed, then type “zed: copy system specs into clipboard”.
placeholder: |
Output of "zed: copy system specs into clipboard"
Zed: v0.215.0 (Zed Nightly bfe141ea79aa4984028934067ba75c48d99136ae)
OS: macOS 15.1
Memory: 36 GiB
Architecture: aarch64
validations:
required: true
- type: textarea
attributes:
label: If applicable, attach your `Zed.log` file to this issue.
label: Attach Zed log file
description: |
From the command palette, run `zed: open log` to see the last 1000 lines.
Or run `zed: reveal log in file manager` to reveal the log file itself.
Open the command palette in Zed, then type `zed: open log` to see the last 1000 lines. Or type `zed: reveal log in file manager` in the command palette to reveal the log file itself.
value: |
<details><summary>Zed.log</summary>
@@ -73,3 +59,41 @@ body:
</details>
validations:
required: false
- type: textarea
attributes:
label: Relevant Zed settings
description: |
Open the command palette in Zed, then type “zed: open settings file” and copy/paste any relevant (e.g., LSP-specific) settings.
value: |
<details><summary>settings.json</summary>
<!-- Paste your settings inside the code block. -->
```json
```
</details>
validations:
required: false
- type: textarea
attributes:
label: (for AI issues) Model provider details
placeholder: |
- Provider: (Anthropic via ZedPro, Anthropic via API key, Copilot Chat, Mistral, OpenAI, etc.)
- Model Name: (Claude Sonnet 4.5, Gemini 3 Pro, GPT-5)
- Mode: (Agent Panel, Inline Assistant, Terminal Assistant or Text Threads)
- Other details (ACPs, MCPs, other settings, etc.):
validations:
required: false
- type: dropdown
attributes:
label: If you are using WSL on Windows, what flavor of Linux are you using?
multiple: false
options:
- Arch Linux
- Ubuntu
- Fedora
- Mint
- Pop!_OS
- NixOS
- Other

View File

@@ -1,42 +1,35 @@
name: Crash Report
description: Zed is Crashing or Hanging
type: "Crash"
name: Report a crash
description: Zed is crashing or freezing or hanging.
type: Crash
labels: "state:needs triage"
body:
- type: textarea
attributes:
label: Summary
description: Summarize the issue with detailed reproduction steps
value: |
<!-- Begin your issue with a one sentence summary -->
SUMMARY_SENTENCE_HERE
### Description
<!-- Include all steps necessary to reproduce from a clean Zed installation. Be verbose -->
Steps to trigger the problem:
1.
2.
3.
Actual Behavior:
Expected Behavior:
validations:
required: true
- type: textarea
id: environment
attributes:
label: Zed Version and System Specs
description: 'Open Zed, and in the command palette select "zed: copy system specs into clipboard"'
label: Reproduction steps
description: A step-by-step description of how to reproduce the crash from a **clean Zed install**. The more context you provide, the easier it is to find and fix the problem fast.
placeholder: |
Output of "zed: copy system specs into clipboard"
1. Start Zed
2. Perform an action
3. Zed crashes
validations:
required: true
- type: textarea
attributes:
label: If applicable, attach your `Zed.log` file to this issue.
label: Zed version and system specs
description: |
From the command palette, run `zed: open log` to see the last 1000 lines.
Or run `zed: reveal log in file manager` to reveal the log file itself.
Open the command palette in Zed, then type “zed: copy system specs into clipboard”.
placeholder: |
Zed: v0.215.0 (Zed Nightly bfe141ea79aa4984028934067ba75c48d99136ae)
OS: macOS 15.1
Memory: 36 GiB
Architecture: aarch64
validations:
required: true
- type: textarea
attributes:
label: Attach Zed log file
description: |
Open the command palette in Zed, then type `zed: open log` to see the last 1000 lines. Or type `zed: reveal log in file manager` in the command palette to reveal the log file itself.
value: |
<details><summary>Zed.log</summary>

View File

@@ -1,19 +0,0 @@
name: Other [Staff Only]
description: Zed Staff Only
body:
- type: textarea
attributes:
label: Summary
value: |
<!-- Please insert a one line summary of the issue below -->
SUMMARY_SENTENCE_HERE
### Description
IF YOU DO NOT WORK FOR ZED INDUSTRIES DO NOT CREATE ISSUES WITH THIS TEMPLATE.
THEY WILL BE AUTO-CLOSED AND MAY RESULT IN YOU BEING BANNED FROM THE ZED ISSUE TRACKER.
FEATURE REQUESTS / SUPPORT REQUESTS SHOULD BE OPENED AS DISCUSSIONS:
https://github.com/zed-industries/zed/discussions/new/choose
validations:
required: true

View File

@@ -1,9 +1,9 @@
# yaml-language-server: $schema=https://www.schemastore.org/github-issue-config.json
blank_issues_enabled: false
contact_links:
- name: Feature Request
- name: Feature request
url: https://github.com/zed-industries/zed/discussions/new/choose
about: To request a feature, open a new Discussion in one of the appropriate Discussion categories
- name: "Zed Discord"
url: https://zed.dev/community-links
about: Real-time discussion and user support
about: To request a feature, open a new discussion under one of the appropriate categories.
- name: Our Discord community
url: https://discord.com/invite/zedindustries
about: Join our Discord server for real-time discussion and user support.

View File

@@ -4,10 +4,8 @@ description: "Runs the tests"
runs:
using: "composite"
steps:
- name: Install Rust
shell: bash -euxo pipefail {0}
run: |
cargo install cargo-nextest --locked
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Install Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4

View File

@@ -11,9 +11,8 @@ runs:
using: "composite"
steps:
- name: Install test runner
shell: powershell
working-directory: ${{ inputs.working-directory }}
run: cargo install cargo-nextest --locked
uses: taiki-e/install-action@nextest
- name: Install Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4

View File

@@ -7,7 +7,7 @@ on:
- published
jobs:
rebuild_releases_page:
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: after_release::rebuild_releases_page::refresh_cloud_releases
@@ -21,7 +21,7 @@ jobs:
post_to_discord:
needs:
- rebuild_releases_page
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- id: get-release-url
@@ -56,14 +56,14 @@ jobs:
- id: set-package-name
name: after_release::publish_winget::set_package_name
run: |
if [ "${{ github.event.release.prerelease }}" == "true" ]; then
PACKAGE_NAME=ZedIndustries.Zed.Preview
else
PACKAGE_NAME=ZedIndustries.Zed
fi
if ("${{ github.event.release.prerelease }}" -eq "true") {
$PACKAGE_NAME = "ZedIndustries.Zed.Preview"
} else {
$PACKAGE_NAME = "ZedIndustries.Zed"
}
echo "PACKAGE_NAME=$PACKAGE_NAME" >> "$GITHUB_OUTPUT"
shell: bash -euxo pipefail {0}
echo "PACKAGE_NAME=$PACKAGE_NAME" >> $env:GITHUB_OUTPUT
shell: pwsh
- name: after_release::publish_winget::winget_releaser
uses: vedantmgoyal9/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f
with:
@@ -71,7 +71,7 @@ jobs:
max-versions-to-keep: 5
token: ${{ secrets.WINGET_TOKEN }}
create_sentry_release:
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: steps::checkout_repo
@@ -86,3 +86,19 @@ jobs:
SENTRY_ORG: zed-dev
SENTRY_PROJECT: zed
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
notify_on_failure:
needs:
- rebuild_releases_page
- post_to_discord
- publish_winget
- create_sentry_release
if: failure()
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: release::notify_on_failure::notify_slack
run: |-
curl -X POST -H 'Content-type: application/json'\
--data '{"text":"${{ github.workflow }} failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' "$SLACK_WEBHOOK"
shell: bash -euxo pipefail {0}
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_WORKFLOW_FAILURES }}

View File

@@ -42,7 +42,7 @@ jobs:
exit 1
;;
esac
which cargo-set-version > /dev/null || cargo install cargo-edit
which cargo-set-version > /dev/null || cargo install cargo-edit -f --no-default-features --features "set-version"
output="$(cargo set-version -p zed --bump patch 2>&1 | sed 's/.* //')"
export GIT_COMMITTER_NAME="Zed Bot"
export GIT_COMMITTER_EMAIL="hi@zed.dev"

View File

@@ -1,6 +1,7 @@
# Generated from xtask::workflows::cherry_pick
# Rebuild with `cargo xtask workflows`.
name: cherry_pick
run-name: 'cherry_pick to ${{ inputs.channel }} #${{ inputs.pr_number }}'
on:
workflow_dispatch:
inputs:
@@ -16,6 +17,10 @@ on:
description: channel
required: true
type: string
pr_number:
description: pr_number
required: true
type: string
jobs:
run_cherry_pick:
runs-on: namespace-profile-2x4-ubuntu-2404

View File

@@ -13,13 +13,72 @@ jobs:
steps:
- name: Check if author is a community champion and apply label
uses: actions/github-script@v7
env:
COMMUNITY_CHAMPIONS: |
0x2CA
5brian
5herlocked
abdelq
afgomez
AidanV
akbxr
AlvaroParker
amtoaer
artemevsevev
bajrangCoder
bcomnes
Be-ing
blopker
bnjjj
bobbymannino
CharlesChen0823
chbk
cppcoffee
davewa
ddoemonn
djsauble
errmayank
fantacell
findrakecil
FloppyDisco
gko
huacnlee
imumesh18
jacobtread
jansol
jeffreyguenther
jenslys
jongretar
lemorage
lnay
marcocondrache
marius851000
mikebronner
ognevny
playdohface
RemcoSmitsDev
romaninsh
Simek
someone13574
sourcefrog
suxiaoshao
Takk8IS
thedadams
tidely
timvermeulen
valentinegb
versecafe
vitallium
warrenjokinen
WhySoBad
ya7010
Zertsov
with:
script: |
const communityChampionBody = `${{ secrets.COMMUNITY_CHAMPIONS }}`;
const communityChampions = communityChampionBody
const communityChampions = process.env.COMMUNITY_CHAMPIONS
.split('\n')
.map(handle => handle.trim().toLowerCase());
.map(handle => handle.trim().toLowerCase())
.filter(handle => handle.length > 0);
let author;
if (context.eventName === 'issues') {

View File

@@ -1,7 +1,7 @@
name: "Close Stale Issues"
on:
schedule:
- cron: "0 7,9,11 * * 3"
- cron: "0 8 31 DEC *"
workflow_dispatch:
jobs:
@@ -26,3 +26,4 @@ jobs:
ascending: true
enable-statistics: true
stale-issue-label: "stale"
exempt-issue-labels: "never stale"

View File

@@ -39,8 +39,7 @@ jobs:
run: ./script/download-wasi-sdk
shell: bash -euxo pipefail {0}
- name: compare_perf::run_perf::install_hyperfine
run: cargo install hyperfine
shell: bash -euxo pipefail {0}
uses: taiki-e/install-action@hyperfine
- name: steps::git_checkout
run: git fetch origin ${{ inputs.base }} && git checkout ${{ inputs.base }}
shell: bash -euxo pipefail {0}

View File

@@ -12,7 +12,7 @@ on:
- main
jobs:
danger:
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: steps::checkout_repo

View File

@@ -43,9 +43,7 @@ jobs:
fetch-depth: 0
- name: Install cargo nextest
shell: bash -euxo pipefail {0}
run: |
cargo install cargo-nextest --locked
uses: taiki-e/install-action@nextest
- name: Limit target directory size
shell: bash -euxo pipefail {0}

147
.github/workflows/extension_bump.yml vendored Normal file
View File

@@ -0,0 +1,147 @@
# Generated from xtask::workflows::extension_bump
# Rebuild with `cargo xtask workflows`.
name: extension_bump
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: '1'
CARGO_INCREMENTAL: '0'
ZED_EXTENSION_CLI_SHA: 7cfce605704d41ca247e3f84804bf323f6c6caaf
on:
workflow_call:
inputs:
bump-type:
description: bump-type
type: string
default: patch
force-bump:
description: force-bump
required: true
type: boolean
secrets:
app-id:
description: The app ID used to create the PR
required: true
app-secret:
description: The app secret for the corresponding app ID
required: true
jobs:
check_bump_needed:
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
clean: false
fetch-depth: 0
- id: compare-versions-check
name: extension_bump::compare_versions
run: |
CURRENT_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)"
PR_PARENT_SHA="${{ github.event.pull_request.head.sha }}"
if [[ -n "$PR_PARENT_SHA" ]]; then
git checkout "$PR_PARENT_SHA"
elif BRANCH_PARENT_SHA="$(git merge-base origin/main origin/zed-zippy-autobump)"; then
git checkout "$BRANCH_PARENT_SHA"
else
git checkout "$(git log -1 --format=%H)"~1
fi
PARENT_COMMIT_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)"
[[ "$CURRENT_VERSION" == "$PARENT_COMMIT_VERSION" ]] && \
echo "needs_bump=true" >> "$GITHUB_OUTPUT" || \
echo "needs_bump=false" >> "$GITHUB_OUTPUT"
echo "current_version=${CURRENT_VERSION}" >> "$GITHUB_OUTPUT"
shell: bash -euxo pipefail {0}
outputs:
needs_bump: ${{ steps.compare-versions-check.outputs.needs_bump }}
current_version: ${{ steps.compare-versions-check.outputs.current_version }}
timeout-minutes: 1
bump_extension_version:
needs:
- check_bump_needed
if: |-
(github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') &&
(inputs.force-bump == 'true' || needs.check_bump_needed.outputs.needs_bump == 'true')
runs-on: namespace-profile-8x16-ubuntu-2204
steps:
- id: generate-token
name: extension_bump::generate_token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.app-id }}
private-key: ${{ secrets.app-secret }}
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
clean: false
- name: extension_bump::install_bump_2_version
run: pip install bump2version
shell: bash -euxo pipefail {0}
- id: bump-version
name: extension_bump::bump_version
run: |
OLD_VERSION="${{ needs.check_bump_needed.outputs.current_version }}"
BUMP_FILES=("extension.toml")
if [[ -f "Cargo.toml" ]]; then
BUMP_FILES+=("Cargo.toml")
fi
bump2version --verbose --current-version "$OLD_VERSION" --no-configured-files ${{ inputs.bump-type }} "${BUMP_FILES[@]}"
if [[ -f "Cargo.toml" ]]; then
cargo update --workspace
fi
NEW_VERSION="$(sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml)"
echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
shell: bash -euxo pipefail {0}
- name: extension_bump::create_pull_request
uses: peter-evans/create-pull-request@v7
with:
title: Bump version to ${{ steps.bump-version.outputs.new_version }}
body: This PR bumps the version of this extension to v${{ steps.bump-version.outputs.new_version }}
commit-message: Bump version to v${{ steps.bump-version.outputs.new_version }}
branch: zed-zippy-autobump
committer: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
base: main
delete-branch: true
token: ${{ steps.generate-token.outputs.token }}
sign-commits: true
timeout-minutes: 1
create_version_label:
needs:
- check_bump_needed
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.check_bump_needed.outputs.needs_bump == 'false'
runs-on: namespace-profile-8x16-ubuntu-2204
steps:
- id: generate-token
name: extension_bump::generate_token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.app-id }}
private-key: ${{ secrets.app-secret }}
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
clean: false
- name: extension_bump::create_version_tag
uses: actions/github-script@v7
with:
script: |-
github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'refs/tags/v${{ needs.check_bump_needed.outputs.current_version }}',
sha: context.sha
})
github-token: ${{ steps.generate-token.outputs.token }}
timeout-minutes: 1
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
cancel-in-progress: true

43
.github/workflows/extension_release.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
# Generated from xtask::workflows::extension_release
# Rebuild with `cargo xtask workflows`.
name: extension_release
on:
workflow_call:
secrets:
app-id:
description: The app ID used to create the PR
required: true
app-secret:
description: The app secret for the corresponding app ID
required: true
jobs:
create_release:
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-8x16-ubuntu-2204
steps:
- id: generate-token
name: extension_bump::generate_token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.app-id }}
private-key: ${{ secrets.app-secret }}
owner: zed-industries
repositories: extensions
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
clean: false
- id: get-extension-id
name: extension_release::get_extension_id
run: |
EXTENSION_ID="$(sed -n 's/id = \"\(.*\)\"/\1/p' < extension.toml)"
echo "extension_id=${EXTENSION_ID}" >> "$GITHUB_OUTPUT"
shell: bash -euxo pipefail {0}
- name: extension_release::release_action
uses: huacnlee/zed-extension-action@v2
with:
extension-name: ${{ steps.get-extension-id.outputs.extension_id }}
push-to: zed-industries/extensions
env:
COMMITTER_TOKEN: ${{ steps.generate-token.outputs.token }}

133
.github/workflows/extension_tests.yml vendored Normal file
View File

@@ -0,0 +1,133 @@
# Generated from xtask::workflows::extension_tests
# Rebuild with `cargo xtask workflows`.
name: extension_tests
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: '1'
CARGO_INCREMENTAL: '0'
ZED_EXTENSION_CLI_SHA: 7cfce605704d41ca247e3f84804bf323f6c6caaf
on:
workflow_call: {}
jobs:
orchestrate:
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
clean: false
fetch-depth: ${{ github.ref == 'refs/heads/main' && 2 || 350 }}
- id: filter
name: filter
run: |
if [ -z "$GITHUB_BASE_REF" ]; then
echo "Not in a PR context (i.e., push to main/stable/preview)"
COMPARE_REV="$(git rev-parse HEAD~1)"
else
echo "In a PR context comparing to pull_request.base.ref"
git fetch origin "$GITHUB_BASE_REF" --depth=350
COMPARE_REV="$(git merge-base "origin/${GITHUB_BASE_REF}" HEAD)"
fi
CHANGED_FILES="$(git diff --name-only "$COMPARE_REV" ${{ github.sha }})"
check_pattern() {
local output_name="$1"
local pattern="$2"
local grep_arg="$3"
echo "$CHANGED_FILES" | grep "$grep_arg" "$pattern" && \
echo "${output_name}=true" >> "$GITHUB_OUTPUT" || \
echo "${output_name}=false" >> "$GITHUB_OUTPUT"
}
check_pattern "check_rust" '^(Cargo.lock|Cargo.toml|.*\.rs)$' -qP
check_pattern "check_extension" '^.*\.scm$' -qP
shell: bash -euxo pipefail {0}
outputs:
check_rust: ${{ steps.filter.outputs.check_rust }}
check_extension: ${{ steps.filter.outputs.check_extension }}
check_rust:
needs:
- orchestrate
if: needs.orchestrate.outputs.check_rust == 'true'
runs-on: namespace-profile-16x32-ubuntu-2204
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
clean: false
- name: steps::cache_rust_dependencies_namespace
uses: namespacelabs/nscloud-cache-action@v1
with:
cache: rust
- name: steps::cargo_fmt
run: cargo fmt --all -- --check
shell: bash -euxo pipefail {0}
- name: extension_tests::run_clippy
run: cargo clippy --release --all-targets --all-features -- --deny warnings
shell: bash -euxo pipefail {0}
- name: steps::cargo_install_nextest
uses: taiki-e/install-action@nextest
- name: steps::cargo_nextest
run: cargo nextest run --workspace --no-fail-fast
shell: bash -euxo pipefail {0}
env:
NEXTEST_NO_TESTS: warn
timeout-minutes: 3
check_extension:
needs:
- orchestrate
if: needs.orchestrate.outputs.check_extension == 'true'
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
clean: false
- id: cache-zed-extension-cli
name: extension_tests::cache_zed_extension_cli
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
with:
path: zed-extension
key: zed-extension-${{ env.ZED_EXTENSION_CLI_SHA }}
- name: extension_tests::download_zed_extension_cli
if: steps.cache-zed-extension-cli.outputs.cache-hit != 'true'
run: |
wget --quiet "https://zed-extension-cli.nyc3.digitaloceanspaces.com/$ZED_EXTENSION_CLI_SHA/x86_64-unknown-linux-gnu/zed-extension"
chmod +x zed-extension
shell: bash -euxo pipefail {0}
- name: extension_tests::check
run: |
mkdir -p /tmp/ext-scratch
mkdir -p /tmp/ext-output
./zed-extension --source-dir . --scratch-dir /tmp/ext-scratch --output-dir /tmp/ext-output
shell: bash -euxo pipefail {0}
timeout-minutes: 2
tests_pass:
needs:
- orchestrate
- check_rust
- check_extension
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && always()
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: run_tests::tests_pass
run: |
set +x
EXIT_CODE=0
check_result() {
echo "* $1: $2"
if [[ "$2" != "skipped" && "$2" != "success" ]]; then EXIT_CODE=1; fi
}
check_result "orchestrate" "${{ needs.orchestrate.result }}"
check_result "check_rust" "${{ needs.check_rust.result }}"
check_result "check_extension" "${{ needs.check_extension.result }}"
exit $EXIT_CODE
shell: bash -euxo pipefail {0}
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
cancel-in-progress: true

View File

@@ -10,7 +10,7 @@ on:
- v*
jobs:
run_tests_mac:
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: self-mini-macos
steps:
- name: steps::checkout_repo
@@ -29,14 +29,11 @@ jobs:
- name: steps::clippy
run: ./script/clippy
shell: bash -euxo pipefail {0}
- name: steps::cargo_install_nextest
run: cargo install cargo-nextest --locked
shell: bash -euxo pipefail {0}
- name: steps::clear_target_dir_if_large
run: ./script/clear-target-dir-if-larger-than 300
shell: bash -euxo pipefail {0}
- name: steps::cargo_nextest
run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
run: cargo nextest run --workspace --no-fail-fast
shell: bash -euxo pipefail {0}
- name: steps::cleanup_cargo_config
if: always()
@@ -45,7 +42,7 @@ jobs:
shell: bash -euxo pipefail {0}
timeout-minutes: 60
run_tests_linux:
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-16x32-ubuntu-2204
steps:
- name: steps::checkout_repo
@@ -78,13 +75,12 @@ jobs:
run: ./script/clippy
shell: bash -euxo pipefail {0}
- name: steps::cargo_install_nextest
run: cargo install cargo-nextest --locked
shell: bash -euxo pipefail {0}
uses: taiki-e/install-action@nextest
- name: steps::clear_target_dir_if_large
run: ./script/clear-target-dir-if-larger-than 250
shell: bash -euxo pipefail {0}
- name: steps::cargo_nextest
run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
run: cargo nextest run --workspace --no-fail-fast
shell: bash -euxo pipefail {0}
- name: steps::cleanup_cargo_config
if: always()
@@ -93,7 +89,7 @@ jobs:
shell: bash -euxo pipefail {0}
timeout-minutes: 60
run_tests_windows:
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: self-32vcpu-windows-2022
steps:
- name: steps::checkout_repo
@@ -112,14 +108,11 @@ jobs:
- name: steps::clippy
run: ./script/clippy.ps1
shell: pwsh
- name: steps::cargo_install_nextest
run: cargo install cargo-nextest --locked
shell: pwsh
- name: steps::clear_target_dir_if_large
run: ./script/clear-target-dir-if-larger-than.ps1 250
shell: pwsh
- name: steps::cargo_nextest
run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
run: cargo nextest run --workspace --no-fail-fast
shell: pwsh
- name: steps::cleanup_cargo_config
if: always()
@@ -128,7 +121,7 @@ jobs:
shell: pwsh
timeout-minutes: 60
check_scripts:
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: steps::checkout_repo
@@ -157,7 +150,7 @@ jobs:
shell: bash -euxo pipefail {0}
timeout-minutes: 60
create_draft_release:
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: steps::checkout_repo
@@ -484,6 +477,20 @@ jobs:
shell: bash -euxo pipefail {0}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
notify_on_failure:
needs:
- upload_release_assets
- auto_release_preview
if: failure()
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: release::notify_on_failure::notify_slack
run: |-
curl -X POST -H 'Content-type: application/json'\
--data '{"text":"${{ github.workflow }} failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' "$SLACK_WEBHOOK"
shell: bash -euxo pipefail {0}
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_WORKFLOW_FAILURES }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
cancel-in-progress: true

View File

@@ -12,7 +12,7 @@ on:
- cron: 0 7 * * *
jobs:
check_style:
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: self-mini-macos
steps:
- name: steps::checkout_repo
@@ -28,7 +28,7 @@ jobs:
shell: bash -euxo pipefail {0}
timeout-minutes: 60
run_tests_windows:
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: self-32vcpu-windows-2022
steps:
- name: steps::checkout_repo
@@ -47,14 +47,11 @@ jobs:
- name: steps::clippy
run: ./script/clippy.ps1
shell: pwsh
- name: steps::cargo_install_nextest
run: cargo install cargo-nextest --locked
shell: pwsh
- name: steps::clear_target_dir_if_large
run: ./script/clear-target-dir-if-larger-than.ps1 250
shell: pwsh
- name: steps::cargo_nextest
run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
run: cargo nextest run --workspace --no-fail-fast
shell: pwsh
- name: steps::cleanup_cargo_config
if: always()
@@ -364,7 +361,7 @@ jobs:
needs:
- check_style
- run_tests_windows
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-32x64-ubuntu-2004
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
@@ -395,7 +392,7 @@ jobs:
needs:
- check_style
- run_tests_windows
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: self-mini-macos
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
@@ -437,7 +434,7 @@ jobs:
- bundle_mac_x86_64
- bundle_windows_aarch64
- bundle_windows_x86_64
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-4x8-ubuntu-2204
steps:
- name: steps::checkout_repo
@@ -493,3 +490,21 @@ jobs:
SENTRY_PROJECT: zed
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
timeout-minutes: 60
notify_on_failure:
needs:
- bundle_linux_aarch64
- bundle_linux_x86_64
- bundle_mac_aarch64
- bundle_mac_x86_64
- bundle_windows_aarch64
- bundle_windows_x86_64
if: failure()
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: release::notify_on_failure::notify_slack
run: |-
curl -X POST -H 'Content-type: application/json'\
--data '{"text":"${{ github.workflow }} failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' "$SLACK_WEBHOOK"
shell: bash -euxo pipefail {0}
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_WORKFLOW_FAILURES }}

View File

@@ -13,7 +13,7 @@ jobs:
bundle_linux_aarch64:
if: |-
(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
runs-on: namespace-profile-8x32-ubuntu-2004-arm-m4
env:
CARGO_INCREMENTAL: 0
@@ -56,7 +56,7 @@ jobs:
bundle_linux_x86_64:
if: |-
(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
runs-on: namespace-profile-32x64-ubuntu-2004
env:
CARGO_INCREMENTAL: 0
@@ -99,7 +99,7 @@ jobs:
bundle_mac_aarch64:
if: |-
(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
runs-on: self-mini-macos
env:
CARGO_INCREMENTAL: 0
@@ -145,7 +145,7 @@ jobs:
bundle_mac_x86_64:
if: |-
(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
runs-on: self-mini-macos
env:
CARGO_INCREMENTAL: 0
@@ -191,7 +191,7 @@ jobs:
bundle_windows_aarch64:
if: |-
(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
runs-on: self-32vcpu-windows-2022
env:
CARGO_INCREMENTAL: 0
@@ -229,7 +229,7 @@ jobs:
bundle_windows_x86_64:
if: |-
(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
runs-on: self-32vcpu-windows-2022
env:
CARGO_INCREMENTAL: 0

View File

@@ -13,6 +13,14 @@ on:
jobs:
cron_unit_evals:
runs-on: namespace-profile-16x32-ubuntu-2204
strategy:
matrix:
model:
- anthropic/claude-sonnet-4-5-latest
- anthropic/claude-opus-4-5-latest
- google/gemini-3-pro
- openai/gpt-5
fail-fast: false
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -37,8 +45,7 @@ jobs:
run: ./script/download-wasi-sdk
shell: bash -euxo pipefail {0}
- name: steps::cargo_install_nextest
run: cargo install cargo-nextest --locked
shell: bash -euxo pipefail {0}
uses: taiki-e/install-action@nextest
- name: steps::clear_target_dir_if_large
run: ./script/clear-target-dir-if-larger-than 250
shell: bash -euxo pipefail {0}
@@ -50,6 +57,7 @@ jobs:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }}
GOOGLE_CLOUD_PROJECT: ${{ secrets.GOOGLE_CLOUD_PROJECT }}
ZED_AGENT_MODEL: ${{ matrix.model }}
- name: steps::cleanup_cargo_config
if: always()
run: |

View File

@@ -15,7 +15,7 @@ on:
- v[0-9]+.[0-9]+.x
jobs:
orchestrate:
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: steps::checkout_repo
@@ -47,7 +47,7 @@ jobs:
}
check_pattern "run_action_checks" '^\.github/(workflows/|actions/|actionlint.yml)|tooling/xtask|script/' -qP
check_pattern "run_docs" '^docs/' -qP
check_pattern "run_docs" '^(docs/|crates/.*\.rs)' -qP
check_pattern "run_licenses" '^(Cargo.lock|script/.*licenses)' -qP
check_pattern "run_nix" '^(nix/|flake\.|Cargo\.|rust-toolchain.toml|\.cargo/config.toml)' -qP
check_pattern "run_tests" '^(docs/|script/update_top_ranking_issues/|\.github/(ISSUE_TEMPLATE|workflows/(?!run_tests)))' -qvP
@@ -59,7 +59,7 @@ jobs:
run_nix: ${{ steps.filter.outputs.run_nix }}
run_tests: ${{ steps.filter.outputs.run_tests }}
check_style:
if: github.repository_owner == 'zed-industries'
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-4x8-ubuntu-2204
steps:
- name: steps::checkout_repo
@@ -84,7 +84,7 @@ jobs:
run: ./script/check-keymaps
shell: bash -euxo pipefail {0}
- name: run_tests::check_style::check_for_typos
uses: crate-ci/typos@80c8a4945eec0f6d464eaf9e65ed98ef085283d1
uses: crate-ci/typos@2d0ce569feab1f8752f1dde43cc2f2aa53236e06
with:
config: ./typos.toml
- name: steps::cargo_fmt
@@ -113,14 +113,11 @@ jobs:
- name: steps::clippy
run: ./script/clippy.ps1
shell: pwsh
- name: steps::cargo_install_nextest
run: cargo install cargo-nextest --locked
shell: pwsh
- name: steps::clear_target_dir_if_large
run: ./script/clear-target-dir-if-larger-than.ps1 250
shell: pwsh
- name: steps::cargo_nextest
run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
run: cargo nextest run --workspace --no-fail-fast
shell: pwsh
- name: steps::cleanup_cargo_config
if: always()
@@ -164,13 +161,12 @@ jobs:
run: ./script/clippy
shell: bash -euxo pipefail {0}
- name: steps::cargo_install_nextest
run: cargo install cargo-nextest --locked
shell: bash -euxo pipefail {0}
uses: taiki-e/install-action@nextest
- name: steps::clear_target_dir_if_large
run: ./script/clear-target-dir-if-larger-than 250
shell: bash -euxo pipefail {0}
- name: steps::cargo_nextest
run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
run: cargo nextest run --workspace --no-fail-fast
shell: bash -euxo pipefail {0}
- name: steps::cleanup_cargo_config
if: always()
@@ -200,14 +196,11 @@ jobs:
- name: steps::clippy
run: ./script/clippy
shell: bash -euxo pipefail {0}
- name: steps::cargo_install_nextest
run: cargo install cargo-nextest --locked
shell: bash -euxo pipefail {0}
- name: steps::clear_target_dir_if_large
run: ./script/clear-target-dir-if-larger-than 300
shell: bash -euxo pipefail {0}
- name: steps::cargo_nextest
run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
run: cargo nextest run --workspace --no-fail-fast
shell: bash -euxo pipefail {0}
- name: steps::cleanup_cargo_config
if: always()
@@ -500,7 +493,10 @@ jobs:
needs:
- orchestrate
if: needs.orchestrate.outputs.run_tests == 'true'
runs-on: self-mini-macos
runs-on: namespace-profile-16x32-ubuntu-2204
env:
GIT_AUTHOR_NAME: Protobuf Action
GIT_AUTHOR_EMAIL: ci@zed.dev
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -524,6 +520,7 @@ jobs:
uses: bufbuild/buf-setup-action@v1
with:
version: v1.29.0
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: run_tests::check_postgres_and_protobuf_migrations::bufbuild_breaking_action
uses: bufbuild/buf-breaking-action@v1
with:
@@ -545,7 +542,7 @@ jobs:
- check_scripts
- build_nix_linux_x86_64
- build_nix_mac_aarch64
if: github.repository_owner == 'zed-industries' && always()
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && always()
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: run_tests::tests_pass

View File

@@ -46,8 +46,7 @@ jobs:
run: ./script/download-wasi-sdk
shell: bash -euxo pipefail {0}
- name: steps::cargo_install_nextest
run: cargo install cargo-nextest --locked
shell: bash -euxo pipefail {0}
uses: taiki-e/install-action@nextest
- name: steps::clear_target_dir_if_large
run: ./script/clear-target-dir-if-larger-than 250
shell: bash -euxo pipefail {0}
@@ -66,5 +65,5 @@ jobs:
rm -rf ./../.cargo
shell: bash -euxo pipefail {0}
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.run_id }}
cancel-in-progress: true

3
.gitignore vendored
View File

@@ -39,3 +39,6 @@ xcuserdata/
# Don't commit any secrets to the repo.
.env
.env.secret.toml
# `nix build` output
/result

2173
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -54,11 +54,12 @@ members = [
"crates/diagnostics",
"crates/docs_preprocessor",
"crates/edit_prediction",
"crates/edit_prediction_button",
"crates/edit_prediction_types",
"crates/edit_prediction_ui",
"crates/edit_prediction_context",
"crates/zeta2_tools",
"crates/editor",
"crates/eval",
"crates/eval_utils",
"crates/explorer_command_injector",
"crates/extension",
"crates/extension_api",
@@ -74,6 +75,7 @@ members = [
"crates/fsevent",
"crates/fuzzy",
"crates/git",
"crates/git_graph",
"crates/git_hosting_providers",
"crates/git_ui",
"crates/go_to_line",
@@ -127,6 +129,7 @@ members = [
"crates/picker",
"crates/prettier",
"crates/project",
"crates/project_benchmarks",
"crates/project_panel",
"crates/project_symbols",
"crates/prompt_store",
@@ -146,7 +149,6 @@ members = [
"crates/rules_library",
"crates/schema_generator",
"crates/search",
"crates/semantic_version",
"crates/session",
"crates/settings",
"crates/settings_json",
@@ -200,11 +202,11 @@ members = [
"crates/zed",
"crates/zed_actions",
"crates/zed_env_vars",
"crates/zeta",
"crates/zeta2",
"crates/zeta_cli",
"crates/edit_prediction_cli",
"crates/zlog",
"crates/zlog_settings",
"crates/ztracing",
"crates/ztracing_macro",
#
# Extensions
@@ -243,7 +245,6 @@ activity_indicator = { path = "crates/activity_indicator" }
agent_ui = { path = "crates/agent_ui" }
agent_settings = { path = "crates/agent_settings" }
agent_servers = { path = "crates/agent_servers" }
ai = { path = "crates/ai" }
ai_onboarding = { path = "crates/ai_onboarding" }
anthropic = { path = "crates/anthropic" }
askpass = { path = "crates/askpass" }
@@ -253,7 +254,6 @@ assistant_slash_command = { path = "crates/assistant_slash_command" }
assistant_slash_commands = { path = "crates/assistant_slash_commands" }
audio = { path = "crates/audio" }
auto_update = { path = "crates/auto_update" }
auto_update_helper = { path = "crates/auto_update_helper" }
auto_update_ui = { path = "crates/auto_update_ui" }
aws_http_client = { path = "crates/aws_http_client" }
bedrock = { path = "crates/bedrock" }
@@ -268,7 +268,6 @@ cloud_api_client = { path = "crates/cloud_api_client" }
cloud_api_types = { path = "crates/cloud_api_types" }
cloud_llm_client = { path = "crates/cloud_llm_client" }
cloud_zeta2_prompt = { path = "crates/cloud_zeta2_prompt" }
collab = { path = "crates/collab" }
collab_ui = { path = "crates/collab_ui" }
collections = { path = "crates/collections", version = "0.1.0" }
command_palette = { path = "crates/command_palette" }
@@ -289,6 +288,7 @@ deepseek = { path = "crates/deepseek" }
derive_refineable = { path = "crates/refineable/derive_refineable" }
diagnostics = { path = "crates/diagnostics" }
editor = { path = "crates/editor" }
eval_utils = { path = "crates/eval_utils" }
extension = { path = "crates/extension" }
extension_host = { path = "crates/extension_host" }
extensions_ui = { path = "crates/extensions_ui" }
@@ -300,6 +300,7 @@ fs = { path = "crates/fs" }
fsevent = { path = "crates/fsevent" }
fuzzy = { path = "crates/fuzzy" }
git = { path = "crates/git" }
git_graph = { path = "crates/git_graph" }
git_hosting_providers = { path = "crates/git_hosting_providers" }
git_ui = { path = "crates/git_ui" }
go_to_line = { path = "crates/go_to_line" }
@@ -312,10 +313,9 @@ http_client = { path = "crates/http_client" }
http_client_tls = { path = "crates/http_client_tls" }
icons = { path = "crates/icons" }
image_viewer = { path = "crates/image_viewer" }
edit_prediction = { path = "crates/edit_prediction" }
edit_prediction_button = { path = "crates/edit_prediction_button" }
edit_prediction_types = { path = "crates/edit_prediction_types" }
edit_prediction_ui = { path = "crates/edit_prediction_ui" }
edit_prediction_context = { path = "crates/edit_prediction_context" }
zeta2_tools = { path = "crates/zeta2_tools" }
inspector_ui = { path = "crates/inspector_ui" }
install_cli = { path = "crates/install_cli" }
journal = { path = "crates/journal" }
@@ -357,8 +357,6 @@ panel = { path = "crates/panel" }
paths = { path = "crates/paths" }
perf = { path = "tooling/perf" }
picker = { path = "crates/picker" }
plugin = { path = "crates/plugin" }
plugin_macros = { path = "crates/plugin_macros" }
prettier = { path = "crates/prettier" }
settings_profile_selector = { path = "crates/settings_profile_selector" }
project = { path = "crates/project" }
@@ -369,18 +367,15 @@ proto = { path = "crates/proto" }
recent_projects = { path = "crates/recent_projects" }
refineable = { path = "crates/refineable" }
release_channel = { path = "crates/release_channel" }
scheduler = { path = "crates/scheduler" }
remote = { path = "crates/remote" }
remote_server = { path = "crates/remote_server" }
repl = { path = "crates/repl" }
reqwest_client = { path = "crates/reqwest_client" }
rich_text = { path = "crates/rich_text" }
rodio = { git = "https://github.com/RustAudio/rodio", rev ="e2074c6c2acf07b57cf717e076bdda7a9ac6e70b", features = ["wav", "playback", "wav_output", "recording"] }
rope = { path = "crates/rope" }
rpc = { path = "crates/rpc" }
rules_library = { path = "crates/rules_library" }
search = { path = "crates/search" }
semantic_version = { path = "crates/semantic_version" }
session = { path = "crates/session" }
settings = { path = "crates/settings" }
settings_json = { path = "crates/settings_json" }
@@ -392,7 +387,6 @@ snippets_ui = { path = "crates/snippets_ui" }
sqlez = { path = "crates/sqlez" }
sqlez_macros = { path = "crates/sqlez_macros" }
story = { path = "crates/story" }
storybook = { path = "crates/storybook" }
streaming_diff = { path = "crates/streaming_diff" }
sum_tree = { path = "crates/sum_tree" }
supermaven = { path = "crates/supermaven" }
@@ -409,7 +403,6 @@ terminal_view = { path = "crates/terminal_view" }
text = { path = "crates/text" }
theme = { path = "crates/theme" }
theme_extension = { path = "crates/theme_extension" }
theme_importer = { path = "crates/theme_importer" }
theme_selector = { path = "crates/theme_selector" }
time_format = { path = "crates/time_format" }
title_bar = { path = "crates/title_bar" }
@@ -433,16 +426,17 @@ x_ai = { path = "crates/x_ai" }
zed = { path = "crates/zed" }
zed_actions = { path = "crates/zed_actions" }
zed_env_vars = { path = "crates/zed_env_vars" }
zeta = { path = "crates/zeta" }
zeta2 = { path = "crates/zeta2" }
edit_prediction = { path = "crates/edit_prediction" }
zlog = { path = "crates/zlog" }
zlog_settings = { path = "crates/zlog_settings" }
ztracing = { path = "crates/ztracing" }
ztracing_macro = { path = "crates/ztracing_macro" }
#
# External crates
#
agent-client-protocol = { version = "0.7.0", features = ["unstable"] }
agent-client-protocol = { version = "=0.9.0", features = ["unstable"] }
aho-corasick = "1.1"
alacritty_terminal = "0.25.1-rc1"
any_vec = "0.14"
@@ -460,7 +454,7 @@ async-tar = "0.5.1"
async-task = "4.7"
async-trait = "0.1"
async-tungstenite = "0.31.0"
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
async_zip = { version = "0.0.18", features = ["deflate", "deflate64"] }
aws-config = { version = "1.6.1", features = ["behavior-version-latest"] }
aws-credential-types = { version = "1.2.2", features = [
"hardcoded-credentials",
@@ -477,6 +471,7 @@ bitflags = "2.6.0"
blade-graphics = { version = "0.7.0" }
blade-macros = { version = "0.3.0" }
blade-util = { version = "0.3.0" }
brotli = "8.0.2"
bytes = "1.0"
cargo_metadata = "0.19"
cargo_toml = "0.21"
@@ -484,7 +479,7 @@ cfg-if = "1.0.3"
chrono = { version = "0.4", features = ["serde"] }
ciborium = "0.2"
circular-buffer = "1.0"
clap = { version = "4.4", features = ["derive"] }
clap = { version = "4.4", features = ["derive", "wrap_help"] }
cocoa = "=0.26.0"
cocoa-foundation = "=0.2.0"
convert_case = "0.8.0"
@@ -505,16 +500,14 @@ ec4rs = "1.1"
emojis = "0.6.1"
env_logger = "0.11"
exec = "0.3.1"
fancy-regex = "0.14.0"
fancy-regex = "0.16.0"
fork = "0.4.0"
futures = "0.3"
futures-batch = "0.6.1"
futures-lite = "1.13"
gh-workflow = { git = "https://github.com/zed-industries/gh-workflow", rev = "3eaa84abca0778eb54272f45a312cb24f9a0b435" }
gh-workflow = { git = "https://github.com/zed-industries/gh-workflow", rev = "09acfdf2bd5c1d6254abefd609c808ff73547b2c" }
git2 = { version = "0.20.1", default-features = false }
globset = "0.4"
handlebars = "4.3"
hashbrown = "0.15.3"
heck = "0.5"
heed = { version = "0.21.0", features = ["read-txn-no-tls"] }
hex = "0.4.3"
@@ -531,10 +524,10 @@ indoc = "2"
inventory = "0.3.19"
itertools = "0.14.0"
json_dotpath = "1.1"
jsonschema = "0.30.0"
jsonschema = "0.37.0"
jsonwebtoken = "9.3"
jupyter-protocol = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
jupyter-websocket-client = { git = "https://github.com/ConradIrwin/runtimed" ,rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
jupyter-protocol = "0.10.0"
jupyter-websocket-client = "0.15.0"
libc = "0.2"
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
linkify = "0.10.0"
@@ -547,10 +540,9 @@ minidumper = "0.8"
moka = { version = "0.12.10", features = ["sync"] }
naga = { version = "25.0", features = ["wgsl-in"] }
nanoid = "0.4"
nbformat = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
nbformat = "0.15.0"
nix = "0.29"
num-format = "0.4.4"
num-traits = "0.2"
objc = "0.2"
objc2-foundation = { version = "=0.3.1", default-features = false, features = [
"NSArray",
@@ -585,14 +577,13 @@ partial-json-fixer = "0.5.3"
parse_int = "0.9"
pciid-parser = "0.8.0"
pathdiff = "0.2"
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
pet-pixi = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
pet-virtualenv = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "e97b9508befa0062929da65a01054d25c4be861c" }
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
pet-virtualenv = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1e86914c3ce2f3a08c0cedbcb0615a7f9fa7a5da" }
portable-pty = "0.9.0"
postage = { version = "0.5", features = ["futures-traits"] }
pretty_assertions = { version = "1.3.0", features = ["unstable"] }
@@ -605,7 +596,6 @@ pulldown-cmark = { version = "0.12.0", default-features = false }
quote = "1.0.9"
rand = "0.9"
rayon = "1.8"
ref-cast = "1.0.24"
regex = "1.5"
# WARNING: If you change this, you must also publish a new version of zed-reqwest to crates.io
reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "c15662463bda39148ba154100dd44d3fba5873a4", default-features = false, features = [
@@ -618,8 +608,8 @@ reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "c15662
"stream",
], package = "zed-reqwest", version = "0.12.15-zed" }
rsa = "0.9.6"
runtimelib = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734", default-features = false, features = [
"async-dispatcher-runtime",
runtimelib = { version = "0.30.0", default-features = false, features = [
"async-dispatcher-runtime", "aws-lc-rs"
] }
rust-embed = { version = "8.4", features = ["include-exclude"] }
rustc-hash = "2.1.0"
@@ -628,7 +618,7 @@ rustls-platform-verifier = "0.5.0"
# WARNING: If you change this, you must also publish a new version of zed-scap to crates.io
scap = { git = "https://github.com/zed-industries/scap", rev = "4afea48c3b002197176fb19cd0f9b180dd36eaac", default-features = false, package = "zed-scap", version = "0.0.8-zed" }
schemars = { version = "1.0", features = ["indexmap2"] }
semver = "1.0"
semver = { version = "1.0", features = ["serde"] }
serde = { version = "1.0.221", features = ["derive", "rc"] }
serde_json = { version = "1.0.144", features = ["preserve_order", "raw_value"] }
serde_json_lenient = { version = "0.2", features = [
@@ -638,7 +628,6 @@ serde_json_lenient = { version = "0.2", features = [
serde_path_to_error = "0.1.17"
serde_repr = "0.1"
serde_urlencoded = "0.7"
serde_with = "3.4.0"
sha2 = "0.10"
shellexpand = "2.1.0"
shlex = "1.3.0"
@@ -658,7 +647,7 @@ sysinfo = "0.37.0"
take-until = "0.2.0"
tempfile = "3.20.0"
thiserror = "2.0.12"
tiktoken-rs = { git = "https://github.com/zed-industries/tiktoken-rs", rev = "30c32a4522751699adeda0d5840c71c3b75ae73d" }
tiktoken-rs = { git = "https://github.com/zed-industries/tiktoken-rs", rev = "2570c4387a8505fb8f1d3f3557454b474f1e8271" }
time = { version = "0.3", features = [
"macros",
"parsing",
@@ -674,7 +663,7 @@ toml = "0.8"
toml_edit = { version = "0.22", default-features = false, features = ["display", "parse", "serde"] }
tower-http = "0.4.4"
tree-sitter = { version = "0.25.10", features = ["wasm"] }
tree-sitter-bash = "0.25.0"
tree-sitter-bash = "0.25.1"
tree-sitter-c = "0.23"
tree-sitter-cpp = { git = "https://github.com/tree-sitter/tree-sitter-cpp", rev = "5cb9b693cfd7bfacab1d9ff4acac1a4150700609" }
tree-sitter-css = "0.23"
@@ -696,6 +685,7 @@ tree-sitter-ruby = "0.23"
tree-sitter-rust = "0.24"
tree-sitter-typescript = { git = "https://github.com/zed-industries/tree-sitter-typescript", rev = "e2c53597d6a5d9cf7bbe8dccde576fe1e46c5899" } # https://github.com/tree-sitter/tree-sitter-typescript/pull/347
tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "baff0b51c64ef6a1fb1f8390f3ad6015b83ec13a" }
tracing = "0.1.40"
unicase = "2.6"
unicode-script = "0.5.7"
unicode-segmentation = "1.10"
@@ -716,13 +706,14 @@ wasmtime = { version = "29", default-features = false, features = [
"parallel-compilation",
] }
wasmtime-wasi = "29"
wax = "0.6"
which = "6.0.0"
windows-core = "0.61"
wit-component = "0.221"
yawc = "0.2.5"
zeroize = "1.8"
zstd = "0.11"
[workspace.dependencies.windows]
version = "0.61"
features = [
@@ -778,6 +769,7 @@ features = [
notify = { git = "https://github.com/zed-industries/notify.git", rev = "b4588b2e5aee68f4c0e100f140e808cbce7b1419" }
notify-types = { git = "https://github.com/zed-industries/notify.git", rev = "b4588b2e5aee68f4c0e100f140e808cbce7b1419" }
windows-capture = { git = "https://github.com/zed-industries/windows-capture.git", rev = "f0d6c1b6691db75461b732f6d5ff56eed002eeb9" }
calloop = { git = "https://github.com/zed-industries/calloop" }
[profile.dev]
split-debuginfo = "unpacked"
@@ -791,14 +783,20 @@ codegen-units = 16
codegen-units = 16
[profile.dev.package]
# proc-macros start
gpui_macros = { opt-level = 3 }
derive_refineable = { opt-level = 3 }
settings_macros = { opt-level = 3 }
sqlez_macros = { opt-level = 3, codegen-units = 1 }
ui_macros = { opt-level = 3 }
util_macros = { opt-level = 3 }
quote = { opt-level = 3 }
syn = { opt-level = 3 }
proc-macro2 = { opt-level = 3 }
# proc-macros end
taffy = { opt-level = 3 }
cranelift-codegen = { opt-level = 3 }
cranelift-codegen-meta = { opt-level = 3 }
cranelift-codegen-shared = { opt-level = 3 }
resvg = { opt-level = 3 }
rustybuzz = { opt-level = 3 }
ttf-parser = { opt-level = 3 }
wasmtime-cranelift = { opt-level = 3 }
wasmtime = { opt-level = 3 }
# Build single-source-file crates with cg=1 as it helps make `cargo build` of a whole workspace a bit faster
activity_indicator = { codegen-units = 1 }
@@ -807,12 +805,11 @@ breadcrumbs = { codegen-units = 1 }
collections = { codegen-units = 1 }
command_palette = { codegen-units = 1 }
command_palette_hooks = { codegen-units = 1 }
extension_cli = { codegen-units = 1 }
feature_flags = { codegen-units = 1 }
file_icons = { codegen-units = 1 }
fsevent = { codegen-units = 1 }
image_viewer = { codegen-units = 1 }
edit_prediction_button = { codegen-units = 1 }
edit_prediction_ui = { codegen-units = 1 }
install_cli = { codegen-units = 1 }
journal = { codegen-units = 1 }
json_schema_store = { codegen-units = 1 }
@@ -827,12 +824,9 @@ project_symbols = { codegen-units = 1 }
refineable = { codegen-units = 1 }
release_channel = { codegen-units = 1 }
reqwest_client = { codegen-units = 1 }
rich_text = { codegen-units = 1 }
semantic_version = { codegen-units = 1 }
session = { codegen-units = 1 }
snippet = { codegen-units = 1 }
snippets_ui = { codegen-units = 1 }
sqlez_macros = { codegen-units = 1 }
story = { codegen-units = 1 }
supermaven_api = { codegen-units = 1 }
telemetry_events = { codegen-units = 1 }

View File

@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.2
FROM rust:1.90-bookworm as builder
FROM rust:1.91.1-bookworm as builder
WORKDIR app
COPY . .

View File

@@ -43,7 +43,9 @@ design
= @danilo-leal
docs
= @miguelraz
= @probably-neb
= @yeskunall
extension
= @kubkon
@@ -51,6 +53,10 @@ extension
git
= @cole-miller
= @danilo-leal
= @dvdsk
= @kubkon
= @Anthony-Eid
= @cameron1024
gpui
= @Anthony-Eid
@@ -98,6 +104,12 @@ settings_ui
= @danilo-leal
= @probably-neb
sum_tree
= @Veykril
support
= @miguelraz
tasks
= @SomeoneToIgnore
= @Veykril
@@ -106,6 +118,9 @@ terminal
= @kubkon
= @Veykril
text
= @Veykril
vim
= @ConradIrwin
= @dinocosta
@@ -115,3 +130,4 @@ vim
windows
= @localcc
= @reflectronic
= @Veykril

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" d="M14 11.333A6 6 0 0 0 4 6.867l-1 .9"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.333" d="M2 4.667v4h4"/><path fill="#000" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" d="M8 12a.667.667 0 1 0 0-1.333A.667.667 0 0 0 8 12Z"/></svg>

Before

Width:  |  Height:  |  Size: 467 B

View File

@@ -1 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" d="M3.333 10 8 14.667 12.667 10M8 5.333v9.334"/><path fill="#000" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" d="M8 2.667a.667.667 0 1 0 0-1.334.667.667 0 0 0 0 1.334Z"/></svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 13H5" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 13H14" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.5 8.5L8 12M8 12L4.5 8.5M8 12L8 3" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 443 B

View File

@@ -1 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" d="M3.333 6 8 1.333 12.667 6M8 10.667V1.333"/><path fill="#000" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" d="M8 13.333a.667.667 0 1 1 0 1.334.667.667 0 0 1 0-1.334Z"/></svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.5 6.5L8 3M8 3L11.5 6.5M8 3V12" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 13H5" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 13H14" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 373 B

After

Width:  |  Height:  |  Size: 439 B

View File

@@ -1 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" d="M2 11.333a6 6 0 0 1 10-4.466l1 .9"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.333" d="M14 4.667v4h-4"/><path fill="#000" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" d="M8 12a.667.667 0 1 1 0-1.333A.667.667 0 0 1 8 12Z"/></svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 11.333C2.00118 10.1752 2.33729 9.04258 2.96777 8.07159C3.59826 7.10059 4.49621 6.33274 5.55331 5.86064C6.61041 5.38853 7.78152 5.23235 8.9254 5.41091C10.0693 5.58947 11.1371 6.09516 12 6.86698L13 7.76698" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 4.66699V8.66699H10" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 13H10" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 468 B

After

Width:  |  Height:  |  Size: 627 B

View File

@@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1_2)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.58747 12.9359C4.35741 12.778 4.17558 12.625 4.17558 12.625L10.092 2.37749C10.092 2.37749 10.3355 2.46782 10.5367 2.56426C10.7903 2.6858 11.0003 2.80429 11.0003 2.80429C13.8681 4.46005 14.8523 8.13267 13.1965 11.0005C11.5407 13.8684 7.8681 14.8525 5.00023 13.1967C5.00023 13.1967 4.79936 13.0812 4.58747 12.9359ZM10.5003 3.67032L5.50023 12.3307C7.89013 13.7105 10.9506 12.8904 12.3305 10.5006C13.7102 8.1106 12.8902 5.05015 10.5003 3.67032ZM3.07664 11.4314C2.87558 11.1403 2.804 11.0006 2.804 11.0006C1.77036 9.20524 1.69456 6.92215 2.80404 5.00046C3.91353 3.07877 5.92859 2.00291 8.0003 2.00036C8.0003 2.00036 8.28 1.99964 8.51289 2.02194C8.86375 2.05556 9.09702 2.10083 9.09702 2.10083L3.43905 11.9007C3.43905 11.9007 3.30482 11.7618 3.07664 11.4314ZM7.40178 3.03702C5.89399 3.22027 4.48727 4.08506 3.67008 5.50052C2.85288 6.9159 2.80733 8.56653 3.40252 9.96401L7.40178 3.03702Z" fill="black" stroke="black" stroke-width="0.1"/>
</g>
<defs>
<clipPath id="clip0_1_2">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 2V10" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 6C12.5304 6 13.0391 5.78929 13.4142 5.41421C13.7893 5.03914 14 4.53043 14 4C14 3.46957 13.7893 2.96086 13.4142 2.58579C13.0391 2.21071 12.5304 2 12 2C11.4696 2 10.9609 2.21071 10.5858 2.58579C10.2107 2.96086 10 3.46957 10 4C10 4.53043 10.2107 5.03914 10.5858 5.41421C10.9609 5.78929 11.4696 6 12 6Z" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4 14C4.53043 14 5.03914 13.7893 5.41421 13.4142C5.78929 13.0391 6 12.5304 6 12C6 11.4696 5.78929 10.9609 5.41421 10.5858C5.03914 10.2107 4.53043 10 4 10C3.46957 10 2.96086 10.2107 2.58579 10.5858C2.21071 10.9609 2 11.4696 2 12C2 12.5304 2.21071 13.0391 2.58579 13.4142C2.96086 13.7893 3.46957 14 4 14Z" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 4C8.4087 4 6.88258 4.63214 5.75736 5.75736C4.63214 6.88258 4 8.4087 4 10" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 10V14" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 12H10" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,11 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" id="svg1378540956_510">
<g clip-path="url(#svg1378540956_510_clip0_1_1506)" transform="translate(4, 4) scale(0.857)">
<path d="M17.0547 0.372066H8.52652L-0.00165176 8.90024V17.4284H8.52652V8.90024H17.0547V0.372066Z" fill="#1A1C20"></path>
<path d="M10.1992 27.6279H18.7274L27.2556 19.0998V10.5716H18.7274V19.0998H10.1992V27.6279Z" fill="#1A1C20"></path>
</g>
<defs>
<clipPath id="svg1378540956_510_clip0_1_1506">
<rect width="27.2559" height="27.2559" fill="white" transform="translate(0 0.37207)"></rect>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 593 B

32
assets/icons/sweep_ai.svg Normal file
View File

@@ -0,0 +1,32 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3348_16)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.97419 6.27207C8.44653 6.29114 8.86622 6.27046 9.23628 6.22425C9.08884 7.48378 8.7346 8.72903 8.16697 9.90688C8.04459 9.83861 7.92582 9.76008 7.81193 9.67108C7.64539 9.54099 7.49799 9.39549 7.37015 9.23818C7.5282 9.54496 7.64901 9.86752 7.73175 10.1986C7.35693 10.6656 6.90663 11.0373 6.412 11.3101C5.01165 10.8075 4.03638 9.63089 4.03638 7.93001C4.03638 6.96185 4.35234 6.07053 4.88281 5.36157C5.34001 5.69449 6.30374 6.20455 7.97419 6.27207ZM8.27511 11.5815C10.3762 11.5349 11.8115 10.7826 12.8347 7.93001C11.6992 7.93001 11.4246 7.10731 11.1188 6.19149C11.0669 6.03596 11.0141 5.87771 10.956 5.72037C10.6733 5.86733 10.2753 6.02782 9.74834 6.13895C9.59658 7.49345 9.20592 8.83238 8.56821 10.0897C8.89933 10.2093 9.24674 10.262 9.5908 10.2502C9.08928 10.4803 8.62468 10.8066 8.22655 11.2255C8.2457 11.3438 8.26186 11.4625 8.27511 11.5815ZM6.62702 7.75422C6.62702 7.50604 6.82821 7.30485 7.07639 7.30485C7.32457 7.30485 7.52576 7.50604 7.52576 7.75422V8.23616C7.52576 8.48435 7.32457 8.68554 7.07639 8.68554C6.82821 8.68554 6.62702 8.48435 6.62702 8.23616V7.75422ZM5.27746 7.30485C5.05086 7.30485 4.86716 7.48854 4.86716 7.71513V8.27525C4.86716 8.50185 5.05086 8.68554 5.27746 8.68554C5.50406 8.68554 5.68776 8.50185 5.68776 8.27525V7.71513C5.68776 7.48854 5.50406 7.30485 5.27746 7.30485Z" fill="white"/>
<mask id="mask0_3348_16" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="4" y="5" width="9" height="7">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.97419 6.27207C8.44653 6.29114 8.86622 6.27046 9.23628 6.22425C9.08884 7.48378 8.7346 8.72903 8.16697 9.90688C8.04459 9.83861 7.92582 9.76008 7.81193 9.67108C7.64539 9.54099 7.49799 9.39549 7.37015 9.23818C7.5282 9.54496 7.64901 9.86752 7.73175 10.1986C7.35693 10.6656 6.90663 11.0373 6.412 11.3101C5.01165 10.8075 4.03638 9.63089 4.03638 7.93001C4.03638 6.96185 4.35234 6.07053 4.88281 5.36157C5.34001 5.69449 6.30374 6.20455 7.97419 6.27207ZM8.27511 11.5815C10.3762 11.5349 11.8115 10.7826 12.8347 7.93001C11.6992 7.93001 11.4246 7.10731 11.1188 6.19149C11.0669 6.03596 11.0141 5.87771 10.956 5.72037C10.6733 5.86733 10.2753 6.02782 9.74834 6.13895C9.59658 7.49345 9.20592 8.83238 8.56821 10.0897C8.89933 10.2093 9.24674 10.262 9.5908 10.2502C9.08928 10.4803 8.62468 10.8066 8.22655 11.2255C8.2457 11.3438 8.26186 11.4625 8.27511 11.5815ZM6.62702 7.75422C6.62702 7.50604 6.82821 7.30485 7.07639 7.30485C7.32457 7.30485 7.52576 7.50604 7.52576 7.75422V8.23616C7.52576 8.48435 7.32457 8.68554 7.07639 8.68554C6.82821 8.68554 6.62702 8.48435 6.62702 8.23616V7.75422ZM5.27746 7.30485C5.05086 7.30485 4.86716 7.48854 4.86716 7.71513V8.27525C4.86716 8.50185 5.05086 8.68554 5.27746 8.68554C5.50406 8.68554 5.68776 8.50185 5.68776 8.27525V7.71513C5.68776 7.48854 5.50406 7.30485 5.27746 7.30485Z" fill="white"/>
</mask>
<g mask="url(#mask0_3348_16)">
<path d="M9.23617 6.22425L9.39588 6.24293L9.41971 6.0393L9.21624 6.06471L9.23617 6.22425ZM8.16687 9.90688L8.08857 10.0473L8.23765 10.1305L8.31174 9.97669L8.16687 9.90688ZM7.37005 9.23819L7.49487 9.13676L7.22714 9.3118L7.37005 9.23819ZM7.73165 10.1986L7.85702 10.2993L7.90696 10.2371L7.88761 10.1597L7.73165 10.1986ZM6.41189 11.3101L6.35758 11.4615L6.42594 11.486L6.48954 11.4509L6.41189 11.3101ZM4.88271 5.36157L4.97736 5.23159L4.84905 5.13817L4.75397 5.26525L4.88271 5.36157ZM8.27501 11.5815L8.11523 11.5993L8.13151 11.7456L8.27859 11.7423L8.27501 11.5815ZM12.8346 7.93001L12.986 7.98428L13.0631 7.76921H12.8346V7.93001ZM10.9559 5.72037L11.1067 5.66469L11.0436 5.49354L10.8817 5.5777L10.9559 5.72037ZM9.74824 6.13896L9.71508 5.98161L9.60139 6.0056L9.58846 6.12102L9.74824 6.13896ZM8.56811 10.0897L8.42469 10.017L8.34242 10.1792L8.51348 10.241L8.56811 10.0897ZM9.5907 10.2502L9.65775 10.3964L9.58519 10.0896L9.5907 10.2502ZM8.22644 11.2255L8.10992 11.1147L8.05502 11.1725L8.06773 11.2512L8.22644 11.2255ZM9.21624 6.06471C8.85519 6.10978 8.44439 6.13015 7.98058 6.11139L7.96756 6.43272C8.44852 6.45215 8.87701 6.43111 9.25607 6.3838L9.21624 6.06471ZM8.31174 9.97669C8.88724 8.78244 9.2464 7.51988 9.39588 6.24293L9.07647 6.20557C8.93108 7.44772 8.58175 8.67563 8.02203 9.83708L8.31174 9.97669ZM8.2452 9.76645C8.12998 9.70219 8.01817 9.62826 7.91082 9.54438L7.71285 9.79779C7.8333 9.8919 7.95895 9.97503 8.08857 10.0473L8.2452 9.76645ZM7.91082 9.54438C7.75387 9.4218 7.61512 9.28479 7.49487 9.13676L7.24526 9.33957C7.38066 9.50619 7.53671 9.66023 7.71285 9.79779L7.91082 9.54438ZM7.22714 9.3118C7.37944 9.60746 7.49589 9.91837 7.57564 10.2376L7.88761 10.1597C7.80196 9.81663 7.67679 9.48248 7.513 9.16453L7.22714 9.3118ZM7.60624 10.098C7.24483 10.5482 6.81083 10.9065 6.33425 11.1693L6.48954 11.4509C7.00223 11.1682 7.46887 10.7829 7.85702 10.2993L7.60624 10.098ZM3.87549 7.93001C3.87548 9.7042 4.89861 10.9378 6.35758 11.4615L6.46622 11.1588C5.12449 10.6772 4.19707 9.55763 4.19707 7.93001H3.87549ZM4.75397 5.26525C4.20309 6.00147 3.87549 6.92646 3.87549 7.93001H4.19707C4.19707 6.99724 4.50139 6.13959 5.01145 5.45791L4.75397 5.26525ZM7.98058 6.11139C6.34236 6.04516 5.40922 5.54604 4.97736 5.23159L4.78806 5.49157C5.27058 5.84291 6.26491 6.3639 7.96756 6.43272L7.98058 6.11139ZM8.27859 11.7423C9.34696 11.7185 10.2682 11.515 11.0542 10.9376C11.8388 10.3612 12.4683 9.4273 12.986 7.98428L12.6833 7.8757C12.1776 9.28534 11.5779 10.1539 10.8638 10.6784C10.1511 11.202 9.30417 11.3978 8.27143 11.4208L8.27859 11.7423ZM12.8346 7.76921C12.3148 7.76921 12.0098 7.58516 11.7925 7.30552C11.5639 7.0114 11.4266 6.60587 11.2712 6.14061L10.9662 6.24242C11.1166 6.69294 11.2695 7.15667 11.5385 7.50285C11.8188 7.86347 12.2189 8.09078 12.8346 8.09078V7.76921ZM11.2712 6.14061C11.2195 5.98543 11.1658 5.82478 11.1067 5.66469L10.805 5.77606C10.8621 5.93065 10.9142 6.0865 10.9662 6.24242L11.2712 6.14061ZM10.8817 5.5777C10.6115 5.71821 10.2273 5.87362 9.71508 5.98161L9.78143 6.29626C10.3232 6.18206 10.735 6.0165 11.0301 5.86301L10.8817 5.5777ZM9.58846 6.12102C9.43882 7.45684 9.05355 8.77717 8.42469 10.017L8.71149 10.1625C9.35809 8.88764 9.75417 7.53011 9.90806 6.15685L9.58846 6.12102ZM9.58519 10.0896C9.26119 10.1006 8.93423 10.051 8.62269 9.93854L8.51348 10.241C8.86427 10.3677 9.23205 10.4234 9.5962 10.4109L9.58519 10.0896ZM8.34301 11.3363C8.72675 10.9325 9.17443 10.6181 9.65775 10.3964L9.52365 10.1041C9.00392 10.3425 8.52241 10.6807 8.10992 11.1147L8.34301 11.3363ZM8.43483 11.5638C8.4213 11.4421 8.40475 11.3207 8.3852 11.1998L8.06773 11.2512C8.08644 11.3668 8.10225 11.4829 8.11523 11.5993L8.43483 11.5638ZM7.07629 7.14405C6.73931 7.14405 6.46613 7.41724 6.46613 7.75423H6.7877C6.7877 7.59484 6.91691 7.46561 7.07629 7.46561V7.14405ZM7.68646 7.75423C7.68646 7.41724 7.41326 7.14405 7.07629 7.14405V7.46561C7.23567 7.46561 7.36489 7.59484 7.36489 7.75423H7.68646ZM7.68646 8.23616V7.75423H7.36489V8.23616H7.68646ZM7.07629 8.84634C7.41326 8.84634 7.68646 8.57315 7.68646 8.23616H7.36489C7.36489 8.39555 7.23567 8.52474 7.07629 8.52474V8.84634ZM6.46613 8.23616C6.46613 8.57315 6.73931 8.84634 7.07629 8.84634V8.52474C6.91691 8.52474 6.7877 8.39555 6.7877 8.23616H6.46613ZM6.46613 7.75423V8.23616H6.7877V7.75423H6.46613ZM5.02785 7.71514C5.02785 7.57734 5.13956 7.46561 5.27736 7.46561V7.14405C4.96196 7.14405 4.70627 7.39974 4.70627 7.71514H5.02785ZM5.02785 8.27525V7.71514H4.70627V8.27525H5.02785ZM5.27736 8.52474C5.13956 8.52474 5.02785 8.41305 5.02785 8.27525H4.70627C4.70627 8.59065 4.96196 8.84634 5.27736 8.84634V8.52474ZM5.52687 8.27525C5.52687 8.41305 5.41516 8.52474 5.27736 8.52474V8.84634C5.59277 8.84634 5.84845 8.59065 5.84845 8.27525H5.52687ZM5.52687 7.71514V8.27525H5.84845V7.71514H5.52687ZM5.27736 7.46561C5.41516 7.46561 5.52687 7.57734 5.52687 7.71514H5.84845C5.84845 7.39974 5.59277 7.14405 5.27736 7.14405V7.46561Z" fill="white"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.12635 14.5901C7.22369 14.3749 7.3069 14.1501 7.37454 13.9167C7.54132 13.3412 7.5998 12.7599 7.56197 12.1948C7.53665 12.5349 7.47589 12.8775 7.37718 13.2181C7.23926 13.694 7.03667 14.1336 6.78174 14.5301C6.89605 14.5547 7.01101 14.5747 7.12635 14.5901Z" fill="white"/>
<path d="M9.71984 7.74796C9.50296 7.74796 9.29496 7.83412 9.14159 7.98745C8.98822 8.14082 8.9021 8.34882 8.9021 8.5657C8.9021 8.78258 8.98822 8.99057 9.14159 9.14394C9.29496 9.29728 9.50296 9.38344 9.71984 9.38344V8.5657V7.74796Z" fill="white"/>
<mask id="mask1_3348_16" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="5" y="2" width="8" height="9">
<path d="M12.3783 2.9985H5.36792V10.3954H12.3783V2.9985Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.75733 3.61999C9.98577 5.80374 9.60089 8.05373 8.56819 10.0898C8.43122 10.0403 8.29704 9.9794 8.16699 9.90688C9.15325 7.86033 9.49538 5.61026 9.22757 3.43526C9.39923 3.51584 9.57682 3.57729 9.75733 3.61999Z" fill="black"/>
</mask>
<g mask="url(#mask1_3348_16)">
<path d="M8.56815 10.0898L8.67689 10.1449L8.62812 10.241L8.52678 10.2044L8.56815 10.0898ZM9.75728 3.61998L9.78536 3.50136L9.86952 3.52127L9.87853 3.6073L9.75728 3.61998ZM8.16695 9.90687L8.1076 10.0133L8.00732 9.9574L8.05715 9.85398L8.16695 9.90687ZM9.22753 3.43524L9.10656 3.45014L9.07958 3.23116L9.27932 3.32491L9.22753 3.43524ZM8.45945 10.0346C9.48122 8.02009 9.86217 5.79374 9.63608 3.63266L9.87853 3.6073C10.1093 5.81372 9.72048 8.0873 8.67689 10.1449L8.45945 10.0346ZM8.22633 9.80041C8.35056 9.86971 8.47876 9.92791 8.60956 9.97514L8.52678 10.2044C8.38363 10.1527 8.24344 10.0891 8.1076 10.0133L8.22633 9.80041ZM9.34849 3.42035C9.61905 5.61792 9.27346 7.89158 8.27675 9.9598L8.05715 9.85398C9.03298 7.82905 9.37158 5.60258 9.10656 3.45014L9.34849 3.42035ZM9.72925 3.7386C9.54064 3.69399 9.3551 3.62977 9.17573 3.54558L9.27932 3.32491C9.44327 3.40188 9.61288 3.46058 9.78536 3.50136L9.72925 3.7386Z" fill="white"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.4118 3.46925L11.2416 3.39926L11.1904 3.57611L11.349 3.62202C11.1904 3.57611 11.1904 3.57615 11.1904 3.5762L11.1903 3.57631L11.1902 3.57658L11.19 3.57741L11.1893 3.58009C11.1886 3.58233 11.1878 3.58548 11.1867 3.58949C11.1845 3.5975 11.1814 3.60897 11.1777 3.62359C11.1703 3.6528 11.1603 3.69464 11.1493 3.74656C11.1275 3.85017 11.102 3.99505 11.0869 4.16045C11.0573 4.4847 11.0653 4.91594 11.2489 5.26595C11.2613 5.28944 11.2643 5.31174 11.2625 5.32629C11.261 5.33849 11.2572 5.34226 11.2536 5.3449C11.0412 5.50026 10.5639 5.78997 9.76653 5.96607C9.76095 6.02373 9.75493 6.08134 9.74848 6.13895C10.601 5.95915 11.1161 5.65017 11.3511 5.4782C11.4413 5.41219 11.4471 5.28823 11.3952 5.18922C11.1546 4.73063 11.2477 4.08248 11.3103 3.78401C11.3314 3.68298 11.349 3.62202 11.349 3.62202C11.3745 3.6325 11.4002 3.63983 11.4259 3.64425C11.9083 3.72709 12.4185 2.78249 12.6294 2.33939C12.6852 2.22212 12.6234 2.08843 12.497 2.05837C11.2595 1.76399 5.46936 0.631807 4.57214 4.96989C4.55907 5.03307 4.57607 5.10106 4.62251 5.14584C4.87914 5.39322 5.86138 6.18665 7.9743 6.27207C8.44664 6.29114 8.86633 6.27046 9.23638 6.22425C9.24295 6.16797 9.24912 6.1117 9.25491 6.05534C8.88438 6.10391 8.46092 6.12641 7.98094 6.10702C5.91152 6.02337 4.96693 5.24843 4.73714 5.02692C4.73701 5.02679 4.73545 5.02525 4.73422 5.0208C4.73292 5.01611 4.73254 5.00987 4.73388 5.00334C4.94996 3.95861 5.4573 3.25195 6.11188 2.77714C6.77039 2.29947 7.58745 2.04983 8.42824 1.94075C10.1122 1.72228 11.8454 2.07312 12.4588 2.21906C12.4722 2.22225 12.4787 2.22927 12.4819 2.2362C12.4853 2.24342 12.4869 2.25443 12.4803 2.2684C12.3706 2.49879 12.183 2.85746 11.9656 3.13057C11.8564 3.26783 11.7479 3.37295 11.6469 3.43216C11.5491 3.48956 11.4752 3.49529 11.4118 3.46925Z" fill="white"/>
<mask id="mask2_3348_16" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="3" y="9" width="7" height="6">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.22654 11.2255C8.62463 10.8066 9.08923 10.4803 9.59075 10.2502C8.97039 10.2715 8.33933 10.0831 7.81189 9.67109C7.64534 9.541 7.49795 9.39549 7.37014 9.23819C7.52815 9.54497 7.64896 9.86752 7.7317 10.1986C6.70151 11.4821 5.1007 12.0466 3.57739 11.8125C3.85909 12.527 4.32941 13.178 4.97849 13.6851C5.8625 14.3756 6.92544 14.6799 7.96392 14.6227C8.32513 13.5174 8.4085 12.351 8.22654 11.2255Z" fill="white"/>
</mask>
<g mask="url(#mask2_3348_16)">
<path d="M9.59085 10.2502L9.58389 10.0472L9.67556 10.4349L9.59085 10.2502ZM8.22663 11.2255L8.02607 11.258L8.00999 11.1585L8.07936 11.0856L8.22663 11.2255ZM7.37024 9.23819L7.18961 9.33119L7.52789 9.11006L7.37024 9.23819ZM7.7318 10.1986L7.92886 10.1494L7.95328 10.2472L7.8902 10.3258L7.7318 10.1986ZM3.57749 11.8125L3.3885 11.887L3.25879 11.5579L3.60835 11.6117L3.57749 11.8125ZM7.96402 14.6227L8.15711 14.6858L8.11397 14.8179L7.97519 14.8255L7.96402 14.6227ZM9.67556 10.4349C9.19708 10.6544 8.7538 10.9657 8.37387 11.3655L8.07936 11.0856C8.49566 10.6475 8.98161 10.3062 9.50614 10.0656L9.67556 10.4349ZM7.93704 9.51099C8.42551 9.89261 9.00942 10.0669 9.58389 10.0472L9.59781 10.4533C8.93151 10.4761 8.25334 10.2737 7.68693 9.83118L7.93704 9.51099ZM7.52789 9.11006C7.64615 9.25565 7.78261 9.39038 7.93704 9.51099L7.68693 9.83118C7.50827 9.69161 7.34994 9.53537 7.21254 9.36627L7.52789 9.11006ZM7.5347 10.2479C7.45573 9.93178 7.34043 9.62393 7.18961 9.33119L7.55082 9.14514C7.71611 9.466 7.84242 9.80326 7.92886 10.1494L7.5347 10.2479ZM3.60835 11.6117C5.06278 11.8352 6.59038 11.2962 7.57335 10.0715L7.8902 10.3258C6.81284 11.6681 5.1388 12.258 3.54663 12.0133L3.60835 11.6117ZM4.85352 13.8452C4.17512 13.3152 3.68312 12.6343 3.3885 11.887L3.76648 11.738C4.03524 12.4197 4.4839 13.0409 5.10364 13.525L4.85352 13.8452ZM7.97519 14.8255C6.8895 14.8853 5.77774 14.5672 4.85352 13.8452L5.10364 13.525C5.94745 14.1842 6.96157 14.4744 7.95285 14.4198L7.97519 14.8255ZM8.42716 11.1931C8.61419 12.3499 8.52858 13.5491 8.15711 14.6858L7.77093 14.5596C8.12191 13.4857 8.20296 12.352 8.02607 11.258L8.42716 11.1931Z" fill="white"/>
</g>
</g>
<defs>
<clipPath id="clip0_3348_16">
<rect width="9.63483" height="14" fill="white" transform="translate(3.19995 1.5)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -41,10 +41,9 @@
"ctrl-f11": "debugger::StepInto",
"shift-f11": "debugger::StepOut",
"f11": "zed::ToggleFullScreen",
"ctrl-alt-z": "edit_prediction::RateCompletions",
"ctrl-alt-z": "edit_prediction::RatePredictions",
"ctrl-alt-shift-i": "edit_prediction::ToggleMenu",
"ctrl-alt-l": "lsp_tool::ToggleMenu",
"ctrl-alt-.": "project_panel::ToggleHideHidden"
"ctrl-alt-l": "lsp_tool::ToggleMenu"
}
},
{
@@ -240,13 +239,11 @@
"ctrl-alt-l": "agent::OpenRulesLibrary",
"ctrl-i": "agent::ToggleProfileSelector",
"ctrl-alt-/": "agent::ToggleModelSelector",
"ctrl-shift-a": "agent::ToggleContextPicker",
"ctrl-shift-j": "agent::ToggleNavigationMenu",
"ctrl-alt-i": "agent::ToggleOptionsMenu",
"ctrl-alt-shift-n": "agent::ToggleNewThreadMenu",
"shift-alt-escape": "agent::ExpandMessageEditor",
"ctrl->": "agent::AddSelectionToThread",
"ctrl-alt-e": "agent::RemoveAllContext",
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-shift-enter": "agent::ContinueThread",
"super-ctrl-b": "agent::ToggleBurnMode",
@@ -323,17 +320,6 @@
"alt-enter": "editor::Newline"
}
},
{
"context": "ContextStrip",
"bindings": {
"up": "agent::FocusUp",
"right": "agent::FocusRight",
"left": "agent::FocusLeft",
"down": "agent::FocusDown",
"backspace": "agent::RemoveFocusedContext",
"enter": "agent::AcceptSuggestedContext"
}
},
{
"context": "AcpThread > ModeSelector",
"bindings": {
@@ -630,8 +616,8 @@
"ctrl-alt-super-p": "settings_profile_selector::Toggle",
"ctrl-t": "project_symbols::Toggle",
"ctrl-p": "file_finder::Toggle",
"ctrl-tab": "tab_switcher::Toggle",
"ctrl-shift-tab": ["tab_switcher::Toggle", { "select_last": true }],
"ctrl-tab": "tab_switcher::Toggle",
"ctrl-e": "file_finder::Toggle",
"f1": "command_palette::Toggle",
"ctrl-shift-p": "command_palette::Toggle",
@@ -825,8 +811,7 @@
"context": "PromptEditor",
"bindings": {
"ctrl-[": "agent::CyclePreviousInlineAssist",
"ctrl-]": "agent::CycleNextInlineAssist",
"ctrl-alt-e": "agent::RemoveAllContext"
"ctrl-]": "agent::CycleNextInlineAssist"
}
},
{
@@ -866,6 +851,7 @@
"context": "ProjectPanel",
"bindings": {
"left": "project_panel::CollapseSelectedEntry",
"ctrl-left": "project_panel::CollapseAllEntries",
"right": "project_panel::ExpandSelectedEntry",
"new": "project_panel::NewFile",
"ctrl-n": "project_panel::NewFile",
@@ -1251,11 +1237,25 @@
"context": "Onboarding",
"use_key_equivalents": true,
"bindings": {
"ctrl-=": ["zed::IncreaseUiFontSize", { "persist": false }],
"ctrl-+": ["zed::IncreaseUiFontSize", { "persist": false }],
"ctrl--": ["zed::DecreaseUiFontSize", { "persist": false }],
"ctrl-0": ["zed::ResetUiFontSize", { "persist": false }],
"ctrl-enter": "onboarding::Finish",
"alt-shift-l": "onboarding::SignIn",
"alt-shift-a": "onboarding::OpenAccount"
}
},
{
"context": "Welcome",
"use_key_equivalents": true,
"bindings": {
"ctrl-=": ["zed::IncreaseUiFontSize", { "persist": false }],
"ctrl-+": ["zed::IncreaseUiFontSize", { "persist": false }],
"ctrl--": ["zed::DecreaseUiFontSize", { "persist": false }],
"ctrl-0": ["zed::ResetUiFontSize", { "persist": false }]
}
},
{
"context": "InvalidBuffer",
"use_key_equivalents": true,
@@ -1279,6 +1279,7 @@
"escape": "workspace::CloseWindow",
"ctrl-m": "settings_editor::Minimize",
"ctrl-f": "search::FocusSearch",
"ctrl-,": "settings_editor::OpenCurrentFile",
"left": "settings_editor::ToggleFocusNav",
"ctrl-shift-e": "settings_editor::ToggleFocusNav",
// todo(settings_ui): cut this down based on the max files and overflow UI
@@ -1321,18 +1322,18 @@
}
},
{
"context": "Zeta2Feedback > Editor",
"context": "EditPredictionContext > Editor",
"bindings": {
"enter": "editor::Newline",
"ctrl-enter up": "dev::Zeta2RatePredictionPositive",
"ctrl-enter down": "dev::Zeta2RatePredictionNegative"
"alt-left": "dev::EditPredictionContextGoBack",
"alt-right": "dev::EditPredictionContextGoForward"
}
},
{
"context": "Zeta2Context > Editor",
"context": "GitBranchSelector || (GitBranchSelector > Picker > Editor)",
"use_key_equivalents": true,
"bindings": {
"alt-left": "dev::Zeta2ContextGoBack",
"alt-right": "dev::Zeta2ContextGoForward"
"ctrl-shift-backspace": "branch_picker::DeleteBranch",
"ctrl-shift-i": "branch_picker::FilterRemotes"
}
}
]

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,8 @@
"ctrl-alt-enter": ["picker::ConfirmInput", { "secondary": true }],
"ctrl-shift-w": "workspace::CloseWindow",
"shift-escape": "workspace::ToggleZoom",
"ctrl-o": "workspace::Open",
"ctrl-o": "workspace::OpenFiles",
"ctrl-k ctrl-o": "workspace::Open",
"ctrl-=": ["zed::IncreaseBufferFontSize", { "persist": false }],
"ctrl-shift-=": ["zed::IncreaseBufferFontSize", { "persist": false }],
"ctrl--": ["zed::DecreaseBufferFontSize", { "persist": false }],
@@ -36,13 +37,12 @@
"shift-f5": "debugger::Stop",
"ctrl-shift-f5": "debugger::RerunSession",
"f6": "debugger::Pause",
"f7": "debugger::StepOver",
"ctrl-f11": "debugger::StepInto",
"f10": "debugger::StepOver",
"shift-f11": "debugger::StepOut",
"f11": "zed::ToggleFullScreen",
"ctrl-shift-i": "edit_prediction::ToggleMenu",
"shift-alt-l": "lsp_tool::ToggleMenu",
"ctrl-alt-.": "project_panel::ToggleHideHidden"
"ctrl-shift-alt-c": "editor::DisplayCursorNames"
}
},
{
@@ -118,7 +118,7 @@
"alt-g m": "git::OpenModifiedFiles",
"menu": "editor::OpenContextMenu",
"shift-f10": "editor::OpenContextMenu",
"ctrl-shift-e": "editor::ToggleEditPrediction",
"ctrl-alt-e": "editor::ToggleEditPrediction",
"f9": "editor::ToggleBreakpoint",
"shift-f9": "editor::EditLogBreakpoint"
}
@@ -216,7 +216,7 @@
"context": "ContextEditor > Editor",
"use_key_equivalents": true,
"bindings": {
"ctrl-enter": "assistant::Assist",
"ctrl-i": "assistant::Assist",
"ctrl-s": "workspace::Save",
"ctrl-shift-,": "assistant::InsertIntoEditor",
"shift-enter": "assistant::Split",
@@ -241,20 +241,18 @@
"shift-alt-p": "agent::ManageProfiles",
"ctrl-i": "agent::ToggleProfileSelector",
"shift-alt-/": "agent::ToggleModelSelector",
"ctrl-shift-a": "agent::ToggleContextPicker",
"ctrl-shift-j": "agent::ToggleNavigationMenu",
"ctrl-alt-i": "agent::ToggleOptionsMenu",
// "ctrl-shift-alt-n": "agent::ToggleNewThreadMenu",
"shift-alt-j": "agent::ToggleNavigationMenu",
"shift-alt-i": "agent::ToggleOptionsMenu",
"ctrl-shift-alt-n": "agent::ToggleNewThreadMenu",
"shift-alt-escape": "agent::ExpandMessageEditor",
"ctrl-shift-.": "agent::AddSelectionToThread",
"shift-alt-e": "agent::RemoveAllContext",
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-shift-enter": "agent::ContinueThread",
"super-ctrl-b": "agent::ToggleBurnMode",
"alt-enter": "agent::ContinueWithBurnMode",
"ctrl-y": "agent::AllowOnce",
"shift-alt-a": "agent::AllowOnce",
"ctrl-alt-y": "agent::AllowAlways",
"ctrl-alt-z": "agent::RejectOnce"
"shift-alt-z": "agent::RejectOnce"
}
},
{
@@ -329,18 +327,6 @@
"alt-enter": "editor::Newline"
}
},
{
"context": "ContextStrip",
"use_key_equivalents": true,
"bindings": {
"up": "agent::FocusUp",
"right": "agent::FocusRight",
"left": "agent::FocusLeft",
"down": "agent::FocusDown",
"backspace": "agent::RemoveFocusedContext",
"enter": "agent::AcceptSuggestedContext"
}
},
{
"context": "AcpThread > ModeSelector",
"bindings": {
@@ -515,10 +501,7 @@
"ctrl-shift-l": "editor::SelectAllMatches", // Select all occurrences of current selection
"ctrl-f2": "editor::SelectAllMatches", // Select all occurrences of current word
"ctrl-d": ["editor::SelectNext", { "replace_newest": false }], // editor.action.addSelectionToNextFindMatch / find_under_expand
"ctrl-shift-down": ["editor::SelectNext", { "replace_newest": false }], // editor.action.addSelectionToNextFindMatch
"ctrl-shift-up": ["editor::SelectPrevious", { "replace_newest": false }], // editor.action.addSelectionToPreviousFindMatch
"ctrl-k ctrl-d": ["editor::SelectNext", { "replace_newest": true }], // editor.action.moveSelectionToNextFindMatch / find_under_expand_skip
"ctrl-k ctrl-shift-d": ["editor::SelectPrevious", { "replace_newest": true }], // editor.action.moveSelectionToPreviousFindMatch
"ctrl-k ctrl-i": "editor::Hover",
"ctrl-k ctrl-b": "editor::BlameHover",
"ctrl-/": ["editor::ToggleComments", { "advance_downwards": false }],
@@ -527,12 +510,8 @@
"f2": "editor::Rename",
"f12": "editor::GoToDefinition",
"alt-f12": "editor::GoToDefinitionSplit",
"ctrl-shift-f10": "editor::GoToDefinitionSplit",
"ctrl-f12": "editor::GoToImplementation",
"shift-f12": "editor::GoToTypeDefinition",
"ctrl-alt-f12": "editor::GoToTypeDefinitionSplit",
"shift-alt-f12": "editor::FindAllReferences",
"ctrl-m": "editor::MoveToEnclosingBracket", // from jetbrains
"ctrl-shift-\\": "editor::MoveToEnclosingBracket",
"ctrl-shift-[": "editor::Fold",
"ctrl-shift-]": "editor::UnfoldLines",
@@ -556,7 +535,6 @@
"ctrl-k r": "editor::RevealInFileManager",
"ctrl-k p": "editor::CopyPath",
"ctrl-\\": "pane::SplitRight",
"ctrl-shift-alt-c": "editor::DisplayCursorNames",
"alt-.": "editor::GoToHunk",
"alt-,": "editor::GoToPreviousHunk"
}
@@ -631,8 +609,8 @@
"ctrl-alt-super-p": "settings_profile_selector::Toggle",
"ctrl-t": "project_symbols::Toggle",
"ctrl-p": "file_finder::Toggle",
"ctrl-tab": "tab_switcher::Toggle",
"ctrl-shift-tab": ["tab_switcher::Toggle", { "select_last": true }],
"ctrl-tab": "tab_switcher::Toggle",
"ctrl-e": "file_finder::Toggle",
"f1": "command_palette::Toggle",
"ctrl-shift-p": "command_palette::Toggle",
@@ -838,8 +816,7 @@
"use_key_equivalents": true,
"bindings": {
"ctrl-[": "agent::CyclePreviousInlineAssist",
"ctrl-]": "agent::CycleNextInlineAssist",
"shift-alt-e": "agent::RemoveAllContext"
"ctrl-]": "agent::CycleNextInlineAssist"
}
},
{
@@ -880,6 +857,7 @@
"use_key_equivalents": true,
"bindings": {
"left": "project_panel::CollapseSelectedEntry",
"ctrl-left": "project_panel::CollapseAllEntries",
"right": "project_panel::ExpandSelectedEntry",
"ctrl-n": "project_panel::NewFile",
"alt-n": "project_panel::NewDirectory",
@@ -1139,7 +1117,7 @@
"shift-insert": "terminal::Paste",
"ctrl-v": "terminal::Paste",
"ctrl-shift-v": "terminal::Paste",
"ctrl-enter": "assistant::InlineAssist",
"ctrl-i": "assistant::InlineAssist",
"alt-b": ["terminal::SendText", "\u001bb"],
"alt-f": ["terminal::SendText", "\u001bf"],
"alt-.": ["terminal::SendText", "\u001b."],
@@ -1151,6 +1129,8 @@
"ctrl-e": ["terminal::SendKeystroke", "ctrl-e"],
"ctrl-o": ["terminal::SendKeystroke", "ctrl-o"],
"ctrl-w": ["terminal::SendKeystroke", "ctrl-w"],
"ctrl-q": ["terminal::SendKeystroke", "ctrl-q"],
"ctrl-r": ["terminal::SendKeystroke", "ctrl-r"],
"ctrl-backspace": ["terminal::SendKeystroke", "ctrl-w"],
"ctrl-shift-a": "editor::SelectAll",
"ctrl-shift-f": "buffer_search::Deploy",
@@ -1285,11 +1265,25 @@
"context": "Onboarding",
"use_key_equivalents": true,
"bindings": {
"ctrl-=": ["zed::IncreaseUiFontSize", { "persist": false }],
"ctrl-+": ["zed::IncreaseUiFontSize", { "persist": false }],
"ctrl--": ["zed::DecreaseUiFontSize", { "persist": false }],
"ctrl-0": ["zed::ResetUiFontSize", { "persist": false }],
"ctrl-enter": "onboarding::Finish",
"alt-shift-l": "onboarding::SignIn",
"shift-alt-a": "onboarding::OpenAccount"
}
},
{
"context": "Welcome",
"use_key_equivalents": true,
"bindings": {
"ctrl-=": ["zed::IncreaseUiFontSize", { "persist": false }],
"ctrl-+": ["zed::IncreaseUiFontSize", { "persist": false }],
"ctrl--": ["zed::DecreaseUiFontSize", { "persist": false }],
"ctrl-0": ["zed::ResetUiFontSize", { "persist": false }]
}
},
{
"context": "GitWorktreeSelector || (GitWorktreeSelector > Picker > Editor)",
"use_key_equivalents": true,
@@ -1306,6 +1300,7 @@
"escape": "workspace::CloseWindow",
"ctrl-m": "settings_editor::Minimize",
"ctrl-f": "search::FocusSearch",
"ctrl-,": "settings_editor::OpenCurrentFile",
"left": "settings_editor::ToggleFocusNav",
"ctrl-shift-e": "settings_editor::ToggleFocusNav",
// todo(settings_ui): cut this down based on the max files and overflow UI
@@ -1349,18 +1344,18 @@
}
},
{
"context": "Zeta2Feedback > Editor",
"context": "EditPredictionContext > Editor",
"bindings": {
"enter": "editor::Newline",
"ctrl-enter up": "dev::Zeta2RatePredictionPositive",
"ctrl-enter down": "dev::Zeta2RatePredictionNegative"
"alt-left": "dev::EditPredictionContextGoBack",
"alt-right": "dev::EditPredictionContextGoForward"
}
},
{
"context": "Zeta2Context > Editor",
"context": "GitBranchSelector || (GitBranchSelector > Picker > Editor)",
"use_key_equivalents": true,
"bindings": {
"alt-left": "dev::Zeta2ContextGoBack",
"alt-right": "dev::Zeta2ContextGoForward"
"ctrl-shift-backspace": "branch_picker::DeleteBranch",
"ctrl-shift-i": "branch_picker::FilterRemotes"
}
}
]

View File

@@ -1,16 +1,18 @@
[
{
"bindings": {
"ctrl-alt-s": "zed::OpenSettingsFile",
"ctrl-alt-s": "zed::OpenSettings",
"ctrl-{": "pane::ActivatePreviousItem",
"ctrl-}": "pane::ActivateNextItem",
"shift-escape": null, // Unmap workspace::zoom
"ctrl-~": "git::Branch",
"ctrl-f2": "debugger::Stop",
"f6": "debugger::Pause",
"f7": "debugger::StepInto",
"f8": "debugger::StepOver",
"shift-f8": "debugger::StepOut",
"f9": "debugger::Continue",
"shift-f9": "debugger::Start",
"alt-shift-f9": "debugger::Start"
}
},
@@ -46,7 +48,7 @@
"alt-f7": "editor::FindAllReferences",
"ctrl-alt-f7": "editor::FindAllReferences",
"ctrl-b": "editor::GoToDefinition", // Conflicts with workspace::ToggleLeftDock
"ctrl-alt-b": "editor::GoToDefinitionSplit", // Conflicts with workspace::ToggleRightDock
"ctrl-alt-b": "editor::GoToImplementation", // Conflicts with workspace::ToggleRightDock
"ctrl-shift-b": "editor::GoToTypeDefinition",
"ctrl-alt-shift-b": "editor::GoToTypeDefinitionSplit",
"f2": "editor::GoToDiagnostic",
@@ -70,7 +72,11 @@
"ctrl-r": ["buffer_search::Deploy", { "replace_enabled": true }],
"ctrl-shift-n": "file_finder::Toggle",
"ctrl-g": "go_to_line::Toggle",
"alt-enter": "editor::ToggleCodeActions"
"alt-enter": "editor::ToggleCodeActions",
"ctrl-space": "editor::ShowCompletions",
"ctrl-q": "editor::Hover",
"ctrl-p": "editor::ShowSignatureHelp",
"ctrl-\\": "assistant::InlineAssist"
}
},
{
@@ -94,9 +100,13 @@
"ctrl-shift-f12": "workspace::ToggleAllDocks",
"ctrl-shift-r": ["pane::DeploySearch", { "replace_enabled": true }],
"alt-shift-f10": "task::Spawn",
"shift-f10": "task::Spawn",
"ctrl-f5": "task::Rerun",
"ctrl-e": "file_finder::Toggle",
// "ctrl-k": "git_panel::ToggleFocus", // bug: This should also focus commit editor
"ctrl-k": "git_panel::ToggleFocus", // bug: This should also focus commit editor
"ctrl-shift-n": "file_finder::Toggle",
"ctrl-n": "project_symbols::Toggle",
"ctrl-alt-n": "file_finder::Toggle",
"ctrl-shift-a": "command_palette::Toggle",
"shift shift": "command_palette::Toggle",
"ctrl-alt-shift-n": "project_symbols::Toggle",
@@ -133,7 +143,9 @@
"context": "Pane",
"bindings": {
"ctrl-alt-left": "pane::GoBack",
"ctrl-alt-right": "pane::GoForward"
"ctrl-alt-right": "pane::GoForward",
"alt-left": "pane::ActivatePreviousItem",
"alt-right": "pane::ActivateNextItem"
}
},
{
@@ -152,8 +164,6 @@
"bindings": {
"ctrl-shift-t": "workspace::NewTerminal",
"alt-f12": "workspace::CloseActiveDock",
"alt-left": "pane::ActivatePreviousItem",
"alt-right": "pane::ActivateNextItem",
"ctrl-up": "terminal::ScrollLineUp",
"ctrl-down": "terminal::ScrollLineDown",
"shift-pageup": "terminal::ScrollPageUp",

View File

@@ -5,12 +5,14 @@
"cmd-}": "pane::ActivateNextItem",
"cmd-0": "git_panel::ToggleFocus", // overrides `cmd-0` zoom reset
"shift-escape": null, // Unmap workspace::zoom
"cmd-~": "git::Branch",
"ctrl-f2": "debugger::Stop",
"f6": "debugger::Pause",
"f7": "debugger::StepInto",
"f8": "debugger::StepOver",
"shift-f8": "debugger::StepOut",
"f9": "debugger::Continue",
"shift-f9": "debugger::Start",
"alt-shift-f9": "debugger::Start"
}
},
@@ -45,7 +47,7 @@
"alt-f7": "editor::FindAllReferences",
"cmd-alt-f7": "editor::FindAllReferences",
"cmd-b": "editor::GoToDefinition", // Conflicts with workspace::ToggleLeftDock
"cmd-alt-b": "editor::GoToDefinitionSplit",
"cmd-alt-b": "editor::GoToImplementation",
"cmd-shift-b": "editor::GoToTypeDefinition",
"cmd-alt-shift-b": "editor::GoToTypeDefinitionSplit",
"f2": "editor::GoToDiagnostic",
@@ -68,7 +70,11 @@
"cmd-r": ["buffer_search::Deploy", { "replace_enabled": true }],
"cmd-shift-o": "file_finder::Toggle",
"cmd-l": "go_to_line::Toggle",
"alt-enter": "editor::ToggleCodeActions"
"alt-enter": "editor::ToggleCodeActions",
"ctrl-space": "editor::ShowCompletions",
"cmd-j": "editor::Hover",
"cmd-p": "editor::ShowSignatureHelp",
"cmd-\\": "assistant::InlineAssist"
}
},
{
@@ -96,9 +102,13 @@
"cmd-shift-f12": "workspace::ToggleAllDocks",
"cmd-shift-r": ["pane::DeploySearch", { "replace_enabled": true }],
"ctrl-alt-r": "task::Spawn",
"shift-f10": "task::Spawn",
"cmd-f5": "task::Rerun",
"cmd-e": "file_finder::Toggle",
// "cmd-k": "git_panel::ToggleFocus", // bug: This should also focus commit editor
"cmd-k": "git_panel::ToggleFocus", // bug: This should also focus commit editor
"cmd-shift-o": "file_finder::Toggle",
"cmd-shift-n": "file_finder::Toggle",
"cmd-n": "project_symbols::Toggle",
"cmd-shift-a": "command_palette::Toggle",
"shift shift": "command_palette::Toggle",
"cmd-alt-o": "project_symbols::Toggle", // JetBrains: Go to Symbol
@@ -135,7 +145,9 @@
"context": "Pane",
"bindings": {
"cmd-alt-left": "pane::GoBack",
"cmd-alt-right": "pane::GoForward"
"cmd-alt-right": "pane::GoForward",
"alt-left": "pane::ActivatePreviousItem",
"alt-right": "pane::ActivateNextItem"
}
},
{

View File

@@ -414,8 +414,9 @@
}
},
{
"context": "vim_mode == helix_normal && !menu",
"context": "VimControl && vim_mode == helix_normal && !menu",
"bindings": {
"escape": "vim::SwitchToHelixNormalMode",
"i": "vim::HelixInsert",
"a": "vim::HelixAppend",
"ctrl-[": "editor::Cancel"
@@ -476,6 +477,9 @@
"alt-p": "editor::SelectPreviousSyntaxNode",
"alt-n": "editor::SelectNextSyntaxNode",
"n": "vim::HelixSelectNext",
"shift-n": "vim::HelixSelectPrevious",
// Goto mode
"g e": "vim::EndOfDocument",
"g h": "vim::StartOfLine",
@@ -853,6 +857,8 @@
"ctrl-w shift-right": "workspace::SwapPaneRight",
"ctrl-w shift-up": "workspace::SwapPaneUp",
"ctrl-w shift-down": "workspace::SwapPaneDown",
"ctrl-w x": "workspace::SwapPaneAdjacent",
"ctrl-w ctrl-x": "workspace::SwapPaneAdjacent",
"ctrl-w shift-h": "workspace::MovePaneLeft",
"ctrl-w shift-l": "workspace::MovePaneRight",
"ctrl-w shift-k": "workspace::MovePaneUp",

View File

@@ -0,0 +1,44 @@
{{#if language_name}}
Here's a file of {{language_name}} that the user is going to ask you to make an edit to.
{{else}}
Here's a file of text that the user is going to ask you to make an edit to.
{{/if}}
The section you'll need to rewrite is marked with <rewrite_this></rewrite_this> tags.
<document>
{{{document_content}}}
</document>
{{#if is_truncated}}
The context around the relevant section has been truncated (possibly in the middle of a line) for brevity.
{{/if}}
{{#if rewrite_section}}
And here's the section to rewrite based on that prompt again for reference:
<rewrite_this>
{{{rewrite_section}}}
</rewrite_this>
{{#if diagnostic_errors}}
Below are the diagnostic errors visible to the user. If the user requests problems to be fixed, use this information, but do not try to fix these errors if the user hasn't asked you to.
{{#each diagnostic_errors}}
<diagnostic_error>
<line_number>{{line_number}}</line_number>
<error_message>{{error_message}}</error_message>
<code_content>{{code_content}}</code_content>
</diagnostic_error>
{{/each}}
{{/if}}
{{/if}}
Only make changes that are necessary to fulfill the prompt, leave everything else as-is. All surrounding {{content_type}} will be preserved.
Start at the indentation level in the original file in the rewritten {{content_type}}.
You must use one of the provided tools to make the rewrite or to provide an explanation as to why the user's request cannot be fulfilled. It is an error if
you simply send back unstructured text. If you need to make a statement or ask a question you must use one of the tools to do so.
It is an error if you try to make a change that cannot be made simply by editing the rewrite_section.

View File

@@ -175,6 +175,16 @@
//
// Default: true
"zoomed_padding": true,
// What draws Zed's window decorations (titlebar):
// 1. Client application (Zed) draws its own window decorations
// "client"
// 2. Display server draws the window decorations. Not supported by GNOME Wayland.
// "server"
//
// This requires restarting Zed for changes to take effect.
//
// Default: "client"
"window_decorations": "client",
// Whether to use the system provided dialogs for Open and Save As.
// When set to false, Zed will use the built-in keyboard-first pickers.
"use_system_path_prompts": true,
@@ -255,6 +265,12 @@
// Whether to display inline and alongside documentation for items in the
// completions menu
"show_completion_documentation": true,
// Whether to colorize brackets in the editor.
// (also known as "rainbow brackets")
//
// The colors that are used for different indentation levels are defined in the theme (theme key: `accents`).
// They can be customized by using theme overrides.
"colorize_brackets": false,
// When to show the scrollbar in the completion menu.
// This setting can take four values:
//
@@ -742,6 +758,16 @@
// "never"
"show": "always"
},
// Sort order for entries in the project panel.
// This setting can take three values:
//
// 1. Show directories first, then files:
// "directories_first"
// 2. Mix directories and files together:
// "mixed"
// 3. Show files first, then directories:
// "files_first"
"sort_mode": "directories_first",
// Whether to enable drag-and-drop operations in the project panel.
"drag_and_drop": true,
// Whether to hide the root entry when only one folder is open in the window.
@@ -1074,13 +1100,22 @@
"preview_tabs": {
// Whether preview tabs should be enabled.
// Preview tabs allow you to open files in preview mode, where they close automatically
// when you switch to another file unless you explicitly pin them.
// when you open another preview tab.
// This is useful for quickly viewing files without cluttering your workspace.
"enabled": true,
// Whether to open tabs in preview mode when opened from the project panel with a single click.
"enable_preview_from_project_panel": true,
// Whether to open tabs in preview mode when selected from the file finder.
"enable_preview_from_file_finder": false,
// Whether a preview tab gets replaced when code navigation is used to navigate away from the tab.
"enable_preview_from_code_navigation": false
// Whether to open tabs in preview mode when opened from a multibuffer.
"enable_preview_from_multibuffer": true,
// Whether to open tabs in preview mode when code navigation is used to open a multibuffer.
"enable_preview_multibuffer_from_code_navigation": false,
// Whether to open tabs in preview mode when code navigation is used to open a single file.
"enable_preview_file_from_code_navigation": true,
// Whether to keep tabs in preview mode when code navigation is used to navigate away from them.
// If `enable_preview_file_from_code_navigation` or `enable_preview_multibuffer_from_code_navigation` is also true, the new tab may replace the existing one.
"enable_keep_preview_on_code_navigation": false
},
// Settings related to the file finder.
"file_finder": {
@@ -1183,6 +1218,13 @@
"tab_size": 4,
// What debuggers are preferred by default for all languages.
"debuggers": [],
// Whether to enable word diff highlighting in the editor.
//
// When enabled, changed words within modified lines are highlighted
// to show exactly what changed.
//
// Default: true
"word_diff_enabled": true,
// Control what info is collected by Zed.
"telemetry": {
// Send debug info like crash reports.
@@ -1306,7 +1348,10 @@
// "hunk_style": "staged_hollow"
// 2. Show unstaged hunks hollow and staged hunks filled:
// "hunk_style": "unstaged_hollow"
"hunk_style": "staged_hollow"
"hunk_style": "staged_hollow",
// Should the name or path be displayed first in the git view.
// "path_style": "file_name_first" or "file_path_first"
"path_style": "file_name_first"
},
// The list of custom Git hosting providers.
"git_hosting_providers": [
@@ -1321,6 +1366,8 @@
// "load_direnv": "direct"
// 2. Load direnv configuration through the shell hook, works for POSIX shells and fish.
// "load_direnv": "shell_hook"
// 3. Don't load direnv configuration at all.
// "load_direnv": "disabled"
"load_direnv": "direct",
"edit_predictions": {
// A list of globs representing files that edit predictions should be disabled for.
@@ -1412,7 +1459,7 @@
"default_height": 320,
// What working directory to use when launching the terminal.
// May take 4 values:
// 1. Use the current file's project directory. Will Fallback to the
// 1. Use the current file's project directory. Fallback to the
// first project directory strategy if unsuccessful
// "working_directory": "current_project_directory"
// 2. Use the first project in this workspace's directory
@@ -1550,6 +1597,8 @@
// Default: 10_000, maximum: 100_000 (all bigger values set will be treated as 100_000), 0 disables the scrolling.
// Existing terminals will not pick up this change until they are recreated.
"max_scroll_history_lines": 10000,
// The multiplier for scrolling speed in the terminal.
"scroll_multiplier": 1.0,
// The minimum APCA perceptual contrast between foreground and background colors.
// APCA (Accessible Perceptual Contrast Algorithm) is more accurate than WCAG 2.x,
// especially for dark mode. Values range from 0 to 106.
@@ -1564,7 +1613,59 @@
//
// Most terminal themes have APCA values of 40-70.
// A value of 45 preserves colorful themes while ensuring legibility.
"minimum_contrast": 45
"minimum_contrast": 45,
// Regexes used to identify paths for hyperlink navigation. Supports optional named capture
// groups `path`, `line`, `column`, and `link`. If none of these are present, the entire match
// is the hyperlink target. If `path` is present, it is the hyperlink target, along with `line`
// and `column` if present. `link` may be used to customize what text in terminal is part of the
// hyperlink. If `link` is not present, the text of the entire match is used. If `line` and
// `column` are not present, the default built-in line and column suffix processing is used
// which parses `line:column` and `(line,column)` variants. The default value handles Python
// diagnostics and common path, line, column syntaxes. This can be extended or replaced to
// handle specific scenarios. For example, to enable support for hyperlinking paths which
// contain spaces in rust output,
//
// [
// "\\s+(-->|:::|at) (?<link>(?<path>.+?))(:$|$)",
// "\\s+(Compiling|Checking|Documenting) [^(]+\\((?<link>(?<path>.+))\\)"
// ],
//
// could be used. Processing stops at the first regex with a match, even if no link is
// produced which is the case when the cursor is not over the hyperlinked text. For best
// performance it is recommended to order regexes from most common to least common. For
// readability and documentation, each regex may be an array of strings which are collected
// into one multi-line regex string for use in terminal path hyperlink detection.
"path_hyperlink_regexes": [
// Python-style diagnostics
"File \"(?<path>[^\"]+)\", line (?<line>[0-9]+)",
// Common path syntax with optional line, column, description, trailing punctuation, or
// surrounding symbols or quotes
[
"(?x)",
"# optionally starts with 0-2 opening prefix symbols",
"[({\\[<]{0,2}",
"# which may be followed by an opening quote",
"(?<quote>[\"'`])?",
"# `path` is the shortest sequence of any non-space character",
"(?<link>(?<path>[^ ]+?",
" # which may end with a line and optionally a column,",
" (?<line_column>:+[0-9]+(:[0-9]+)?|:?\\([0-9]+([,:][0-9]+)?\\))?",
"))",
"# which must be followed by a matching quote",
"(?(<quote>)\\k<quote>)",
"# and optionally a single closing symbol",
"[)}\\]>]?",
"# if line/column matched, may be followed by a description",
"(?(<line_column>):[^ 0-9][^ ]*)?",
"# which may be followed by trailing punctuation",
"[.,:)}\\]>]*",
"# and always includes trailing whitespace or end of line",
"([ ]+|$)"
]
],
// Timeout for hover and Cmd-click path hyperlink discovery in milliseconds. Specifying a
// timeout of `0` will disable path hyperlinking in terminal.
"path_hyperlink_timeout_ms": 1
},
"code_actions_on_format": {},
// Settings related to running tasks.
@@ -1806,7 +1907,7 @@
}
},
"PHP": {
"language_servers": ["phpactor", "!intelephense", "..."],
"language_servers": ["phpactor", "!intelephense", "!phptools", "..."],
"prettier": {
"allowed": true,
"plugins": ["@prettier/plugin-php"],
@@ -2047,6 +2148,18 @@
"dev": {
// "theme": "Andromeda"
},
// Settings overrides to use when using Linux.
"linux": {},
// Settings overrides to use when using macOS.
"macos": {},
// Settings overrides to use when using Windows.
"windows": {
"languages": {
"PHP": {
"language_servers": ["intelephense", "!phpactor", "!phptools", "..."]
}
}
},
// Whether to show full labels in line indicator or short ones
//
// Values:

View File

@@ -45,6 +45,7 @@
"tab.inactive_background": "#1f2127ff",
"tab.active_background": "#0d1016ff",
"search.match_background": "#5ac2fe66",
"search.active_match_background": "#ea570166",
"panel.background": "#1f2127ff",
"panel.focused_border": "#5ac1feff",
"pane.focused_border": null,
@@ -436,6 +437,7 @@
"tab.inactive_background": "#ececedff",
"tab.active_background": "#fcfcfcff",
"search.match_background": "#3b9ee566",
"search.active_match_background": "#f88b3666",
"panel.background": "#ececedff",
"panel.focused_border": "#3b9ee5ff",
"pane.focused_border": null,
@@ -827,6 +829,7 @@
"tab.inactive_background": "#353944ff",
"tab.active_background": "#242835ff",
"search.match_background": "#73cffe66",
"search.active_match_background": "#fd722b66",
"panel.background": "#353944ff",
"panel.focused_border": null,
"pane.focused_border": null,

View File

@@ -46,6 +46,7 @@
"tab.inactive_background": "#3a3735ff",
"tab.active_background": "#282828ff",
"search.match_background": "#83a59866",
"search.active_match_background": "#c09f3f66",
"panel.background": "#3a3735ff",
"panel.focused_border": "#83a598ff",
"pane.focused_border": null,
@@ -452,6 +453,7 @@
"tab.inactive_background": "#393634ff",
"tab.active_background": "#1d2021ff",
"search.match_background": "#83a59866",
"search.active_match_background": "#c9653666",
"panel.background": "#393634ff",
"panel.focused_border": "#83a598ff",
"pane.focused_border": null,
@@ -858,6 +860,7 @@
"tab.inactive_background": "#3b3735ff",
"tab.active_background": "#32302fff",
"search.match_background": "#83a59866",
"search.active_match_background": "#aea85166",
"panel.background": "#3b3735ff",
"panel.focused_border": null,
"pane.focused_border": null,
@@ -1264,6 +1267,7 @@
"tab.inactive_background": "#ecddb4ff",
"tab.active_background": "#fbf1c7ff",
"search.match_background": "#0b667866",
"search.active_match_background": "#ba2d1166",
"panel.background": "#ecddb4ff",
"panel.focused_border": null,
"pane.focused_border": null,
@@ -1670,6 +1674,7 @@
"tab.inactive_background": "#ecddb5ff",
"tab.active_background": "#f9f5d7ff",
"search.match_background": "#0b667866",
"search.active_match_background": "#dc351466",
"panel.background": "#ecddb5ff",
"panel.focused_border": null,
"pane.focused_border": null,
@@ -2076,6 +2081,7 @@
"tab.inactive_background": "#ecdcb3ff",
"tab.active_background": "#f2e5bcff",
"search.match_background": "#0b667866",
"search.active_match_background": "#d7331466",
"panel.background": "#ecdcb3ff",
"panel.focused_border": null,
"pane.focused_border": null,

View File

@@ -45,6 +45,7 @@
"tab.inactive_background": "#2f343eff",
"tab.active_background": "#282c33ff",
"search.match_background": "#74ade866",
"search.active_match_background": "#e8af7466",
"panel.background": "#2f343eff",
"panel.focused_border": null,
"pane.focused_border": null,
@@ -98,6 +99,8 @@
"link_text.hover": "#74ade8ff",
"version_control.added": "#27a657ff",
"version_control.modified": "#d3b020ff",
"version_control.word_added": "#2EA04859",
"version_control.word_deleted": "#78081BCC",
"version_control.deleted": "#e06c76ff",
"version_control.conflict_marker.ours": "#a1c1811a",
"version_control.conflict_marker.theirs": "#74ade81a",
@@ -446,6 +449,7 @@
"tab.inactive_background": "#ebebecff",
"tab.active_background": "#fafafaff",
"search.match_background": "#5c79e266",
"search.active_match_background": "#d0a92366",
"panel.background": "#ebebecff",
"panel.focused_border": null,
"pane.focused_border": null,
@@ -499,6 +503,8 @@
"link_text.hover": "#5c78e2ff",
"version_control.added": "#27a657ff",
"version_control.modified": "#d3b020ff",
"version_control.word_added": "#2EA04859",
"version_control.word_deleted": "#F85149CC",
"version_control.deleted": "#e06c76ff",
"conflict": "#a48819ff",
"conflict.background": "#faf2e6ff",

View File

@@ -201,17 +201,19 @@ impl ToolCall {
};
let mut content = Vec::with_capacity(tool_call.content.len());
for item in tool_call.content {
content.push(ToolCallContent::from_acp(
if let Some(item) = ToolCallContent::from_acp(
item,
language_registry.clone(),
path_style,
terminals,
cx,
)?);
)? {
content.push(item);
}
}
let result = Self {
id: tool_call.id,
id: tool_call.tool_call_id,
label: cx
.new(|cx| Markdown::new(title.into(), Some(language_registry.clone()), None, cx)),
kind: tool_call.kind,
@@ -241,6 +243,7 @@ impl ToolCall {
locations,
raw_input,
raw_output,
..
} = fields;
if let Some(kind) = kind {
@@ -262,21 +265,29 @@ impl ToolCall {
}
if let Some(content) = content {
let new_content_len = content.len();
let mut new_content_len = content.len();
let mut content = content.into_iter();
// Reuse existing content if we can
for (old, new) in self.content.iter_mut().zip(content.by_ref()) {
old.update_from_acp(new, language_registry.clone(), path_style, terminals, cx)?;
let valid_content =
old.update_from_acp(new, language_registry.clone(), path_style, terminals, cx)?;
if !valid_content {
new_content_len -= 1;
}
}
for new in content {
self.content.push(ToolCallContent::from_acp(
if let Some(new) = ToolCallContent::from_acp(
new,
language_registry.clone(),
path_style,
terminals,
cx,
)?)
)? {
self.content.push(new);
} else {
new_content_len -= 1;
}
}
self.content.truncate(new_content_len);
}
@@ -347,13 +358,13 @@ impl ToolCall {
let buffer = buffer.await.log_err()?;
let position = buffer
.update(cx, |buffer, _| {
let snapshot = buffer.snapshot();
if let Some(row) = location.line {
let snapshot = buffer.snapshot();
let column = snapshot.indent_size_for_line(row).len;
let point = snapshot.clip_point(Point::new(row, column), Bias::Left);
snapshot.anchor_before(point)
} else {
Anchor::MIN
Anchor::min_for_buffer(snapshot.remote_id())
}
})
.ok()?;
@@ -425,6 +436,7 @@ impl From<acp::ToolCallStatus> for ToolCallStatus {
acp::ToolCallStatus::InProgress => Self::InProgress,
acp::ToolCallStatus::Completed => Self::Completed,
acp::ToolCallStatus::Failed => Self::Failed,
_ => Self::Pending,
}
}
}
@@ -537,7 +549,7 @@ impl ContentBlock {
..
}) => Self::resource_link_md(&uri, path_style),
acp::ContentBlock::Image(image) => Self::image_md(&image),
acp::ContentBlock::Audio(_) | acp::ContentBlock::Resource(_) => String::new(),
_ => String::new(),
}
}
@@ -591,15 +603,17 @@ impl ToolCallContent {
path_style: PathStyle,
terminals: &HashMap<acp::TerminalId, Entity<Terminal>>,
cx: &mut App,
) -> Result<Self> {
) -> Result<Option<Self>> {
match content {
acp::ToolCallContent::Content { content } => Ok(Self::ContentBlock(ContentBlock::new(
content,
&language_registry,
path_style,
cx,
))),
acp::ToolCallContent::Diff { diff } => Ok(Self::Diff(cx.new(|cx| {
acp::ToolCallContent::Content(acp::Content { content, .. }) => {
Ok(Some(Self::ContentBlock(ContentBlock::new(
content,
&language_registry,
path_style,
cx,
))))
}
acp::ToolCallContent::Diff(diff) => Ok(Some(Self::Diff(cx.new(|cx| {
Diff::finalized(
diff.path.to_string_lossy().into_owned(),
diff.old_text,
@@ -607,12 +621,13 @@ impl ToolCallContent {
language_registry,
cx,
)
}))),
acp::ToolCallContent::Terminal { terminal_id } => terminals
})))),
acp::ToolCallContent::Terminal(acp::Terminal { terminal_id, .. }) => terminals
.get(&terminal_id)
.cloned()
.map(Self::Terminal)
.map(|terminal| Some(Self::Terminal(terminal)))
.ok_or_else(|| anyhow::anyhow!("Terminal with id `{}` not found", terminal_id)),
_ => Ok(None),
}
}
@@ -623,9 +638,9 @@ impl ToolCallContent {
path_style: PathStyle,
terminals: &HashMap<acp::TerminalId, Entity<Terminal>>,
cx: &mut App,
) -> Result<()> {
) -> Result<bool> {
let needs_update = match (&self, &new) {
(Self::Diff(old_diff), acp::ToolCallContent::Diff { diff: new_diff }) => {
(Self::Diff(old_diff), acp::ToolCallContent::Diff(new_diff)) => {
old_diff.read(cx).needs_update(
new_diff.old_text.as_deref().unwrap_or(""),
&new_diff.new_text,
@@ -635,10 +650,14 @@ impl ToolCallContent {
_ => true,
};
if needs_update {
*self = Self::from_acp(new, language_registry, path_style, terminals, cx)?;
if let Some(update) = Self::from_acp(new, language_registry, path_style, terminals, cx)? {
if needs_update {
*self = update;
}
Ok(true)
} else {
Ok(false)
}
Ok(())
}
pub fn to_markdown(&self, cx: &App) -> String {
@@ -660,7 +679,7 @@ pub enum ToolCallUpdate {
impl ToolCallUpdate {
fn id(&self) -> &acp::ToolCallId {
match self {
Self::UpdateFields(update) => &update.id,
Self::UpdateFields(update) => &update.tool_call_id,
Self::UpdateDiff(diff) => &diff.id,
Self::UpdateTerminal(terminal) => &terminal.id,
}
@@ -732,6 +751,7 @@ impl Plan {
acp::PlanEntryStatus::Completed => {
stats.completed += 1;
}
_ => {}
}
}
@@ -1154,6 +1174,7 @@ impl AcpThread {
current_mode_id,
..
}) => cx.emit(AcpThreadEvent::ModeUpdated(current_mode_id)),
_ => {}
}
Ok(())
}
@@ -1287,11 +1308,7 @@ impl AcpThread {
label: cx.new(|cx| Markdown::new("Tool call not found".into(), None, None, cx)),
kind: acp::ToolKind::Fetch,
content: vec![ToolCallContent::ContentBlock(ContentBlock::new(
acp::ContentBlock::Text(acp::TextContent {
text: "Tool call not found".to_string(),
annotations: None,
meta: None,
}),
"Tool call not found".into(),
&languages,
path_style,
cx,
@@ -1315,7 +1332,7 @@ impl AcpThread {
let location_updated = update.fields.locations.is_some();
call.update_fields(update.fields, languages, path_style, &self.terminals, cx)?;
if location_updated {
self.resolve_locations(update.id, cx);
self.resolve_locations(update.tool_call_id, cx);
}
}
ToolCallUpdate::UpdateDiff(update) => {
@@ -1353,7 +1370,7 @@ impl AcpThread {
) -> Result<(), acp::Error> {
let language_registry = self.project.read(cx).languages().clone();
let path_style = self.project.read(cx).path_style(cx);
let id = update.id.clone();
let id = update.tool_call_id.clone();
let agent = self.connection().telemetry_id();
let session = self.session_id();
@@ -1518,16 +1535,16 @@ impl AcpThread {
// some tools would (incorrectly) continue to auto-accept.
if let Some(allow_once_option) = options.iter().find_map(|option| {
if matches!(option.kind, acp::PermissionOptionKind::AllowOnce) {
Some(option.id.clone())
Some(option.option_id.clone())
} else {
None
}
}) {
self.upsert_tool_call_inner(tool_call, ToolCallStatus::Pending, cx)?;
return Ok(async {
acp::RequestPermissionOutcome::Selected {
option_id: allow_once_option,
}
acp::RequestPermissionOutcome::Selected(acp::SelectedPermissionOutcome::new(
allow_once_option,
))
}
.boxed());
}
@@ -1543,7 +1560,9 @@ impl AcpThread {
let fut = async {
match rx.await {
Ok(option) => acp::RequestPermissionOutcome::Selected { option_id: option },
Ok(option) => acp::RequestPermissionOutcome::Selected(
acp::SelectedPermissionOutcome::new(option),
),
Err(oneshot::Canceled) => acp::RequestPermissionOutcome::Cancelled,
}
}
@@ -1570,6 +1589,7 @@ impl AcpThread {
acp::PermissionOptionKind::AllowOnce | acp::PermissionOptionKind::AllowAlways => {
ToolCallStatus::InProgress
}
_ => ToolCallStatus::InProgress,
};
let curr_status = mem::replace(&mut call.status, new_status);
@@ -1648,14 +1668,7 @@ impl AcpThread {
message: &str,
cx: &mut Context<Self>,
) -> BoxFuture<'static, Result<()>> {
self.send(
vec![acp::ContentBlock::Text(acp::TextContent {
text: message.to_string(),
annotations: None,
meta: None,
})],
cx,
)
self.send(vec![message.into()], cx)
}
pub fn send(
@@ -1669,11 +1682,7 @@ impl AcpThread {
self.project.read(cx).path_style(cx),
cx,
);
let request = acp::PromptRequest {
prompt: message.clone(),
session_id: self.session_id.clone(),
meta: None,
};
let request = acp::PromptRequest::new(self.session_id.clone(), message.clone());
let git_store = self.project.read(cx).git_store().clone();
let message_id = if self.connection.truncate(&self.session_id, cx).is_some() {
@@ -1765,7 +1774,7 @@ impl AcpThread {
result,
Ok(Ok(acp::PromptResponse {
stop_reason: acp::StopReason::Cancelled,
meta: None,
..
}))
);
@@ -1781,7 +1790,7 @@ impl AcpThread {
// Handle refusal - distinguish between user prompt and tool call refusals
if let Ok(Ok(acp::PromptResponse {
stop_reason: acp::StopReason::Refusal,
meta: _,
..
})) = result
{
if let Some((user_msg_ix, _)) = this.last_user_message() {
@@ -2017,7 +2026,7 @@ impl AcpThread {
})?;
Ok(project.open_buffer(path, cx))
})
.map_err(|e| acp::Error::internal_error().with_data(e.to_string()))
.map_err(|e| acp::Error::internal_error().data(e.to_string()))
.flatten()?;
let buffer = load.await?;
@@ -2050,7 +2059,7 @@ impl AcpThread {
let start_position = Point::new(line, 0);
if start_position > max_point {
return Err(acp::Error::invalid_params().with_data(format!(
return Err(acp::Error::invalid_params().data(format!(
"Attempting to read beyond the end of the file, line {}:{}",
max_point.row + 1,
max_point.column
@@ -2120,7 +2129,7 @@ impl AcpThread {
position: edits
.last()
.map(|(range, _)| range.end)
.unwrap_or(Anchor::MIN),
.unwrap_or(Anchor::min_for_buffer(buffer.read(cx).remote_id())),
}),
cx,
);
@@ -2202,7 +2211,7 @@ impl AcpThread {
let language_registry = project.read(cx).languages().clone();
let is_windows = project.read(cx).path_style(cx).is_windows();
let terminal_id = acp::TerminalId(Uuid::new_v4().to_string().into());
let terminal_id = acp::TerminalId::new(Uuid::new_v4().to_string());
let terminal_task = cx.spawn({
let terminal_id = terminal_id.clone();
async move |_this, cx| {
@@ -2412,7 +2421,7 @@ mod tests {
.await
.unwrap();
let terminal_id = acp::TerminalId(uuid::Uuid::new_v4().to_string().into());
let terminal_id = acp::TerminalId::new(uuid::Uuid::new_v4().to_string());
// Send Output BEFORE Created - should be buffered by acp_thread
thread.update(cx, |thread, cx| {
@@ -2474,7 +2483,7 @@ mod tests {
.await
.unwrap();
let terminal_id = acp::TerminalId(uuid::Uuid::new_v4().to_string().into());
let terminal_id = acp::TerminalId::new(uuid::Uuid::new_v4().to_string());
// Send Output BEFORE Created
thread.update(cx, |thread, cx| {
@@ -2492,11 +2501,7 @@ mod tests {
thread.on_terminal_provider_event(
TerminalProviderEvent::Exit {
terminal_id: terminal_id.clone(),
status: acp::TerminalExitStatus {
exit_code: Some(0),
signal: None,
meta: None,
},
status: acp::TerminalExitStatus::new().exit_code(0),
},
cx,
);
@@ -2553,15 +2558,7 @@ mod tests {
// Test creating a new user message
thread.update(cx, |thread, cx| {
thread.push_user_content_block(
None,
acp::ContentBlock::Text(acp::TextContent {
annotations: None,
text: "Hello, ".to_string(),
meta: None,
}),
cx,
);
thread.push_user_content_block(None, "Hello, ".into(), cx);
});
thread.update(cx, |thread, cx| {
@@ -2577,15 +2574,7 @@ mod tests {
// Test appending to existing user message
let message_1_id = UserMessageId::new();
thread.update(cx, |thread, cx| {
thread.push_user_content_block(
Some(message_1_id.clone()),
acp::ContentBlock::Text(acp::TextContent {
annotations: None,
text: "world!".to_string(),
meta: None,
}),
cx,
);
thread.push_user_content_block(Some(message_1_id.clone()), "world!".into(), cx);
});
thread.update(cx, |thread, cx| {
@@ -2600,26 +2589,14 @@ mod tests {
// Test creating new user message after assistant message
thread.update(cx, |thread, cx| {
thread.push_assistant_content_block(
acp::ContentBlock::Text(acp::TextContent {
annotations: None,
text: "Assistant response".to_string(),
meta: None,
}),
false,
cx,
);
thread.push_assistant_content_block("Assistant response".into(), false, cx);
});
let message_2_id = UserMessageId::new();
thread.update(cx, |thread, cx| {
thread.push_user_content_block(
Some(message_2_id.clone()),
acp::ContentBlock::Text(acp::TextContent {
annotations: None,
text: "New user message".to_string(),
meta: None,
}),
"New user message".into(),
cx,
);
});
@@ -2647,27 +2624,22 @@ mod tests {
thread.update(&mut cx, |thread, cx| {
thread
.handle_session_update(
acp::SessionUpdate::AgentThoughtChunk(acp::ContentChunk {
content: "Thinking ".into(),
meta: None,
}),
acp::SessionUpdate::AgentThoughtChunk(acp::ContentChunk::new(
"Thinking ".into(),
)),
cx,
)
.unwrap();
thread
.handle_session_update(
acp::SessionUpdate::AgentThoughtChunk(acp::ContentChunk {
content: "hard!".into(),
meta: None,
}),
acp::SessionUpdate::AgentThoughtChunk(acp::ContentChunk::new(
"hard!".into(),
)),
cx,
)
.unwrap();
})?;
Ok(acp::PromptResponse {
stop_reason: acp::StopReason::EndTurn,
meta: None,
})
Ok(acp::PromptResponse::new(acp::StopReason::EndTurn))
}
.boxed_local()
},
@@ -2735,10 +2707,7 @@ mod tests {
.unwrap()
.await
.unwrap();
Ok(acp::PromptResponse {
stop_reason: acp::StopReason::EndTurn,
meta: None,
})
Ok(acp::PromptResponse::new(acp::StopReason::EndTurn))
}
.boxed_local()
},
@@ -2960,7 +2929,7 @@ mod tests {
.await
.unwrap_err();
assert_eq!(err.code, acp::ErrorCode::RESOURCE_NOT_FOUND.code);
assert_eq!(err.code, acp::ErrorCode::ResourceNotFound);
}
#[gpui::test]
@@ -2969,7 +2938,7 @@ mod tests {
let fs = FakeFs::new(cx.executor());
let project = Project::test(fs, [], cx).await;
let id = acp::ToolCallId("test".into());
let id = acp::ToolCallId::new("test");
let connection = Rc::new(FakeAgentConnection::new().on_user_message({
let id = id.clone();
@@ -2979,26 +2948,17 @@ mod tests {
thread
.update(&mut cx, |thread, cx| {
thread.handle_session_update(
acp::SessionUpdate::ToolCall(acp::ToolCall {
id: id.clone(),
title: "Label".into(),
kind: acp::ToolKind::Fetch,
status: acp::ToolCallStatus::InProgress,
content: vec![],
locations: vec![],
raw_input: None,
raw_output: None,
meta: None,
}),
acp::SessionUpdate::ToolCall(
acp::ToolCall::new(id.clone(), "Label")
.kind(acp::ToolKind::Fetch)
.status(acp::ToolCallStatus::InProgress),
),
cx,
)
})
.unwrap()
.unwrap();
Ok(acp::PromptResponse {
stop_reason: acp::StopReason::EndTurn,
meta: None,
})
Ok(acp::PromptResponse::new(acp::StopReason::EndTurn))
}
.boxed_local()
}
@@ -3040,14 +3000,10 @@ mod tests {
thread
.update(cx, |thread, cx| {
thread.handle_session_update(
acp::SessionUpdate::ToolCallUpdate(acp::ToolCallUpdate {
acp::SessionUpdate::ToolCallUpdate(acp::ToolCallUpdate::new(
id,
fields: acp::ToolCallUpdateFields {
status: Some(acp::ToolCallStatus::Completed),
..Default::default()
},
meta: None,
}),
acp::ToolCallUpdateFields::new().status(acp::ToolCallStatus::Completed),
)),
cx,
)
})
@@ -3079,33 +3035,21 @@ mod tests {
thread
.update(&mut cx, |thread, cx| {
thread.handle_session_update(
acp::SessionUpdate::ToolCall(acp::ToolCall {
id: acp::ToolCallId("test".into()),
title: "Label".into(),
kind: acp::ToolKind::Edit,
status: acp::ToolCallStatus::Completed,
content: vec![acp::ToolCallContent::Diff {
diff: acp::Diff {
path: "/test/test.txt".into(),
old_text: None,
new_text: "foo".into(),
meta: None,
},
}],
locations: vec![],
raw_input: None,
raw_output: None,
meta: None,
}),
acp::SessionUpdate::ToolCall(
acp::ToolCall::new("test", "Label")
.kind(acp::ToolKind::Edit)
.status(acp::ToolCallStatus::Completed)
.content(vec![acp::ToolCallContent::Diff(acp::Diff::new(
"/test/test.txt",
"foo",
))]),
),
cx,
)
})
.unwrap()
.unwrap();
Ok(acp::PromptResponse {
stop_reason: acp::StopReason::EndTurn,
meta: None,
})
Ok(acp::PromptResponse::new(acp::StopReason::EndTurn))
}
.boxed_local()
}
@@ -3158,18 +3102,14 @@ mod tests {
thread.update(&mut cx, |thread, cx| {
thread
.handle_session_update(
acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk {
content: content.text.to_uppercase().into(),
meta: None,
}),
acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk::new(
content.text.to_uppercase().into(),
)),
cx,
)
.unwrap();
})?;
Ok(acp::PromptResponse {
stop_reason: acp::StopReason::EndTurn,
meta: None,
})
Ok(acp::PromptResponse::new(acp::StopReason::EndTurn))
}
.boxed_local()
}
@@ -3325,34 +3265,22 @@ mod tests {
thread.update(&mut cx, |thread, cx| {
thread
.handle_session_update(
acp::SessionUpdate::ToolCall(acp::ToolCall {
id: acp::ToolCallId("tool1".into()),
title: "Test Tool".into(),
kind: acp::ToolKind::Fetch,
status: acp::ToolCallStatus::Completed,
content: vec![],
locations: vec![],
raw_input: Some(serde_json::json!({"query": "test"})),
raw_output: Some(
serde_json::json!({"result": "inappropriate content"}),
),
meta: None,
}),
acp::SessionUpdate::ToolCall(
acp::ToolCall::new("tool1", "Test Tool")
.kind(acp::ToolKind::Fetch)
.status(acp::ToolCallStatus::Completed)
.raw_input(serde_json::json!({"query": "test"}))
.raw_output(serde_json::json!({"result": "inappropriate content"})),
),
cx,
)
.unwrap();
})?;
// Now return refusal because of the tool result
Ok(acp::PromptResponse {
stop_reason: acp::StopReason::Refusal,
meta: None,
})
Ok(acp::PromptResponse::new(acp::StopReason::Refusal))
} else {
Ok(acp::PromptResponse {
stop_reason: acp::StopReason::EndTurn,
meta: None,
})
Ok(acp::PromptResponse::new(acp::StopReason::EndTurn))
}
}
.boxed_local()
@@ -3380,16 +3308,7 @@ mod tests {
});
// Send a user message - this will trigger tool call and then refusal
let send_task = thread.update(cx, |thread, cx| {
thread.send(
vec![acp::ContentBlock::Text(acp::TextContent {
text: "Hello".into(),
annotations: None,
meta: None,
})],
cx,
)
});
let send_task = thread.update(cx, |thread, cx| thread.send(vec!["Hello".into()], cx));
cx.background_executor.spawn(send_task).detach();
cx.run_until_parked();
@@ -3435,21 +3354,11 @@ mod tests {
let refuse_next = refuse_next.clone();
move |_request, _thread, _cx| {
if refuse_next.load(SeqCst) {
async move {
Ok(acp::PromptResponse {
stop_reason: acp::StopReason::Refusal,
meta: None,
})
}
.boxed_local()
async move { Ok(acp::PromptResponse::new(acp::StopReason::Refusal)) }
.boxed_local()
} else {
async move {
Ok(acp::PromptResponse {
stop_reason: acp::StopReason::EndTurn,
meta: None,
})
}
.boxed_local()
async move { Ok(acp::PromptResponse::new(acp::StopReason::EndTurn)) }
.boxed_local()
}
}
}));
@@ -3506,10 +3415,7 @@ mod tests {
let refuse_next = refuse_next.clone();
async move {
if refuse_next.load(SeqCst) {
return Ok(acp::PromptResponse {
stop_reason: acp::StopReason::Refusal,
meta: None,
});
return Ok(acp::PromptResponse::new(acp::StopReason::Refusal));
}
let acp::ContentBlock::Text(content) = &request.prompt[0] else {
@@ -3518,18 +3424,14 @@ mod tests {
thread.update(&mut cx, |thread, cx| {
thread
.handle_session_update(
acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk {
content: content.text.to_uppercase().into(),
meta: None,
}),
acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk::new(
content.text.to_uppercase().into(),
)),
cx,
)
.unwrap();
})?;
Ok(acp::PromptResponse {
stop_reason: acp::StopReason::EndTurn,
meta: None,
})
Ok(acp::PromptResponse::new(acp::StopReason::EndTurn))
}
.boxed_local()
}
@@ -3668,13 +3570,12 @@ mod tests {
_cwd: &Path,
cx: &mut App,
) -> Task<gpui::Result<Entity<AcpThread>>> {
let session_id = acp::SessionId(
let session_id = acp::SessionId::new(
rand::rng()
.sample_iter(&distr::Alphanumeric)
.take(7)
.map(char::from)
.collect::<String>()
.into(),
.collect::<String>(),
);
let action_log = cx.new(|_| ActionLog::new(project.clone()));
let thread = cx.new(|cx| {
@@ -3684,12 +3585,12 @@ mod tests {
project,
action_log,
session_id.clone(),
watch::Receiver::constant(acp::PromptCapabilities {
image: true,
audio: true,
embedded_context: true,
meta: None,
}),
watch::Receiver::constant(
acp::PromptCapabilities::new()
.image(true)
.audio(true)
.embedded_context(true),
),
cx,
)
});
@@ -3718,10 +3619,7 @@ mod tests {
let thread = thread.clone();
cx.spawn(async move |cx| handler(params, thread, cx.clone()).await)
} else {
Task::ready(Ok(acp::PromptResponse {
stop_reason: acp::StopReason::EndTurn,
meta: None,
}))
Task::ready(Ok(acp::PromptResponse::new(acp::StopReason::EndTurn)))
}
}
@@ -3776,17 +3674,13 @@ mod tests {
.unwrap();
// Try to update a tool call that doesn't exist
let nonexistent_id = acp::ToolCallId("nonexistent-tool-call".into());
let nonexistent_id = acp::ToolCallId::new("nonexistent-tool-call");
thread.update(cx, |thread, cx| {
let result = thread.handle_session_update(
acp::SessionUpdate::ToolCallUpdate(acp::ToolCallUpdate {
id: nonexistent_id.clone(),
fields: acp::ToolCallUpdateFields {
status: Some(acp::ToolCallStatus::Completed),
..Default::default()
},
meta: None,
}),
acp::SessionUpdate::ToolCallUpdate(acp::ToolCallUpdate::new(
nonexistent_id.clone(),
acp::ToolCallUpdateFields::new().status(acp::ToolCallStatus::Completed),
)),
cx,
);
@@ -3861,7 +3755,7 @@ mod tests {
.unwrap();
// Create 2 terminals BEFORE the checkpoint that have completed running
let terminal_id_1 = acp::TerminalId(uuid::Uuid::new_v4().to_string().into());
let terminal_id_1 = acp::TerminalId::new(uuid::Uuid::new_v4().to_string());
let mock_terminal_1 = cx.new(|cx| {
let builder = ::terminal::TerminalBuilder::new_display_only(
::terminal::terminal_settings::CursorShape::default(),
@@ -3900,17 +3794,13 @@ mod tests {
thread.on_terminal_provider_event(
TerminalProviderEvent::Exit {
terminal_id: terminal_id_1.clone(),
status: acp::TerminalExitStatus {
exit_code: Some(0),
signal: None,
meta: None,
},
status: acp::TerminalExitStatus::new().exit_code(0),
},
cx,
);
});
let terminal_id_2 = acp::TerminalId(uuid::Uuid::new_v4().to_string().into());
let terminal_id_2 = acp::TerminalId::new(uuid::Uuid::new_v4().to_string());
let mock_terminal_2 = cx.new(|cx| {
let builder = ::terminal::TerminalBuilder::new_display_only(
::terminal::terminal_settings::CursorShape::default(),
@@ -3949,11 +3839,7 @@ mod tests {
thread.on_terminal_provider_event(
TerminalProviderEvent::Exit {
terminal_id: terminal_id_2.clone(),
status: acp::TerminalExitStatus {
exit_code: Some(0),
signal: None,
meta: None,
},
status: acp::TerminalExitStatus::new().exit_code(0),
},
cx,
);
@@ -3973,7 +3859,7 @@ mod tests {
// Create a terminal AFTER the checkpoint we'll restore to.
// This simulates the AI agent starting a long-running terminal command.
let terminal_id = acp::TerminalId(uuid::Uuid::new_v4().to_string().into());
let terminal_id = acp::TerminalId::new(uuid::Uuid::new_v4().to_string());
let mock_terminal = cx.new(|cx| {
let builder = ::terminal::TerminalBuilder::new_display_only(
::terminal::terminal_settings::CursorShape::default(),
@@ -4015,21 +3901,15 @@ mod tests {
thread.update(cx, |thread, cx| {
thread
.handle_session_update(
acp::SessionUpdate::ToolCall(acp::ToolCall {
id: acp::ToolCallId("terminal-tool-1".into()),
title: "Running command".into(),
kind: acp::ToolKind::Execute,
status: acp::ToolCallStatus::InProgress,
content: vec![acp::ToolCallContent::Terminal {
terminal_id: terminal_id.clone(),
}],
locations: vec![],
raw_input: Some(
serde_json::json!({"command": "sleep 1000", "cd": "/test"}),
),
raw_output: None,
meta: None,
}),
acp::SessionUpdate::ToolCall(
acp::ToolCall::new("terminal-tool-1", "Running command")
.kind(acp::ToolKind::Execute)
.status(acp::ToolCallStatus::InProgress)
.content(vec![acp::ToolCallContent::Terminal(acp::Terminal::new(
terminal_id.clone(),
))])
.raw_input(serde_json::json!({"command": "sleep 1000", "cd": "/test"})),
),
cx,
)
.unwrap();

View File

@@ -197,6 +197,11 @@ pub trait AgentModelSelector: 'static {
fn watch(&self, _cx: &mut App) -> Option<watch::Receiver<()>> {
None
}
/// Returns whether the model picker should render a footer.
fn should_render_footer(&self) -> bool {
false
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -331,7 +336,7 @@ mod test_support {
_cwd: &Path,
cx: &mut gpui::App,
) -> Task<gpui::Result<Entity<AcpThread>>> {
let session_id = acp::SessionId(self.sessions.lock().len().to_string().into());
let session_id = acp::SessionId::new(self.sessions.lock().len().to_string());
let action_log = cx.new(|_| ActionLog::new(project.clone()));
let thread = cx.new(|cx| {
AcpThread::new(
@@ -340,12 +345,12 @@ mod test_support {
project,
action_log,
session_id.clone(),
watch::Receiver::constant(acp::PromptCapabilities {
image: true,
audio: true,
embedded_context: true,
meta: None,
}),
watch::Receiver::constant(
acp::PromptCapabilities::new()
.image(true)
.audio(true)
.embedded_context(true),
),
cx,
)
});
@@ -384,10 +389,7 @@ mod test_support {
response_tx.replace(tx);
cx.spawn(async move |_| {
let stop_reason = rx.await?;
Ok(acp::PromptResponse {
stop_reason,
meta: None,
})
Ok(acp::PromptResponse::new(stop_reason))
})
} else {
for update in self.next_prompt_updates.lock().drain(..) {
@@ -395,7 +397,7 @@ mod test_support {
let update = update.clone();
let permission_request = if let acp::SessionUpdate::ToolCall(tool_call) =
&update
&& let Some(options) = self.permission_requests.get(&tool_call.id)
&& let Some(options) = self.permission_requests.get(&tool_call.tool_call_id)
{
Some((tool_call.clone(), options.clone()))
} else {
@@ -424,10 +426,7 @@ mod test_support {
cx.spawn(async move |_| {
try_join_all(tasks).await?;
Ok(acp::PromptResponse {
stop_reason: acp::StopReason::EndTurn,
meta: None,
})
Ok(acp::PromptResponse::new(acp::StopReason::EndTurn))
})
}
}

View File

@@ -50,9 +50,14 @@ impl Diff {
let hunk_ranges = {
let buffer = buffer.read(cx);
let diff = diff.read(cx);
diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, buffer, cx)
.map(|diff_hunk| diff_hunk.buffer_range.to_point(buffer))
.collect::<Vec<_>>()
diff.hunks_intersecting_range(
Anchor::min_for_buffer(buffer.remote_id())
..Anchor::max_for_buffer(buffer.remote_id()),
buffer,
cx,
)
.map(|diff_hunk| diff_hunk.buffer_range.to_point(buffer))
.collect::<Vec<_>>()
};
multibuffer.set_excerpts_for_path(
@@ -316,7 +321,12 @@ impl PendingDiff {
let buffer = self.new_buffer.read(cx);
let diff = self.diff.read(cx);
let mut ranges = diff
.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, buffer, cx)
.hunks_intersecting_range(
Anchor::min_for_buffer(buffer.remote_id())
..Anchor::max_for_buffer(buffer.remote_id()),
buffer,
cx,
)
.map(|diff_hunk| diff_hunk.buffer_range.to_point(buffer))
.collect::<Vec<_>>();
ranges.extend(

View File

@@ -108,7 +108,7 @@ impl MentionUri {
if let Some(thread_id) = path.strip_prefix("/agent/thread/") {
let name = single_query_param(&url, "name")?.context("Missing thread name")?;
Ok(Self::Thread {
id: acp::SessionId(thread_id.into()),
id: acp::SessionId::new(thread_id),
name,
})
} else if let Some(path) = path.strip_prefix("/agent/text-thread/") {

View File

@@ -75,11 +75,9 @@ impl Terminal {
let exit_status = exit_status.map(portable_pty::ExitStatus::from);
acp::TerminalExitStatus {
exit_code: exit_status.as_ref().map(|e| e.exit_code()),
signal: exit_status.and_then(|e| e.signal().map(Into::into)),
meta: None,
}
acp::TerminalExitStatus::new()
.exit_code(exit_status.as_ref().map(|e| e.exit_code()))
.signal(exit_status.and_then(|e| e.signal().map(ToOwned::to_owned)))
})
.shared(),
}
@@ -103,25 +101,19 @@ impl Terminal {
if let Some(output) = self.output.as_ref() {
let exit_status = output.exit_status.map(portable_pty::ExitStatus::from);
acp::TerminalOutputResponse {
output: output.content.clone(),
truncated: output.original_content_len > output.content.len(),
exit_status: Some(acp::TerminalExitStatus {
exit_code: exit_status.as_ref().map(|e| e.exit_code()),
signal: exit_status.and_then(|e| e.signal().map(Into::into)),
meta: None,
}),
meta: None,
}
acp::TerminalOutputResponse::new(
output.content.clone(),
output.original_content_len > output.content.len(),
)
.exit_status(
acp::TerminalExitStatus::new()
.exit_code(exit_status.as_ref().map(|e| e.exit_code()))
.signal(exit_status.and_then(|e| e.signal().map(ToOwned::to_owned))),
)
} else {
let (current_content, original_len) = self.truncated_output(cx);
acp::TerminalOutputResponse {
truncated: current_content.len() < original_len,
output: current_content,
exit_status: None,
meta: None,
}
let truncated = current_content.len() < original_len;
acp::TerminalOutputResponse::new(current_content, truncated)
}
}

View File

@@ -528,7 +528,7 @@ impl Render for AcpTools {
.with_sizing_behavior(gpui::ListSizingBehavior::Auto)
.size_full(),
)
.vertical_scrollbar_for(connection.list_state.clone(), window, cx)
.vertical_scrollbar_for(&connection.list_state, window, cx)
.into_any()
}
}

View File

@@ -409,9 +409,11 @@ impl ActionLog {
let new_diff_base = new_diff_base.clone();
async move {
let mut unreviewed_edits = Patch::default();
for hunk in diff_snapshot
.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer_snapshot)
{
for hunk in diff_snapshot.hunks_intersecting_range(
Anchor::min_for_buffer(buffer_snapshot.remote_id())
..Anchor::max_for_buffer(buffer_snapshot.remote_id()),
&buffer_snapshot,
) {
let old_range = new_diff_base
.offset_to_point(hunk.diff_base_byte_range.start)
..new_diff_base.offset_to_point(hunk.diff_base_byte_range.end);
@@ -732,12 +734,10 @@ impl ActionLog {
cx: &mut Context<Self>,
) -> Task<()> {
let futures = self.changed_buffers(cx).into_keys().map(|buffer| {
let reject = self.reject_edits_in_ranges(
buffer,
vec![Anchor::MIN..Anchor::MAX],
telemetry.clone(),
cx,
);
let buffer_ranges = vec![Anchor::min_max_range_for_buffer(
buffer.read(cx).remote_id(),
)];
let reject = self.reject_edits_in_ranges(buffer, buffer_ranges, telemetry.clone(), cx);
async move {
reject.await.log_err();
@@ -2010,7 +2010,8 @@ mod tests {
// User accepts the single hunk
action_log.update(cx, |log, cx| {
log.keep_edits_in_range(buffer.clone(), Anchor::MIN..Anchor::MAX, None, cx)
let buffer_range = Anchor::min_max_range_for_buffer(buffer.read(cx).remote_id());
log.keep_edits_in_range(buffer.clone(), buffer_range, None, cx)
});
cx.run_until_parked();
assert_eq!(unreviewed_hunks(&action_log, cx), vec![]);
@@ -2031,7 +2032,14 @@ mod tests {
// User rejects the hunk
action_log
.update(cx, |log, cx| {
log.reject_edits_in_ranges(buffer.clone(), vec![Anchor::MIN..Anchor::MAX], None, cx)
log.reject_edits_in_ranges(
buffer.clone(),
vec![Anchor::min_max_range_for_buffer(
buffer.read(cx).remote_id(),
)],
None,
cx,
)
})
.await
.unwrap();

View File

@@ -23,6 +23,7 @@ gpui.workspace = true
language.workspace = true
project.workspace = true
proto.workspace = true
semver.workspace = true
smallvec.workspace = true
ui.workspace = true
util.workspace = true

View File

@@ -925,15 +925,15 @@ impl StatusItemView for ActivityIndicator {
#[cfg(test)]
mod tests {
use gpui::SemanticVersion;
use release_channel::AppCommitSha;
use semver::Version;
use super::*;
#[test]
fn test_version_tooltip_message() {
let message = ActivityIndicator::version_tooltip_message(&VersionCheckType::Semantic(
SemanticVersion::new(1, 0, 0),
Version::new(1, 0, 0),
));
assert_eq!(message, "Version: 1.0.0");

View File

@@ -83,6 +83,7 @@ ctor.workspace = true
db = { workspace = true, "features" = ["test-support"] }
editor = { workspace = true, "features" = ["test-support"] }
env_logger.workspace = true
eval_utils.workspace = true
fs = { workspace = true, "features" = ["test-support"] }
git = { workspace = true, "features" = ["test-support"] }
gpui = { workspace = true, "features" = ["test-support"] }

View File

@@ -170,7 +170,7 @@ impl LanguageModels {
}
fn model_id(model: &Arc<dyn LanguageModel>) -> acp::ModelId {
acp::ModelId(format!("{}/{}", model.provider_id().0, model.id().0).into())
acp::ModelId::new(format!("{}/{}", model.provider_id().0, model.id().0))
}
fn authenticate_all_language_model_providers(cx: &mut App) -> Task<()> {
@@ -789,28 +789,12 @@ impl NativeAgentConnection {
}
ThreadEvent::AgentText(text) => {
acp_thread.update(cx, |thread, cx| {
thread.push_assistant_content_block(
acp::ContentBlock::Text(acp::TextContent {
text,
annotations: None,
meta: None,
}),
false,
cx,
)
thread.push_assistant_content_block(text.into(), false, cx)
})?;
}
ThreadEvent::AgentThinking(text) => {
acp_thread.update(cx, |thread, cx| {
thread.push_assistant_content_block(
acp::ContentBlock::Text(acp::TextContent {
text,
annotations: None,
meta: None,
}),
true,
cx,
)
thread.push_assistant_content_block(text.into(), true, cx)
})?;
}
ThreadEvent::ToolCallAuthorization(ToolCallAuthorization {
@@ -824,8 +808,9 @@ impl NativeAgentConnection {
)
})??;
cx.background_spawn(async move {
if let acp::RequestPermissionOutcome::Selected { option_id } =
outcome_task.await
if let acp::RequestPermissionOutcome::Selected(
acp::SelectedPermissionOutcome { option_id, .. },
) = outcome_task.await
{
response
.send(option_id)
@@ -852,10 +837,7 @@ impl NativeAgentConnection {
}
ThreadEvent::Stop(stop_reason) => {
log::debug!("Assistant message complete: {:?}", stop_reason);
return Ok(acp::PromptResponse {
stop_reason,
meta: None,
});
return Ok(acp::PromptResponse::new(stop_reason));
}
}
}
@@ -867,10 +849,7 @@ impl NativeAgentConnection {
}
log::debug!("Response stream completed");
anyhow::Ok(acp::PromptResponse {
stop_reason: acp::StopReason::EndTurn,
meta: None,
})
anyhow::Ok(acp::PromptResponse::new(acp::StopReason::EndTurn))
})
}
}
@@ -961,6 +940,10 @@ impl acp_thread::AgentModelSelector for NativeAgentModelSelector {
fn watch(&self, cx: &mut App) -> Option<watch::Receiver<()>> {
Some(self.connection.0.read(cx).models.watch())
}
fn should_render_footer(&self) -> bool {
true
}
}
impl acp_thread::AgentConnection for NativeAgentConnection {
@@ -1370,7 +1353,7 @@ mod internal_tests {
IndexMap::from_iter([(
AgentModelGroupName("Fake".into()),
vec![AgentModelInfo {
id: acp::ModelId("fake/fake".into()),
id: acp::ModelId::new("fake/fake"),
name: "Fake".into(),
description: None,
icon: Some(ui::IconName::ZedAssistant),
@@ -1431,7 +1414,7 @@ mod internal_tests {
// Select a model
let selector = connection.model_selector(&session_id).unwrap();
let model_id = acp::ModelId("fake/fake".into());
let model_id = acp::ModelId::new("fake/fake");
cx.update(|cx| selector.select_model(model_id.clone(), cx))
.await
.unwrap();
@@ -1517,20 +1500,14 @@ mod internal_tests {
thread.send(
vec![
"What does ".into(),
acp::ContentBlock::ResourceLink(acp::ResourceLink {
name: "b.md".into(),
uri: MentionUri::File {
acp::ContentBlock::ResourceLink(acp::ResourceLink::new(
"b.md",
MentionUri::File {
abs_path: path!("/a/b.md").into(),
}
.to_uri()
.to_string(),
annotations: None,
description: None,
mime_type: None,
size: None,
title: None,
meta: None,
}),
)),
" mean?".into(),
],
cx,

View File

@@ -150,6 +150,7 @@ impl DbThread {
.unwrap_or_default(),
input: tool_use.input,
is_input_complete: true,
thought_signature: None,
},
));
}
@@ -181,6 +182,7 @@ impl DbThread {
crate::Message::Agent(AgentMessage {
content,
tool_results,
reasoning_details: None,
})
}
language_model::Role::System => {
@@ -364,7 +366,7 @@ impl ThreadsDatabase {
for (id, summary, updated_at) in rows {
threads.push(DbThreadMetadata {
id: acp::SessionId(id),
id: acp::SessionId::new(id),
title: summary.into(),
updated_at: DateTime::parse_from_rfc3339(&updated_at)?.with_timezone(&Utc),
});
@@ -422,4 +424,20 @@ impl ThreadsDatabase {
Ok(())
})
}
pub fn delete_threads(&self) -> Task<Result<()>> {
let connection = self.connection.clone();
self.executor.spawn(async move {
let connection = connection.lock();
let mut delete = connection.exec_bound::<()>(indoc! {"
DELETE FROM threads
"})?;
delete(())?;
Ok(())
})
}
}

View File

@@ -172,14 +172,14 @@ impl EditAgent {
project.set_agent_location(
Some(AgentLocation {
buffer: buffer.downgrade(),
position: language::Anchor::MAX,
position: language::Anchor::max_for_buffer(buffer.read(cx).remote_id()),
}),
cx,
)
});
output_events_tx
.unbounded_send(EditAgentOutputEvent::Edited(
language::Anchor::MIN..language::Anchor::MAX,
Anchor::min_max_range_for_buffer(buffer.read(cx).remote_id()),
))
.ok();
})?;
@@ -187,7 +187,7 @@ impl EditAgent {
while let Some(event) = parse_rx.next().await {
match event? {
CreateFileParserEvent::NewTextChunk { chunk } => {
cx.update(|cx| {
let buffer_id = cx.update(|cx| {
buffer.update(cx, |buffer, cx| buffer.append(chunk, cx));
self.action_log
.update(cx, |log, cx| log.buffer_edited(buffer.clone(), cx));
@@ -195,15 +195,18 @@ impl EditAgent {
project.set_agent_location(
Some(AgentLocation {
buffer: buffer.downgrade(),
position: language::Anchor::MAX,
position: language::Anchor::max_for_buffer(
buffer.read(cx).remote_id(),
),
}),
cx,
)
});
buffer.read(cx).remote_id()
})?;
output_events_tx
.unbounded_send(EditAgentOutputEvent::Edited(
language::Anchor::MIN..language::Anchor::MAX,
Anchor::min_max_range_for_buffer(buffer_id),
))
.ok();
}
@@ -703,6 +706,7 @@ impl EditAgent {
role: Role::User,
content: vec![MessageContent::Text(prompt)],
cache: false,
reasoning_details: None,
});
// Include tools in the request so that we can take advantage of
@@ -1199,7 +1203,9 @@ mod tests {
project.read_with(cx, |project, _| project.agent_location()),
Some(AgentLocation {
buffer: buffer.downgrade(),
position: language::Anchor::MAX
position: language::Anchor::max_for_buffer(
cx.update(|cx| buffer.read(cx).remote_id())
),
})
);
@@ -1217,7 +1223,9 @@ mod tests {
project.read_with(cx, |project, _| project.agent_location()),
Some(AgentLocation {
buffer: buffer.downgrade(),
position: language::Anchor::MAX
position: language::Anchor::max_for_buffer(
cx.update(|cx| buffer.read(cx).remote_id())
),
})
);
@@ -1235,7 +1243,9 @@ mod tests {
project.read_with(cx, |project, _| project.agent_location()),
Some(AgentLocation {
buffer: buffer.downgrade(),
position: language::Anchor::MAX
position: language::Anchor::max_for_buffer(
cx.update(|cx| buffer.read(cx).remote_id())
),
})
);
@@ -1253,7 +1263,9 @@ mod tests {
project.read_with(cx, |project, _| project.agent_location()),
Some(AgentLocation {
buffer: buffer.downgrade(),
position: language::Anchor::MAX
position: language::Anchor::max_for_buffer(
cx.update(|cx| buffer.read(cx).remote_id())
),
})
);
@@ -1268,7 +1280,9 @@ mod tests {
project.read_with(cx, |project, _| project.agent_location()),
Some(AgentLocation {
buffer: buffer.downgrade(),
position: language::Anchor::MAX
position: language::Anchor::max_for_buffer(
cx.update(|cx| buffer.read(cx).remote_id())
),
})
);
}

View File

@@ -15,12 +15,14 @@ const SEPARATOR_MARKER: &str = "=======";
const REPLACE_MARKER: &str = ">>>>>>> REPLACE";
const SONNET_PARAMETER_INVOKE_1: &str = "</parameter>\n</invoke>";
const SONNET_PARAMETER_INVOKE_2: &str = "</parameter></invoke>";
const END_TAGS: [&str; 5] = [
const SONNET_PARAMETER_INVOKE_3: &str = "</parameter>";
const END_TAGS: [&str; 6] = [
OLD_TEXT_END_TAG,
NEW_TEXT_END_TAG,
EDITS_END_TAG,
SONNET_PARAMETER_INVOKE_1, // Remove this after switching to streaming tool call
SONNET_PARAMETER_INVOKE_1, // Remove these after switching to streaming tool call
SONNET_PARAMETER_INVOKE_2,
SONNET_PARAMETER_INVOKE_3,
];
#[derive(Debug)]
@@ -567,21 +569,29 @@ mod tests {
parse_random_chunks(
indoc! {"
<old_text>some text</old_text><new_text>updated text</parameter></invoke>
<old_text>more text</old_text><new_text>upd</parameter></new_text>
"},
&mut parser,
&mut rng
),
vec![Edit {
old_text: "some text".to_string(),
new_text: "updated text".to_string(),
line_hint: None,
},]
vec![
Edit {
old_text: "some text".to_string(),
new_text: "updated text".to_string(),
line_hint: None,
},
Edit {
old_text: "more text".to_string(),
new_text: "upd".to_string(),
line_hint: None,
},
]
);
assert_eq!(
parser.finish(),
EditParserMetrics {
tags: 2,
mismatched_tags: 1
tags: 4,
mismatched_tags: 2
}
);
}

View File

@@ -4,7 +4,7 @@ use crate::{
};
use Role::*;
use client::{Client, UserStore};
use collections::HashMap;
use eval_utils::{EvalOutput, EvalOutputProcessor, OutcomeKind};
use fs::FakeFs;
use futures::{FutureExt, future::LocalBoxFuture};
use gpui::{AppContext, TestAppContext, Timer};
@@ -20,16 +20,62 @@ use rand::prelude::*;
use reqwest_client::ReqwestClient;
use serde_json::json;
use std::{
cmp::Reverse,
fmt::{self, Display},
io::Write as _,
path::Path,
str::FromStr,
sync::mpsc,
time::Duration,
};
use util::path;
#[derive(Default, Clone, Debug)]
struct EditAgentOutputProcessor {
mismatched_tag_threshold: f32,
cumulative_tags: usize,
cumulative_mismatched_tags: usize,
eval_outputs: Vec<EvalOutput<EditEvalMetadata>>,
}
fn mismatched_tag_threshold(mismatched_tag_threshold: f32) -> EditAgentOutputProcessor {
EditAgentOutputProcessor {
mismatched_tag_threshold,
cumulative_tags: 0,
cumulative_mismatched_tags: 0,
eval_outputs: Vec::new(),
}
}
#[derive(Clone, Debug)]
struct EditEvalMetadata {
tags: usize,
mismatched_tags: usize,
}
impl EvalOutputProcessor for EditAgentOutputProcessor {
type Metadata = EditEvalMetadata;
fn process(&mut self, output: &EvalOutput<Self::Metadata>) {
if matches!(output.outcome, OutcomeKind::Passed | OutcomeKind::Failed) {
self.cumulative_mismatched_tags += output.metadata.mismatched_tags;
self.cumulative_tags += output.metadata.tags;
self.eval_outputs.push(output.clone());
}
}
fn assert(&mut self) {
let mismatched_tag_ratio =
self.cumulative_mismatched_tags as f32 / self.cumulative_tags as f32;
if mismatched_tag_ratio > self.mismatched_tag_threshold {
for eval_output in &self.eval_outputs {
println!("{}", eval_output.data);
}
panic!(
"Too many mismatched tags: {:?}",
self.cumulative_mismatched_tags
);
}
}
}
#[test]
#[cfg_attr(not(feature = "unit-eval"), ignore)]
fn eval_extract_handle_command_output() {
@@ -55,22 +101,19 @@ fn eval_extract_handle_command_output() {
include_str!("evals/fixtures/extract_handle_command_output/possible-07.diff"),
];
let edit_description = "Extract `handle_command_output` method from `run_git_blame`.";
eval(
100,
0.95,
0.05,
EvalInput::from_conversation(
eval_utils::eval(100, 0.95, mismatched_tag_threshold(0.05), move || {
run_eval(EvalInput::from_conversation(
vec![
message(
User,
[text(formatdoc! {"
Read the `{input_file_path}` file and extract a method in
the final stanza of `run_git_blame` to deal with command failures,
call it `handle_command_output` and take the std::process::Output as the only parameter.
Do not document the method and do not add any comments.
Read the `{input_file_path}` file and extract a method in
the final stanza of `run_git_blame` to deal with command failures,
call it `handle_command_output` and take the std::process::Output as the only parameter.
Do not document the method and do not add any comments.
Add it right next to `run_git_blame` and copy it verbatim from `run_git_blame`.
"})],
Add it right next to `run_git_blame` and copy it verbatim from `run_git_blame`.
"})],
),
message(
Assistant,
@@ -102,9 +145,9 @@ fn eval_extract_handle_command_output() {
),
],
Some(input_file_content.into()),
EvalAssertion::assert_diff_any(possible_diffs),
),
);
EvalAssertion::assert_diff_any(possible_diffs.clone()),
))
});
}
#[test]
@@ -122,18 +165,16 @@ fn eval_delete_run_git_blame() {
let input_file_content = include_str!("evals/fixtures/delete_run_git_blame/before.rs");
let output_file_content = include_str!("evals/fixtures/delete_run_git_blame/after.rs");
let edit_description = "Delete the `run_git_blame` function.";
eval(
100,
0.95,
0.05,
EvalInput::from_conversation(
eval_utils::eval(100, 0.95, mismatched_tag_threshold(0.05), move || {
run_eval(EvalInput::from_conversation(
vec![
message(
User,
[text(formatdoc! {"
Read the `{input_file_path}` file and delete `run_git_blame`. Just that
one function, not its usages.
"})],
Read the `{input_file_path}` file and delete `run_git_blame`. Just that
one function, not its usages.
"})],
),
message(
Assistant,
@@ -166,8 +207,8 @@ fn eval_delete_run_git_blame() {
],
Some(input_file_content.into()),
EvalAssertion::assert_eq(output_file_content),
),
);
))
});
}
#[test]
@@ -185,18 +226,16 @@ fn eval_translate_doc_comments() {
let input_file_path = "root/canvas.rs";
let input_file_content = include_str!("evals/fixtures/translate_doc_comments/before.rs");
let edit_description = "Translate all doc comments to Italian";
eval(
200,
1.,
0.05,
EvalInput::from_conversation(
eval_utils::eval(200, 1., mismatched_tag_threshold(0.05), move || {
run_eval(EvalInput::from_conversation(
vec![
message(
User,
[text(formatdoc! {"
Read the {input_file_path} file and edit it (without overwriting it),
translating all the doc comments to italian.
"})],
Read the {input_file_path} file and edit it (without overwriting it),
translating all the doc comments to italian.
"})],
),
message(
Assistant,
@@ -229,8 +268,8 @@ fn eval_translate_doc_comments() {
],
Some(input_file_content.into()),
EvalAssertion::judge_diff("Doc comments were translated to Italian"),
),
);
))
});
}
#[test]
@@ -249,33 +288,31 @@ fn eval_use_wasi_sdk_in_compile_parser_to_wasm() {
let input_file_content =
include_str!("evals/fixtures/use_wasi_sdk_in_compile_parser_to_wasm/before.rs");
let edit_description = "Update compile_parser_to_wasm to use wasi-sdk instead of emscripten";
eval(
100,
0.95,
0.05,
EvalInput::from_conversation(
eval_utils::eval(100, 0.95, mismatched_tag_threshold(0.05), move || {
run_eval(EvalInput::from_conversation(
vec![
message(
User,
[text(formatdoc! {"
Read the `{input_file_path}` file and change `compile_parser_to_wasm` to use `wasi-sdk` instead of emscripten.
Use `ureq` to download the SDK for the current platform and architecture.
Extract the archive into a sibling of `lib` inside the `tree-sitter` directory in the cache_dir.
Compile the parser to wasm using the `bin/clang` executable (or `bin/clang.exe` on windows)
that's inside of the archive.
Don't re-download the SDK if that executable already exists.
Read the `{input_file_path}` file and change `compile_parser_to_wasm` to use `wasi-sdk` instead of emscripten.
Use `ureq` to download the SDK for the current platform and architecture.
Extract the archive into a sibling of `lib` inside the `tree-sitter` directory in the cache_dir.
Compile the parser to wasm using the `bin/clang` executable (or `bin/clang.exe` on windows)
that's inside of the archive.
Don't re-download the SDK if that executable already exists.
Use these clang flags: -fPIC -shared -Os -Wl,--export=tree_sitter_{{language_name}}
Use these clang flags: -fPIC -shared -Os -Wl,--export=tree_sitter_{{language_name}}
Here are the available wasi-sdk assets:
- wasi-sdk-25.0-x86_64-macos.tar.gz
- wasi-sdk-25.0-arm64-macos.tar.gz
- wasi-sdk-25.0-x86_64-linux.tar.gz
- wasi-sdk-25.0-arm64-linux.tar.gz
- wasi-sdk-25.0-x86_64-linux.tar.gz
- wasi-sdk-25.0-arm64-linux.tar.gz
- wasi-sdk-25.0-x86_64-windows.tar.gz
"})],
Here are the available wasi-sdk assets:
- wasi-sdk-25.0-x86_64-macos.tar.gz
- wasi-sdk-25.0-arm64-macos.tar.gz
- wasi-sdk-25.0-x86_64-linux.tar.gz
- wasi-sdk-25.0-arm64-linux.tar.gz
- wasi-sdk-25.0-x86_64-linux.tar.gz
- wasi-sdk-25.0-arm64-linux.tar.gz
- wasi-sdk-25.0-x86_64-windows.tar.gz
"})],
),
message(
Assistant,
@@ -352,11 +389,11 @@ fn eval_use_wasi_sdk_in_compile_parser_to_wasm() {
],
Some(input_file_content.into()),
EvalAssertion::judge_diff(indoc! {"
- The compile_parser_to_wasm method has been changed to use wasi-sdk
- ureq is used to download the SDK for current platform and architecture
"}),
),
);
- The compile_parser_to_wasm method has been changed to use wasi-sdk
- ureq is used to download the SDK for current platform and architecture
"}),
))
});
}
#[test]
@@ -380,11 +417,8 @@ fn eval_disable_cursor_blinking() {
include_str!("evals/fixtures/disable_cursor_blinking/possible-03.diff"),
include_str!("evals/fixtures/disable_cursor_blinking/possible-04.diff"),
];
eval(
100,
0.51,
0.05,
EvalInput::from_conversation(
eval_utils::eval(100, 0.51, mismatched_tag_threshold(0.05), move || {
run_eval(EvalInput::from_conversation(
vec![
message(User, [text("Let's research how to cursor blinking works.")]),
message(
@@ -421,10 +455,10 @@ fn eval_disable_cursor_blinking() {
message(
User,
[text(indoc! {"
Comment out the lines that interact with the BlinkManager.
Keep the outer `update` blocks, but comments everything that's inside (including if statements).
Don't add additional comments.
"})],
Comment out the lines that interact with the BlinkManager.
Keep the outer `update` blocks, but comments everything that's inside (including if statements).
Don't add additional comments.
"})],
),
message(
Assistant,
@@ -440,9 +474,9 @@ fn eval_disable_cursor_blinking() {
),
],
Some(input_file_content.into()),
EvalAssertion::assert_diff_any(possible_diffs),
),
);
EvalAssertion::assert_diff_any(possible_diffs.clone()),
))
});
}
#[test]
@@ -467,20 +501,16 @@ fn eval_from_pixels_constructor() {
let input_file_path = "root/canvas.rs";
let input_file_content = include_str!("evals/fixtures/from_pixels_constructor/before.rs");
let edit_description = "Implement from_pixels constructor and add tests.";
eval(
100,
0.95,
// For whatever reason, this eval produces more mismatched tags.
// Increasing for now, let's see if we can bring this down.
0.25,
EvalInput::from_conversation(
eval_utils::eval(100, 0.95, mismatched_tag_threshold(0.25), move || {
run_eval(EvalInput::from_conversation(
vec![
message(
User,
[text(indoc! {"
Introduce a new `from_pixels` constructor in Canvas and
also add tests for it in the same file.
"})],
Introduce a new `from_pixels` constructor in Canvas and
also add tests for it in the same file.
"})],
),
message(
Assistant,
@@ -545,92 +575,92 @@ fn eval_from_pixels_constructor() {
"tool_4",
"grep",
indoc! {"
Found 6 matches:
Found 6 matches:
## Matches in font-kit/src/loaders/core_text.rs
## Matches in font-kit/src/loaders/core_text.rs
### mod test L926-936
```
mod test {
use super::Font;
use crate::properties::{Stretch, Weight};
### mod test L926-936
```
mod test {
use super::Font;
use crate::properties::{Stretch, Weight};
#[cfg(feature = \"source\")]
use crate::source::SystemSource;
#[cfg(feature = \"source\")]
use crate::source::SystemSource;
static TEST_FONT_POSTSCRIPT_NAME: &'static str = \"ArialMT\";
static TEST_FONT_POSTSCRIPT_NAME: &'static str = \"ArialMT\";
#[cfg(feature = \"source\")]
#[test]
```
#[cfg(feature = \"source\")]
#[test]
```
55 lines remaining in ancestor node. Read the file to see all.
55 lines remaining in ancestor node. Read the file to see all.
### mod test L947-951
```
}
### mod test L947-951
```
}
#[test]
fn test_core_text_to_css_font_weight() {
// Exact matches
```
#[test]
fn test_core_text_to_css_font_weight() {
// Exact matches
```
### mod test L959-963
```
}
### mod test L959-963
```
}
#[test]
fn test_core_text_to_css_font_stretch() {
// Exact matches
```
#[test]
fn test_core_text_to_css_font_stretch() {
// Exact matches
```
## Matches in font-kit/src/loaders/freetype.rs
## Matches in font-kit/src/loaders/freetype.rs
### mod test L1238-1248
```
mod test {
use crate::loaders::freetype::Font;
### mod test L1238-1248
```
mod test {
use crate::loaders::freetype::Font;
static PCF_FONT_PATH: &str = \"resources/tests/times-roman-pcf/timR12.pcf\";
static PCF_FONT_POSTSCRIPT_NAME: &str = \"Times-Roman\";
static PCF_FONT_PATH: &str = \"resources/tests/times-roman-pcf/timR12.pcf\";
static PCF_FONT_POSTSCRIPT_NAME: &str = \"Times-Roman\";
#[test]
fn get_pcf_postscript_name() {
let font = Font::from_path(PCF_FONT_PATH, 0).unwrap();
assert_eq!(font.postscript_name().unwrap(), PCF_FONT_POSTSCRIPT_NAME);
}
```
#[test]
fn get_pcf_postscript_name() {
let font = Font::from_path(PCF_FONT_PATH, 0).unwrap();
assert_eq!(font.postscript_name().unwrap(), PCF_FONT_POSTSCRIPT_NAME);
}
```
1 lines remaining in ancestor node. Read the file to see all.
1 lines remaining in ancestor node. Read the file to see all.
## Matches in font-kit/src/sources/core_text.rs
## Matches in font-kit/src/sources/core_text.rs
### mod test L265-275
```
mod test {
use crate::properties::{Stretch, Weight};
### mod test L265-275
```
mod test {
use crate::properties::{Stretch, Weight};
#[test]
fn test_css_to_core_text_font_weight() {
// Exact matches
assert_eq!(super::css_to_core_text_font_weight(Weight(100.0)), -0.7);
assert_eq!(super::css_to_core_text_font_weight(Weight(400.0)), 0.0);
assert_eq!(super::css_to_core_text_font_weight(Weight(700.0)), 0.4);
assert_eq!(super::css_to_core_text_font_weight(Weight(900.0)), 0.8);
#[test]
fn test_css_to_core_text_font_weight() {
// Exact matches
assert_eq!(super::css_to_core_text_font_weight(Weight(100.0)), -0.7);
assert_eq!(super::css_to_core_text_font_weight(Weight(400.0)), 0.0);
assert_eq!(super::css_to_core_text_font_weight(Weight(700.0)), 0.4);
assert_eq!(super::css_to_core_text_font_weight(Weight(900.0)), 0.8);
```
```
27 lines remaining in ancestor node. Read the file to see all.
27 lines remaining in ancestor node. Read the file to see all.
### mod test L278-282
```
}
### mod test L278-282
```
}
#[test]
fn test_css_to_core_text_font_stretch() {
// Exact matches
```
"},
#[test]
fn test_css_to_core_text_font_stretch() {
// Exact matches
```
"},
)],
),
message(
@@ -648,11 +678,11 @@ fn eval_from_pixels_constructor() {
],
Some(input_file_content.into()),
EvalAssertion::judge_diff(indoc! {"
- The diff contains a new `from_pixels` constructor
- The diff contains new tests for the `from_pixels` constructor
"}),
),
);
- The diff contains a new `from_pixels` constructor
- The diff contains new tests for the `from_pixels` constructor
"}),
))
});
}
#[test]
@@ -670,11 +700,9 @@ fn eval_zode() {
let input_file_path = "root/zode.py";
let input_content = None;
let edit_description = "Create the main Zode CLI script";
eval(
50,
1.,
0.05,
EvalInput::from_conversation(
eval_utils::eval(50, 1., mismatched_tag_threshold(0.05), move || {
run_eval(EvalInput::from_conversation(
vec![
message(User, [text(include_str!("evals/fixtures/zode/prompt.md"))]),
message(
@@ -733,7 +761,7 @@ fn eval_zode() {
],
),
],
input_content,
input_content.clone(),
EvalAssertion::new(async move |sample, _, _cx| {
let invalid_starts = [' ', '`', '\n'];
let mut message = String::new();
@@ -758,8 +786,8 @@ fn eval_zode() {
})
}
}),
),
);
))
});
}
#[test]
@@ -777,19 +805,17 @@ fn eval_add_overwrite_test() {
let input_file_path = "root/action_log.rs";
let input_file_content = include_str!("evals/fixtures/add_overwrite_test/before.rs");
let edit_description = "Add a new test for overwriting a file in action_log.rs";
eval(
200,
0.5, // TODO: make this eval better
0.05,
EvalInput::from_conversation(
eval_utils::eval(200, 0.5, mismatched_tag_threshold(0.05), move || {
run_eval(EvalInput::from_conversation(
vec![
message(
User,
[text(indoc! {"
Introduce a new test in `action_log.rs` to test overwriting a file.
That is, a file already exists, but we call `buffer_created` as if the file were new.
Take inspiration from all the other tests in the file.
"})],
Introduce a new test in `action_log.rs` to test overwriting a file.
That is, a file already exists, but we call `buffer_created` as if the file were new.
Take inspiration from all the other tests in the file.
"})],
),
message(
Assistant,
@@ -809,81 +835,81 @@ fn eval_add_overwrite_test() {
"tool_1",
"read_file",
indoc! {"
pub struct ActionLog [L13-20]
tracked_buffers [L15]
edited_since_project_diagnostics_check [L17]
project [L19]
impl ActionLog [L22-498]
pub fn new [L24-30]
pub fn project [L32-34]
pub fn checked_project_diagnostics [L37-39]
pub fn has_edited_files_since_project_diagnostics_check [L42-44]
fn track_buffer_internal [L46-101]
fn handle_buffer_event [L103-116]
fn handle_buffer_edited [L118-123]
fn handle_buffer_file_changed [L125-158]
async fn maintain_diff [L160-264]
pub fn buffer_read [L267-269]
pub fn buffer_created [L272-276]
pub fn buffer_edited [L279-287]
pub fn will_delete_buffer [L289-304]
pub fn keep_edits_in_range [L306-364]
pub fn reject_edits_in_ranges [L366-459]
pub fn keep_all_edits [L461-473]
pub fn changed_buffers [L476-482]
pub fn stale_buffers [L485-497]
fn apply_non_conflicting_edits [L500-561]
fn diff_snapshots [L563-585]
fn point_to_row_edit [L587-614]
enum ChangeAuthor [L617-620]
User [L618]
Agent [L619]
enum TrackedBufferStatus [L623-627]
Created [L624]
Modified [L625]
Deleted [L626]
struct TrackedBuffer [L629-641]
buffer [L630]
base_text [L631]
unreviewed_changes [L632]
status [L633]
version [L634]
diff [L635]
snapshot [L636]
diff_update [L637]
_open_lsp_handle [L638]
_maintain_diff [L639]
_subscription [L640]
impl TrackedBuffer [L643-657]
fn has_changes [L644-650]
fn schedule_diff_update [L652-656]
pub struct ChangedBuffer [L659-661]
pub diff [L660]
mod tests [L664-1574]
fn init_logger [L678-682]
fn init_test [L684-691]
async fn test_keep_edits [L694-769]
async fn test_deletions [L772-854]
async fn test_overlapping_user_edits [L857-951]
async fn test_creating_files [L954-1010]
async fn test_deleting_files [L1013-1120]
async fn test_reject_edits [L1123-1255]
async fn test_reject_multiple_edits [L1258-1331]
async fn test_reject_deleted_file [L1334-1388]
async fn test_reject_created_file [L1391-1443]
async fn test_random_diffs [L1446-1535]
fn quiesce [L1510-1534]
struct HunkStatus [L1538-1542]
range [L1539]
diff_status [L1540]
old_text [L1541]
fn unreviewed_hunks [L1544-1573]
pub struct ActionLog [L13-20]
tracked_buffers [L15]
edited_since_project_diagnostics_check [L17]
project [L19]
impl ActionLog [L22-498]
pub fn new [L24-30]
pub fn project [L32-34]
pub fn checked_project_diagnostics [L37-39]
pub fn has_edited_files_since_project_diagnostics_check [L42-44]
fn track_buffer_internal [L46-101]
fn handle_buffer_event [L103-116]
fn handle_buffer_edited [L118-123]
fn handle_buffer_file_changed [L125-158]
async fn maintain_diff [L160-264]
pub fn buffer_read [L267-269]
pub fn buffer_created [L272-276]
pub fn buffer_edited [L279-287]
pub fn will_delete_buffer [L289-304]
pub fn keep_edits_in_range [L306-364]
pub fn reject_edits_in_ranges [L366-459]
pub fn keep_all_edits [L461-473]
pub fn changed_buffers [L476-482]
pub fn stale_buffers [L485-497]
fn apply_non_conflicting_edits [L500-561]
fn diff_snapshots [L563-585]
fn point_to_row_edit [L587-614]
enum ChangeAuthor [L617-620]
User [L618]
Agent [L619]
enum TrackedBufferStatus [L623-627]
Created [L624]
Modified [L625]
Deleted [L626]
struct TrackedBuffer [L629-641]
buffer [L630]
base_text [L631]
unreviewed_changes [L632]
status [L633]
version [L634]
diff [L635]
snapshot [L636]
diff_update [L637]
_open_lsp_handle [L638]
_maintain_diff [L639]
_subscription [L640]
impl TrackedBuffer [L643-657]
fn has_changes [L644-650]
fn schedule_diff_update [L652-656]
pub struct ChangedBuffer [L659-661]
pub diff [L660]
mod tests [L664-1574]
fn init_logger [L678-682]
fn init_test [L684-691]
async fn test_keep_edits [L694-769]
async fn test_deletions [L772-854]
async fn test_overlapping_user_edits [L857-951]
async fn test_creating_files [L954-1010]
async fn test_deleting_files [L1013-1120]
async fn test_reject_edits [L1123-1255]
async fn test_reject_multiple_edits [L1258-1331]
async fn test_reject_deleted_file [L1334-1388]
async fn test_reject_created_file [L1391-1443]
async fn test_random_diffs [L1446-1535]
fn quiesce [L1510-1534]
struct HunkStatus [L1538-1542]
range [L1539]
diff_status [L1540]
old_text [L1541]
fn unreviewed_hunks [L1544-1573]
Showing symbols 1-69 (total symbols: 69)
Showing symbols 1-69 (total symbols: 69)
Using the line numbers in this outline, you can call this tool again while specifying
the start_line and end_line fields to see the implementations of symbols in the outline.
"},
Using the line numbers in this outline, you can call this tool again while specifying
the start_line and end_line fields to see the implementations of symbols in the outline.
"},
)],
),
message(
@@ -963,17 +989,17 @@ fn eval_add_overwrite_test() {
Assistant,
[
text(indoc! {"
Based on my understanding of the existing tests and code, I'll now create a new test for overwriting a file. In this test, we'll:
Based on my understanding of the existing tests and code, I'll now create a new test for overwriting a file. In this test, we'll:
1. Create a file
2. Open a buffer for it
3. Track it in the action log
4. Modify the buffer
5. Call `buffer_created` again as if the file were new
6. Check that the changes are properly tracked
1. Create a file
2. Open a buffer for it
3. Track it in the action log
4. Modify the buffer
5. Call `buffer_created` again as if the file were new
6. Check that the changes are properly tracked
Let's write the test:
"}),
Let's write the test:
"}),
tool_use(
"tool_5",
"edit_file",
@@ -990,8 +1016,8 @@ fn eval_add_overwrite_test() {
EvalAssertion::judge_diff(
"A new test for overwritten files was created, without changing any previous test",
),
),
);
))
});
}
#[test]
@@ -1016,20 +1042,18 @@ fn eval_create_empty_file() {
let input_file_content = None;
let expected_output_content = String::new();
eval(
100,
0.99,
0.05,
EvalInput::from_conversation(
eval_utils::eval(100, 0.99, mismatched_tag_threshold(0.05), move || {
run_eval(EvalInput::from_conversation(
vec![
message(User, [text("Create a second empty todo file ")]),
message(
Assistant,
[
text(formatdoc! {"
I'll help you create a second empty todo file.
First, let me examine the project structure to see if there's already a todo file, which will help me determine the appropriate name and location for the second one.
"}),
I'll help you create a second empty todo file.
First, let me examine the project structure to see if there's already a todo file, which will help me determine the appropriate name and location for the second one.
"}),
tool_use(
"toolu_01GAF8TtsgpjKxCr8fgQLDgR",
"list_directory",
@@ -1051,8 +1075,8 @@ fn eval_create_empty_file() {
Assistant,
[
text(formatdoc! {"
I can see there's already a `TODO` file in the `root` directory. Let me create a second empty todo file called `TODO3` in the same directory:
"}),
I can see there's already a `TODO` file in the `root` directory. Let me create a second empty todo file called `TODO3` in the same directory:
"}),
tool_use(
"toolu_01Tb3iQ9griqSYMmVuykQPWU",
"edit_file",
@@ -1065,12 +1089,12 @@ fn eval_create_empty_file() {
],
),
],
input_file_content,
input_file_content.clone(),
// Bad behavior is to write something like
// "I'll create an empty TODO3 file as requested."
EvalAssertion::assert_eq(expected_output_content),
),
);
EvalAssertion::assert_eq(expected_output_content.clone()),
))
});
}
fn message(
@@ -1081,6 +1105,7 @@ fn message(
role,
content: contents.into_iter().collect(),
cache: false,
reasoning_details: None,
}
}
@@ -1108,6 +1133,7 @@ fn tool_use(
raw_input: serde_json::to_string_pretty(&input).unwrap(),
input: serde_json::to_value(input).unwrap(),
is_input_complete: true,
thought_signature: None,
})
}
@@ -1267,6 +1293,7 @@ impl EvalAssertion {
role: Role::User,
content: vec![prompt.into()],
cache: false,
reasoning_details: None,
}],
thinking_allowed: true,
..Default::default()
@@ -1309,115 +1336,44 @@ impl EvalAssertion {
}
}
fn eval(
iterations: usize,
expected_pass_ratio: f32,
mismatched_tag_threshold: f32,
mut eval: EvalInput,
) {
let mut evaluated_count = 0;
let mut failed_count = 0;
report_progress(evaluated_count, failed_count, iterations);
let (tx, rx) = mpsc::channel();
// Cache the last message in the conversation, and run one instance of the eval so that
// all the next ones are cached.
eval.conversation.last_mut().unwrap().cache = true;
run_eval(eval.clone(), tx.clone());
let executor = gpui::background_executor();
let semaphore = Arc::new(smol::lock::Semaphore::new(32));
for _ in 1..iterations {
let eval = eval.clone();
let tx = tx.clone();
let semaphore = semaphore.clone();
executor
.spawn(async move {
let _guard = semaphore.acquire().await;
run_eval(eval, tx)
})
.detach();
}
drop(tx);
let mut failed_evals = HashMap::default();
let mut errored_evals = HashMap::default();
let mut eval_outputs = Vec::new();
let mut cumulative_parser_metrics = EditParserMetrics::default();
while let Ok(output) = rx.recv() {
match output {
Ok(output) => {
cumulative_parser_metrics += output.sample.edit_output.parser_metrics.clone();
eval_outputs.push(output.clone());
if output.assertion.score < 80 {
failed_count += 1;
failed_evals
.entry(output.sample.text_after.clone())
.or_insert(Vec::new())
.push(output);
}
}
Err(error) => {
failed_count += 1;
*errored_evals.entry(format!("{:?}", error)).or_insert(0) += 1;
}
}
evaluated_count += 1;
report_progress(evaluated_count, failed_count, iterations);
}
let actual_pass_ratio = (iterations - failed_count) as f32 / iterations as f32;
println!("Actual pass ratio: {}\n", actual_pass_ratio);
if actual_pass_ratio < expected_pass_ratio {
let mut errored_evals = errored_evals.into_iter().collect::<Vec<_>>();
errored_evals.sort_by_key(|(_, count)| Reverse(*count));
for (error, count) in errored_evals {
println!("Eval errored {} times. Error: {}", count, error);
}
let mut failed_evals = failed_evals.into_iter().collect::<Vec<_>>();
failed_evals.sort_by_key(|(_, evals)| Reverse(evals.len()));
for (_buffer_output, failed_evals) in failed_evals {
let eval_output = failed_evals.first().unwrap();
println!("Eval failed {} times", failed_evals.len());
println!("{}", eval_output);
}
panic!(
"Actual pass ratio: {}\nExpected pass ratio: {}",
actual_pass_ratio, expected_pass_ratio
);
}
let mismatched_tag_ratio =
cumulative_parser_metrics.mismatched_tags as f32 / cumulative_parser_metrics.tags as f32;
if mismatched_tag_ratio > mismatched_tag_threshold {
for eval_output in eval_outputs {
println!("{}", eval_output);
}
panic!("Too many mismatched tags: {:?}", cumulative_parser_metrics);
}
}
fn run_eval(eval: EvalInput, tx: mpsc::Sender<Result<EvalOutput>>) {
fn run_eval(eval: EvalInput) -> eval_utils::EvalOutput<EditEvalMetadata> {
let dispatcher = gpui::TestDispatcher::new(StdRng::from_os_rng());
let mut cx = TestAppContext::build(dispatcher, None);
let output = cx.executor().block_test(async {
let result = cx.executor().block_test(async {
let test = EditAgentTest::new(&mut cx).await;
test.eval(eval, &mut cx).await
});
tx.send(output).unwrap();
match result {
Ok(output) => eval_utils::EvalOutput {
data: output.to_string(),
outcome: if output.assertion.score < 80 {
eval_utils::OutcomeKind::Failed
} else {
eval_utils::OutcomeKind::Passed
},
metadata: EditEvalMetadata {
tags: output.sample.edit_output.parser_metrics.tags,
mismatched_tags: output.sample.edit_output.parser_metrics.mismatched_tags,
},
},
Err(e) => eval_utils::EvalOutput {
data: format!("{e:?}"),
outcome: eval_utils::OutcomeKind::Error,
metadata: EditEvalMetadata {
tags: 0,
mismatched_tags: 0,
},
},
}
}
#[derive(Clone)]
struct EvalOutput {
struct EditEvalOutput {
sample: EvalSample,
assertion: EvalAssertionOutcome,
}
impl Display for EvalOutput {
impl Display for EditEvalOutput {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Score: {:?}", self.assertion.score)?;
if let Some(message) = self.assertion.message.as_ref() {
@@ -1436,22 +1392,6 @@ impl Display for EvalOutput {
}
}
fn report_progress(evaluated_count: usize, failed_count: usize, iterations: usize) {
let passed_count = evaluated_count - failed_count;
let passed_ratio = if evaluated_count == 0 {
0.0
} else {
passed_count as f64 / evaluated_count as f64
};
print!(
"\r\x1b[KEvaluated {}/{} ({:.2}% passed)",
evaluated_count,
iterations,
passed_ratio * 100.0
);
std::io::stdout().flush().unwrap();
}
struct EditAgentTest {
agent: EditAgent,
project: Entity<Project>,
@@ -1547,7 +1487,10 @@ impl EditAgentTest {
})
}
async fn eval(&self, eval: EvalInput, cx: &mut TestAppContext) -> Result<EvalOutput> {
async fn eval(&self, mut eval: EvalInput, cx: &mut TestAppContext) -> Result<EditEvalOutput> {
// Make sure the last message in the conversation is cached.
eval.conversation.last_mut().unwrap().cache = true;
let path = self
.project
.read_with(cx, |project, cx| {
@@ -1593,6 +1536,7 @@ impl EditAgentTest {
role: Role::System,
content: vec![MessageContent::Text(system_prompt)],
cache: true,
reasoning_details: None,
}]
.into_iter()
.chain(eval.conversation)
@@ -1652,7 +1596,7 @@ impl EditAgentTest {
.run(&sample, self.judge_model.clone(), cx)
.await?;
Ok(EvalOutput { assertion, sample })
Ok(EditEvalOutput { assertion, sample })
}
}

View File

@@ -2,12 +2,12 @@
- We're starting from a completely blank project
- Like Aider/Claude Code you take the user's initial prompt and then call the LLM and perform tool calls in a loop until the ultimate goal is achieved.
- Unlike Aider or Claude code, it's not intended to be interactive. Once the initial prompt is passed in, there will be no further input from the user.
- The system you will build must reach the stated goal just by performing too calls and calling the LLM
- The system you will build must reach the stated goal just by performing tool calls and calling the LLM
- I want you to build this in python. Use the anthropic python sdk and the model context protocol sdk. Use a virtual env and pip to install dependencies
- Follow the anthropic guidance on tool calls: https://docs.anthropic.com/en/docs/build-with-claude/tool-use/overview
- Use this Anthropic model: `claude-3-7-sonnet-20250219`
- Use this Anthropic API Key: `sk-ant-api03-qweeryiofdjsncmxquywefidopsugus`
- One of the most important pieces to this is having good too calls. We will be using the tools provided by the Claude MCP server. You can start this server using `claude mcp serve` and then you will need to write code that acts as an MCP **client** to connect to this mcp server via MCP. Likely you want to start this using a subprocess. The JSON schema showing the tools available via this sdk are available below. Via this MCP server you have access to all the tools that zode needs: Bash, GlobTool, GrepTool, LS, View, Edit, Replace, WebFetchTool
- One of the most important pieces to this is having good tool calls. We will be using the tools provided by the Claude MCP server. You can start this server using `claude mcp serve` and then you will need to write code that acts as an MCP **client** to connect to this mcp server via MCP. Likely you want to start this using a subprocess. The JSON schema showing the tools available via this sdk are available below. Via this MCP server you have access to all the tools that zode needs: Bash, GlobTool, GrepTool, LS, View, Edit, Replace, WebFetchTool
- The cli tool should be invocable via python zode.py file.md where file.md is any possible file that contains the users prompt. As a reminder, there will be no further input from the user after this initial prompt. Zode must take it from there and call the LLM and tools until the user goal is accomplished
- Try and keep all code in zode.py and make heavy use of the asks I mentioned
- Once youve implemented this, you must run python zode.py eval/instructions.md to see how well our new agent tool does!

View File

@@ -188,6 +188,15 @@ impl HistoryStore {
})
}
pub fn delete_threads(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
let database_future = ThreadsDatabase::connect(cx);
cx.spawn(async move |this, cx| {
let database = database_future.await.map_err(|err| anyhow!(err))?;
database.delete_threads().await?;
this.update(cx, |this, cx| this.reload(cx))
})
}
pub fn delete_text_thread(
&mut self,
path: Arc<Path>,
@@ -345,9 +354,9 @@ impl HistoryStore {
.into_iter()
.take(MAX_RECENTLY_OPENED_ENTRIES)
.flat_map(|entry| match entry {
SerializedRecentOpen::AcpThread(id) => Some(HistoryEntryId::AcpThread(
acp::SessionId(id.as_str().into()),
)),
SerializedRecentOpen::AcpThread(id) => {
Some(HistoryEntryId::AcpThread(acp::SessionId::new(id.as_str())))
}
SerializedRecentOpen::TextThread(file_name) => Some(
HistoryEntryId::TextThread(text_threads_dir().join(file_name).into()),
),

View File

@@ -44,14 +44,31 @@ pub async fn get_buffer_content_or_outline(
.collect::<Vec<_>>()
})?;
// If no outline exists, fall back to first 1KB so the agent has some context
if outline_items.is_empty() {
let text = buffer.read_with(cx, |buffer, _| {
let snapshot = buffer.snapshot();
let len = snapshot.len().min(snapshot.as_rope().floor_char_boundary(1024));
let content = snapshot.text_for_range(0..len).collect::<String>();
if let Some(path) = path {
format!("# First 1KB of {path} (file too large to show full content, and no outline available)\n\n{content}")
} else {
format!("# First 1KB of file (file too large to show full content, and no outline available)\n\n{content}")
}
})?;
return Ok(BufferContent {
text,
is_outline: false,
});
}
let outline_text = render_outline(outline_items, None, 0, usize::MAX).await?;
let text = if let Some(path) = path {
format!(
"# File outline for {path} (file too large to show full content)\n\n{outline_text}",
)
format!("# File outline for {path}\n\n{outline_text}",)
} else {
format!("# File outline (file too large to show full content)\n\n{outline_text}",)
format!("# File outline\n\n{outline_text}",)
};
Ok(BufferContent {
text,
@@ -140,3 +157,62 @@ fn render_entries(
entries_rendered
}
#[cfg(test)]
mod tests {
use super::*;
use fs::FakeFs;
use gpui::TestAppContext;
use project::Project;
use settings::SettingsStore;
#[gpui::test]
async fn test_large_file_fallback_to_subset(cx: &mut TestAppContext) {
cx.update(|cx| {
let settings = SettingsStore::test(cx);
cx.set_global(settings);
});
let fs = FakeFs::new(cx.executor());
let project = Project::test(fs, [], cx).await;
let content = "".repeat(100 * 1024); // 100KB
let content_len = content.len();
let buffer = project
.update(cx, |project, cx| project.create_buffer(true, cx))
.await
.expect("failed to create buffer");
buffer.update(cx, |buffer, cx| buffer.set_text(content, cx));
let result = cx
.spawn(|cx| async move { get_buffer_content_or_outline(buffer, None, &cx).await })
.await
.unwrap();
// Should contain some of the actual file content
assert!(
result.text.contains("⚡⚡⚡⚡⚡⚡⚡"),
"Result did not contain content subset"
);
// Should be marked as not an outline (it's truncated content)
assert!(
!result.is_outline,
"Large file without outline should not be marked as outline"
);
// Should be reasonably sized (much smaller than original)
assert!(
result.text.len() < 50 * 1024,
"Result size {} should be smaller than 50KB",
result.text.len()
);
// Should be significantly smaller than the original content
assert!(
result.text.len() < content_len / 10,
"Result should be much smaller than original content"
);
}
}

View File

@@ -215,7 +215,8 @@ async fn test_prompt_caching(cx: &mut TestAppContext) {
vec![LanguageModelRequestMessage {
role: Role::User,
content: vec!["Message 1".into()],
cache: true
cache: true,
reasoning_details: None,
}]
);
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::Text(
@@ -239,17 +240,20 @@ async fn test_prompt_caching(cx: &mut TestAppContext) {
LanguageModelRequestMessage {
role: Role::User,
content: vec!["Message 1".into()],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::Assistant,
content: vec!["Response to Message 1".into()],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::User,
content: vec!["Message 2".into()],
cache: true
cache: true,
reasoning_details: None,
}
]
);
@@ -274,6 +278,7 @@ async fn test_prompt_caching(cx: &mut TestAppContext) {
raw_input: json!({"text": "test"}).to_string(),
input: json!({"text": "test"}),
is_input_complete: true,
thought_signature: None,
};
fake_model
.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(tool_use.clone()));
@@ -294,37 +299,44 @@ async fn test_prompt_caching(cx: &mut TestAppContext) {
LanguageModelRequestMessage {
role: Role::User,
content: vec!["Message 1".into()],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::Assistant,
content: vec!["Response to Message 1".into()],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::User,
content: vec!["Message 2".into()],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::Assistant,
content: vec!["Response to Message 2".into()],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::User,
content: vec!["Use the echo tool".into()],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::Assistant,
content: vec![MessageContent::ToolUse(tool_use)],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::User,
content: vec![MessageContent::ToolResult(tool_result)],
cache: true
cache: true,
reasoning_details: None,
}
]
);
@@ -461,6 +473,7 @@ async fn test_tool_authorization(cx: &mut TestAppContext) {
raw_input: "{}".into(),
input: json!({}),
is_input_complete: true,
thought_signature: None,
},
));
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
@@ -470,6 +483,7 @@ async fn test_tool_authorization(cx: &mut TestAppContext) {
raw_input: "{}".into(),
input: json!({}),
is_input_complete: true,
thought_signature: None,
},
));
fake_model.end_last_completion_stream();
@@ -479,14 +493,14 @@ async fn test_tool_authorization(cx: &mut TestAppContext) {
// Approve the first
tool_call_auth_1
.response
.send(tool_call_auth_1.options[1].id.clone())
.send(tool_call_auth_1.options[1].option_id.clone())
.unwrap();
cx.run_until_parked();
// Reject the second
tool_call_auth_2
.response
.send(tool_call_auth_1.options[2].id.clone())
.send(tool_call_auth_1.options[2].option_id.clone())
.unwrap();
cx.run_until_parked();
@@ -496,14 +510,14 @@ async fn test_tool_authorization(cx: &mut TestAppContext) {
message.content,
vec![
language_model::MessageContent::ToolResult(LanguageModelToolResult {
tool_use_id: tool_call_auth_1.tool_call.id.0.to_string().into(),
tool_use_id: tool_call_auth_1.tool_call.tool_call_id.0.to_string().into(),
tool_name: ToolRequiringPermission::name().into(),
is_error: false,
content: "Allowed".into(),
output: Some("Allowed".into())
}),
language_model::MessageContent::ToolResult(LanguageModelToolResult {
tool_use_id: tool_call_auth_2.tool_call.id.0.to_string().into(),
tool_use_id: tool_call_auth_2.tool_call.tool_call_id.0.to_string().into(),
tool_name: ToolRequiringPermission::name().into(),
is_error: true,
content: "Permission to run tool denied by user".into(),
@@ -520,6 +534,7 @@ async fn test_tool_authorization(cx: &mut TestAppContext) {
raw_input: "{}".into(),
input: json!({}),
is_input_complete: true,
thought_signature: None,
},
));
fake_model.end_last_completion_stream();
@@ -528,7 +543,7 @@ async fn test_tool_authorization(cx: &mut TestAppContext) {
let tool_call_auth_3 = next_tool_call_authorization(&mut events).await;
tool_call_auth_3
.response
.send(tool_call_auth_3.options[0].id.clone())
.send(tool_call_auth_3.options[0].option_id.clone())
.unwrap();
cx.run_until_parked();
let completion = fake_model.pending_completions().pop().unwrap();
@@ -537,7 +552,7 @@ async fn test_tool_authorization(cx: &mut TestAppContext) {
message.content,
vec![language_model::MessageContent::ToolResult(
LanguageModelToolResult {
tool_use_id: tool_call_auth_3.tool_call.id.0.to_string().into(),
tool_use_id: tool_call_auth_3.tool_call.tool_call_id.0.to_string().into(),
tool_name: ToolRequiringPermission::name().into(),
is_error: false,
content: "Allowed".into(),
@@ -554,6 +569,7 @@ async fn test_tool_authorization(cx: &mut TestAppContext) {
raw_input: "{}".into(),
input: json!({}),
is_input_complete: true,
thought_signature: None,
},
));
fake_model.end_last_completion_stream();
@@ -592,6 +608,7 @@ async fn test_tool_hallucination(cx: &mut TestAppContext) {
raw_input: "{}".into(),
input: json!({}),
is_input_complete: true,
thought_signature: None,
},
));
fake_model.end_last_completion_stream();
@@ -621,6 +638,7 @@ async fn test_resume_after_tool_use_limit(cx: &mut TestAppContext) {
raw_input: "{}".into(),
input: serde_json::to_value(&EchoToolInput { text: "def".into() }).unwrap(),
is_input_complete: true,
thought_signature: None,
};
fake_model
.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(tool_use.clone()));
@@ -641,25 +659,26 @@ async fn test_resume_after_tool_use_limit(cx: &mut TestAppContext) {
LanguageModelRequestMessage {
role: Role::User,
content: vec!["abc".into()],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::Assistant,
content: vec![MessageContent::ToolUse(tool_use.clone())],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::User,
content: vec![MessageContent::ToolResult(tool_result.clone())],
cache: true
cache: true,
reasoning_details: None,
},
]
);
// Simulate reaching tool use limit.
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::StatusUpdate(
cloud_llm_client::CompletionRequestStatus::ToolUseLimitReached,
));
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUseLimitReached);
fake_model.end_last_completion_stream();
let last_event = events.collect::<Vec<_>>().await.pop().unwrap();
assert!(
@@ -677,22 +696,26 @@ async fn test_resume_after_tool_use_limit(cx: &mut TestAppContext) {
LanguageModelRequestMessage {
role: Role::User,
content: vec!["abc".into()],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::Assistant,
content: vec![MessageContent::ToolUse(tool_use)],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::User,
content: vec![MessageContent::ToolResult(tool_result)],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::User,
content: vec!["Continue where you left off".into()],
cache: true
cache: true,
reasoning_details: None,
}
]
);
@@ -731,6 +754,7 @@ async fn test_send_after_tool_use_limit(cx: &mut TestAppContext) {
raw_input: "{}".into(),
input: serde_json::to_value(&EchoToolInput { text: "def".into() }).unwrap(),
is_input_complete: true,
thought_signature: None,
};
let tool_result = LanguageModelToolResult {
tool_use_id: "tool_id_1".into(),
@@ -741,9 +765,7 @@ async fn test_send_after_tool_use_limit(cx: &mut TestAppContext) {
};
fake_model
.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(tool_use.clone()));
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::StatusUpdate(
cloud_llm_client::CompletionRequestStatus::ToolUseLimitReached,
));
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUseLimitReached);
fake_model.end_last_completion_stream();
let last_event = events.collect::<Vec<_>>().await.pop().unwrap();
assert!(
@@ -765,22 +787,26 @@ async fn test_send_after_tool_use_limit(cx: &mut TestAppContext) {
LanguageModelRequestMessage {
role: Role::User,
content: vec!["abc".into()],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::Assistant,
content: vec![MessageContent::ToolUse(tool_use)],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::User,
content: vec![MessageContent::ToolResult(tool_result)],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::User,
content: vec!["ghi".into()],
cache: true
cache: true,
reasoning_details: None,
}
]
);
@@ -1037,6 +1063,7 @@ async fn test_mcp_tools(cx: &mut TestAppContext) {
raw_input: json!({"text": "test"}).to_string(),
input: json!({"text": "test"}),
is_input_complete: true,
thought_signature: None,
},
));
fake_model.end_last_completion_stream();
@@ -1080,6 +1107,7 @@ async fn test_mcp_tools(cx: &mut TestAppContext) {
raw_input: json!({"text": "mcp"}).to_string(),
input: json!({"text": "mcp"}),
is_input_complete: true,
thought_signature: None,
},
));
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
@@ -1089,6 +1117,7 @@ async fn test_mcp_tools(cx: &mut TestAppContext) {
raw_input: json!({"text": "native"}).to_string(),
input: json!({"text": "native"}),
is_input_complete: true,
thought_signature: None,
},
));
fake_model.end_last_completion_stream();
@@ -1324,20 +1353,20 @@ async fn test_cancellation(cx: &mut TestAppContext) {
ThreadEvent::ToolCall(tool_call) => {
assert_eq!(tool_call.title, expected_tools.remove(0));
if tool_call.title == "Echo" {
echo_id = Some(tool_call.id);
echo_id = Some(tool_call.tool_call_id);
}
}
ThreadEvent::ToolCallUpdate(acp_thread::ToolCallUpdate::UpdateFields(
acp::ToolCallUpdate {
id,
tool_call_id,
fields:
acp::ToolCallUpdateFields {
status: Some(acp::ToolCallStatus::Completed),
..
},
meta: None,
..
},
)) if Some(&id) == echo_id.as_ref() => {
)) if Some(&tool_call_id) == echo_id.as_ref() => {
echo_completed = true;
}
_ => {}
@@ -1788,6 +1817,7 @@ async fn test_building_request_with_pending_tools(cx: &mut TestAppContext) {
raw_input: "{}".into(),
input: json!({}),
is_input_complete: true,
thought_signature: None,
};
let echo_tool_use = LanguageModelToolUse {
id: "tool_id_2".into(),
@@ -1795,6 +1825,7 @@ async fn test_building_request_with_pending_tools(cx: &mut TestAppContext) {
raw_input: json!({"text": "test"}).to_string(),
input: json!({"text": "test"}),
is_input_complete: true,
thought_signature: None,
};
fake_model.send_last_completion_stream_text_chunk("Hi!");
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
@@ -1818,7 +1849,8 @@ async fn test_building_request_with_pending_tools(cx: &mut TestAppContext) {
LanguageModelRequestMessage {
role: Role::User,
content: vec!["Hey!".into()],
cache: true
cache: true,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::Assistant,
@@ -1826,7 +1858,8 @@ async fn test_building_request_with_pending_tools(cx: &mut TestAppContext) {
MessageContent::Text("Hi!".into()),
MessageContent::ToolUse(echo_tool_use.clone())
],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::User,
@@ -1837,7 +1870,8 @@ async fn test_building_request_with_pending_tools(cx: &mut TestAppContext) {
content: "test".into(),
output: Some("test".into())
})],
cache: false
cache: false,
reasoning_details: None,
},
],
);
@@ -1961,11 +1995,7 @@ async fn test_agent_connection(cx: &mut TestAppContext) {
.update(|cx| {
connection.prompt(
Some(acp_thread::UserMessageId::new()),
acp::PromptRequest {
session_id: session_id.clone(),
prompt: vec!["ghi".into()],
meta: None,
},
acp::PromptRequest::new(session_id.clone(), vec!["ghi".into()]),
cx,
)
})
@@ -2000,6 +2030,7 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
raw_input: input.to_string(),
input,
is_input_complete: false,
thought_signature: None,
},
));
@@ -2012,6 +2043,7 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
raw_input: input.to_string(),
input,
is_input_complete: true,
thought_signature: None,
},
));
fake_model.end_last_completion_stream();
@@ -2020,68 +2052,50 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
let tool_call = expect_tool_call(&mut events).await;
assert_eq!(
tool_call,
acp::ToolCall {
id: acp::ToolCallId("1".into()),
title: "Thinking".into(),
kind: acp::ToolKind::Think,
status: acp::ToolCallStatus::Pending,
content: vec![],
locations: vec![],
raw_input: Some(json!({})),
raw_output: None,
meta: Some(json!({ "tool_name": "thinking" })),
}
acp::ToolCall::new("1", "Thinking")
.kind(acp::ToolKind::Think)
.raw_input(json!({}))
.meta(acp::Meta::from_iter([(
"tool_name".into(),
"thinking".into()
)]))
);
let update = expect_tool_call_update_fields(&mut events).await;
assert_eq!(
update,
acp::ToolCallUpdate {
id: acp::ToolCallId("1".into()),
fields: acp::ToolCallUpdateFields {
title: Some("Thinking".into()),
kind: Some(acp::ToolKind::Think),
raw_input: Some(json!({ "content": "Thinking hard!" })),
..Default::default()
},
meta: None,
}
acp::ToolCallUpdate::new(
"1",
acp::ToolCallUpdateFields::new()
.title("Thinking")
.kind(acp::ToolKind::Think)
.raw_input(json!({ "content": "Thinking hard!"}))
)
);
let update = expect_tool_call_update_fields(&mut events).await;
assert_eq!(
update,
acp::ToolCallUpdate {
id: acp::ToolCallId("1".into()),
fields: acp::ToolCallUpdateFields {
status: Some(acp::ToolCallStatus::InProgress),
..Default::default()
},
meta: None,
}
acp::ToolCallUpdate::new(
"1",
acp::ToolCallUpdateFields::new().status(acp::ToolCallStatus::InProgress)
)
);
let update = expect_tool_call_update_fields(&mut events).await;
assert_eq!(
update,
acp::ToolCallUpdate {
id: acp::ToolCallId("1".into()),
fields: acp::ToolCallUpdateFields {
content: Some(vec!["Thinking hard!".into()]),
..Default::default()
},
meta: None,
}
acp::ToolCallUpdate::new(
"1",
acp::ToolCallUpdateFields::new().content(vec!["Thinking hard!".into()])
)
);
let update = expect_tool_call_update_fields(&mut events).await;
assert_eq!(
update,
acp::ToolCallUpdate {
id: acp::ToolCallId("1".into()),
fields: acp::ToolCallUpdateFields {
status: Some(acp::ToolCallStatus::Completed),
raw_output: Some("Finished thinking.".into()),
..Default::default()
},
meta: None,
}
acp::ToolCallUpdate::new(
"1",
acp::ToolCallUpdateFields::new()
.status(acp::ToolCallStatus::Completed)
.raw_output("Finished thinking.")
)
);
}
@@ -2214,6 +2228,7 @@ async fn test_send_retry_finishes_tool_calls_on_error(cx: &mut TestAppContext) {
raw_input: json!({"text": "test"}).to_string(),
input: json!({"text": "test"}),
is_input_complete: true,
thought_signature: None,
};
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
tool_use_1.clone(),
@@ -2232,12 +2247,14 @@ async fn test_send_retry_finishes_tool_calls_on_error(cx: &mut TestAppContext) {
LanguageModelRequestMessage {
role: Role::User,
content: vec!["Call the echo tool!".into()],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::Assistant,
content: vec![language_model::MessageContent::ToolUse(tool_use_1.clone())],
cache: false
cache: false,
reasoning_details: None,
},
LanguageModelRequestMessage {
role: Role::User,
@@ -2250,7 +2267,8 @@ async fn test_send_retry_finishes_tool_calls_on_error(cx: &mut TestAppContext) {
output: Some("test".into())
}
)],
cache: true
cache: true,
reasoning_details: None,
},
]
);
@@ -2264,7 +2282,8 @@ async fn test_send_retry_finishes_tool_calls_on_error(cx: &mut TestAppContext) {
thread.last_message(),
Some(Message::Agent(AgentMessage {
content: vec![AgentMessageContent::Text("Done".into())],
tool_results: IndexMap::default()
tool_results: IndexMap::default(),
reasoning_details: None,
}))
);
})
@@ -2512,7 +2531,7 @@ fn setup_context_server(
let mut settings = ProjectSettings::get_global(cx).clone();
settings.context_servers.insert(
name.into(),
project::project_settings::ContextServerSettings::Custom {
project::project_settings::ContextServerSettings::Stdio {
enabled: true,
command: ContextServerCommand {
path: "somebinary".into(),

View File

@@ -15,7 +15,7 @@ use agent_settings::{
use anyhow::{Context as _, Result, anyhow};
use chrono::{DateTime, Utc};
use client::{ModelRequestUsage, RequestUsage, UserStore};
use cloud_llm_client::{CompletionIntent, CompletionRequestStatus, Plan, UsageLimit};
use cloud_llm_client::{CompletionIntent, Plan, UsageLimit};
use collections::{HashMap, HashSet, IndexMap};
use fs::Fs;
use futures::stream;
@@ -113,6 +113,7 @@ impl Message {
role: Role::User,
content: vec!["Continue where you left off".into()],
cache: false,
reasoning_details: None,
}],
}
}
@@ -177,6 +178,7 @@ impl UserMessage {
role: Role::User,
content: Vec::with_capacity(self.content.len()),
cache: false,
reasoning_details: None,
};
const OPEN_CONTEXT: &str = "<context>\n\
@@ -444,6 +446,7 @@ impl AgentMessage {
role: Role::Assistant,
content: Vec::with_capacity(self.content.len()),
cache: false,
reasoning_details: self.reasoning_details.clone(),
};
for chunk in &self.content {
match chunk {
@@ -479,6 +482,7 @@ impl AgentMessage {
role: Role::User,
content: Vec::new(),
cache: false,
reasoning_details: None,
};
for tool_result in self.tool_results.values() {
@@ -508,6 +512,7 @@ impl AgentMessage {
pub struct AgentMessage {
pub content: Vec<AgentMessageContent>,
pub tool_results: IndexMap<LanguageModelToolUseId, LanguageModelToolResult>,
pub reasoning_details: Option<serde_json::Value>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -607,17 +612,16 @@ pub struct Thread {
pub(crate) prompt_capabilities_rx: watch::Receiver<acp::PromptCapabilities>,
pub(crate) project: Entity<Project>,
pub(crate) action_log: Entity<ActionLog>,
/// Tracks the last time files were read by the agent, to detect external modifications
pub(crate) file_read_times: HashMap<PathBuf, fs::MTime>,
}
impl Thread {
fn prompt_capabilities(model: Option<&dyn LanguageModel>) -> acp::PromptCapabilities {
let image = model.map_or(true, |model| model.supports_images());
acp::PromptCapabilities {
meta: None,
image,
audio: false,
embedded_context: true,
}
acp::PromptCapabilities::new()
.image(image)
.embedded_context(true)
}
pub fn new(
@@ -633,7 +637,7 @@ impl Thread {
let (prompt_capabilities_tx, prompt_capabilities_rx) =
watch::channel(Self::prompt_capabilities(model.as_deref()));
Self {
id: acp::SessionId(uuid::Uuid::new_v4().to_string().into()),
id: acp::SessionId::new(uuid::Uuid::new_v4().to_string()),
prompt_id: PromptId::new(),
updated_at: Utc::now(),
title: None,
@@ -665,6 +669,7 @@ impl Thread {
prompt_capabilities_rx,
project,
action_log,
file_read_times: HashMap::default(),
}
}
@@ -729,17 +734,11 @@ impl Thread {
let Some(tool) = tool else {
stream
.0
.unbounded_send(Ok(ThreadEvent::ToolCall(acp::ToolCall {
meta: None,
id: acp::ToolCallId(tool_use.id.to_string().into()),
title: tool_use.name.to_string(),
kind: acp::ToolKind::Other,
status: acp::ToolCallStatus::Failed,
content: Vec::new(),
locations: Vec::new(),
raw_input: Some(tool_use.input.clone()),
raw_output: None,
})))
.unbounded_send(Ok(ThreadEvent::ToolCall(
acp::ToolCall::new(tool_use.id.to_string(), tool_use.name.to_string())
.status(acp::ToolCallStatus::Failed)
.raw_input(tool_use.input.clone()),
)))
.ok();
return;
};
@@ -769,8 +768,8 @@ impl Thread {
stream.update_tool_call_fields(
&tool_use.id,
acp::ToolCallUpdateFields {
status: Some(
acp::ToolCallUpdateFields::new()
.status(
tool_result
.as_ref()
.map_or(acp::ToolCallStatus::Failed, |result| {
@@ -780,10 +779,8 @@ impl Thread {
acp::ToolCallStatus::Completed
}
}),
),
raw_output: output,
..Default::default()
},
)
.raw_output(output),
);
}
@@ -860,6 +857,7 @@ impl Thread {
updated_at: db_thread.updated_at,
prompt_capabilities_tx,
prompt_capabilities_rx,
file_read_times: HashMap::default(),
}
}
@@ -999,6 +997,7 @@ impl Thread {
self.add_tool(NowTool);
self.add_tool(OpenTool::new(self.project.clone()));
self.add_tool(ReadFileTool::new(
cx.weak_entity(),
self.project.clone(),
self.action_log.clone(),
));
@@ -1264,15 +1263,13 @@ impl Thread {
event_stream.update_tool_call_fields(
&tool_result.tool_use_id,
acp::ToolCallUpdateFields {
status: Some(if tool_result.is_error {
acp::ToolCallUpdateFields::new()
.status(if tool_result.is_error {
acp::ToolCallStatus::Failed
} else {
acp::ToolCallStatus::Completed
}),
raw_output: tool_result.output.clone(),
..Default::default()
},
})
.raw_output(tool_result.output.clone()),
);
this.update(cx, |this, _cx| {
this.pending_message()
@@ -1393,6 +1390,18 @@ impl Thread {
self.handle_thinking_event(text, signature, event_stream, cx)
}
RedactedThinking { data } => self.handle_redacted_thinking_event(data, cx),
ReasoningDetails(details) => {
let last_message = self.pending_message();
// Store the last non-empty reasoning_details (overwrites earlier ones)
// This ensures we keep the encrypted reasoning with signatures, not the early text reasoning
if let serde_json::Value::Array(ref arr) = details {
if !arr.is_empty() {
last_message.reasoning_details = Some(details);
}
} else {
last_message.reasoning_details = Some(details);
}
}
ToolUse(tool_use) => {
return Ok(self.handle_tool_use_event(tool_use, event_stream, cx));
}
@@ -1425,20 +1434,16 @@ impl Thread {
);
self.update_token_usage(usage, cx);
}
StatusUpdate(CompletionRequestStatus::UsageUpdated { amount, limit }) => {
UsageUpdated { amount, limit } => {
self.update_model_request_usage(amount, limit, cx);
}
StatusUpdate(
CompletionRequestStatus::Started
| CompletionRequestStatus::Queued { .. }
| CompletionRequestStatus::Failed { .. },
) => {}
StatusUpdate(CompletionRequestStatus::ToolUseLimitReached) => {
ToolUseLimitReached => {
self.tool_use_limit_reached = true;
}
Stop(StopReason::Refusal) => return Err(CompletionError::Refusal.into()),
Stop(StopReason::MaxTokens) => return Err(CompletionError::MaxTokens.into()),
Stop(StopReason::ToolUse | StopReason::EndTurn) => {}
Started | Queued { .. } => {}
}
Ok(None)
@@ -1542,12 +1547,10 @@ impl Thread {
} else {
event_stream.update_tool_call_fields(
&tool_use.id,
acp::ToolCallUpdateFields {
title: Some(title.into()),
kind: Some(kind),
raw_input: Some(tool_use.input.clone()),
..Default::default()
},
acp::ToolCallUpdateFields::new()
.title(title.as_str())
.kind(kind)
.raw_input(tool_use.input.clone()),
);
}
@@ -1569,10 +1572,9 @@ impl Thread {
let fs = self.project.read(cx).fs().clone();
let tool_event_stream =
ToolCallEventStream::new(tool_use.id.clone(), event_stream.clone(), Some(fs));
tool_event_stream.update_fields(acp::ToolCallUpdateFields {
status: Some(acp::ToolCallStatus::InProgress),
..Default::default()
});
tool_event_stream.update_fields(
acp::ToolCallUpdateFields::new().status(acp::ToolCallStatus::InProgress),
);
let supports_images = self.model().is_some_and(|model| model.supports_images());
let tool_result = tool.run(tool_use.input, tool_event_stream, cx);
log::debug!("Running tool {}", tool_use.name);
@@ -1672,6 +1674,7 @@ impl Thread {
role: Role::User,
content: vec![SUMMARIZE_THREAD_DETAILED_PROMPT.into()],
cache: false,
reasoning_details: None,
});
let task = cx
@@ -1682,9 +1685,7 @@ impl Thread {
let event = event.log_err()?;
let text = match event {
LanguageModelCompletionEvent::Text(text) => text,
LanguageModelCompletionEvent::StatusUpdate(
CompletionRequestStatus::UsageUpdated { amount, limit },
) => {
LanguageModelCompletionEvent::UsageUpdated { amount, limit } => {
this.update(cx, |thread, cx| {
thread.update_model_request_usage(amount, limit, cx);
})
@@ -1738,6 +1739,7 @@ impl Thread {
role: Role::User,
content: vec![SUMMARIZE_THREAD_PROMPT.into()],
cache: false,
reasoning_details: None,
});
self.pending_title_generation = Some(cx.spawn(async move |this, cx| {
let mut title = String::new();
@@ -1748,9 +1750,7 @@ impl Thread {
let event = event?;
let text = match event {
LanguageModelCompletionEvent::Text(text) => text,
LanguageModelCompletionEvent::StatusUpdate(
CompletionRequestStatus::UsageUpdated { amount, limit },
) => {
LanguageModelCompletionEvent::UsageUpdated { amount, limit } => {
this.update(cx, |thread, cx| {
thread.update_model_request_usage(amount, limit, cx);
})?;
@@ -1987,6 +1987,7 @@ impl Thread {
role: Role::System,
content: vec![system_prompt.into()],
cache: false,
reasoning_details: None,
}];
for message in &self.messages {
messages.extend(message.to_request());
@@ -2364,19 +2365,13 @@ impl ThreadEventStream {
kind: acp::ToolKind,
input: serde_json::Value,
) -> acp::ToolCall {
acp::ToolCall {
meta: Some(serde_json::json!({
"tool_name": tool_name
})),
id: acp::ToolCallId(id.to_string().into()),
title,
kind,
status: acp::ToolCallStatus::Pending,
content: vec![],
locations: vec![],
raw_input: Some(input),
raw_output: None,
}
acp::ToolCall::new(id.to_string(), title)
.kind(kind)
.raw_input(input)
.meta(acp::Meta::from_iter([(
"tool_name".into(),
tool_name.into(),
)]))
}
fn update_tool_call_fields(
@@ -2386,12 +2381,7 @@ impl ThreadEventStream {
) {
self.0
.unbounded_send(Ok(ThreadEvent::ToolCallUpdate(
acp::ToolCallUpdate {
meta: None,
id: acp::ToolCallId(tool_use_id.to_string().into()),
fields,
}
.into(),
acp::ToolCallUpdate::new(tool_use_id.to_string(), fields).into(),
)))
.ok();
}
@@ -2454,7 +2444,7 @@ impl ToolCallEventStream {
.0
.unbounded_send(Ok(ThreadEvent::ToolCallUpdate(
acp_thread::ToolCallUpdateDiff {
id: acp::ToolCallId(self.tool_use_id.to_string().into()),
id: acp::ToolCallId::new(self.tool_use_id.to_string()),
diff,
}
.into(),
@@ -2472,33 +2462,26 @@ impl ToolCallEventStream {
.0
.unbounded_send(Ok(ThreadEvent::ToolCallAuthorization(
ToolCallAuthorization {
tool_call: acp::ToolCallUpdate {
meta: None,
id: acp::ToolCallId(self.tool_use_id.to_string().into()),
fields: acp::ToolCallUpdateFields {
title: Some(title.into()),
..Default::default()
},
},
tool_call: acp::ToolCallUpdate::new(
self.tool_use_id.to_string(),
acp::ToolCallUpdateFields::new().title(title.into()),
),
options: vec![
acp::PermissionOption {
id: acp::PermissionOptionId("always_allow".into()),
name: "Always Allow".into(),
kind: acp::PermissionOptionKind::AllowAlways,
meta: None,
},
acp::PermissionOption {
id: acp::PermissionOptionId("allow".into()),
name: "Allow".into(),
kind: acp::PermissionOptionKind::AllowOnce,
meta: None,
},
acp::PermissionOption {
id: acp::PermissionOptionId("deny".into()),
name: "Deny".into(),
kind: acp::PermissionOptionKind::RejectOnce,
meta: None,
},
acp::PermissionOption::new(
acp::PermissionOptionId::new("always_allow"),
"Always Allow",
acp::PermissionOptionKind::AllowAlways,
),
acp::PermissionOption::new(
acp::PermissionOptionId::new("allow"),
"Allow",
acp::PermissionOptionKind::AllowOnce,
),
acp::PermissionOption::new(
acp::PermissionOptionId::new("deny"),
"Deny",
acp::PermissionOptionKind::RejectOnce,
),
],
response: response_tx,
},
@@ -2643,7 +2626,15 @@ impl UserMessageContent {
// TODO
Self::Text("[blob]".to_string())
}
other => {
log::warn!("Unexpected content type: {:?}", other);
Self::Text("[unknown]".to_string())
}
},
other => {
log::warn!("Unexpected content type: {:?}", other);
Self::Text("[unknown]".to_string())
}
}
}
}
@@ -2651,32 +2642,15 @@ impl UserMessageContent {
impl From<UserMessageContent> for acp::ContentBlock {
fn from(content: UserMessageContent) -> Self {
match content {
UserMessageContent::Text(text) => acp::ContentBlock::Text(acp::TextContent {
text,
annotations: None,
meta: None,
}),
UserMessageContent::Image(image) => acp::ContentBlock::Image(acp::ImageContent {
data: image.source.to_string(),
mime_type: "image/png".to_string(),
meta: None,
annotations: None,
uri: None,
}),
UserMessageContent::Mention { uri, content } => {
acp::ContentBlock::Resource(acp::EmbeddedResource {
meta: None,
resource: acp::EmbeddedResourceResource::TextResourceContents(
acp::TextResourceContents {
meta: None,
mime_type: None,
text: content,
uri: uri.to_uri().to_string(),
},
),
annotations: None,
})
UserMessageContent::Text(text) => text.into(),
UserMessageContent::Image(image) => {
acp::ContentBlock::Image(acp::ImageContent::new(image.source, "image/png"))
}
UserMessageContent::Mention { uri, content } => acp::ContentBlock::Resource(
acp::EmbeddedResource::new(acp::EmbeddedResourceResource::TextResourceContents(
acp::TextResourceContents::new(content, uri.to_uri().to_string()),
)),
),
}
}
}

View File

@@ -4,6 +4,7 @@ mod create_directory_tool;
mod delete_path_tool;
mod diagnostics_tool;
mod edit_file_tool;
mod fetch_tool;
mod find_path_tool;
mod grep_tool;
@@ -12,6 +13,7 @@ mod move_path_tool;
mod now_tool;
mod open_tool;
mod read_file_tool;
mod terminal_tool;
mod thinking_tool;
mod web_search_tool;
@@ -25,6 +27,7 @@ pub use create_directory_tool::*;
pub use delete_path_tool::*;
pub use diagnostics_tool::*;
pub use edit_file_tool::*;
pub use fetch_tool::*;
pub use find_path_tool::*;
pub use grep_tool::*;
@@ -33,6 +36,7 @@ pub use move_path_tool::*;
pub use now_tool::*;
pub use open_tool::*;
pub use read_file_tool::*;
pub use terminal_tool::*;
pub use thinking_tool::*;
pub use web_search_tool::*;

View File

@@ -273,14 +273,9 @@ impl AgentTool for EditFileTool {
};
let abs_path = project.read(cx).absolute_path(&project_path, cx);
if let Some(abs_path) = abs_path.clone() {
event_stream.update_fields(ToolCallUpdateFields {
locations: Some(vec![acp::ToolCallLocation {
path: abs_path,
line: None,
meta: None,
}]),
..Default::default()
});
event_stream.update_fields(
ToolCallUpdateFields::new().locations(vec![acp::ToolCallLocation::new(abs_path)]),
);
}
let authorize = self.authorize(&input, &event_stream, cx);
@@ -309,6 +304,40 @@ impl AgentTool for EditFileTool {
})?
.await?;
// Check if the file has been modified since the agent last read it
if let Some(abs_path) = abs_path.as_ref() {
let (last_read_mtime, current_mtime, is_dirty) = self.thread.update(cx, |thread, cx| {
let last_read = thread.file_read_times.get(abs_path).copied();
let current = buffer.read(cx).file().and_then(|file| file.disk_state().mtime());
let dirty = buffer.read(cx).is_dirty();
(last_read, current, dirty)
})?;
// Check for unsaved changes first - these indicate modifications we don't know about
if is_dirty {
anyhow::bail!(
"This file cannot be written to because it has unsaved changes. \
Please end the current conversation immediately by telling the user you want to write to this file (mention its path explicitly) but you can't write to it because it has unsaved changes. \
Ask the user to save that buffer's changes and to inform you when it's ok to proceed."
);
}
// Check if the file was modified on disk since we last read it
if let (Some(last_read), Some(current)) = (last_read_mtime, current_mtime) {
// MTime can be unreliable for comparisons, so our newtype intentionally
// doesn't support comparing them. If the mtime at all different
// (which could be because of a modification or because e.g. system clock changed),
// we pessimistically assume it was modified.
if current != last_read {
anyhow::bail!(
"The file {} has been modified since you last read it. \
Please read the file again to get the current state before editing it.",
input.path.display()
);
}
}
}
let diff = cx.new(|cx| Diff::new(buffer.clone(), cx))?;
event_stream.update_diff(diff.clone());
let _finalize_diff = util::defer({
@@ -355,10 +384,7 @@ impl AgentTool for EditFileTool {
range.start.to_point(&buffer.snapshot()).row
}).ok();
if let Some(abs_path) = abs_path.clone() {
event_stream.update_fields(ToolCallUpdateFields {
locations: Some(vec![ToolCallLocation { path: abs_path, line, meta: None }]),
..Default::default()
});
event_stream.update_fields(ToolCallUpdateFields::new().locations(vec![ToolCallLocation::new(abs_path).line(line)]));
}
emitted_location = true;
}
@@ -421,6 +447,17 @@ impl AgentTool for EditFileTool {
log.buffer_edited(buffer.clone(), cx);
})?;
// Update the recorded read time after a successful edit so consecutive edits work
if let Some(abs_path) = abs_path.as_ref() {
if let Some(new_mtime) = buffer.read_with(cx, |buffer, _| {
buffer.file().and_then(|file| file.disk_state().mtime())
})? {
self.thread.update(cx, |thread, _| {
thread.file_read_times.insert(abs_path.to_path_buf(), new_mtime);
})?;
}
}
let new_snapshot = buffer.read_with(cx, |buffer, _cx| buffer.snapshot())?;
let (new_text, unified_diff) = cx
.background_spawn({
@@ -1748,10 +1785,426 @@ mod tests {
}
}
#[gpui::test]
async fn test_file_read_times_tracking(cx: &mut TestAppContext) {
init_test(cx);
let fs = project::FakeFs::new(cx.executor());
fs.insert_tree(
"/root",
json!({
"test.txt": "original content"
}),
)
.await;
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
let context_server_registry =
cx.new(|cx| ContextServerRegistry::new(project.read(cx).context_server_store(), cx));
let model = Arc::new(FakeLanguageModel::default());
let thread = cx.new(|cx| {
Thread::new(
project.clone(),
cx.new(|_cx| ProjectContext::default()),
context_server_registry,
Templates::new(),
Some(model.clone()),
cx,
)
});
let action_log = thread.read_with(cx, |thread, _| thread.action_log().clone());
// Initially, file_read_times should be empty
let is_empty = thread.read_with(cx, |thread, _| thread.file_read_times.is_empty());
assert!(is_empty, "file_read_times should start empty");
// Create read tool
let read_tool = Arc::new(crate::ReadFileTool::new(
thread.downgrade(),
project.clone(),
action_log,
));
// Read the file to record the read time
cx.update(|cx| {
read_tool.clone().run(
crate::ReadFileToolInput {
path: "root/test.txt".to_string(),
start_line: None,
end_line: None,
},
ToolCallEventStream::test().0,
cx,
)
})
.await
.unwrap();
// Verify that file_read_times now contains an entry for the file
let has_entry = thread.read_with(cx, |thread, _| {
thread.file_read_times.len() == 1
&& thread
.file_read_times
.keys()
.any(|path| path.ends_with("test.txt"))
});
assert!(
has_entry,
"file_read_times should contain an entry after reading the file"
);
// Read the file again - should update the entry
cx.update(|cx| {
read_tool.clone().run(
crate::ReadFileToolInput {
path: "root/test.txt".to_string(),
start_line: None,
end_line: None,
},
ToolCallEventStream::test().0,
cx,
)
})
.await
.unwrap();
// Should still have exactly one entry
let has_one_entry = thread.read_with(cx, |thread, _| thread.file_read_times.len() == 1);
assert!(
has_one_entry,
"file_read_times should still have one entry after re-reading"
);
}
fn init_test(cx: &mut TestAppContext) {
cx.update(|cx| {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
});
}
#[gpui::test]
async fn test_consecutive_edits_work(cx: &mut TestAppContext) {
init_test(cx);
let fs = project::FakeFs::new(cx.executor());
fs.insert_tree(
"/root",
json!({
"test.txt": "original content"
}),
)
.await;
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
let context_server_registry =
cx.new(|cx| ContextServerRegistry::new(project.read(cx).context_server_store(), cx));
let model = Arc::new(FakeLanguageModel::default());
let thread = cx.new(|cx| {
Thread::new(
project.clone(),
cx.new(|_cx| ProjectContext::default()),
context_server_registry,
Templates::new(),
Some(model.clone()),
cx,
)
});
let languages = project.read_with(cx, |project, _| project.languages().clone());
let action_log = thread.read_with(cx, |thread, _| thread.action_log().clone());
let read_tool = Arc::new(crate::ReadFileTool::new(
thread.downgrade(),
project.clone(),
action_log,
));
let edit_tool = Arc::new(EditFileTool::new(
project.clone(),
thread.downgrade(),
languages,
Templates::new(),
));
// Read the file first
cx.update(|cx| {
read_tool.clone().run(
crate::ReadFileToolInput {
path: "root/test.txt".to_string(),
start_line: None,
end_line: None,
},
ToolCallEventStream::test().0,
cx,
)
})
.await
.unwrap();
// First edit should work
let edit_result = {
let edit_task = cx.update(|cx| {
edit_tool.clone().run(
EditFileToolInput {
display_description: "First edit".into(),
path: "root/test.txt".into(),
mode: EditFileMode::Edit,
},
ToolCallEventStream::test().0,
cx,
)
});
cx.executor().run_until_parked();
model.send_last_completion_stream_text_chunk(
"<old_text>original content</old_text><new_text>modified content</new_text>"
.to_string(),
);
model.end_last_completion_stream();
edit_task.await
};
assert!(
edit_result.is_ok(),
"First edit should succeed, got error: {:?}",
edit_result.as_ref().err()
);
// Second edit should also work because the edit updated the recorded read time
let edit_result = {
let edit_task = cx.update(|cx| {
edit_tool.clone().run(
EditFileToolInput {
display_description: "Second edit".into(),
path: "root/test.txt".into(),
mode: EditFileMode::Edit,
},
ToolCallEventStream::test().0,
cx,
)
});
cx.executor().run_until_parked();
model.send_last_completion_stream_text_chunk(
"<old_text>modified content</old_text><new_text>further modified content</new_text>".to_string(),
);
model.end_last_completion_stream();
edit_task.await
};
assert!(
edit_result.is_ok(),
"Second consecutive edit should succeed, got error: {:?}",
edit_result.as_ref().err()
);
}
#[gpui::test]
async fn test_external_modification_detected(cx: &mut TestAppContext) {
init_test(cx);
let fs = project::FakeFs::new(cx.executor());
fs.insert_tree(
"/root",
json!({
"test.txt": "original content"
}),
)
.await;
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
let context_server_registry =
cx.new(|cx| ContextServerRegistry::new(project.read(cx).context_server_store(), cx));
let model = Arc::new(FakeLanguageModel::default());
let thread = cx.new(|cx| {
Thread::new(
project.clone(),
cx.new(|_cx| ProjectContext::default()),
context_server_registry,
Templates::new(),
Some(model.clone()),
cx,
)
});
let languages = project.read_with(cx, |project, _| project.languages().clone());
let action_log = thread.read_with(cx, |thread, _| thread.action_log().clone());
let read_tool = Arc::new(crate::ReadFileTool::new(
thread.downgrade(),
project.clone(),
action_log,
));
let edit_tool = Arc::new(EditFileTool::new(
project.clone(),
thread.downgrade(),
languages,
Templates::new(),
));
// Read the file first
cx.update(|cx| {
read_tool.clone().run(
crate::ReadFileToolInput {
path: "root/test.txt".to_string(),
start_line: None,
end_line: None,
},
ToolCallEventStream::test().0,
cx,
)
})
.await
.unwrap();
// Simulate external modification - advance time and save file
cx.background_executor
.advance_clock(std::time::Duration::from_secs(2));
fs.save(
path!("/root/test.txt").as_ref(),
&"externally modified content".into(),
language::LineEnding::Unix,
)
.await
.unwrap();
// Reload the buffer to pick up the new mtime
let project_path = project
.read_with(cx, |project, cx| {
project.find_project_path("root/test.txt", cx)
})
.expect("Should find project path");
let buffer = project
.update(cx, |project, cx| project.open_buffer(project_path, cx))
.await
.unwrap();
buffer
.update(cx, |buffer, cx| buffer.reload(cx))
.await
.unwrap();
cx.executor().run_until_parked();
// Try to edit - should fail because file was modified externally
let result = cx
.update(|cx| {
edit_tool.clone().run(
EditFileToolInput {
display_description: "Edit after external change".into(),
path: "root/test.txt".into(),
mode: EditFileMode::Edit,
},
ToolCallEventStream::test().0,
cx,
)
})
.await;
assert!(
result.is_err(),
"Edit should fail after external modification"
);
let error_msg = result.unwrap_err().to_string();
assert!(
error_msg.contains("has been modified since you last read it"),
"Error should mention file modification, got: {}",
error_msg
);
}
#[gpui::test]
async fn test_dirty_buffer_detected(cx: &mut TestAppContext) {
init_test(cx);
let fs = project::FakeFs::new(cx.executor());
fs.insert_tree(
"/root",
json!({
"test.txt": "original content"
}),
)
.await;
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
let context_server_registry =
cx.new(|cx| ContextServerRegistry::new(project.read(cx).context_server_store(), cx));
let model = Arc::new(FakeLanguageModel::default());
let thread = cx.new(|cx| {
Thread::new(
project.clone(),
cx.new(|_cx| ProjectContext::default()),
context_server_registry,
Templates::new(),
Some(model.clone()),
cx,
)
});
let languages = project.read_with(cx, |project, _| project.languages().clone());
let action_log = thread.read_with(cx, |thread, _| thread.action_log().clone());
let read_tool = Arc::new(crate::ReadFileTool::new(
thread.downgrade(),
project.clone(),
action_log,
));
let edit_tool = Arc::new(EditFileTool::new(
project.clone(),
thread.downgrade(),
languages,
Templates::new(),
));
// Read the file first
cx.update(|cx| {
read_tool.clone().run(
crate::ReadFileToolInput {
path: "root/test.txt".to_string(),
start_line: None,
end_line: None,
},
ToolCallEventStream::test().0,
cx,
)
})
.await
.unwrap();
// Open the buffer and make it dirty by editing without saving
let project_path = project
.read_with(cx, |project, cx| {
project.find_project_path("root/test.txt", cx)
})
.expect("Should find project path");
let buffer = project
.update(cx, |project, cx| project.open_buffer(project_path, cx))
.await
.unwrap();
// Make an in-memory edit to the buffer (making it dirty)
buffer.update(cx, |buffer, cx| {
let end_point = buffer.max_point();
buffer.edit([(end_point..end_point, " added text")], None, cx);
});
// Verify buffer is dirty
let is_dirty = buffer.read_with(cx, |buffer, _| buffer.is_dirty());
assert!(is_dirty, "Buffer should be dirty after in-memory edit");
// Try to edit - should fail because buffer has unsaved changes
let result = cx
.update(|cx| {
edit_tool.clone().run(
EditFileToolInput {
display_description: "Edit with dirty buffer".into(),
path: "root/test.txt".into(),
mode: EditFileMode::Edit,
},
ToolCallEventStream::test().0,
cx,
)
})
.await;
assert!(result.is_err(), "Edit should fail when buffer is dirty");
let error_msg = result.unwrap_err().to_string();
assert!(
error_msg.contains("cannot be written to because it has unsaved changes"),
"Error should mention unsaved changes, got: {}",
error_msg
);
}
}

View File

@@ -118,33 +118,29 @@ impl AgentTool for FindPathTool {
let paginated_matches: &[PathBuf] = &matches[cmp::min(input.offset, matches.len())
..cmp::min(input.offset + RESULTS_PER_PAGE, matches.len())];
event_stream.update_fields(acp::ToolCallUpdateFields {
title: Some(if paginated_matches.is_empty() {
"No matches".into()
} else if paginated_matches.len() == 1 {
"1 match".into()
} else {
format!("{} matches", paginated_matches.len())
}),
content: Some(
paginated_matches
.iter()
.map(|path| acp::ToolCallContent::Content {
content: acp::ContentBlock::ResourceLink(acp::ResourceLink {
uri: format!("file://{}", path.display()),
name: path.to_string_lossy().into(),
annotations: None,
description: None,
mime_type: None,
size: None,
title: None,
meta: None,
}),
})
.collect(),
),
..Default::default()
});
event_stream.update_fields(
acp::ToolCallUpdateFields::new()
.title(if paginated_matches.is_empty() {
"No matches".into()
} else if paginated_matches.len() == 1 {
"1 match".into()
} else {
format!("{} matches", paginated_matches.len())
})
.content(
paginated_matches
.iter()
.map(|path| {
acp::ToolCallContent::Content(acp::Content::new(
acp::ContentBlock::ResourceLink(acp::ResourceLink::new(
path.to_string_lossy(),
format!("file://{}", path.display()),
)),
))
})
.collect::<Vec<_>>(),
),
);
Ok(FindPathToolOutput {
offset: input.offset,
@@ -177,7 +173,7 @@ fn search_paths(glob: &str, project: Entity<Project>, cx: &mut App) -> Task<Resu
let mut results = Vec::new();
for snapshot in snapshots {
for entry in snapshot.entries(false, 0) {
if path_matcher.is_match(snapshot.root_name().join(&entry.path).as_std_path()) {
if path_matcher.is_match(&snapshot.root_name().join(&entry.path)) {
results.push(snapshot.absolutize(&entry.path));
}
}

View File

@@ -32,8 +32,21 @@ pub struct GrepToolInput {
/// Do NOT specify a path here! This will only be matched against the code **content**.
pub regex: String,
/// A glob pattern for the paths of files to include in the search.
/// Supports standard glob patterns like "**/*.rs" or "src/**/*.ts".
/// Supports standard glob patterns like "**/*.rs" or "frontend/src/**/*.ts".
/// If omitted, all files in the project will be searched.
///
/// The glob pattern is matched against the full path including the project root directory.
///
/// <example>
/// If the project has the following root directories:
///
/// - /a/b/backend
/// - /c/d/frontend
///
/// Use "backend/**/*.rs" to search only Rust files in the backend root directory.
/// Use "frontend/src/**/*.ts" to search TypeScript files only in the frontend root directory (sub-directory "src").
/// Use "**/*.rs" to search Rust files across all root directories.
/// </example>
pub include_pattern: Option<String>,
/// Optional starting position for paginated results (0-based).
/// When not provided, starts from the beginning.
@@ -132,8 +145,7 @@ impl AgentTool for GrepTool {
let exclude_patterns = global_settings
.file_scan_exclusions
.sources()
.iter()
.chain(global_settings.private_files.sources().iter());
.chain(global_settings.private_files.sources());
match PathMatcher::new(exclude_patterns, path_style) {
Ok(matcher) => matcher,
@@ -310,7 +322,6 @@ mod tests {
use super::*;
use gpui::{TestAppContext, UpdateGlobal};
use language::{Language, LanguageConfig, LanguageMatcher};
use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
@@ -552,7 +563,7 @@ mod tests {
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
project.update(cx, |project, _cx| {
project.languages().add(rust_lang().into())
project.languages().add(language::rust_lang())
});
project
@@ -781,22 +792,6 @@ mod tests {
});
}
fn rust_lang() -> Language {
Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
..Default::default()
},
Some(tree_sitter_rust::LANGUAGE.into()),
)
.with_outline_query(include_str!("../../../languages/src/rust/outline.scm"))
.unwrap()
}
#[gpui::test]
async fn test_grep_security_boundaries(cx: &mut TestAppContext) {
init_test(cx);

View File

@@ -1,7 +1,7 @@
use action_log::ActionLog;
use agent_client_protocol::{self as acp, ToolCallUpdateFields};
use anyhow::{Context as _, Result, anyhow};
use gpui::{App, Entity, SharedString, Task};
use gpui::{App, Entity, SharedString, Task, WeakEntity};
use indoc::formatdoc;
use language::Point;
use language_model::{LanguageModelImage, LanguageModelToolResultContent};
@@ -12,11 +12,14 @@ use settings::Settings;
use std::sync::Arc;
use util::markdown::MarkdownCodeBlock;
use crate::{AgentTool, ToolCallEventStream, outline};
use crate::{AgentTool, Thread, ToolCallEventStream, outline};
/// Reads the content of the given file in the project.
///
/// - Never attempt to read a path that hasn't been previously mentioned.
/// - For large files, this tool returns a file outline with symbol names and line numbers instead of the full content.
/// This outline IS a successful response - use the line numbers to read specific sections with start_line/end_line.
/// Do NOT retry reading the same file without line numbers if you receive an outline.
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct ReadFileToolInput {
/// The relative path of the file to read.
@@ -42,13 +45,19 @@ pub struct ReadFileToolInput {
}
pub struct ReadFileTool {
thread: WeakEntity<Thread>,
project: Entity<Project>,
action_log: Entity<ActionLog>,
}
impl ReadFileTool {
pub fn new(project: Entity<Project>, action_log: Entity<ActionLog>) -> Self {
pub fn new(
thread: WeakEntity<Thread>,
project: Entity<Project>,
action_log: Entity<ActionLog>,
) -> Self {
Self {
thread,
project,
action_log,
}
@@ -144,14 +153,10 @@ impl AgentTool for ReadFileTool {
let file_path = input.path.clone();
event_stream.update_fields(ToolCallUpdateFields {
locations: Some(vec![acp::ToolCallLocation {
path: abs_path.clone(),
line: input.start_line.map(|line| line.saturating_sub(1)),
meta: None,
}]),
..Default::default()
});
event_stream.update_fields(ToolCallUpdateFields::new().locations(vec![
acp::ToolCallLocation::new(&abs_path)
.line(input.start_line.map(|line| line.saturating_sub(1))),
]));
if image_store::is_image_file(&self.project, &project_path, cx) {
return cx.spawn(async move |cx| {
@@ -195,6 +200,17 @@ impl AgentTool for ReadFileTool {
anyhow::bail!("{file_path} not found");
}
// Record the file read time and mtime
if let Some(mtime) = buffer.read_with(cx, |buffer, _| {
buffer.file().and_then(|file| file.disk_state().mtime())
})? {
self.thread
.update(cx, |thread, _| {
thread.file_read_times.insert(abs_path.to_path_buf(), mtime);
})
.ok();
}
let mut anchor = None;
// Check if specific line ranges are provided
@@ -237,16 +253,15 @@ impl AgentTool for ReadFileTool {
if buffer_content.is_outline {
Ok(formatdoc! {"
This file was too big to read all at once.
SUCCESS: File outline retrieved. This file is too large to read all at once, so the outline below shows the file's structure with line numbers.
IMPORTANT: Do NOT retry this call without line numbers - you will get the same outline.
Instead, use the line numbers below to read specific sections by calling this tool again with start_line and end_line parameters.
{}
Using the line numbers in this outline, you can call this tool again
while specifying the start_line and end_line fields to see the
implementations of symbols in the outline.
Alternatively, you can fall back to the `grep` tool (if available)
to search the file for specific content.", buffer_content.text
NEXT STEPS: To read a specific symbol's implementation, call read_file with the same path plus start_line and end_line from the outline above.
For example, to read a function shown as [L100-150], use start_line: 100 and end_line: 150.", buffer_content.text
}
.into())
} else {
@@ -258,7 +273,9 @@ impl AgentTool for ReadFileTool {
project.set_agent_location(
Some(AgentLocation {
buffer: buffer.downgrade(),
position: anchor.unwrap_or(text::Anchor::MIN),
position: anchor.unwrap_or_else(|| {
text::Anchor::min_for_buffer(buffer.read(cx).remote_id())
}),
}),
cx,
);
@@ -268,12 +285,9 @@ impl AgentTool for ReadFileTool {
text,
}
.to_string();
event_stream.update_fields(ToolCallUpdateFields {
content: Some(vec![acp::ToolCallContent::Content {
content: markdown.into(),
}]),
..Default::default()
})
event_stream.update_fields(ToolCallUpdateFields::new().content(vec![
acp::ToolCallContent::Content(acp::Content::new(markdown)),
]));
}
})?;
@@ -285,11 +299,14 @@ impl AgentTool for ReadFileTool {
#[cfg(test)]
mod test {
use super::*;
use crate::{ContextServerRegistry, Templates, Thread};
use gpui::{AppContext, TestAppContext, UpdateGlobal as _};
use language::{Language, LanguageConfig, LanguageMatcher, tree_sitter_rust};
use language_model::fake_provider::FakeLanguageModel;
use project::{FakeFs, Project};
use prompt_store::ProjectContext;
use serde_json::json;
use settings::SettingsStore;
use std::sync::Arc;
use util::path;
#[gpui::test]
@@ -300,7 +317,20 @@ mod test {
fs.insert_tree(path!("/root"), json!({})).await;
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
let action_log = cx.new(|_| ActionLog::new(project.clone()));
let tool = Arc::new(ReadFileTool::new(project, action_log));
let context_server_registry =
cx.new(|cx| ContextServerRegistry::new(project.read(cx).context_server_store(), cx));
let model = Arc::new(FakeLanguageModel::default());
let thread = cx.new(|cx| {
Thread::new(
project.clone(),
cx.new(|_cx| ProjectContext::default()),
context_server_registry,
Templates::new(),
Some(model),
cx,
)
});
let tool = Arc::new(ReadFileTool::new(thread.downgrade(), project, action_log));
let (event_stream, _) = ToolCallEventStream::test();
let result = cx
@@ -333,7 +363,20 @@ mod test {
.await;
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
let action_log = cx.new(|_| ActionLog::new(project.clone()));
let tool = Arc::new(ReadFileTool::new(project, action_log));
let context_server_registry =
cx.new(|cx| ContextServerRegistry::new(project.read(cx).context_server_store(), cx));
let model = Arc::new(FakeLanguageModel::default());
let thread = cx.new(|cx| {
Thread::new(
project.clone(),
cx.new(|_cx| ProjectContext::default()),
context_server_registry,
Templates::new(),
Some(model),
cx,
)
});
let tool = Arc::new(ReadFileTool::new(thread.downgrade(), project, action_log));
let result = cx
.update(|cx| {
let input = ReadFileToolInput {
@@ -361,9 +404,22 @@ mod test {
.await;
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(Arc::new(rust_lang()));
language_registry.add(language::rust_lang());
let action_log = cx.new(|_| ActionLog::new(project.clone()));
let tool = Arc::new(ReadFileTool::new(project, action_log));
let context_server_registry =
cx.new(|cx| ContextServerRegistry::new(project.read(cx).context_server_store(), cx));
let model = Arc::new(FakeLanguageModel::default());
let thread = cx.new(|cx| {
Thread::new(
project.clone(),
cx.new(|_cx| ProjectContext::default()),
context_server_registry,
Templates::new(),
Some(model),
cx,
)
});
let tool = Arc::new(ReadFileTool::new(thread.downgrade(), project, action_log));
let result = cx
.update(|cx| {
let input = ReadFileToolInput {
@@ -378,7 +434,7 @@ mod test {
let content = result.to_str().unwrap();
assert_eq!(
content.lines().skip(4).take(6).collect::<Vec<_>>(),
content.lines().skip(7).take(6).collect::<Vec<_>>(),
vec![
"struct Test0 [L1-4]",
" a [L2]",
@@ -413,7 +469,7 @@ mod test {
pretty_assertions::assert_eq!(
content
.lines()
.skip(4)
.skip(7)
.take(expected_content.len())
.collect::<Vec<_>>(),
expected_content
@@ -435,7 +491,20 @@ mod test {
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
let action_log = cx.new(|_| ActionLog::new(project.clone()));
let tool = Arc::new(ReadFileTool::new(project, action_log));
let context_server_registry =
cx.new(|cx| ContextServerRegistry::new(project.read(cx).context_server_store(), cx));
let model = Arc::new(FakeLanguageModel::default());
let thread = cx.new(|cx| {
Thread::new(
project.clone(),
cx.new(|_cx| ProjectContext::default()),
context_server_registry,
Templates::new(),
Some(model),
cx,
)
});
let tool = Arc::new(ReadFileTool::new(thread.downgrade(), project, action_log));
let result = cx
.update(|cx| {
let input = ReadFileToolInput {
@@ -463,7 +532,20 @@ mod test {
.await;
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
let action_log = cx.new(|_| ActionLog::new(project.clone()));
let tool = Arc::new(ReadFileTool::new(project, action_log));
let context_server_registry =
cx.new(|cx| ContextServerRegistry::new(project.read(cx).context_server_store(), cx));
let model = Arc::new(FakeLanguageModel::default());
let thread = cx.new(|cx| {
Thread::new(
project.clone(),
cx.new(|_cx| ProjectContext::default()),
context_server_registry,
Templates::new(),
Some(model),
cx,
)
});
let tool = Arc::new(ReadFileTool::new(thread.downgrade(), project, action_log));
// start_line of 0 should be treated as 1
let result = cx
@@ -512,49 +594,6 @@ mod test {
});
}
fn rust_lang() -> Language {
Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
..Default::default()
},
Some(tree_sitter_rust::LANGUAGE.into()),
)
.with_outline_query(
r#"
(line_comment) @annotation
(struct_item
"struct" @context
name: (_) @name) @item
(enum_item
"enum" @context
name: (_) @name) @item
(enum_variant
name: (_) @name) @item
(field_declaration
name: (_) @name) @item
(impl_item
"impl" @context
trait: (_)? @name
"for"? @context
type: (_) @name
body: (_ "{" (_)* "}")) @item
(function_item
"fn" @context
name: (_) @name) @item
(mod_item
"mod" @context
name: (_) @name) @item
"#,
)
.unwrap()
}
#[gpui::test]
async fn test_read_file_security(cx: &mut TestAppContext) {
init_test(cx);
@@ -607,7 +646,20 @@ mod test {
let project = Project::test(fs.clone(), [path!("/project_root").as_ref()], cx).await;
let action_log = cx.new(|_| ActionLog::new(project.clone()));
let tool = Arc::new(ReadFileTool::new(project, action_log));
let context_server_registry =
cx.new(|cx| ContextServerRegistry::new(project.read(cx).context_server_store(), cx));
let model = Arc::new(FakeLanguageModel::default());
let thread = cx.new(|cx| {
Thread::new(
project.clone(),
cx.new(|_cx| ProjectContext::default()),
context_server_registry,
Templates::new(),
Some(model),
cx,
)
});
let tool = Arc::new(ReadFileTool::new(thread.downgrade(), project, action_log));
// Reading a file outside the project worktree should fail
let result = cx
@@ -821,7 +873,24 @@ mod test {
.await;
let action_log = cx.new(|_| ActionLog::new(project.clone()));
let tool = Arc::new(ReadFileTool::new(project.clone(), action_log.clone()));
let context_server_registry =
cx.new(|cx| ContextServerRegistry::new(project.read(cx).context_server_store(), cx));
let model = Arc::new(FakeLanguageModel::default());
let thread = cx.new(|cx| {
Thread::new(
project.clone(),
cx.new(|_cx| ProjectContext::default()),
context_server_registry,
Templates::new(),
Some(model),
cx,
)
});
let tool = Arc::new(ReadFileTool::new(
thread.downgrade(),
project.clone(),
action_log.clone(),
));
// Test reading allowed files in worktree1
let result = cx

View File

@@ -112,10 +112,9 @@ impl AgentTool for TerminalTool {
.await?;
let terminal_id = terminal.id(cx)?;
event_stream.update_fields(acp::ToolCallUpdateFields {
content: Some(vec![acp::ToolCallContent::Terminal { terminal_id }]),
..Default::default()
});
event_stream.update_fields(acp::ToolCallUpdateFields::new().content(vec![
acp::ToolCallContent::Terminal(acp::Terminal::new(terminal_id)),
]));
let exit_status = terminal.wait_for_exit(cx)?.await;
let output = terminal.current_output(cx)?;

View File

@@ -43,10 +43,8 @@ impl AgentTool for ThinkingTool {
event_stream: ToolCallEventStream,
_cx: &mut App,
) -> Task<Result<String>> {
event_stream.update_fields(acp::ToolCallUpdateFields {
content: Some(vec![input.content.into()]),
..Default::default()
});
event_stream
.update_fields(acp::ToolCallUpdateFields::new().content(vec![input.content.into()]));
Task::ready(Ok("Finished thinking.".to_string()))
}
}

View File

@@ -76,10 +76,8 @@ impl AgentTool for WebSearchTool {
let response = match search_task.await {
Ok(response) => response,
Err(err) => {
event_stream.update_fields(acp::ToolCallUpdateFields {
title: Some("Web Search Failed".to_string()),
..Default::default()
});
event_stream
.update_fields(acp::ToolCallUpdateFields::new().title("Web Search Failed"));
return Err(err);
}
};
@@ -107,26 +105,23 @@ fn emit_update(response: &WebSearchResponse, event_stream: &ToolCallEventStream)
} else {
format!("{} results", response.results.len())
};
event_stream.update_fields(acp::ToolCallUpdateFields {
title: Some(format!("Searched the web: {result_text}")),
content: Some(
response
.results
.iter()
.map(|result| acp::ToolCallContent::Content {
content: acp::ContentBlock::ResourceLink(acp::ResourceLink {
name: result.title.clone(),
uri: result.url.clone(),
title: Some(result.title.clone()),
description: Some(result.text.clone()),
mime_type: None,
annotations: None,
size: None,
meta: None,
}),
})
.collect(),
),
..Default::default()
});
event_stream.update_fields(
acp::ToolCallUpdateFields::new()
.title(format!("Searched the web: {result_text}"))
.content(
response
.results
.iter()
.map(|result| {
acp::ToolCallContent::Content(acp::Content::new(
acp::ContentBlock::ResourceLink(
acp::ResourceLink::new(result.title.clone(), result.url.clone())
.title(result.title.clone())
.description(result.text.clone()),
),
))
})
.collect::<Vec<_>>(),
),
);
}

View File

@@ -35,6 +35,7 @@ pub struct AcpConnection {
auth_methods: Vec<acp::AuthMethod>,
agent_capabilities: acp::AgentCapabilities,
default_mode: Option<acp::SessionModeId>,
default_model: Option<acp::ModelId>,
root_dir: PathBuf,
// NB: Don't move this into the wait_task, since we need to ensure the process is
// killed on drop (setting kill_on_drop on the command seems to not always work).
@@ -57,6 +58,7 @@ pub async fn connect(
command: AgentServerCommand,
root_dir: &Path,
default_mode: Option<acp::SessionModeId>,
default_model: Option<acp::ModelId>,
is_remote: bool,
cx: &mut AsyncApp,
) -> Result<Rc<dyn AgentConnection>> {
@@ -66,6 +68,7 @@ pub async fn connect(
command.clone(),
root_dir,
default_mode,
default_model,
is_remote,
cx,
)
@@ -73,7 +76,7 @@ pub async fn connect(
Ok(Rc::new(conn) as _)
}
const MINIMUM_SUPPORTED_VERSION: acp::ProtocolVersion = acp::V1;
const MINIMUM_SUPPORTED_VERSION: acp::ProtocolVersion = acp::ProtocolVersion::V1;
impl AcpConnection {
pub async fn stdio(
@@ -82,6 +85,7 @@ impl AcpConnection {
command: AgentServerCommand,
root_dir: &Path,
default_mode: Option<acp::SessionModeId>,
default_model: Option<acp::ModelId>,
is_remote: bool,
cx: &mut AsyncApp,
) -> Result<Self> {
@@ -170,28 +174,25 @@ impl AcpConnection {
})?;
let response = connection
.initialize(acp::InitializeRequest {
protocol_version: acp::VERSION,
client_capabilities: acp::ClientCapabilities {
fs: acp::FileSystemCapability {
read_text_file: true,
write_text_file: true,
meta: None,
},
terminal: true,
meta: Some(serde_json::json!({
// Experimental: Allow for rendering terminal output from the agents
"terminal_output": true,
"terminal-auth": true,
})),
},
client_info: Some(acp::Implementation {
name: "zed".to_owned(),
title: release_channel.map(|c| c.to_owned()),
version,
}),
meta: None,
})
.initialize(
acp::InitializeRequest::new(acp::ProtocolVersion::V1)
.client_capabilities(
acp::ClientCapabilities::new()
.fs(acp::FileSystemCapability::new()
.read_text_file(true)
.write_text_file(true))
.terminal(true)
// Experimental: Allow for rendering terminal output from the agents
.meta(acp::Meta::from_iter([
("terminal_output".into(), true.into()),
("terminal-auth".into(), true.into()),
])),
)
.client_info(
acp::Implementation::new("zed", version)
.title(release_channel.map(ToOwned::to_owned)),
),
)
.await?;
if response.protocol_version < MINIMUM_SUPPORTED_VERSION {
@@ -207,6 +208,7 @@ impl AcpConnection {
sessions,
agent_capabilities: response.agent_capabilities,
default_mode,
default_model,
_io_task: io_task,
_wait_task: wait_task,
_stderr_task: stderr_task,
@@ -245,6 +247,7 @@ impl AgentConnection for AcpConnection {
let conn = self.connection.clone();
let sessions = self.sessions.clone();
let default_mode = self.default_mode.clone();
let default_model = self.default_model.clone();
let cwd = cwd.to_path_buf();
let context_server_store = project.read(cx).context_server_store().read(cx);
let mcp_servers = if project.read(cx).is_local() {
@@ -253,23 +256,37 @@ impl AgentConnection for AcpConnection {
.iter()
.filter_map(|id| {
let configuration = context_server_store.configuration_for_server(id)?;
let command = configuration.command();
Some(acp::McpServer::Stdio {
name: id.0.to_string(),
command: command.path.clone(),
args: command.args.clone(),
env: if let Some(env) = command.env.as_ref() {
env.iter()
.map(|(name, value)| acp::EnvVariable {
name: name.clone(),
value: value.clone(),
meta: None,
})
.collect()
} else {
vec![]
},
})
match &*configuration {
project::context_server_store::ContextServerConfiguration::Custom {
command,
..
}
| project::context_server_store::ContextServerConfiguration::Extension {
command,
..
} => Some(acp::McpServer::Stdio(
acp::McpServerStdio::new(id.0.to_string(), &command.path)
.args(command.args.clone())
.env(if let Some(env) = command.env.as_ref() {
env.iter()
.map(|(name, value)| acp::EnvVariable::new(name, value))
.collect()
} else {
vec![]
}),
)),
project::context_server_store::ContextServerConfiguration::Http {
url,
headers,
} => Some(acp::McpServer::Http(
acp::McpServerHttp::new(id.0.to_string(), url.to_string()).headers(
headers
.iter()
.map(|(name, value)| acp::HttpHeader::new(name, value))
.collect(),
),
)),
}
})
.collect()
} else {
@@ -281,13 +298,13 @@ impl AgentConnection for AcpConnection {
cx.spawn(async move |cx| {
let response = conn
.new_session(acp::NewSessionRequest { mcp_servers, cwd, meta: None })
.new_session(acp::NewSessionRequest::new(cwd).mcp_servers(mcp_servers))
.await
.map_err(|err| {
if err.code == acp::ErrorCode::AUTH_REQUIRED.code {
if err.code == acp::ErrorCode::AuthRequired {
let mut error = AuthRequired::new();
if err.message != acp::ErrorCode::AUTH_REQUIRED.message {
if err.message != acp::ErrorCode::AuthRequired.to_string() {
error = error.with_description(err.message);
}
@@ -312,12 +329,9 @@ impl AgentConnection for AcpConnection {
let default_mode = default_mode.clone();
let session_id = response.session_id.clone();
let modes = modes.clone();
let conn = conn.clone();
async move |_| {
let result = conn.set_session_mode(acp::SetSessionModeRequest {
session_id,
mode_id: default_mode,
meta: None,
})
let result = conn.set_session_mode(acp::SetSessionModeRequest::new(session_id, default_mode))
.await.log_err();
if result.is_none() {
@@ -346,6 +360,49 @@ impl AgentConnection for AcpConnection {
}
}
if let Some(default_model) = default_model {
if let Some(models) = models.as_ref() {
let mut models_ref = models.borrow_mut();
let has_model = models_ref.available_models.iter().any(|model| model.model_id == default_model);
if has_model {
let initial_model_id = models_ref.current_model_id.clone();
cx.spawn({
let default_model = default_model.clone();
let session_id = response.session_id.clone();
let models = models.clone();
let conn = conn.clone();
async move |_| {
let result = conn.set_session_model(acp::SetSessionModelRequest::new(session_id, default_model))
.await.log_err();
if result.is_none() {
models.borrow_mut().current_model_id = initial_model_id;
}
}
}).detach();
models_ref.current_model_id = default_model;
} else {
let available_models = models_ref
.available_models
.iter()
.map(|model| format!("- `{}`: {}", model.model_id, model.name))
.collect::<Vec<_>>()
.join("\n");
log::warn!(
"`{default_model}` is not a valid {name} model. Available options:\n{available_models}",
);
}
} else {
log::warn!(
"`{name}` does not support model selection, but `default_model` was set in settings.",
);
}
}
let session_id = response.session_id;
let action_log = cx.new(|_| ActionLog::new(project.clone()))?;
let thread = cx.new(|cx| {
@@ -381,12 +438,8 @@ impl AgentConnection for AcpConnection {
fn authenticate(&self, method_id: acp::AuthMethodId, cx: &mut App) -> Task<Result<()>> {
let conn = self.connection.clone();
cx.foreground_executor().spawn(async move {
conn.authenticate(acp::AuthenticateRequest {
method_id: method_id.clone(),
meta: None,
})
.await?;
conn.authenticate(acp::AuthenticateRequest::new(method_id))
.await?;
Ok(())
})
}
@@ -413,11 +466,11 @@ impl AgentConnection for AcpConnection {
match result {
Ok(response) => Ok(response),
Err(err) => {
if err.code == acp::ErrorCode::AUTH_REQUIRED.code {
if err.code == acp::ErrorCode::AuthRequired {
return Err(anyhow!(acp::Error::auth_required()));
}
if err.code != ErrorCode::INTERNAL_ERROR.code {
if err.code != ErrorCode::InternalError {
anyhow::bail!(err)
}
@@ -440,10 +493,7 @@ impl AgentConnection for AcpConnection {
&& (details.contains("This operation was aborted")
|| details.contains("The user aborted a request"))
{
Ok(acp::PromptResponse {
stop_reason: acp::StopReason::Cancelled,
meta: None,
})
Ok(acp::PromptResponse::new(acp::StopReason::Cancelled))
} else {
Err(anyhow!(details))
}
@@ -460,10 +510,7 @@ impl AgentConnection for AcpConnection {
session.suppress_abort_err = true;
}
let conn = self.connection.clone();
let params = acp::CancelNotification {
session_id: session_id.clone(),
meta: None,
};
let params = acp::CancelNotification::new(session_id.clone());
cx.foreground_executor()
.spawn(async move { conn.cancel(params).await })
.detach();
@@ -544,11 +591,7 @@ impl acp_thread::AgentSessionModes for AcpSessionModes {
let state = self.state.clone();
cx.foreground_executor().spawn(async move {
let result = connection
.set_session_mode(acp::SetSessionModeRequest {
session_id,
mode_id,
meta: None,
})
.set_session_mode(acp::SetSessionModeRequest::new(session_id, mode_id))
.await;
if result.is_err() {
@@ -607,11 +650,7 @@ impl acp_thread::AgentModelSelector for AcpModelSelector {
let state = self.state.clone();
cx.foreground_executor().spawn(async move {
let result = connection
.set_session_model(acp::SetSessionModelRequest {
session_id,
model_id,
meta: None,
})
.set_session_model(acp::SetSessionModelRequest::new(session_id, model_id))
.await;
if result.is_err() {
@@ -673,10 +712,7 @@ impl acp::Client for ClientDelegate {
let outcome = task.await;
Ok(acp::RequestPermissionResponse {
outcome,
meta: None,
})
Ok(acp::RequestPermissionResponse::new(outcome))
}
async fn write_text_file(
@@ -708,10 +744,7 @@ impl acp::Client for ClientDelegate {
let content = task.await?;
Ok(acp::ReadTextFileResponse {
content,
meta: None,
})
Ok(acp::ReadTextFileResponse::new(content))
}
async fn session_notification(
@@ -746,7 +779,7 @@ impl acp::Client for ClientDelegate {
if let Some(terminal_info) = meta.get("terminal_info") {
if let Some(id_str) = terminal_info.get("terminal_id").and_then(|v| v.as_str())
{
let terminal_id = acp::TerminalId(id_str.into());
let terminal_id = acp::TerminalId::new(id_str);
let cwd = terminal_info
.get("cwd")
.and_then(|v| v.as_str().map(PathBuf::from));
@@ -762,7 +795,7 @@ impl acp::Client for ClientDelegate {
let lower = cx.new(|cx| builder.subscribe(cx));
thread.on_terminal_provider_event(
TerminalProviderEvent::Created {
terminal_id: terminal_id.clone(),
terminal_id,
label: tc.title.clone(),
cwd,
output_byte_limit: None,
@@ -787,15 +820,12 @@ impl acp::Client for ClientDelegate {
if let Some(meta) = &tcu.meta {
if let Some(term_out) = meta.get("terminal_output") {
if let Some(id_str) = term_out.get("terminal_id").and_then(|v| v.as_str()) {
let terminal_id = acp::TerminalId(id_str.into());
let terminal_id = acp::TerminalId::new(id_str);
if let Some(s) = term_out.get("data").and_then(|v| v.as_str()) {
let data = s.as_bytes().to_vec();
let _ = session.thread.update(&mut self.cx.clone(), |thread, cx| {
thread.on_terminal_provider_event(
TerminalProviderEvent::Output {
terminal_id: terminal_id.clone(),
data,
},
TerminalProviderEvent::Output { terminal_id, data },
cx,
);
});
@@ -806,21 +836,24 @@ impl acp::Client for ClientDelegate {
// terminal_exit
if let Some(term_exit) = meta.get("terminal_exit") {
if let Some(id_str) = term_exit.get("terminal_id").and_then(|v| v.as_str()) {
let terminal_id = acp::TerminalId(id_str.into());
let status = acp::TerminalExitStatus {
exit_code: term_exit
.get("exit_code")
.and_then(|v| v.as_u64())
.map(|i| i as u32),
signal: term_exit
.get("signal")
.and_then(|v| v.as_str().map(|s| s.to_string())),
meta: None,
};
let terminal_id = acp::TerminalId::new(id_str);
let status = acp::TerminalExitStatus::new()
.exit_code(
term_exit
.get("exit_code")
.and_then(|v| v.as_u64())
.map(|i| i as u32),
)
.signal(
term_exit
.get("signal")
.and_then(|v| v.as_str().map(|s| s.to_string())),
);
let _ = session.thread.update(&mut self.cx.clone(), |thread, cx| {
thread.on_terminal_provider_event(
TerminalProviderEvent::Exit {
terminal_id: terminal_id.clone(),
terminal_id,
status,
},
cx,
@@ -857,7 +890,7 @@ impl acp::Client for ClientDelegate {
// Register with renderer
let terminal_entity = thread.update(&mut self.cx.clone(), |thread, cx| {
thread.register_terminal_created(
acp::TerminalId(uuid::Uuid::new_v4().to_string().into()),
acp::TerminalId::new(uuid::Uuid::new_v4().to_string()),
format!("{} {}", args.command, args.args.join(" ")),
args.cwd.clone(),
args.output_byte_limit,
@@ -867,10 +900,7 @@ impl acp::Client for ClientDelegate {
})?;
let terminal_id =
terminal_entity.read_with(&self.cx, |terminal, _| terminal.id().clone())?;
Ok(acp::CreateTerminalResponse {
terminal_id,
meta: None,
})
Ok(acp::CreateTerminalResponse::new(terminal_id))
}
async fn kill_terminal_command(
@@ -931,10 +961,7 @@ impl acp::Client for ClientDelegate {
})??
.await;
Ok(acp::WaitForTerminalExitResponse {
exit_status,
meta: None,
})
Ok(acp::WaitForTerminalExitResponse::new(exit_status))
}
}

View File

@@ -68,6 +68,18 @@ pub trait AgentServer: Send {
) {
}
fn default_model(&self, _cx: &mut App) -> Option<agent_client_protocol::ModelId> {
None
}
fn set_default_model(
&self,
_model_id: Option<agent_client_protocol::ModelId>,
_fs: Arc<dyn Fs>,
_cx: &mut App,
) {
}
fn connect(
&self,
root_dir: Option<&Path>,

View File

@@ -41,7 +41,7 @@ impl AgentServer for ClaudeCode {
settings
.as_ref()
.and_then(|s| s.default_mode.clone().map(|m| acp::SessionModeId(m.into())))
.and_then(|s| s.default_mode.clone().map(acp::SessionModeId::new))
}
fn set_default_mode(&self, mode_id: Option<acp::SessionModeId>, fs: Arc<dyn Fs>, cx: &mut App) {
@@ -55,6 +55,27 @@ impl AgentServer for ClaudeCode {
});
}
fn default_model(&self, cx: &mut App) -> Option<acp::ModelId> {
let settings = cx.read_global(|settings: &SettingsStore, _| {
settings.get::<AllAgentServersSettings>(None).claude.clone()
});
settings
.as_ref()
.and_then(|s| s.default_model.clone().map(acp::ModelId::new))
}
fn set_default_model(&self, model_id: Option<acp::ModelId>, fs: Arc<dyn Fs>, cx: &mut App) {
update_settings_file(fs, cx, |settings, _| {
settings
.agent_servers
.get_or_insert_default()
.claude
.get_or_insert_default()
.default_model = model_id.map(|m| m.to_string())
});
}
fn connect(
&self,
root_dir: Option<&Path>,
@@ -68,6 +89,7 @@ impl AgentServer for ClaudeCode {
let store = delegate.store.downgrade();
let extra_env = load_proxy_env(cx);
let default_mode = self.default_mode(cx);
let default_model = self.default_model(cx);
cx.spawn(async move |cx| {
let (command, root_dir, login) = store
@@ -90,6 +112,7 @@ impl AgentServer for ClaudeCode {
command,
root_dir.as_ref(),
default_mode,
default_model,
is_remote,
cx,
)

View File

@@ -42,7 +42,7 @@ impl AgentServer for Codex {
settings
.as_ref()
.and_then(|s| s.default_mode.clone().map(|m| acp::SessionModeId(m.into())))
.and_then(|s| s.default_mode.clone().map(acp::SessionModeId::new))
}
fn set_default_mode(&self, mode_id: Option<acp::SessionModeId>, fs: Arc<dyn Fs>, cx: &mut App) {
@@ -56,6 +56,27 @@ impl AgentServer for Codex {
});
}
fn default_model(&self, cx: &mut App) -> Option<acp::ModelId> {
let settings = cx.read_global(|settings: &SettingsStore, _| {
settings.get::<AllAgentServersSettings>(None).codex.clone()
});
settings
.as_ref()
.and_then(|s| s.default_model.clone().map(acp::ModelId::new))
}
fn set_default_model(&self, model_id: Option<acp::ModelId>, fs: Arc<dyn Fs>, cx: &mut App) {
update_settings_file(fs, cx, |settings, _| {
settings
.agent_servers
.get_or_insert_default()
.codex
.get_or_insert_default()
.default_model = model_id.map(|m| m.to_string())
});
}
fn connect(
&self,
root_dir: Option<&Path>,
@@ -69,6 +90,7 @@ impl AgentServer for Codex {
let store = delegate.store.downgrade();
let extra_env = load_proxy_env(cx);
let default_mode = self.default_mode(cx);
let default_model = self.default_model(cx);
cx.spawn(async move |cx| {
let (command, root_dir, login) = store
@@ -92,6 +114,7 @@ impl AgentServer for Codex {
command,
root_dir.as_ref(),
default_mode,
default_model,
is_remote,
cx,
)

View File

@@ -44,19 +44,63 @@ impl crate::AgentServer for CustomAgentServer {
settings
.as_ref()
.and_then(|s| s.default_mode.clone().map(|m| acp::SessionModeId(m.into())))
.and_then(|s| s.default_mode().map(acp::SessionModeId::new))
}
fn set_default_mode(&self, mode_id: Option<acp::SessionModeId>, fs: Arc<dyn Fs>, cx: &mut App) {
let name = self.name();
update_settings_file(fs, cx, move |settings, _| {
if let Some(settings) = settings
let settings = settings
.agent_servers
.get_or_insert_default()
.custom
.get_mut(&name)
{
settings.default_mode = mode_id.map(|m| m.to_string())
.entry(name.clone())
.or_insert_with(|| settings::CustomAgentServerSettings::Extension {
default_model: None,
default_mode: None,
});
match settings {
settings::CustomAgentServerSettings::Custom { default_mode, .. }
| settings::CustomAgentServerSettings::Extension { default_mode, .. } => {
*default_mode = mode_id.map(|m| m.to_string());
}
}
});
}
fn default_model(&self, cx: &mut App) -> Option<acp::ModelId> {
let settings = cx.read_global(|settings: &SettingsStore, _| {
settings
.get::<AllAgentServersSettings>(None)
.custom
.get(&self.name())
.cloned()
});
settings
.as_ref()
.and_then(|s| s.default_model().map(acp::ModelId::new))
}
fn set_default_model(&self, model_id: Option<acp::ModelId>, fs: Arc<dyn Fs>, cx: &mut App) {
let name = self.name();
update_settings_file(fs, cx, move |settings, _| {
let settings = settings
.agent_servers
.get_or_insert_default()
.custom
.entry(name.clone())
.or_insert_with(|| settings::CustomAgentServerSettings::Extension {
default_model: None,
default_mode: None,
});
match settings {
settings::CustomAgentServerSettings::Custom { default_model, .. }
| settings::CustomAgentServerSettings::Extension { default_model, .. } => {
*default_model = model_id.map(|m| m.to_string());
}
}
});
}
@@ -72,6 +116,7 @@ impl crate::AgentServer for CustomAgentServer {
let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
let is_remote = delegate.project.read(cx).is_via_remote_server();
let default_mode = self.default_mode(cx);
let default_model = self.default_model(cx);
let store = delegate.store.downgrade();
let extra_env = load_proxy_env(cx);
@@ -98,6 +143,7 @@ impl crate::AgentServer for CustomAgentServer {
command,
root_dir.as_ref(),
default_mode,
default_model,
is_remote,
cx,
)

View File

@@ -82,26 +82,9 @@ where
.update(cx, |thread, cx| {
thread.send(
vec![
acp::ContentBlock::Text(acp::TextContent {
text: "Read the file ".into(),
annotations: None,
meta: None,
}),
acp::ContentBlock::ResourceLink(acp::ResourceLink {
uri: "foo.rs".into(),
name: "foo.rs".into(),
annotations: None,
description: None,
mime_type: None,
size: None,
title: None,
meta: None,
}),
acp::ContentBlock::Text(acp::TextContent {
text: " and tell me what the content of the println! is".into(),
annotations: None,
meta: None,
}),
"Read the file ".into(),
acp::ContentBlock::ResourceLink(acp::ResourceLink::new("foo.rs", "foo.rs")),
" and tell me what the content of the println! is".into(),
],
cx,
)
@@ -429,7 +412,7 @@ macro_rules! common_e2e_tests {
async fn tool_call_with_permission(cx: &mut ::gpui::TestAppContext) {
$crate::e2e_tests::test_tool_call_with_permission(
$server,
::agent_client_protocol::PermissionOptionId($allow_option_id.into()),
::agent_client_protocol::PermissionOptionId::new($allow_option_id),
cx,
)
.await;
@@ -476,6 +459,7 @@ pub async fn init_test(cx: &mut TestAppContext) -> Arc<FakeFs> {
env: None,
ignore_system_version: None,
default_mode: None,
default_model: None,
}),
gemini: Some(crate::gemini::tests::local_command().into()),
codex: Some(BuiltinAgentServerSettings {
@@ -484,6 +468,7 @@ pub async fn init_test(cx: &mut TestAppContext) -> Arc<FakeFs> {
env: None,
ignore_system_version: None,
default_mode: None,
default_model: None,
}),
custom: collections::HashMap::default(),
},

View File

@@ -37,6 +37,7 @@ impl AgentServer for Gemini {
let store = delegate.store.downgrade();
let mut extra_env = load_proxy_env(cx);
let default_mode = self.default_mode(cx);
let default_model = self.default_model(cx);
cx.spawn(async move |cx| {
extra_env.insert("SURFACE".to_owned(), "zed".to_owned());
@@ -69,6 +70,7 @@ impl AgentServer for Gemini {
command,
root_dir.as_ref(),
default_mode,
default_model,
is_remote,
cx,
)

View File

@@ -13,7 +13,8 @@ path = "src/agent_ui.rs"
doctest = false
[features]
test-support = ["gpui/test-support", "language/test-support"]
test-support = ["gpui/test-support", "language/test-support", "reqwest_client"]
unit-eval = []
[dependencies]
acp_thread.workspace = true
@@ -47,6 +48,7 @@ fs.workspace = true
futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
gpui_tokio.workspace = true
html_to_markdown.workspace = true
http_client.workspace = true
indoc.workspace = true
@@ -69,7 +71,6 @@ postage.workspace = true
project.workspace = true
prompt_store.workspace = true
proto.workspace = true
ref-cast.workspace = true
release_channel.workspace = true
rope.workspace = true
rules_library.workspace = true
@@ -93,19 +94,23 @@ time_format.workspace = true
ui.workspace = true
ui_input.workspace = true
url.workspace = true
urlencoding.workspace = true
util.workspace = true
watch.workspace = true
workspace.workspace = true
zed_actions.workspace = true
image.workspace = true
async-fs.workspace = true
reqwest_client = { workspace = true, optional = true }
[dev-dependencies]
acp_thread = { workspace = true, features = ["test-support"] }
agent = { workspace = true, features = ["test-support"] }
assistant_text_thread = { workspace = true, features = ["test-support"] }
buffer_diff = { workspace = true, features = ["test-support"] }
clock.workspace = true
db = { workspace = true, features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
eval_utils.workspace = true
gpui = { workspace = true, "features" = ["test-support"] }
indoc.workspace = true
language = { workspace = true, "features" = ["test-support"] }
@@ -113,6 +118,8 @@ languages = { workspace = true, features = ["test-support"] }
language_model = { workspace = true, "features" = ["test-support"] }
pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
semver.workspace = true
rand.workspace = true
reqwest_client.workspace = true
tree-sitter-md.workspace = true
unindent.workspace = true

View File

@@ -1,4 +1,3 @@
mod completion_provider;
mod entry_view_state;
mod message_editor;
mod mode_selector;

View File

@@ -22,7 +22,7 @@ use crate::acp::message_editor::{MessageEditor, MessageEditorEvent};
pub struct EntryViewState {
workspace: WeakEntity<Workspace>,
project: Entity<Project>,
project: WeakEntity<Project>,
history_store: Entity<HistoryStore>,
prompt_store: Option<Entity<PromptStore>>,
entries: Vec<Entry>,
@@ -34,7 +34,7 @@ pub struct EntryViewState {
impl EntryViewState {
pub fn new(
workspace: WeakEntity<Workspace>,
project: Entity<Project>,
project: WeakEntity<Project>,
history_store: Entity<HistoryStore>,
prompt_store: Option<Entity<PromptStore>>,
prompt_capabilities: Rc<RefCell<acp::PromptCapabilities>>,
@@ -328,7 +328,7 @@ impl Entry {
fn create_terminal(
workspace: WeakEntity<Workspace>,
project: Entity<Project>,
project: WeakEntity<Project>,
terminal: Entity<acp_thread::Terminal>,
window: &mut Window,
cx: &mut App,
@@ -336,9 +336,9 @@ fn create_terminal(
cx.new(|cx| {
let mut view = TerminalView::new(
terminal.read(cx).inner().clone(),
workspace.clone(),
workspace,
None,
project.downgrade(),
project,
window,
cx,
);
@@ -405,7 +405,7 @@ mod tests {
use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
use editor::RowInfo;
use fs::FakeFs;
use gpui::{AppContext as _, SemanticVersion, TestAppContext};
use gpui::{AppContext as _, TestAppContext};
use crate::acp::entry_view_state::EntryViewState;
use multi_buffer::MultiBufferRow;
@@ -432,24 +432,11 @@ mod tests {
let (workspace, cx) =
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
let tool_call = acp::ToolCall {
id: acp::ToolCallId("tool".into()),
title: "Tool call".into(),
kind: acp::ToolKind::Other,
status: acp::ToolCallStatus::InProgress,
content: vec![acp::ToolCallContent::Diff {
diff: acp::Diff {
path: "/project/hello.txt".into(),
old_text: Some("hi world".into()),
new_text: "hello world".into(),
meta: None,
},
}],
locations: vec![],
raw_input: None,
raw_output: None,
meta: None,
};
let tool_call = acp::ToolCall::new("tool", "Tool call")
.status(acp::ToolCallStatus::InProgress)
.content(vec![acp::ToolCallContent::Diff(
acp::Diff::new("/project/hello.txt", "hello world").old_text("hi world"),
)]);
let connection = Rc::new(StubAgentConnection::new());
let thread = cx
.update(|_, cx| {
@@ -471,7 +458,7 @@ mod tests {
let view_state = cx.new(|_cx| {
EntryViewState::new(
workspace.downgrade(),
project.clone(),
project.downgrade(),
history_store,
None,
Default::default(),
@@ -539,7 +526,7 @@ mod tests {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
theme::init(theme::LoadThemes::JustBase, cx);
release_channel::init(SemanticVersion::default(), cx);
release_channel::init(semver::Version::new(0, 0, 0), cx);
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,7 @@ use ui::{
PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*,
};
use crate::{CycleModeSelector, ToggleProfileSelector};
use crate::{CycleModeSelector, ToggleProfileSelector, ui::HoldForDefault};
pub struct ModeSelector {
connection: Rc<dyn AgentSessionModes>,
@@ -56,6 +56,10 @@ impl ModeSelector {
self.set_mode(all_modes[next_index].id.clone(), cx);
}
pub fn mode(&self) -> acp::SessionModeId {
self.connection.current_mode()
}
pub fn set_mode(&mut self, mode: acp::SessionModeId, cx: &mut Context<Self>) {
let task = self.connection.set_mode(mode, cx);
self.setting_mode = true;
@@ -104,36 +108,11 @@ impl ModeSelector {
entry.documentation_aside(side, DocumentationEdge::Bottom, {
let description = description.clone();
move |cx| {
move |_| {
v_flex()
.gap_1()
.child(Label::new(description.clone()))
.child(
h_flex()
.pt_1()
.border_t_1()
.border_color(cx.theme().colors().border_variant)
.gap_0p5()
.text_sm()
.text_color(Color::Muted.color(cx))
.child("Hold")
.child(h_flex().flex_shrink_0().children(
ui::render_modifiers(
&gpui::Modifiers::secondary_key(),
PlatformStyle::platform(),
None,
Some(ui::TextSize::Default.rems(cx).into()),
true,
),
))
.child(div().map(|this| {
if is_default {
this.child("to also unset as default")
} else {
this.child("to also set as default")
}
})),
)
.child(HoldForDefault::new(is_default))
.into_any_element()
}
})
@@ -182,7 +161,7 @@ impl Render for ModeSelector {
.map(|mode| mode.name.clone())
.unwrap_or_else(|| "Unknown".into());
let this = cx.entity();
let this = cx.weak_entity();
let icon = if self.menu_handle.is_deployed() {
IconName::ChevronUp
@@ -243,7 +222,8 @@ impl Render for ModeSelector {
y: px(-2.0),
})
.menu(move |window, cx| {
Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
this.update(cx, |this, cx| this.build_context_menu(window, cx))
.ok()
})
}
}

View File

@@ -1,27 +1,38 @@
use std::{cmp::Reverse, rc::Rc, sync::Arc};
use acp_thread::{AgentModelInfo, AgentModelList, AgentModelSelector};
use agent_servers::AgentServer;
use anyhow::Result;
use collections::IndexMap;
use fs::Fs;
use futures::FutureExt;
use fuzzy::{StringMatchCandidate, match_strings};
use gpui::{AsyncWindowContext, BackgroundExecutor, DismissEvent, Task, WeakEntity};
use gpui::{
Action, AsyncWindowContext, BackgroundExecutor, DismissEvent, FocusHandle, Task, WeakEntity,
};
use ordered_float::OrderedFloat;
use picker::{Picker, PickerDelegate};
use ui::{
DocumentationAside, DocumentationEdge, DocumentationSide, IntoElement, ListItem,
DocumentationAside, DocumentationEdge, DocumentationSide, IntoElement, KeyBinding, ListItem,
ListItemSpacing, prelude::*,
};
use util::ResultExt;
use zed_actions::agent::OpenSettings;
use crate::ui::HoldForDefault;
pub type AcpModelSelector = Picker<AcpModelPickerDelegate>;
pub fn acp_model_selector(
selector: Rc<dyn AgentModelSelector>,
agent_server: Rc<dyn AgentServer>,
fs: Arc<dyn Fs>,
focus_handle: FocusHandle,
window: &mut Window,
cx: &mut Context<AcpModelSelector>,
) -> AcpModelSelector {
let delegate = AcpModelPickerDelegate::new(selector, window, cx);
let delegate =
AcpModelPickerDelegate::new(selector, agent_server, fs, focus_handle, window, cx);
Picker::list(delegate, window, cx)
.show_scrollbar(true)
.width(rems(20.))
@@ -35,17 +46,23 @@ enum AcpModelPickerEntry {
pub struct AcpModelPickerDelegate {
selector: Rc<dyn AgentModelSelector>,
agent_server: Rc<dyn AgentServer>,
fs: Arc<dyn Fs>,
filtered_entries: Vec<AcpModelPickerEntry>,
models: Option<AgentModelList>,
selected_index: usize,
selected_description: Option<(usize, SharedString)>,
selected_description: Option<(usize, SharedString, bool)>,
selected_model: Option<AgentModelInfo>,
_refresh_models_task: Task<()>,
focus_handle: FocusHandle,
}
impl AcpModelPickerDelegate {
fn new(
selector: Rc<dyn AgentModelSelector>,
agent_server: Rc<dyn AgentServer>,
fs: Arc<dyn Fs>,
focus_handle: FocusHandle,
window: &mut Window,
cx: &mut Context<AcpModelSelector>,
) -> Self {
@@ -86,12 +103,15 @@ impl AcpModelPickerDelegate {
Self {
selector,
agent_server,
fs,
filtered_entries: Vec::new(),
models: None,
selected_model: None,
selected_index: 0,
selected_description: None,
_refresh_models_task: refresh_models_task,
focus_handle,
}
}
@@ -181,6 +201,21 @@ impl PickerDelegate for AcpModelPickerDelegate {
if let Some(AcpModelPickerEntry::Model(model_info)) =
self.filtered_entries.get(self.selected_index)
{
if window.modifiers().secondary() {
let default_model = self.agent_server.default_model(cx);
let is_default = default_model.as_ref() == Some(&model_info.id);
self.agent_server.set_default_model(
if is_default {
None
} else {
Some(model_info.id.clone())
},
self.fs.clone(),
cx,
);
}
self.selector
.select_model(model_info.id.clone(), cx)
.detach_and_log_err(cx);
@@ -225,6 +260,8 @@ impl PickerDelegate for AcpModelPickerDelegate {
),
AcpModelPickerEntry::Model(model_info) => {
let is_selected = Some(model_info) == self.selected_model.as_ref();
let default_model = self.agent_server.default_model(cx);
let is_default = default_model.as_ref() == Some(&model_info.id);
let model_icon_color = if is_selected {
Color::Accent
@@ -239,8 +276,8 @@ impl PickerDelegate for AcpModelPickerDelegate {
this
.on_hover(cx.listener(move |menu, hovered, _, cx| {
if *hovered {
menu.delegate.selected_description = Some((ix, description.clone()));
} else if matches!(menu.delegate.selected_description, Some((id, _)) if id == ix) {
menu.delegate.selected_description = Some((ix, description.clone(), is_default));
} else if matches!(menu.delegate.selected_description, Some((id, _, _)) if id == ix) {
menu.delegate.selected_description = None;
}
cx.notify();
@@ -251,17 +288,17 @@ impl PickerDelegate for AcpModelPickerDelegate {
.inset(true)
.spacing(ListItemSpacing::Sparse)
.toggle_state(selected)
.start_slot::<Icon>(model_info.icon.map(|icon| {
Icon::new(icon)
.color(model_icon_color)
.size(IconSize::Small)
}))
.child(
h_flex()
.w_full()
.pl_0p5()
.gap_1p5()
.w(px(240.))
.when_some(model_info.icon, |this, icon| {
this.child(
Icon::new(icon)
.color(model_icon_color)
.size(IconSize::Small)
)
})
.child(Label::new(model_info.name.clone()).truncate()),
)
.end_slot(div().pr_3().when(is_selected, |this| {
@@ -283,14 +320,57 @@ impl PickerDelegate for AcpModelPickerDelegate {
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) -> Option<ui::DocumentationAside> {
self.selected_description.as_ref().map(|(_, description)| {
let description = description.clone();
DocumentationAside::new(
DocumentationSide::Left,
DocumentationEdge::Top,
Rc::new(move |_| Label::new(description.clone()).into_any_element()),
)
})
self.selected_description
.as_ref()
.map(|(_, description, is_default)| {
let description = description.clone();
let is_default = *is_default;
DocumentationAside::new(
DocumentationSide::Left,
DocumentationEdge::Top,
Rc::new(move |_| {
v_flex()
.gap_1()
.child(Label::new(description.clone()))
.child(HoldForDefault::new(is_default))
.into_any_element()
}),
)
})
}
fn render_footer(
&self,
_window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Option<AnyElement> {
let focus_handle = self.focus_handle.clone();
if !self.selector.should_render_footer() {
return None;
}
Some(
h_flex()
.w_full()
.p_1p5()
.border_t_1()
.border_color(cx.theme().colors().border_variant)
.child(
Button::new("configure", "Configure")
.full_width()
.style(ButtonStyle::Outlined)
.key_binding(
KeyBinding::for_action_in(&OpenSettings, &focus_handle, cx)
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click(|_, window, cx| {
window.dispatch_action(OpenSettings.boxed_clone(), cx);
}),
)
.into_any(),
)
}
}
@@ -384,7 +464,7 @@ mod tests {
models
.into_iter()
.map(|model| acp_thread::AgentModelInfo {
id: acp::ModelId(model.to_string().into()),
id: acp::ModelId::new(model.to_string()),
name: model.to_string().into(),
description: None,
icon: None,

View File

@@ -1,6 +1,9 @@
use std::rc::Rc;
use std::sync::Arc;
use acp_thread::{AgentModelInfo, AgentModelSelector};
use agent_servers::AgentServer;
use fs::Fs;
use gpui::{Entity, FocusHandle};
use picker::popover_menu::PickerPopoverMenu;
use ui::{
@@ -20,13 +23,25 @@ pub struct AcpModelSelectorPopover {
impl AcpModelSelectorPopover {
pub(crate) fn new(
selector: Rc<dyn AgentModelSelector>,
agent_server: Rc<dyn AgentServer>,
fs: Arc<dyn Fs>,
menu_handle: PopoverMenuHandle<AcpModelSelector>,
focus_handle: FocusHandle,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let focus_handle_clone = focus_handle.clone();
Self {
selector: cx.new(move |cx| acp_model_selector(selector, window, cx)),
selector: cx.new(move |cx| {
acp_model_selector(
selector,
agent_server,
fs,
focus_handle_clone.clone(),
window,
cx,
)
}),
menu_handle,
focus_handle,
}

View File

@@ -1,5 +1,5 @@
use crate::acp::AcpThreadView;
use crate::{AgentPanel, RemoveSelectedThread};
use crate::{AgentPanel, RemoveHistory, RemoveSelectedThread};
use agent::{HistoryEntry, HistoryStore};
use chrono::{Datelike as _, Local, NaiveDate, TimeDelta};
use editor::{Editor, EditorEvent};
@@ -12,7 +12,7 @@ use std::{fmt::Display, ops::Range};
use text::Bias;
use time::{OffsetDateTime, UtcOffset};
use ui::{
HighlightedLabel, IconButtonShape, ListItem, ListItemSpacing, Tooltip, WithScrollbar,
HighlightedLabel, IconButtonShape, ListItem, ListItemSpacing, Tab, Tooltip, WithScrollbar,
prelude::*,
};
@@ -25,6 +25,7 @@ pub struct AcpThreadHistory {
search_query: SharedString,
visible_items: Vec<ListItemType>,
local_timezone: UtcOffset,
confirming_delete_history: bool,
_update_task: Task<()>,
_subscriptions: Vec<gpui::Subscription>,
}
@@ -98,6 +99,7 @@ impl AcpThreadHistory {
)
.unwrap(),
search_query: SharedString::default(),
confirming_delete_history: false,
_subscriptions: vec![search_editor_subscription, history_store_subscription],
_update_task: Task::ready(()),
};
@@ -331,6 +333,24 @@ impl AcpThreadHistory {
task.detach_and_log_err(cx);
}
fn remove_history(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
self.history_store.update(cx, |store, cx| {
store.delete_threads(cx).detach_and_log_err(cx)
});
self.confirming_delete_history = false;
cx.notify();
}
fn prompt_delete_history(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
self.confirming_delete_history = true;
cx.notify();
}
fn cancel_delete_history(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
self.confirming_delete_history = false;
cx.notify();
}
fn render_list_items(
&mut self,
range: Range<usize>,
@@ -426,9 +446,10 @@ impl AcpThreadHistory {
.tooltip(move |_window, cx| {
Tooltip::for_action("Delete", &RemoveSelectedThread, cx)
})
.on_click(
cx.listener(move |this, _, _, cx| this.remove_thread(ix, cx)),
),
.on_click(cx.listener(move |this, _, _, cx| {
this.remove_thread(ix, cx);
cx.stop_propagation()
})),
)
} else {
None
@@ -447,6 +468,8 @@ impl Focusable for AcpThreadHistory {
impl Render for AcpThreadHistory {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let has_no_history = self.history_store.read(cx).is_empty(cx);
v_flex()
.key_context("ThreadHistory")
.size_full()
@@ -457,9 +480,12 @@ impl Render for AcpThreadHistory {
.on_action(cx.listener(Self::select_last))
.on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::remove_selected_thread))
.on_action(cx.listener(|this, _: &RemoveHistory, window, cx| {
this.remove_history(window, cx);
}))
.child(
h_flex()
.h(px(41.)) // Match the toolbar perfectly
.h(Tab::container_height(cx))
.w_full()
.py_1()
.px_2()
@@ -481,7 +507,7 @@ impl Render for AcpThreadHistory {
.overflow_hidden()
.flex_grow();
if self.history_store.read(cx).is_empty(cx) {
if has_no_history {
view.justify_center().items_center().child(
Label::new("You don't have any past threads yet.")
.size(LabelSize::Small)
@@ -502,16 +528,74 @@ impl Render for AcpThreadHistory {
)
.p_1()
.pr_4()
.track_scroll(self.scroll_handle.clone())
.track_scroll(&self.scroll_handle)
.flex_grow(),
)
.vertical_scrollbar_for(
self.scroll_handle.clone(),
window,
cx,
)
.vertical_scrollbar_for(&self.scroll_handle, window, cx)
}
})
.when(!has_no_history, |this| {
this.child(
h_flex()
.p_2()
.border_t_1()
.border_color(cx.theme().colors().border_variant)
.when(!self.confirming_delete_history, |this| {
this.child(
Button::new("delete_history", "Delete All History")
.full_width()
.style(ButtonStyle::Outlined)
.label_size(LabelSize::Small)
.on_click(cx.listener(|this, _, window, cx| {
this.prompt_delete_history(window, cx);
})),
)
})
.when(self.confirming_delete_history, |this| {
this.w_full()
.gap_2()
.flex_wrap()
.justify_between()
.child(
h_flex()
.flex_wrap()
.gap_1()
.child(
Label::new("Delete all threads?")
.size(LabelSize::Small),
)
.child(
Label::new("You won't be able to recover them later.")
.size(LabelSize::Small)
.color(Color::Muted),
),
)
.child(
h_flex()
.gap_1()
.child(
Button::new("cancel_delete", "Cancel")
.label_size(LabelSize::Small)
.on_click(cx.listener(|this, _, window, cx| {
this.cancel_delete_history(window, cx);
})),
)
.child(
Button::new("confirm_delete", "Delete")
.style(ButtonStyle::Tinted(ui::TintColor::Error))
.color(Color::Error)
.label_size(LabelSize::Small)
.on_click(cx.listener(|_, _, window, cx| {
window.dispatch_action(
Box::new(RemoveHistory),
cx,
);
})),
),
)
}),
)
})
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
mod add_llm_provider_modal;
mod configure_context_server_modal;
pub mod configure_context_server_modal;
mod configure_context_server_tools_modal;
mod manage_profiles_modal;
mod tool_picker;
@@ -12,7 +12,7 @@ use client::zed_urls;
use cloud_llm_client::{Plan, PlanV1, PlanV2};
use collections::HashMap;
use context_server::ContextServerId;
use editor::{Editor, SelectionEffects, scroll::Autoscroll};
use editor::{Editor, MultiBufferOffset, SelectionEffects, scroll::Autoscroll};
use extension::ExtensionManifest;
use extension_host::ExtensionStore;
use fs::Fs;
@@ -36,7 +36,7 @@ use settings::{Settings, SettingsStore, update_settings_file};
use ui::{
Button, ButtonStyle, Chip, CommonAnimationExt, ContextMenu, ContextMenuEntry, Disclosure,
Divider, DividerColor, ElevationIndex, IconName, IconPosition, IconSize, Indicator, LabelSize,
PopoverMenu, Switch, SwitchColor, Tooltip, WithScrollbar, prelude::*,
PopoverMenu, Switch, Tooltip, WithScrollbar, prelude::*,
};
use util::ResultExt as _;
use workspace::{Workspace, create_and_open_local_file};
@@ -46,9 +46,8 @@ pub(crate) use configure_context_server_modal::ConfigureContextServerModal;
pub(crate) use configure_context_server_tools_modal::ConfigureContextServerToolsModal;
pub(crate) use manage_profiles_modal::ManageProfilesModal;
use crate::{
AddContextServer,
agent_configuration::add_llm_provider_modal::{AddLlmProviderModal, LlmCompatibleProvider},
use crate::agent_configuration::add_llm_provider_modal::{
AddLlmProviderModal, LlmCompatibleProvider,
};
pub struct AgentConfiguration {
@@ -553,7 +552,9 @@ impl AgentConfiguration {
move |window, cx| {
Some(ContextMenu::build(window, cx, |menu, _window, _cx| {
menu.entry("Add Custom Server", None, {
|window, cx| window.dispatch_action(AddContextServer.boxed_clone(), cx)
|window, cx| {
window.dispatch_action(crate::AddContextServer.boxed_clone(), cx)
}
})
.entry("Install from Extensions", None, {
|window, cx| {
@@ -651,7 +652,7 @@ impl AgentConfiguration {
let is_running = matches!(server_status, ContextServerStatus::Running);
let item_id = SharedString::from(context_server_id.0.clone());
// Servers without a configuration can only be provided by extensions.
let provided_by_extension = server_configuration.is_none_or(|config| {
let provided_by_extension = server_configuration.as_ref().is_none_or(|config| {
matches!(
config.as_ref(),
ContextServerConfiguration::Extension { .. }
@@ -707,7 +708,10 @@ impl AgentConfiguration {
"Server is stopped.",
),
};
let is_remote = server_configuration
.as_ref()
.map(|config| matches!(config.as_ref(), ContextServerConfiguration::Http { .. }))
.unwrap_or(false);
let context_server_configuration_menu = PopoverMenu::new("context-server-config-menu")
.trigger_with_tooltip(
IconButton::new("context-server-config-menu", IconName::Settings)
@@ -730,14 +734,25 @@ impl AgentConfiguration {
let language_registry = language_registry.clone();
let workspace = workspace.clone();
move |window, cx| {
ConfigureContextServerModal::show_modal_for_existing_server(
context_server_id.clone(),
language_registry.clone(),
workspace.clone(),
window,
cx,
)
.detach_and_log_err(cx);
if is_remote {
crate::agent_configuration::configure_context_server_modal::ConfigureContextServerModal::show_modal_for_existing_server(
context_server_id.clone(),
language_registry.clone(),
workspace.clone(),
window,
cx,
)
.detach();
} else {
ConfigureContextServerModal::show_modal_for_existing_server(
context_server_id.clone(),
language_registry.clone(),
workspace.clone(),
window,
cx,
)
.detach();
}
}
}).when(tool_count > 0, |this| this.entry("View Tools", None, {
let context_server_id = context_server_id.clone();
@@ -864,7 +879,6 @@ impl AgentConfiguration {
.child(context_server_configuration_menu)
.child(
Switch::new("context-server-switch", is_running.into())
.color(SwitchColor::Accent)
.on_click({
let context_server_manager = self.context_server_store.clone();
let fs = self.fs.clone();
@@ -1194,7 +1208,7 @@ impl Render for AgentConfiguration {
.child(self.render_context_servers_section(window, cx))
.child(self.render_provider_configuration_section(cx)),
)
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx),
.vertical_scrollbar_for(&self.scroll_handle, window, cx),
)
}
}
@@ -1328,11 +1342,12 @@ async fn open_new_agent_servers_entry_in_settings_editor(
.custom
.insert(
server_name,
settings::CustomAgentServerSettings {
settings::CustomAgentServerSettings::Custom {
path: "path_to_executable".into(),
args: vec![],
env: Some(HashMap::default()),
default_mode: None,
default_model: None,
},
);
}
@@ -1347,7 +1362,15 @@ async fn open_new_agent_servers_entry_in_settings_editor(
.map(|(range, _)| range.clone())
.collect::<Vec<_>>();
item.edit(edits, cx);
item.edit(
edits.into_iter().map(|(range, s)| {
(
MultiBufferOffset(range.start)..MultiBufferOffset(range.end),
s,
)
}),
cx,
);
if let Some((unique_server_name, buffer)) =
unique_server_name.zip(item.buffer().read(cx).as_singleton())
{
@@ -1360,7 +1383,9 @@ async fn open_new_agent_servers_entry_in_settings_editor(
window,
cx,
|selections| {
selections.select_ranges(vec![range]);
selections.select_ranges(vec![
MultiBufferOffset(range.start)..MultiBufferOffset(range.end),
]);
},
);
}

View File

@@ -3,16 +3,42 @@ use std::sync::Arc;
use anyhow::Result;
use collections::HashSet;
use fs::Fs;
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Render, Task};
use gpui::{
DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Render, ScrollHandle, Task,
};
use language_model::LanguageModelRegistry;
use language_models::provider::open_ai_compatible::{AvailableModel, ModelCapabilities};
use settings::{OpenAiCompatibleSettingsContent, update_settings_file};
use ui::{
Banner, Checkbox, KeyBinding, Modal, ModalFooter, ModalHeader, Section, ToggleState, prelude::*,
Banner, Checkbox, KeyBinding, Modal, ModalFooter, ModalHeader, Section, ToggleState,
WithScrollbar, prelude::*,
};
use ui_input::InputField;
use workspace::{ModalView, Workspace};
fn single_line_input(
label: impl Into<SharedString>,
placeholder: impl Into<SharedString>,
text: Option<&str>,
tab_index: isize,
window: &mut Window,
cx: &mut App,
) -> Entity<InputField> {
cx.new(|cx| {
let input = InputField::new(window, cx, placeholder)
.label(label)
.tab_index(tab_index)
.tab_stop(true);
if let Some(text) = text {
input
.editor()
.update(cx, |editor, cx| editor.set_text(text, window, cx));
}
input
})
}
#[derive(Clone, Copy)]
pub enum LlmCompatibleProvider {
OpenAi,
@@ -41,12 +67,14 @@ struct AddLlmProviderInput {
impl AddLlmProviderInput {
fn new(provider: LlmCompatibleProvider, window: &mut Window, cx: &mut App) -> Self {
let provider_name = single_line_input("Provider Name", provider.name(), None, window, cx);
let api_url = single_line_input("API URL", provider.api_url(), None, window, cx);
let provider_name =
single_line_input("Provider Name", provider.name(), None, 1, window, cx);
let api_url = single_line_input("API URL", provider.api_url(), None, 2, window, cx);
let api_key = single_line_input(
"API Key",
"000000000000000000000000000000000000000000000000",
None,
3,
window,
cx,
);
@@ -55,12 +83,13 @@ impl AddLlmProviderInput {
provider_name,
api_url,
api_key,
models: vec![ModelInput::new(window, cx)],
models: vec![ModelInput::new(0, window, cx)],
}
}
fn add_model(&mut self, window: &mut Window, cx: &mut App) {
self.models.push(ModelInput::new(window, cx));
let model_index = self.models.len();
self.models.push(ModelInput::new(model_index, window, cx));
}
fn remove_model(&mut self, index: usize) {
@@ -84,11 +113,14 @@ struct ModelInput {
}
impl ModelInput {
fn new(window: &mut Window, cx: &mut App) -> Self {
fn new(model_index: usize, window: &mut Window, cx: &mut App) -> Self {
let base_tab_index = (3 + (model_index * 4)) as isize;
let model_name = single_line_input(
"Model Name",
"e.g. gpt-4o, claude-opus-4, gemini-2.5-pro",
None,
base_tab_index + 1,
window,
cx,
);
@@ -96,6 +128,7 @@ impl ModelInput {
"Max Completion Tokens",
"200000",
Some("200000"),
base_tab_index + 2,
window,
cx,
);
@@ -103,16 +136,26 @@ impl ModelInput {
"Max Output Tokens",
"Max Output Tokens",
Some("32000"),
base_tab_index + 3,
window,
cx,
);
let max_tokens = single_line_input("Max Tokens", "Max Tokens", Some("200000"), window, cx);
let max_tokens = single_line_input(
"Max Tokens",
"Max Tokens",
Some("200000"),
base_tab_index + 4,
window,
cx,
);
let ModelCapabilities {
tools,
images,
parallel_tool_calls,
prompt_cache_key,
} = ModelCapabilities::default();
Self {
name: model_name,
max_completion_tokens,
@@ -165,24 +208,6 @@ impl ModelInput {
}
}
fn single_line_input(
label: impl Into<SharedString>,
placeholder: impl Into<SharedString>,
text: Option<&str>,
window: &mut Window,
cx: &mut App,
) -> Entity<InputField> {
cx.new(|cx| {
let input = InputField::new(window, cx, placeholder).label(label);
if let Some(text) = text {
input
.editor()
.update(cx, |editor, cx| editor.set_text(text, window, cx));
}
input
})
}
fn save_provider_to_settings(
input: &AddLlmProviderInput,
cx: &mut App,
@@ -258,6 +283,7 @@ fn save_provider_to_settings(
pub struct AddLlmProviderModal {
provider: LlmCompatibleProvider,
input: AddLlmProviderInput,
scroll_handle: ScrollHandle,
focus_handle: FocusHandle,
last_error: Option<SharedString>,
}
@@ -278,6 +304,7 @@ impl AddLlmProviderModal {
provider,
last_error: None,
focus_handle: cx.focus_handle(),
scroll_handle: ScrollHandle::new(),
}
}
@@ -418,6 +445,19 @@ impl AddLlmProviderModal {
)
})
}
fn on_tab(&mut self, _: &menu::SelectNext, window: &mut Window, _: &mut Context<Self>) {
window.focus_next();
}
fn on_tab_prev(
&mut self,
_: &menu::SelectPrevious,
window: &mut Window,
_: &mut Context<Self>,
) {
window.focus_prev();
}
}
impl EventEmitter<DismissEvent> for AddLlmProviderModal {}
@@ -431,15 +471,27 @@ impl Focusable for AddLlmProviderModal {
impl ModalView for AddLlmProviderModal {}
impl Render for AddLlmProviderModal {
fn render(&mut self, _window: &mut ui::Window, cx: &mut ui::Context<Self>) -> impl IntoElement {
fn render(&mut self, window: &mut ui::Window, cx: &mut ui::Context<Self>) -> impl IntoElement {
let focus_handle = self.focus_handle(cx);
div()
let window_size = window.viewport_size();
let rem_size = window.rem_size();
let is_large_window = window_size.height / rem_size > rems_from_px(600.).0;
let modal_max_height = if is_large_window {
rems_from_px(450.)
} else {
rems_from_px(200.)
};
v_flex()
.id("add-llm-provider-modal")
.key_context("AddLlmProviderModal")
.w(rems(34.))
.elevation_3(cx)
.on_action(cx.listener(Self::cancel))
.on_action(cx.listener(Self::on_tab))
.on_action(cx.listener(Self::on_tab_prev))
.capture_any_mouse_down(cx.listener(|this, _, window, cx| {
this.focus_handle(cx).focus(window);
}))
@@ -462,17 +514,25 @@ impl Render for AddLlmProviderModal {
)
})
.child(
v_flex()
.id("modal_content")
div()
.size_full()
.max_h_128()
.overflow_y_scroll()
.px(DynamicSpacing::Base12.rems(cx))
.gap(DynamicSpacing::Base04.rems(cx))
.child(self.input.provider_name.clone())
.child(self.input.api_url.clone())
.child(self.input.api_key.clone())
.child(self.render_model_section(cx)),
.vertical_scrollbar_for(&self.scroll_handle, window, cx)
.child(
v_flex()
.id("modal_content")
.size_full()
.tab_group()
.max_h(modal_max_height)
.pl_3()
.pr_4()
.gap_2()
.overflow_y_scroll()
.track_scroll(&self.scroll_handle)
.child(self.input.provider_name.clone())
.child(self.input.api_url.clone())
.child(self.input.api_key.clone())
.child(self.render_model_section(cx)),
),
)
.footer(
ModalFooter::new().end_slot(
@@ -642,7 +702,7 @@ mod tests {
let cx = setup_test(cx).await;
cx.update(|window, cx| {
let model_input = ModelInput::new(window, cx);
let model_input = ModelInput::new(0, window, cx);
model_input.name.update(cx, |input, cx| {
input.editor().update(cx, |editor, cx| {
editor.set_text("somemodel", window, cx);
@@ -678,7 +738,7 @@ mod tests {
let cx = setup_test(cx).await;
cx.update(|window, cx| {
let mut model_input = ModelInput::new(window, cx);
let mut model_input = ModelInput::new(0, window, cx);
model_input.name.update(cx, |input, cx| {
input.editor().update(cx, |editor, cx| {
editor.set_text("somemodel", window, cx);
@@ -703,7 +763,7 @@ mod tests {
let cx = setup_test(cx).await;
cx.update(|window, cx| {
let mut model_input = ModelInput::new(window, cx);
let mut model_input = ModelInput::new(0, window, cx);
model_input.name.update(cx, |input, cx| {
input.editor().update(cx, |editor, cx| {
editor.set_text("somemodel", window, cx);
@@ -767,7 +827,7 @@ mod tests {
models.iter().enumerate()
{
if i >= input.models.len() {
input.models.push(ModelInput::new(window, cx));
input.models.push(ModelInput::new(i, window, cx));
}
let model = &mut input.models[i];
set_text(&model.name, name, window, cx);

View File

@@ -1,14 +1,12 @@
use std::{
path::PathBuf,
sync::{Arc, Mutex},
};
use std::sync::{Arc, Mutex};
use anyhow::{Context as _, Result};
use collections::HashMap;
use context_server::{ContextServerCommand, ContextServerId};
use editor::{Editor, EditorElement, EditorStyle};
use gpui::{
AsyncWindowContext, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Task,
TextStyle, TextStyleRefinement, UnderlineStyle, WeakEntity, prelude::*,
AsyncWindowContext, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, ScrollHandle,
Task, TextStyle, TextStyleRefinement, UnderlineStyle, WeakEntity, prelude::*,
};
use language::{Language, LanguageRegistry};
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
@@ -20,10 +18,12 @@ use project::{
project_settings::{ContextServerSettings, ProjectSettings},
worktree_store::WorktreeStore,
};
use serde::Deserialize;
use settings::{Settings as _, update_settings_file};
use theme::ThemeSettings;
use ui::{
CommonAnimationExt, KeyBinding, Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*,
CommonAnimationExt, KeyBinding, Modal, ModalFooter, ModalHeader, Section, Tooltip,
WithScrollbar, prelude::*,
};
use util::ResultExt as _;
use workspace::{ModalView, Workspace};
@@ -36,6 +36,11 @@ enum ConfigurationTarget {
id: ContextServerId,
command: ContextServerCommand,
},
ExistingHttp {
id: ContextServerId,
url: String,
headers: HashMap<String, String>,
},
Extension {
id: ContextServerId,
repository_url: Option<SharedString>,
@@ -46,9 +51,11 @@ enum ConfigurationTarget {
enum ConfigurationSource {
New {
editor: Entity<Editor>,
is_http: bool,
},
Existing {
editor: Entity<Editor>,
is_http: bool,
},
Extension {
id: ContextServerId,
@@ -96,6 +103,7 @@ impl ConfigurationSource {
match target {
ConfigurationTarget::New => ConfigurationSource::New {
editor: create_editor(context_server_input(None), jsonc_language, window, cx),
is_http: false,
},
ConfigurationTarget::Existing { id, command } => ConfigurationSource::Existing {
editor: create_editor(
@@ -104,6 +112,20 @@ impl ConfigurationSource {
window,
cx,
),
is_http: false,
},
ConfigurationTarget::ExistingHttp {
id,
url,
headers: auth,
} => ConfigurationSource::Existing {
editor: create_editor(
context_server_http_input(Some((id, url, auth))),
jsonc_language,
window,
cx,
),
is_http: true,
},
ConfigurationTarget::Extension {
id,
@@ -140,16 +162,30 @@ impl ConfigurationSource {
fn output(&self, cx: &mut App) -> Result<(ContextServerId, ContextServerSettings)> {
match self {
ConfigurationSource::New { editor } | ConfigurationSource::Existing { editor } => {
parse_input(&editor.read(cx).text(cx)).map(|(id, command)| {
(
id,
ContextServerSettings::Custom {
enabled: true,
command,
},
)
})
ConfigurationSource::New { editor, is_http }
| ConfigurationSource::Existing { editor, is_http } => {
if *is_http {
parse_http_input(&editor.read(cx).text(cx)).map(|(id, url, auth)| {
(
id,
ContextServerSettings::Http {
enabled: true,
url,
headers: auth,
},
)
})
} else {
parse_input(&editor.read(cx).text(cx)).map(|(id, command)| {
(
id,
ContextServerSettings::Stdio {
enabled: true,
command,
},
)
})
}
}
ConfigurationSource::Extension {
id,
@@ -185,11 +221,12 @@ fn context_server_input(existing: Option<(ContextServerId, ContextServerCommand)
Some((id, cmd)) => {
let args = serde_json::to_string(&cmd.args).unwrap();
let env = serde_json::to_string(&cmd.env.unwrap_or_default()).unwrap();
(id.0.to_string(), cmd.path, args, env)
let cmd_path = serde_json::to_string(&cmd.path).unwrap();
(id.0.to_string(), cmd_path, args, env)
}
None => (
"some-mcp-server".to_string(),
PathBuf::new(),
"".to_string(),
"[]".to_string(),
"{}".to_string(),
),
@@ -200,17 +237,76 @@ fn context_server_input(existing: Option<(ContextServerId, ContextServerCommand)
/// The name of your MCP server
"{name}": {{
/// The command which runs the MCP server
"command": "{}",
"command": {command},
/// The arguments to pass to the MCP server
"args": {args},
/// The environment variables to set
"env": {env}
}}
}}"#,
command.display()
}}"#
)
}
fn context_server_http_input(
existing: Option<(ContextServerId, String, HashMap<String, String>)>,
) -> String {
let (name, url, headers) = match existing {
Some((id, url, headers)) => {
let header = if headers.is_empty() {
r#"// "Authorization": "Bearer <token>"#.to_string()
} else {
let json = serde_json::to_string_pretty(&headers).unwrap();
let mut lines = json.split("\n").collect::<Vec<_>>();
if lines.len() > 1 {
lines.remove(0);
lines.pop();
}
lines
.into_iter()
.map(|line| format!(" {}", line))
.collect::<String>()
};
(id.0.to_string(), url, header)
}
None => (
"some-remote-server".to_string(),
"https://example.com/mcp".to_string(),
r#"// "Authorization": "Bearer <token>"#.to_string(),
),
};
format!(
r#"{{
/// The name of your remote MCP server
"{name}": {{
/// The URL of the remote MCP server
"url": "{url}",
"headers": {{
/// Any headers to send along
{headers}
}}
}}
}}"#
)
}
fn parse_http_input(text: &str) -> Result<(ContextServerId, String, HashMap<String, String>)> {
#[derive(Deserialize)]
struct Temp {
url: String,
#[serde(default)]
headers: HashMap<String, String>,
}
let value: HashMap<String, Temp> = serde_json_lenient::from_str(text)?;
if value.len() != 1 {
anyhow::bail!("Expected exactly one context server configuration");
}
let (key, value) = value.into_iter().next().unwrap();
Ok((ContextServerId(key.into()), value.url, value.headers))
}
fn resolve_context_server_extension(
id: ContextServerId,
worktree_store: Entity<WorktreeStore>,
@@ -252,6 +348,7 @@ pub struct ConfigureContextServerModal {
source: ConfigurationSource,
state: State,
original_server_id: Option<ContextServerId>,
scroll_handle: ScrollHandle,
}
impl ConfigureContextServerModal {
@@ -303,13 +400,22 @@ impl ConfigureContextServerModal {
window.spawn(cx, async move |cx| {
let target = match settings {
ContextServerSettings::Custom {
ContextServerSettings::Stdio {
enabled: _,
command,
} => Some(ConfigurationTarget::Existing {
id: server_id,
command,
}),
ContextServerSettings::Http {
enabled: _,
url,
headers,
} => Some(ConfigurationTarget::ExistingHttp {
id: server_id,
url,
headers,
}),
ContextServerSettings::Extension { .. } => {
match workspace
.update(cx, |workspace, cx| {
@@ -351,6 +457,7 @@ impl ConfigureContextServerModal {
state: State::Idle,
original_server_id: match &target {
ConfigurationTarget::Existing { id, .. } => Some(id.clone()),
ConfigurationTarget::ExistingHttp { id, .. } => Some(id.clone()),
ConfigurationTarget::Extension { id, .. } => Some(id.clone()),
ConfigurationTarget::New => None,
},
@@ -361,6 +468,7 @@ impl ConfigureContextServerModal {
window,
cx,
),
scroll_handle: ScrollHandle::new(),
})
})
})
@@ -478,7 +586,7 @@ impl ModalView for ConfigureContextServerModal {}
impl Focusable for ConfigureContextServerModal {
fn focus_handle(&self, cx: &App) -> FocusHandle {
match &self.source {
ConfigurationSource::New { editor } => editor.focus_handle(cx),
ConfigurationSource::New { editor, .. } => editor.focus_handle(cx),
ConfigurationSource::Existing { editor, .. } => editor.focus_handle(cx),
ConfigurationSource::Extension { editor, .. } => editor
.as_ref()
@@ -525,8 +633,8 @@ impl ConfigureContextServerModal {
fn render_modal_content(&self, cx: &App) -> AnyElement {
let editor = match &self.source {
ConfigurationSource::New { editor } => editor,
ConfigurationSource::Existing { editor } => editor,
ConfigurationSource::New { editor, .. } => editor,
ConfigurationSource::Existing { editor, .. } => editor,
ConfigurationSource::Extension { editor, .. } => {
let Some(editor) = editor else {
return div().into_any_element();
@@ -598,6 +706,36 @@ impl ConfigureContextServerModal {
move |_, _, cx| cx.open_url(&repository_url)
}),
)
} else if let ConfigurationSource::New { is_http, .. } = &self.source {
let label = if *is_http {
"Configure Local"
} else {
"Configure Remote"
};
let tooltip = if *is_http {
"Configure an MCP server that runs on stdin/stdout."
} else {
"Configure an MCP server that you connect to over HTTP"
};
Some(
Button::new("toggle-kind", label)
.tooltip(Tooltip::text(tooltip))
.on_click(cx.listener(|this, _, window, cx| match &mut this.source {
ConfigurationSource::New { editor, is_http } => {
*is_http = !*is_http;
let new_text = if *is_http {
context_server_http_input(None)
} else {
context_server_input(None)
};
editor.update(cx, |editor, cx| {
editor.set_text(new_text, window, cx);
})
}
_ => {}
})),
)
} else {
None
},
@@ -699,14 +837,29 @@ impl Render for ConfigureContextServerModal {
Modal::new("configure-context-server", None)
.header(self.render_modal_header())
.section(
Section::new()
.child(self.render_modal_description(window, cx))
.child(self.render_modal_content(cx))
.child(match &self.state {
State::Idle => div(),
State::Waiting => Self::render_waiting_for_context_server(),
State::Error(error) => Self::render_modal_error(error.clone()),
}),
Section::new().child(
div()
.size_full()
.child(
div()
.id("modal-content")
.max_h(vh(0.7, window))
.overflow_y_scroll()
.track_scroll(&self.scroll_handle)
.child(self.render_modal_description(window, cx))
.child(self.render_modal_content(cx))
.child(match &self.state {
State::Idle => div(),
State::Waiting => {
Self::render_waiting_for_context_server()
}
State::Error(error) => {
Self::render_modal_error(error.clone())
}
}),
)
.vertical_scrollbar_for(&self.scroll_handle, window, cx),
),
)
.footer(self.render_modal_footer(cx)),
)

View File

@@ -138,7 +138,7 @@ impl ConfigureContextServerToolsModal {
items
})),
)
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
.vertical_scrollbar_for(&self.scroll_handle, window, cx)
.into_any_element()
}
}

View File

@@ -253,6 +253,7 @@ impl ManageProfilesModal {
});
},
false, // Do not use popover styles for the model picker
self.focus_handle.clone(),
window,
cx,
)

Some files were not shown because too many files have changed in this diff Show More