Compare commits

..

833 Commits

Author SHA1 Message Date
Nate Butler
152d69f273 wip 2025-04-28 22:21:15 -04:00
Nate Butler
9b9746a859 wip 2025-04-28 21:51:31 -04:00
Marshall Bowers
e3c987e2fb zed: Don't feature-gate zed: open account settings action (#29542)
This PR removes the feature-gating of the `zed: open account settings`
action, as everyone has access to the account page now.

Release Notes:

- N/A
2025-04-28 15:40:24 +00:00
Piotr Osiewicz
4dc0551105 debugger_ui: Show zoom buttons only when the pane is hovered (#29543)
Closes #ISSUE

Release Notes:

- N/A
2025-04-28 17:36:54 +02:00
chbk
bf9e5b4f76 Fix Python builtin type highlighting (#29475)
| Zed 0.183.11 | With this PR |
| --- | --- |
|
![0.183.11](https://github.com/user-attachments/assets/6bb16c9b-2d6a-4424-a2ea-9ff051eb3085)
|
![pr](https://github.com/user-attachments/assets/d39d12d1-e6c3-4c06-8829-c5ee508ae83a)
|

```python
class A:
  @classmethod
  def list(cls, list: list) -> list:
    return list

A.list(list())
A.list(list=list())
A.list(list=list(A.list(list())))

list: list = list()
A.list(list)
A.list(list=list)
A.list(list=A.list(list))
```

Release Notes:

  - Improved Python builtin type highlighting
2025-04-28 11:34:55 -04:00
Tuur Vanhoutte
cfb8cae29c docs: Fix linux crash logs location (#29444)
Release Notes:

- N/A
2025-04-28 15:33:19 +00:00
Mani Rash Ahmadi
68e0105627 Agent: Include partial output if terminal tool fails (#29115)
This PR addresses the behavior of the agent's terminal tool when the
executed command is interrupted or fails after producing some output.
Currently, if the command doesn't finish successfully, any partial
output captured before the interruption/failure is discarded, and only
an error message (or a generic cancellation message) is returned to the
LLM.

This change modifies the `run_command_limited` function in the terminal
tool to catch errors when awaiting the command's status (which includes
interruptions). In the case of such an error, it now includes any
partial stdout/stderr captured up to that point within the error message
returned to the `ToolUseState`. This ensures the LLM receives the
partial context even when the command doesn't complete cleanly, framed
appropriately as part of an error/interruption message.

Closes #29101

Release Notes:

- N/A
2025-04-28 12:25:11 -03:00
Marshall Bowers
e98e6c7426 title_bar: Put plan behind new-billing feature flag (#29540)
This PR puts the displaying of the plan in the user menu behind the
`new-billing` feature flag instead of the old `zed-pro` feature flag.

Release Notes:

- N/A
2025-04-28 15:23:25 +00:00
Finn Evers
3a1bd38503 reqwest_client: Only register proxies with valid proxy URIs (#27773)
Closes #27641

This PR fixes invalid proxy URIs being registered despite the URI not
being a valid proxy URI.

Whilst investigating #27641 , I noticed that currently any proxy URI
passed to `RequestClient::proxy_and_user_agent` will be assigned to the
created client, even if the URI is not a valid proxy URI. Given a test
as an example:

We create an URI here and pass it as a proxy to
`ReqwestClient::proxy_and_user_agent`:

https://github.com/zed-industries/zed/blob/main/crates/reqwest_client/src/reqwest_client.rs#L272-L273

In `ReqwestClient::proxy_and_user_agent`we take the proxy parameter here

9b40770e9f/crates/reqwest_client/src/reqwest_client.rs (L46)

and set it unconditionally here:

9b40770e9f/crates/reqwest_client/src/reqwest_client.rs (L62)

, not considering at all whether the proxy was successfully created
above. Concluding, we currently do not actually check whether a proxy
was successfully created, but rather whether an URI is equal to itself,
which trivially holds. The existing test for a malformed proxy URI


9b40770e9f/crates/reqwest_client/src/reqwest_client.rs (L293-L297)

does not check whether invalid proxies cause an error, but rather checks
whether `http::Uri::from_static` panics on an invalid URI, [which it
does as
documented](https://docs.rs/http/latest/http/uri/struct.Uri.html#panics).
Thus, the tests currently do not really check anything proxy-related and
invalid proxies are assigned as valid proxies.

---

This PR fixes the behaviour by considering whether the proxy was
actually properly parsed and only assigning it if that is the case.
Furthermore, it improves logging in case of errors so issues like the
linked one are easier to debug (for the linked issue, the log will now
include that the proxy schema is not supported in the logs).
Lastly, it also updates the test for a malformed proxy URI. The test now
actually checks that malformed proxy URIs are not registered for the
client rather than testing the `http` crate.

The update also initially caused the [test for a `socks4a`
proxy](9b40770e9f/crates/reqwest_client/src/reqwest_client.rs (L280C1-L282C50))
to fail. This happened because the reqwest-library introduced supports
for `socks4a` proxies in [version
0.12.13](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md#v01213).
Thus, this PR includes a bump of the reqwest library to add proper
support for socks4a proxies.

Release Notes:

- Added support for socks4a proxies.

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-04-28 11:12:16 -04:00
张小白
8a69d252f5 Fix a bug in blob-store.sh (#29537)
This will overwrite the `acl` parameter that we pass in

Release Notes:

- N/A
2025-04-28 11:10:58 -04:00
jneem
bf30beacc2 Honor default_mode in NormalBefore (#29518)
Addresses
[this](https://github.com/zed-industries/zed/pull/28103#issuecomment-2832038415)
comment.

Release Notes:

- Improved default helix keybindings
2025-04-28 08:51:06 -06:00
Danilo Leal
2a0be48875 agent: Simplify elements of the thread design (#29533)
Namely, this PR removes the layout shift when you click on a user
message to edit it and displays the feedback disclaimer only upon
hovering the thumbs up/down button container.

Release Notes:

- N/A
2025-04-28 11:36:06 -03:00
Hendrik Sollich
1c4ba07b20 theme_selector: Don't select last theme when fuzzy searching (#28278)
The theme selector should select the last match to when opening to keep
the current theme active but it should select the first when searching
to pick the best match.

fixes #28081


https://github.com/user-attachments/assets/b46b9742-4715-4c7a-8f17-2c19a8668333

Release Notes:

- Fixed selecting the correct theme when searching

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-04-28 14:29:17 +00:00
tidely
8a717abe0d ollama: Fix build with default features (#29502)
The `ollama` crate has a `use schemars::JsonSchema` statement even when
building with default features, which doesn't include the `schemars`
crate.

Release Notes:

- N/A
2025-04-28 09:58:10 -04:00
Danilo Leal
f735c90c3f agent: Bring title editing back to text threads (#29425)
This also fixes a little UI bug where the text thread title would push
the buttons away from the UI when there was still space.

Release Notes:

- agent: Made text thread titles editable again.

---------

Co-authored-by: Michael Sloan <mgsloan@gmail.com>
2025-04-28 09:09:19 -03:00
Piotr Osiewicz
ddfeb202a3 editor: Show tooltips on breakpoints (#29523)
Closes #ISSUE

Release Notes:

- N/A
2025-04-28 12:28:13 +02:00
Oleksiy Syvokon
9bd0828303 agent tools: Make read_file.end_line inclusive (#29524)
One motivation is that the outlines returned by `read_file` for large
files list line numbers assuming an inclusive `end_line`. As a result,
when the agent uses these outlines for `read_line` calls, it would
otherwise miss the last line.

Release Notes:

- N/A
2025-04-28 09:37:13 +00:00
Julia Ryan
4dff47ae20 Add searchable global tab switcher (#28047)
resolves #24655
resolves #23945

I haven't yet added a default binding for the new command. #27797 added `:ls` and
`:buffers` which in my opinion should use the global searchable version
given that that matches the vim semantics of those commands better than
just showing the tabs in the local pane.

There's also a question of what to do when you select a tab from another
pane, should the focus jump to that pane or should that tab move to the
currently focused pane? For now I've implemented the former.

Release Notes:

- Added `tab_switcher::ToggleAll` to search open tabs from all panes and focus the selected one.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-28 09:21:27 +00:00
Smit Barmase
52eef3c35d editor: Fix inconsistent relative indent when using tab with multi cursors (#29519)
Do not insert hard/soft tabs for cursors at the suggested indent level
if any other cursor lies before the suggested indent level. This PR
brings us one step closer to fixing
https://github.com/zed-industries/zed/issues/26157.

Before:


https://github.com/user-attachments/assets/8fd5cde4-99f4-4363-9292-5da8dadab658

After:


https://github.com/user-attachments/assets/17c9f8ca-5842-452b-8665-7c7138d50162

Release Notes:

- Fixed an issue where using tab with multiple cursors would result in
inconsistent relative indentation across lines.
2025-04-28 13:13:53 +05:30
tidely
f060918b57 zed: Remove unnecessary clones (#29513)
`App::http_client` and `Client::http_client` both return an owned `Arc`
which it clones internally. This means we can remove unnecessary clones
when calling these methods.

Release Notes:

- N/A
2025-04-27 19:23:37 -07:00
Michael Sloan
609c528ceb Refactor markdown formatting utilities to avoid building intermediate strings (#29511)
These were nearly always used when using `format!` / `write!` etc, so it
makes sense to not have an intermediate `String`.

Release Notes:

- N/A
2025-04-27 19:04:51 +00:00
Peter Tripp
6db974dd32 docs: TOML LSP (Taplo) requires a manual restart to apply changes (#29504)
Add note about manual restart
Remove taplo LSP settings example (broken)

Release Notes:

- N/A
2025-04-27 11:53:55 -04:00
Michael Sloan
60ec55b179 Use u64 instead of usize in ElementId (#29493)
Truncation to a 32 bit `usize` could cause two distinct IDs to be
considered the same element.

Release Notes:

- N/A
2025-04-27 02:31:25 +00:00
Oleksiy Syvokon
bb7a5b13df [WIP] markdown: Add a test to reproduce the parser's panic (#29479)
Backtrace of the panic in the Agent pane:
```
Thread "<unnamed>" panicked with "called `Option::unwrap()` on a `None` value" at crates/markdown/src/parser.rs:264:55
3fdbc3090d/src/crates/markdown/src/parser.rs (L264) (may not be uploaded, line may be incorrect if files modified)
   0: zed::reliability::init_panic_hook::{{closure}}
             at /home/silver/develop/zed/crates/zed/src/reliability.rs:56:29
   1: <alloc::boxed::Box<F,A> as core::ops::function::Fn<Args>>::call
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/alloc/src/boxed.rs:1990:9
      std::panicking::rust_panic_with_hook
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:839:13
   2: std::panicking::begin_panic_handler::{{closure}}
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:697:13
   3: std::sys::backtrace::__rust_end_short_backtrace
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/sys/backtrace.rs:168:18
   4: rust_begin_unwind
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:695:5
   5: core::panicking::panic_fmt
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/panicking.rs:75:14
   6: core::panicking::panic
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/panicking.rs:145:5
   7: core::option::unwrap_failed
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/option.rs:2015:5
   8: core::option::Option<T>::unwrap
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:978:21
      markdown::parser::parse_markdown
             at /home/silver/develop/zed/crates/markdown/src/parser.rs:264:37
   9: markdown::Markdown::parse::{{closure}}
             at /home/silver/develop/zed/crates/markdown/src/markdown.rs:282:51
  10: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/future.rs:124:9
  11: async_task::raw::RawTask<F,T,S,M>::run
             at /home/silver/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/async-task-4.7.1/src/raw.rs:557:17
  12: async_task::runnable::Runnable<M>::run
             at /home/silver/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/async-task-4.7.1/src/runnable.rs:781:18
  13: gpui::platform::linux::dispatcher::LinuxDispatcher::new::{{closure}}::{{closure}}
             at /home/silver/develop/zed/crates/gpui/src/platform/linux/dispatcher.rs:44:25
  14: std::sys::backtrace::__rust_begin_short_backtrace
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:152:18
  15: std::thread::Builder::spawn_unchecked_::{{closure}}::{{closure}}
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/mod.rs:559:17
  16: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panic/unwind_safe.rs:272:9
  17: std::panicking::try::do_call
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:587:40
  18: __rust_try
  19: std::panicking::try
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:550:19
      std::panic::catch_unwind
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:358:14
      std::thread::Builder::spawn_unchecked_::{{closure}}
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/mod.rs:557:30
  20: core::ops::function::FnOnce::call_once{{vtable.shim}}
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
  21: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/alloc/src/boxed.rs:1976:9
      <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/alloc/src/boxed.rs:1976:9
      std::sys::pal::unix::thread::Thread::new::thread_start
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/sys/pal/unix/thread.rs:106:17
  22: start_thread
             at ./nptl/pthread_create.c:447:8
  23: clone3
             at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:78:0

Segmentation fault
```


Release Notes:

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

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-26 15:31:06 +00:00
Piotr Osiewicz
1e47dfce79 debugger: Improve focus states (#29469)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-04-26 15:02:07 +02:00
Antonio Scandurra
3fdbc3090d Fix error when deserializing Gemini streams (#29470)
Sometimes Gemini would report `Content` without a `parts` field.

Release Notes:

- Fixed a bug that would sometimes cause Gemini models to fail streaming
their response.
2025-04-26 11:51:04 +00:00
Smit Barmase
f2b4004c00 editor: Improve code completions by prioritizing prefix matching (#29456)
- Use common prefix length-based matching as primary criteria.
- Test added for multiple cases.

Before:
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/8c653225-cac2-41bd-95f0-0fb8724284c9"
/>

After:
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/a3d59399-cff2-435d-9b56-69a530f35da4"
/>

Release Notes:

- Fixed issues with code completions where they wouldn't show
completions with matched prefix at top.
2025-04-26 17:15:48 +05:30
Oleksiy Syvokon
ec5821f76d docs: Fix relative navigation links (#29468)
Release Notes:

- N/A
2025-04-26 10:29:05 +00:00
Piotr Osiewicz
e22cae6459 chore: Update .gitignore (#29466)
Closes #ISSUE

Release Notes:

- N/A
2025-04-26 10:00:36 +00:00
Oleksiy Syvokon
21bafd7856 docs: Fix relative navigation links (#29465)
Release Notes:

- N/A
2025-04-26 09:42:12 +00:00
Oleksiy Syvokon
ee74edbbb1 docs: Add ‹› navigation buttons (#29461)
While visually unintrusive, these navigation links enable proper
navigation in readers and extensions like Vimium that rely on
rel=next/prev

Release Notes:

- N/A
2025-04-26 06:52:02 +00:00
Marshall Bowers
d832b8e687 Sort Cargo.toml (#29459)
This PR sorts the workspace `Cargo.toml`.

Release Notes:

- N/A
2025-04-26 04:07:09 +00:00
Marshall Bowers
539f4f1576 collab: Update billing preference endpoints with new preferences (#29458)
This PR updates the billing preference endpoints with the new
overage-related billing preferences.

Release Notes:

- N/A
2025-04-26 03:11:44 +00:00
Marshall Bowers
9a325a23e5 collab: Set overage preferences in LLM token claims (#29457)
This PR sets the user's overage preferences in the LLM token claims.

Release Notes:

- N/A
2025-04-26 02:27:47 +00:00
Marshall Bowers
ce31312268 collab: Return feature flags with authenticated user (#29455)
This PR makes it so the `GET /user` endpoint returns the user's feature
flags with the authenticated user.

Release Notes:

- N/A
2025-04-26 01:49:25 +00:00
Piotr Osiewicz
d46890978a debugger: Fix new session modal not having worktree scenarios (#29453)
Closes #ISSUE

Release Notes:

- N/A
2025-04-26 00:16:21 +00:00
Piotr Osiewicz
67615b968b debugger/tasks: Remove TaskType enum (#29208)
Closes #ISSUE

Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Anthony <anthony@zed.dev>
Co-authored-by: Conrad <conrad@zed.dev>
2025-04-26 01:44:56 +02:00
Conrad Irwin
053fafa90e Fix markdown escaping
Closes #29255

Release Notes:

- Improved handling of markdown escape sequences
2025-04-25 16:53:44 -06:00
Ben Kunkle
d23024609f askpass: Shell escape Zed path in askpass script (#29447)
Closes #29439

Add shell escaping as well as additional sanity check for Zed path when
used in askpass. This caused issues on preview and nightly as the
standard paths for those releases contain spaces which were not escaped
appropriately leading to erroneous "Permission denied" errors from SSH
when the askpass script failed

Release Notes:

- Fixed a missing shell-escape in askpass resulting in erroneous
"Permission denied" errors when trying to connect to a remote server
over ssh (effecting preview release v0.184.1 and nightly only)
2025-04-25 21:16:43 +00:00
Smit Barmase
3961d87ae0 editor: Improve fuzzy match bucket logic for code completions (#29442)
Add new test and improve fuzzy match bucket logic which results into far
better balance between LSP and fuzzy search.

Before:
<img width="500" alt="before"
src="https://github.com/user-attachments/assets/3e8900a6-c0ff-4f37-b88e-b0e3783b7e9a"
/>

After:
<img width="500" alt="after"
src="https://github.com/user-attachments/assets/738c074c-d446-4697-aac6-9814362e88db"
/>

Release Notes:

- N/A
2025-04-26 02:14:18 +05:30
Marshall Bowers
8b910e1cd9 collab: Add LLM request overage columns to billing_preferences (#29446)
This PR adds two new columns to the `billing_preferences` table to allow
users to opt in to overages on LLM requests.

Release Notes:

- N/A
2025-04-25 20:43:43 +00:00
Michael Sloan
12c645e154 Fix inclusion of message when counting tokens from message editor (#29443)
Accidentally omitted this in #29233

Release Notes:

- N/A
2025-04-25 20:21:20 +00:00
Michael Sloan
cfb7a30724 Fix agent rules files for remote project by loading via buffer (#29440)
When using the agent with a project shared by a collaborator, rules file
loading didn't work as it was trying to read from the client's
filesystem

Release Notes:

- Fixed rules file loading when using the agent with a project shared by
a collaborator.
2025-04-25 20:06:40 +00:00
Cole Miller
7623fce4b4 Start improving support for keyboard-driven debugging (#29380)
Closes #ISSUE

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <peterosiewicz@gmail.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-04-25 19:14:47 +00:00
Cole Miller
7f5c874a38 git: Use the CLI for loading commit SHAs and details (#29351)
Since #28065 merged we've seen deadlocks inside iconv when opening Zed
in a repository containing many submodules. These calls to iconv happen
inside libgit2, in our implementations of the methods `head_sha`,
`merge_head_shas`, and `show` on `RealGitRepository`. This PR moves
those methods to use the git CLI instead, sidestepping the issue. For
the sake of efficiency, a new `revparse_batch` method is added that uses
`git cat-file` to resolve several ref names in one invocation. I
originally intended to make `show` operate in batch mode as well (or
instead), but I can't see a good way to do that with the git CLI; `git
show` always bails on the first ref that it can't resolve, and
`for-each-ref` doesn't support symbolic refs like `HEAD`.

Separately, I removed the calls to `show` in `MergeDetails::load`, going
back to only loading the SHAs of the various merge heads. Loading full
commit details was intended to support the inlays feature that ended up
being cut from #28065, and we can add it back in when we need it.

Release Notes:

- N/A
2025-04-25 14:46:02 -04:00
Marshall Bowers
8cc2ade21c collab: Add subscription_usage_meters table (#29433)
This PR adds a new `subscription_usage_meters` table to the LLM
database.

We'll use this to track usage of individual models over the number of
requests built-in to the plan.

Release Notes:

- N/A
2025-04-25 14:32:00 -04:00
Anthony Eid
c3177e6f5b debugger: Fix deadlock in on_app_quit with debugger running (#29372)
This fixes a deadlock that would occur when `DapStore` had its on quit
handler called. The deadlock was caused by `DapStore` spawning on the
main thread while `App::shutdown` blocks the main thread.

We added a debug_panic in GPUI that panics if a foreground task is
spawned while the App context is shutting down. This will help tests
catch hangs in `cx.on_app_quit` calls.

Release Notes:

- N/A

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-25 14:15:10 -04:00
Danilo Leal
c3570fbcf3 agent: Render path search results with ToolCard (#28894)
Implementing the `ToolCard` for the path_search tool. It also adds the
"jump to file" functionality if you expand the results.

Release Notes:

- N/A

---------

Co-authored-by: Richard Feldman <oss@rtfeldman.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-04-25 14:42:51 -03:00
Danilo Leal
3aa313010f agent: Make markdown code blocks uncollapsed by default (#29424)
Seeing the markdown code stream in is actually more helpful than hiding
parts of it; parts that you may be interested in.

Release Notes:

- N/A
2025-04-25 14:39:51 -03:00
Marshall Bowers
5f9c91d05a danger: Update PR prefix pattern (#29432)
This PR updates the Danger PR prefix pattern to allow underscores (`_`)
and spaces (` `) in the prefix.

Release Notes:

- N/A
2025-04-25 17:19:56 +00:00
Conrad Irwin
6692bd9f2b Maybe fix panic (#29352)
Since around the time we shipped block diagnostics, we've been seeing an
out of range panic in the editor.

Although the code is heavily inlined, so the stacktrace is missing, this
seems like a likely place that indexing may have gone wrong.

Release Notes:

- Fixed a rare panic in the editor
2025-04-25 11:12:16 -06:00
Smit Barmase
cc57bc7c96 editor: Add setting for snippet sorting behavior for code completion (#29429)
Added `snippet_sort_order`, which determines how snippets are sorted
relative to other completion items. It can have the values `top`,
`bottom`, or `inline`, with `inline` being the default.

This mimics VS Code’s setting:
https://code.visualstudio.com/docs/editing/intellisense#_snippets-in-suggestions

Release Notes:

- Added support for `snippet_sort_order` to control snippet sorting
behavior in code completion menus.
2025-04-25 22:35:12 +05:30
Agus Zubiaga
c157b1c455 rules: How to run clippy (#29247)
Tell model how to run clippy in `.rules`

Release Notes:

- N/A
2025-04-25 13:25:39 -03:00
Ben Kunkle
136e83e0b1 zlog: Fix incorrect assumption with filters (#29428)
- **do not assume logs over LEVEL_ENABLED_MAX_STATIC (the static global
log level) are enabled**
- **make it so filters that are just module names get overridden by
submodule path filters**

Closes #ISSUE

Release Notes:

- N/A
2025-04-25 16:11:58 +00:00
Marshall Bowers
b28756ae3f eval: Use workspace dependencies (#29430)
This PR updates the `eval` crate to use workspace dependencies.

Also did a bit of cleanup of the `Cargo.toml`.

Release Notes:

- N/A
2025-04-25 16:11:26 +00:00
Cole Miller
65401d6d7b debugger: Make debug panes zoomable (#29365)
- [x] Buttons
- [x] Make it keyboard-driven

Co-authored-by: Anthony <anthony@zed.dev>

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-04-25 11:56:16 -04:00
Marshall Bowers
a5405fcbd7 eval: Add support for reading from a .env file (#29426)
This PR adds support for the eval to read environment variables from a
`.env` file located in the `crates/eval` directory.

For instance, you can use it to set your Anthropic API key:

```
ANTHROPIC_API_KEY=<secret>
```

Release Notes:

- N/A
2025-04-25 15:53:02 +00:00
Kirill Bulatov
4f9cadabf7 Bump Danger's packages (#29422)
https://github.com/zed-industries/zed/security/dependabot reports a
number of vulnerabilities, attempt to fix them with the dependency bump.

Release Notes:

- N/A
2025-04-25 15:21:58 +00:00
Marshall Bowers
7443f89a2e xtask: Ignore workspace-hack when checking for non-workspace dependencies (#29419)
This PR makes it so `workspace-hack` is ignored by `cargo xtask
package-conformity` when looking for non-workspace dependencies.

Also added `zed_extension_api` to the exclude list.

Release Notes:

- N/A
2025-04-25 15:01:53 +00:00
Marshall Bowers
9bee765d7f ci: Fix typo (#29421)
This PR fixes a small typo in a comment added in #29420.

Release Notes:

- N/A
2025-04-25 15:00:12 +00:00
Marshall Bowers
8c553ee9f0 ci: Add no-op job for "Run Agent Eval" workflow (#29420)
This PR adds a no-op job for the "Run Agent Eval" workflow.

This aims to avoid marking the check as failed on a PR that does not
include the `run-eval` label.

Release Notes:

- N/A
2025-04-25 10:54:12 -04:00
Oleksiy Syvokon
3389327df5 eval: Add HTML overview for evaluation runs (#29413)
This update generates a single self-contained .html file that shows an
overview of evaluation threads in the browser. It's useful for:

- Quickly reviewing results
- Sharing evaluation runs
- Debugging
- Comparing models (TBD)

Features:

- Export thread JSON from the UI
- Keyboard navigation (j/k or Ctrl + ←/→)
- Toggle between compact and full views

Generating the overview:

- `cargo run -p eval` will write this file in the run dir's root.
- Or you can call `cargo run -p eval --bin explorer` to generate it
without running evals.


Screenshot:

![image](https://github.com/user-attachments/assets/4ead71f6-da08-48ea-8fcb-2148d2e4b4db)


Release Notes:

- N/A
2025-04-25 17:49:05 +03:00
Kirill Bulatov
f106dfca42 Avoid unnecessary DB writes (#29417)
Part of https://github.com/zed-industries/zed/issues/16472

* Adds debug logging to everywhere near INSERT/UPDATEs in the DB

So something like 
`env RUST_LOG=debug,wasmtime_cranelift=off,cranelift_codegen=off,vte=off
cargo run` could be used to view these (current zlog seems to process
the exclusions odd, so not sure this is the optimal RUST_LOG line) can
be used to debug any further writes.

* Removes excessive window stack serialization

Previously, it serialized unconditionally every 100ms.
Now, only if the stack had changed, which is now check every 500ms.

* Removes excessive terminal serialization

Previously, it serialized its `cwd` on every `ItemEvent::UpdateTab`
which was caused by e.g. any character output.
Now, only if the `cwd` has changed at the next event processing time.

Release Notes:

- Fixed more excessive DB writes
2025-04-25 17:41:49 +03:00
Bennet Bo Fenner
37fa437990 agent: Allow to explictly disable tools when using enable_all_context_servers (#29414)
Previously, all MCP tools would be completed regardless if they were
disabled/enabled for the profile. This meant that the "Write" profile
was always using all MCP tools, even if you disabled them in the
settings.

Now, when `enable_all_context_servers` is set to `true`, we will enable
all tools from all MCP servers by default but disable the ones that are
explicitly disabled for the profile.

Also fixes an issue where the tools would not show up as enabled when
using `enable_all_context_servers: true`

Release Notes:

- agent: Fix an issue where MCP tools could not be enabled/disabled
2025-04-25 14:19:04 +00:00
Marshall Bowers
9be7bf72a4 language_models: Remove language-models feature flag (#29416)
This PR removes the `language-models` feature flag.

This feature is already generally available, so we no longer need the
feature flag.

Release Notes:

- N/A
2025-04-25 14:18:48 +00:00
Smit Barmase
357e38b471 workspace: Add right border to pinned tabs only on scroll (#29405)
Before (scrolled state, bug):
<img width="392" alt="before - with scroll"
src="https://github.com/user-attachments/assets/5f62f050-41e0-4740-8f90-9822348eaa4b"
/>

After (scrolled state, bug fixed):
<img width="344" alt="after - with scroll"
src="https://github.com/user-attachments/assets/33003358-0009-4748-8a6e-642158114b82"
/>

Before (without scrolled state, as it is):
<img width="541" alt="before - without scroll"
src="https://github.com/user-attachments/assets/42487d61-7f7c-49a7-a087-da7faf5a0a89"
/>

After (without scrolled state, as it is):
<img width="462" alt="after - without scroll"
src="https://github.com/user-attachments/assets/738a4d24-3a89-466b-8976-2bf47cfeb0f5"
/>

cc @danilo-leal 

Release Notes:

- N/A
2025-04-25 18:46:13 +05:30
Bennet Bo Fenner
ae37f3ca2e agent: Improve MCP tools compatibility with Gemini models (#29411)
Release Notes:

- agent: Improve MCP tools compatibility with Gemini models
2025-04-25 15:14:16 +02:00
Kirill Bulatov
49003d8038 When hovering paths in terminal, search worktree entries for relative ones only (#29406)
Follow-up of https://github.com/zed-industries/zed/pull/29274

Release Notes:

- N/A
2025-04-25 15:34:09 +03:00
Bennet Bo Fenner
93862838bd assistant: Fix issue when using inline assistant with Gemini models (#29407)
Closes #29020

Release Notes:

- assistant: Fix issue when using inline assistant with Gemini models
2025-04-25 12:24:21 +00:00
Julia Ryan
c39adc5242 Select collab channel filter query upon focusing (#29383)
In the process of implementing this I learned that you can also hit
escape to clear the query which is a decent workaround, but I think this
behavior more closely matches expectations. For example when you run the
"focus search" actions, those select the query.

Release Notes:

- N/A
2025-04-25 04:00:25 -07:00
Julia Ryan
ebb39d9231 Add "upstream" as a hardcoded remote name (#29382)
The ideal solution here would be the ability to pick a default remote
the first time you click on a PR or commit link from a blame, and then
store that state in the repo or project and allow you to change it
somehow.

Because that's complicated, and because the vast majority of users
follow the convention of using `upstream` and `origin`, this change just
adds `upstream` as a possible remote that takes precedence for
generating links. I've sometimes seen `origin` and `fork` used for the
same purposes, which will still work fine with this change.

Here are some sources recommending the `upstream`/`origin` convention:
-
https://www.atlassian.com/git/tutorials/comparing-workflows/forking-workflow
-
https://github.blog/open-source/git/git-2-5-including-multiple-worktrees-and-triangular-workflows/
- https://cli.github.com/manual/gh_repo_fork

The fact that the github cli renames them to those when you `gh repo
fork` is pretty strong evidence that it's worth supporting them even if
users can set arbitrary remote names or could actually want to open a PR
link on their fork.

Resolves #13511

Release Notes:

- Git blame links now prefer the `upstream` remote over `origin` if it
exists.
2025-04-25 03:59:38 -07:00
Marshall Bowers
187f851613 feature_flags: Add FeatureFlag suffix to feature flag types (#29392)
This PR adds the `FeatureFlag` suffix to the feature flag types that
were missing them.

This makes the names easier to search in the codebase.

Release Notes:

- N/A
2025-04-25 04:07:49 +00:00
Marshall Bowers
a77db45865 feature_flags: Remove remoting feature flag (#29390)
This PR removes the `remoting` feature flag.

The feature is shipped, and we aren't referencing the flag anywhere
anymore.

Release Notes:

- N/A
2025-04-25 03:41:11 +00:00
Marshall Bowers
6bb6be826d language_models: Use POST /completions endpoint for Zed provider (#29389)
This PR updates the Zed provider to use the `POST /completions`
endpoint.

There is no functional difference from `POST /completion`, but the
pluralized version reads better.

Release Notes:

- N/A
2025-04-25 02:58:02 +00:00
Michael Sloan
7d9a55d101 Bring back reload of agent context before sending message (#29385)
Realized after merging #29233 that this behavior is desired

Release Notes:

- N/A
2025-04-24 20:32:53 -06:00
Max Brunsfeld
57d8397f53 Remove unnecessary fields from the tool schemas (#29381)
This PR removes two fields from JSON schemas (`$schema` and `title`),
which are not expected by any model provider, but were spuriously
included by our JSON schema library, `schemars`.

These added noise to requests and cost wasted input tokens.

### Old

```json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "FetchToolInput",
  "type": "object",
  "required": [
    "url"
  ],
  "properties": {
    "url": {
      "description": "The URL to fetch.",
      "type": "string"
    }
  }
}
```

### New:

```json
{
  "properties": {
    "url": {
      "description": "The URL to fetch.",
      "type": "string"
    }
  },
  "required": [
    "url"
  ],
  "type": "object"
}
```

- N/A
2025-04-24 18:09:25 -07:00
Michael Sloan
17ecf94f6f Restructure agent context (#29233)
Simplifies the data structures involved in agent context by removing
caching and limiting the use of ContextId:

* `AssistantContext` enum is now like an ID / handle to context that
does not need to be updated. `ContextId` still exists but is only used
for generating unique `ElementId`.
* `ContextStore` has a `IndexMap<ContextSetEntry>`. Only need to keep a
`HashSet<ThreadId>` consistent with it. `ContextSetEntry` is a newtype
wrapper around `AssistantContext` which implements eq / hash on a subset
of fields.
* Thread `Message` directly stores its context.

Fixes the following bugs:

* If a context entry is removed from the strip and added again, it was
reincluded in the next message.
* Clicking file context in the thread that has been removed from the
context strip didn't jump to the file.
* Refresh of directory context didn't reflect added / removed files.
* Deleted directories would remain in the message editor context strip.
* Token counting requests didn't include image context.
* File, directory, and symbol context deduplication relied on
`ProjectPath` for identity, and so didn't handle renames.
* Symbol context line numbers didn't update when shifted

Known bugs (not fixed):

* Deleting a directory causes it to disappear from messages in threads.
Fixing this in a nice way is tricky. One easy fix is to store the
original path and show that on deletion. It's weird that deletion would
cause the name to "revert", though. Another possibility would be to
snapshot context metadata on add (ala `AddedContext`), and keep that
around despite deletion.

Release Notes:

- N/A
2025-04-24 21:29:33 +00:00
Nathan Sobo
d492939bed Back off the eval to once a day for now (#29378)
cc @maxbrunsfeld 

Release Notes:

- N/A
2025-04-24 14:54:54 -06:00
Richard Feldman
720dfee803 Treat invalid JSON in tool calls as failed tool calls (#29375)
Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-04-24 16:54:27 -04:00
Anthony Eid
a98c648201 debugger: Fix spawned debug adapters taking over Zed's shell (#29373)
This fixes a bug where Zed wasn't closable via ctl-c in the shell it was
spawned in after starting a debug adapter

Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-24 20:46:25 +00:00
Conrad Irwin
c147daae4a Terminal in debugger (#29328)
- **debug-terminal**
- **Use terminal inside debugger to spawn commands**

Closes #ISSUE

Release Notes:

- N/A
2025-04-24 14:26:09 -06:00
Smit Barmase
d3911e34de editor: Move blame popover from hover_tooltip to editor prepaint (#29320)
WIP!

In light of having more control over blame popover from editor.

This fixes: https://github.com/zed-industries/zed/issues/28645,
https://github.com/zed-industries/zed/issues/26304

- [x] Initial rendering
- [x] Handle smart positioning (edge detection, etc)
- [x] Delayed hovering, release, etc
- [x] Test blame message selection
- [x] Fix tagged issues

Release Notes:

- Git inline blame popover now dismisses when the cursor is moved, the
editor is scrolled, or the command palette is opened.
2025-04-25 01:52:24 +05:30
Danilo Leal
87f85f1863 Rename "Prompt Library" to "Rules Library" (#29349)
There's probably more to do to fully make the transition, and we'll
still debate a bit internally whether this is the name, but just opening
this PR up now for visibility.

Release Notes:

- N/A
2025-04-24 16:42:06 -03:00
Dan Dascalescu
1a4dab97db docs: Remove redundant word in "Configuring Zed" (#29364)
Release Notes:

- N/A
2025-04-24 21:42:19 +03:00
Bennet Bo Fenner
cd365b0cf5 gemini: Fix issue when deserializing tool call (#29363)
Fixes a regression introduced in #29322

Release Notes:

- N/A

Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-04-24 18:19:05 +00:00
Agus Zubiaga
58604fba86 agent: Do not reuse assistant message across generations (#29360)
#29354 introduced a bug where we would append tool uses to the last
assistant message even if it was from a previous request.

Release Notes:

- N/A

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-04-24 17:56:47 +00:00
shenjack
b0609272c0 ollama: Add DeepSeek v3 max token length (#29156)
Add deepseek-v3 max token length for ollama

Release Notes:

- N/A
2025-04-24 13:20:22 -04:00
Peter Tripp
a17807d8b1 docs: Rust-analyzer example settings for alternate targets (#29353)
Release Notes:

- N/A
2025-04-24 13:12:41 -04:00
Agus Zubiaga
f81e65ae7c agent: Do not create user messages for tool results in thread (#29354)
We used to insert empty user messages into the `Thread::messages` `Vec`
when tools finished running and then we would attach the results when
creating the request. This approach was very easy to mess up during
state handling, leading to empty user messages displayed in the
conversation and API failures.

Instead, we will no longer insert actual user messages for tool results
to the `Thread`, and will only do this on the fly when creating the
model request. This simplifies a lot of code and show fix the mentioned
errors.

Release Notes:

- agent: Improve reliability of LLM requests when including tool results

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Oleksiy Syvokon <oleksiy.syvokon@gmail.com>
2025-04-24 16:30:15 +00:00
Marshall Bowers
952fe34aaa anthropic: Remove list of supported countries (#29346)
This PR removes the list of supported countries from the `anthropic`
crate, as it is no longer referenced in this repo.

Release Notes:

- N/A
2025-04-24 15:17:33 +00:00
Marshall Bowers
f527df6fa1 google_ai: Remove list of supported countries (#29348)
This PR removes the list of supported countries from the `google_ai`
crate, as it is no longer referenced in this repo.

Release Notes:

- N/A
2025-04-24 15:04:45 +00:00
Marshall Bowers
b54bbebc03 open_ai: Remove list of supported countries (#29347)
This PR removes the list of supported countries from the `open_ai`
crate, as it is no longer referenced in this repo.

Release Notes:

- N/A
2025-04-24 14:55:37 +00:00
Vojtěch Hořánek
8bb7a1f9e7 Remove linked_edits issue description from Elm doc (#29350)
The known issue with `linked_edits` seems to be fixed in this PR:
https://github.com/elm-tooling/elm-language-server/pull/1364. This PR
removes the section from Zeds documentation to avoid confusion.

Release Notes:

- Remove known issues section from Elm documentation.
2025-04-24 14:54:12 +00:00
Danilo Leal
e70d8d4dfd agent: Simplify user message design more (#29326)
Follow-up to https://github.com/zed-industries/zed/pull/29165 where the
user message design is simplified even more. The edit button is not
visible anymore, and you can click on the whole message block to edit a
message.

Release Notes:

- N/A
2025-04-24 11:24:36 -03:00
Marshall Bowers
ea5ce2a1a4 collab: Remove unused RateLimiter (#29343)
This PR removes the `RateLimiter` from the collab codebase, as it is no
longer used.

Release Notes:

- N/A
2025-04-24 14:23:17 +00:00
Peter Tripp
fd8eeb537d Fix ctrl-enter opening inline-assistant in assistant text threads (#29313)
Closes: https://github.com/zed-industries/zed/issues/24501

This has been broken for a while on linux (at least since Feb 8th!) for Assistant1.
It is also broken for Text Threads in Assitant2 (on macos and linux).

This should fix both.

Potentially related:
- https://github.com/zed-industries/zed/pull/29107

Release Notes:

- Fix for `ctrl-enter` shortcut in Assistant text threads incorrectly
opening inline assist instead of triggering Send.

Co-authored-by: Conrad Irwin <conrad@zed.dev>
2025-04-24 09:17:35 -04:00
Marshall Bowers
92f21ee39d collab: Return current plan based on subscription status (#29341)
This PR makes collab return the current plan based on subscription
status instead of based on the staff bit.

Release Notes:

- N/A
2025-04-24 13:04:25 +00:00
Kirill Bulatov
fcfeea4825 Allow creating entries when nothing is selected in the project panel (#29336)
Closes https://github.com/zed-industries/zed/issues/29249

Release Notes:

- Allowed creating entries when nothing is selected in the project panel
2025-04-24 08:46:35 +00:00
Conrad Irwin
c0f8e0f605 Fix context_stack race in KeyContextView (#29324)
cc @notpeter

Before this change we used our own copy of `cx.key_context()` when
matching.
This led to races where the context queried could be either before (or
after) the
context used in dispatching.

To avoid the race, gpui now passes out the context stack actually used
instead.

Release Notes:

- Fixed a bug where the Key Context View could show the incorrect
context,
  causing confusing results.
2025-04-23 23:34:39 -06:00
Conrad Irwin
9d10489607 Show diagnostic codes (#29296)
Closes #28135
Closes #4388
Closes #28136

Release Notes:

- diagnostics: Show the diagnostic code if available

---------

Co-authored-by: Neo Nie <nihgwu@live.com>
Co-authored-by: Zed AI <ai+claude-3.7@zed.dev>
2025-04-23 20:51:01 -06:00
Nathan Sobo
8836c6fb42 Introduce LanguageModelToolUse::raw_input (#29322)
This is to enable alternative streaming solutions at the application
layer. I'm not sure we really should have performed parsing of the input
at this layer. Either way I want to experiment with streaming approaches
in a separate crate on a branch, and this will help.

/cc @maxdeviant @bennetbo @rtfeldman

Closes #ISSUE

Release Notes:

- N/A
2025-04-24 02:30:48 +00:00
Max Brunsfeld
f125353b6f Add tree-sitter example to the eval (#29321)
Interesting things about this example:
* It's a useful, non-trivial change I made with the agent in Tree-sitter
* It runs fast
* It frequently showcases edit file errors
* It occasionally completely errors out due to errors parsing tool call
input JSON

Release Notes:

- N/A
2025-04-23 18:46:38 -07:00
Marshall Bowers
fef2681cfa language_models: Count Google AI tokens through LLM service (#29319)
This PR wires the counting of Google AI tokens back up.

It now goes through the LLM service instead of collab's RPC.

Still only available for Zed staff.

Release Notes:

- N/A
2025-04-24 01:21:53 +00:00
Agus Zubiaga
8b5835de17 agent: Improve initial file search quality (#29317)
This PR significantly improves the quality of the initial file search
that occurs when the model doesn't yet know the full path to a file it
needs to read/edit.

Previously, the assertions in file_search often failed on main as the
model attempted to guess full file paths. On this branch, it reliably
calls `find_path` (previously `path_search`) before reading files.

After getting the model to find paths first, I noticed it would try
using `grep` instead of `path_search`. This motivated renaming
`path_search` to `find_path` (continuing the analogy to unix commands)
and adding system prompt instructions about proper tool selection.

Note: I know the command is just called `find`, but that seemed too
general.

In my eval runs, the `file_search` example improved from 40% ± 10% to
98% ± 2%. The only assertion I'm seeing occasionally fail is "glob
starts with `**` or project". We can probably add some instructions in
that regard.

Release Notes:

- N/A
2025-04-23 21:24:41 -03:00
Agus Zubiaga
2124b7ea99 agent: Encourage model to include displayed fields first (#29308)
Instructs the model to include the fields that we display first in the
input object, so that e.g the user can see the path of a file while the
model generates the content.

Release Notes:

- N/A
2025-04-23 20:16:15 -03:00
Marshall Bowers
74442b68ea collab: Remove CountLanguageModelTokens RPC message (#29314)
This PR removes the `CountLanguageModelTokens` RPC message from collab.

We were only using this for Google AI models through the Zed provider
(which is only available to Zed staff).

For now we're returning `0`, but will bring back soon.

Release Notes:

- N/A
2025-04-23 23:10:47 +00:00
Danilo Leal
ba3d82629e ui: Add inline_code method to label (#29306)
This makes it easy to have a label that looks like Markdown inline code
via the `inline_code(cx)` method.

Release Notes:

- N/A
2025-04-23 19:27:56 -03:00
Marshall Bowers
ecc600a68f collab: Remove code for embeddings (#29310)
This PR removes the embeddings-related code from collab and the
protocol, as we weren't using it anywhere.

Release Notes:

- N/A
2025-04-23 18:27:46 -04:00
Remco Smits
218496744c debugger: Add support for inline value hints (#28656)
This PR uses Tree Sitter to show inline values while a user is in a
debug session.

We went with Tree Sitter over the LSP Inline Values request because the
LSP request isn't widely supported. Tree Sitter is easy for
languages/extensions to add support to. Tree Sitter can compute the
inline values locally, so there's no need to add extra RPC messages for
Collab. Tree Sitter also gives Zed more control over how we want to show
variables.

There's still more work to be done after this PR, namely differentiating
between global/local scoped variables, but it's a great starting point
to start iteratively improving it.

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <peterosiewicz@gmail.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Anthony <anthony@zed.dev>
Co-authored-by: Kirill <kirill@zed.dev>
2025-04-23 22:27:27 +00:00
Marshall Bowers
d095bab8ad agent: Read the user's plan from the UserStore (#29305)
This PR updates the Agent panel to read the user's plan from the
`UserStore` instead of hard-coding it.

Release Notes:

- N/A
2025-04-23 22:27:11 +00:00
Finn Evers
f8c3fe7871 editor: Fix broken mouse scrolling on main (#29307)
This PR is a quick follow-up to #29234 , which unfortunately broke
scrolling with the mouse in editors on main.

The linked PR introduced the possiblilty to completely disable scrolling
for editors. Unfortunately, it also disabled scrolling for editors by
default. This PR fixes this by re-enabling it by default.

This change also needs to be backported to v0.184.x. Otherwise, mouse
scrolling in the next preview release will not work!

Release Notes:

- N/A
2025-04-23 22:26:19 +00:00
Oleksiy Syvokon
aa161078fb agent: Add "copy to clipboard" button to error message popups (#29299)
This change makes agent errors copy-able to clipboard:


![image](https://github.com/user-attachments/assets/bd34a3f2-ecd4-4092-9b3b-960953ed1879)



Release Notes:

- N/A
2025-04-24 01:12:27 +03:00
Julia Ryan
f11c749353 VSCode Settings import (#29018)
Things this doesn't currently handle:

- [x] ~testing~
- ~we really need an snapshot test that takes a vscode settings file
with all options that we support, and verifies the zed settings file you
get from importing it, both from an empty starting file or one with lots
of conflicts. that way we can open said vscode settings file in vscode
to ensure that those options all still exist in the future.~
- Discussed this, we don't think this will meaningfully protect us from
future failures, and we will just do this as a manual validation step
before merging this PR. Any imports that have meaningfully complex
translation steps should still be tested.
- [x] confirmation (right now it just clobbers your settings file
silently)
- it'd be really cool if we could show a diff multibuffer of your
current settings with the result of the vscode import and let you pick
"hunks" to keep, but that's probably too much effort for this feature,
especially given that we expect most of the people using it to have an
empty/barebones zed config when they run the import.
- [x] ~UI in the "welcome" page~
- we're planning on redoing our welcome/walkthrough experience anyways,
but in the meantime it'd be nice to conditionally show a button there if
we see a user level vscode config
- we'll add it to the UI when we land the new walkthrough experience,
for now it'll be accessible through the action
- [ ] project-specific settings
- handling translation of `.vscode/settings.json` or `.code-workspace`
settings to `.zed/settings.json` will come in a future PR, along with UI
to prompt the user for those actions when opening a project with local
vscode settings for the first time
- [ ] extension settings
- we probably want to do a best-effort pass of popular extensions like
vim and git lens
- it's also possible to look for installed/enabled extensions with `code
--list-extensions`, but we'd have to maintain some sort of mapping of
those to our settings and/or extensions
- [ ] LSP settings
- these are tricky without access to the json schemas for various
language server extensions. we could probably manage to do translations
for a couple popular languages and avoid solving it in the general case.
- [ ] platform specific settings (`[macos].blah`)
  - this is blocked on #16392 which I'm hoping to address soon
- [ ] language specific settings (`[rust].foo`)
  - totally doable, just haven't gotten to it yet
 
~We may want to put this behind some kind of flag and/or not land it
until some of the above issues are addressed, given that we expect
people to only run this importer once there's an incentive to get it
right the first time. Maybe we land it alongside a keymap importer so
you don't have to go through separate imports for those?~

We are gonna land this as-is, all these unchecked items at the bottom
will be addressed in followup PRs, so maybe don't run the importer for
now if you have a large and complex VsCode settings file you'd like to
import.

Release Notes:

- Added a VSCode settings importer, available via a
`zed::ImportVsCodeSettings` action

---------

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-04-23 20:54:09 +00:00
Danilo Leal
40b5a1b028 agent: Improve feedback text and buttons wrapping (#29302)
Just a little UI improvement here.

Release Notes:

- N/A
2025-04-23 17:12:52 -03:00
Peter Tripp
2d43818c04 rust-analyzer: Fix for deserialization error of CargoRunnableArgs (#29291)
Fix for error:

```log
2025-04-23T13:02:14-04:00 INFO  [lsp] starting language server process. binary path: "/Users/peter/Library/Application Support/Zed/languages/rust-analyzer/rust-analyzer-2025-04-21", working directory: "/Users/peter/zcode/zed", args: []
2025-04-23T13:02:16-04:00 ERROR [lsp] failed to deserialize response from language server: data did not match any variant of untagged enum RunnableArgs at line 1 column 199. response from language server: "[{\"label\":\"cargo check --workspace\",\"kind\":\"cargo\",\"args\":{\"cwd\":\"/Users/peter/zcode/zed/crates/gpui/src/platform/linux\",\"overrideCargo\":null,\"cargoArgs\":[\"check\",\"--workspace\"],\"executableArgs\":[]}}]"
2025-04-23T13:02:16-04:00 WARN  [project::lsp_store] LSP Runnables via rust-analyzer failed: failed to deserialize response
2025-04-23T13:02:16-04:00 ERROR [*unknown*] LSP Runnables via rust-analyzer failed: failed to deserialize response
```

Object is missing `environment`:
```json
[
  {
    "label": "cargo check --workspace",
    "kind": "cargo",
    "args": {
      "cwd": "/Users/peter/zcode/zed/crates/gpui/src/platform/linux",
      "overrideCargo": null,
      "cargoArgs": ["check", "--workspace"],
      "executableArgs": []
    }
  }
]
```

Follow-up to: https://github.com/zed-industries/zed/pull/28359

Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-04-23 20:11:01 +00:00
Smit Barmase
636c6e7f2d editor: Make SelectNext and SelectPrevious preserve cursor direction (#29293)
Closes #27652

Now, if the last selection is reversed, subsequent `SelectNext` or
`SelectPrevious` selection will also be reversed.


https://github.com/user-attachments/assets/dff31abf-ac9e-4d35-bd2c-34e7b0f3ca23

Release Notes:

- Fixed an issue where `SelectNext` and `SelectPrevious` did not
preserve the last selection's cursor direction.
2025-04-24 00:17:17 +05:30
Agus Zubiaga
45d3f5168a eval: New add_arg_to_trait_method example (#29297)
Release Notes:

- N/A

---------

Co-authored-by: Richard Feldman <oss@rtfeldman.com>
2025-04-23 18:46:39 +00:00
Danilo Leal
8366cd0b52 agent: Render diffs for the edit file tool (#29234)
This PR implements the `ToolCard` for the edit file tool, which allow us
to display an editor with a diff in the thread view with the changes
performed by the model.

- [x] Fix buffer sometimes displaying empty
- [x] Stop buffer from scrolling together with the thread
- [x] Fix multibuffer header sometimes appearing
- [x] Fix buffer height issue
- [x] Implement "full height" expand button
- [x] Add "Jump To File" functionality
- [x] Polish and refine styles

Release Notes:

- agent: Added diff preview cards in the thread view for edits performed
by the agent.

---------

Co-authored-by: João Marcos <marcospb19@hotmail.com>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-23 15:43:33 -03:00
Richard Feldman
f6774ae60d More graceful invalid JSON handling (#29295)
Now we're more tolerant of invalid JSON coming back from the model
(possibly because it was incomplete and we're streaming), plus if we do
end up with invalid JSON once it has all streamed back, we report what
the malformed JSON actually was:

<img width="444" alt="Screenshot 2025-04-23 at 1 49 14 PM"
src="https://github.com/user-attachments/assets/480f5da7-869b-49f3-9ffd-8f08ccddb33d"
/>

Release Notes:

- N/A
2025-04-23 14:08:26 -04:00
Marshall Bowers
92e810bfec language_models: Pass up mode for completion requests through Zed (#29294)
This PR makes it so we pass up the `mode` for completion requests
through the Zed provider.

Release Notes:

- N/A
2025-04-23 18:02:03 +00:00
Cole Miller
724c935196 Highlight merge conflicts and provide for resolving them (#28065)
TODO:

- [x] Make it work in the project diff:
  - [x] Support non-singleton buffers
  - [x] Adjust excerpt boundaries to show full conflicts
- [x] Write tests for conflict-related events and state management
- [x] Prevent hunk buttons from appearing inside conflicts
- [x] Make sure it works over SSH, collab
- [x] Allow separate theming of markers

Bonus:

- [ ] Count of conflicts in toolbar
- [ ] Keyboard-driven navigation and resolution
- [ ] ~~Inlay hints to contextualize "ours"/"theirs"~~

Release Notes:

- Implemented initial support for resolving merge conflicts.

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-04-23 12:38:46 -04:00
Kirill Bulatov
ef54b58346 Fix relative paths not properly resolved in the terminal during cmd-click (#29289)
Closes https://github.com/zed-industries/zed/pull/28342
Closes https://github.com/zed-industries/zed/issues/28339
Fixes
https://github.com/zed-industries/zed/pull/29274#issuecomment-2824794396

Release Notes:

- Fixed relative paths not properly resolved in the terminal during
cmd-click
2025-04-23 19:36:58 +03:00
Joseph T. Lyons
01bdd170ec Bump Zed to v0.185 (#29287)
Release Notes:

-N/A
2025-04-23 16:20:08 +00:00
Cole Miller
4b9f4feff1 debugger: Fix stack frame list flickering (#29282)
Closes #ISSUE

Release Notes:

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

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-04-23 16:12:53 +00:00
Bibiana André
19fb1e1b0d Fix workspace bottom obscured when bottom dock is full height (#27689)
When dragging the pane separator of the bottom dock to full window
height, the contents at the bottom of the dock and workspace window
overflowed the screen, becoming obscured. This happened because setting
a new size in resize_bottom_dock(...) was not taking in consideration
the top bounds of the workspace window, which caused the bottom bounds
of both dock and workspace to overflow. The issue was fixed by
subtracting the workspace.bounds.top() value to the dock's new size.

Closes #12966

Release Notes:

- N/A
2025-04-23 15:43:20 +00:00
Marshall Bowers
f2cb6d69d5 collab: Add head_commit_details column to project_repositories (#29284)
This PR adds the `head_commit_details` column to the
`project_repositories` table, since it was missed in
https://github.com/zed-industries/zed/pull/29007.

Release Notes:

- N/A
2025-04-23 15:35:49 +00:00
Bennet Bo Fenner
822b6f837d agent: Expose web search tool to beta users (#29273)
This gives all beta users access to the web search tool

Release Notes:

- agent: Added `web_search` tool
2025-04-23 15:30:20 +00:00
Antonio Scandurra
09db31288a Fix panic when copying smart quotes in MarkdownElement (#29285)
Release Notes:

- Fixed a panic that could sometimes happen when copying text in the
agent.
2025-04-23 15:17:27 +00:00
Conrad Irwin
a320d324f1 Fix shift-y on empty line in vim mode (#29253)
Release Notes:

- Fixes a regression where `shift-v up` on an empty line would appear to
have selected the line after (though in reality it did not)
2025-04-23 09:06:55 -06:00
Marshall Bowers
266c41ed9a collab: Add can_use_web_search_tool to LLM token claims (#29278)
This PR adds a `can_use_web_search_tool` field to the LLM token claims.

Currently anyone in the `assistant2` feature flag will have access to
the web search tool.

Co-authored-by: Bennet <bennet@zed.dev>

Release Notes:

- N/A
2025-04-23 14:22:18 +00:00
Variant9
4f4bbf264f theme_importer: Handle comma-separated token scopes (#27740)
This PR allows the `theme-importer` utility to handle comma-separated
token scopes.

Normally, a token in a VS Code theme is defined as either a string or a
string array:
```json
    {
      "scope": "token.debug-token",
      "settings": {
        "foreground": "#d55fde"
      }
    },
    {
      "name": "String interpolation",
      "scope": [
        "punctuation.definition.template-expression.begin",
        "punctuation.definition.template-expression.end",
        "punctuation.section.embedded"
      ],
      "settings": {
        "foreground": "#d55fde"
      }
    },
```

However, [some
themes](ac85540d64/src/variants/TokenColors.ts (L1771-L1777))
seem to use comma-separated values in a single scope string which VS
Code seems to accept as well:
```json
    {
      "name": "Comments",
      "scope": "comment, punctuation.definition.comment",
      "settings": {
        "foreground": "#7f848e"
      }
    },
```

This PR handles these definitions by splitting scopes by commas before
trying to match them with the scopes that match Zed syntax tokens.

Release Notes:

- N/A
2025-04-23 14:06:58 +00:00
Peter Finn
990ca48744 docs: Update macOS development instructions (#27611)
Updating macOS development readme with some gotchas that I ran into
while getting setup.
- Linked to collab readme because that contained the steps to setup the
postgres database so integration tests pass
- Added section under troubleshooting. Recommending `cargo-nextest`
since the CI uses it and it got me past the failures I was seeing.

Release Notes:

- N/A

---------

Co-authored-by: KyleBarton <kjbarton4@gmail.com>
2025-04-23 13:50:04 +00:00
Oleksiy Syvokon
f69aeb6311 Do not log unfinished tools use that are in the middle of streaming (#29275)
Release Notes:

- N/A
2025-04-23 13:19:01 +00:00
Kirill Bulatov
d5f3fbdc88 Lookup relative paths in a worktree more robustly (#29274)
Attempt to lookup exact relative paths before full worktree traversal,
only do the full traversal if all other methods fail.

Closes https://github.com/zed-industries/zed/issues/28407

Release Notes:

- Fixed wrong paths opening when cmd-clicking in the terminal
2025-04-23 13:13:28 +00:00
Oleksiy Syvokon
76a78b550b eval: Write JSON-serialized thread (#29271)
This adds `last.message.json` file that contains the full request plus
response (serialized as a message from assistant for consistency with
other messages).

Motivation: to capture more info and to make analysis of finished runs
easier.

Release Notes:

- N/A
2025-04-23 15:22:19 +03:00
Antonio Scandurra
e515b2c714 Polish agent checkpoints (#29265)
Release Notes:

- Improved performance of agent checkpoint creation.
- Fixed a bug that sometimes caused accidental deletions when restoring
to a previous agent checkpoint.
- Fixed a bug that caused checkpoints to be visible in the Git history.
2025-04-23 11:37:55 +00:00
Antonio Scandurra
55ea481707 Restore file to original content when rejecting file recreated by agent (#29264)
Release Notes:

- Fixed a bug that could sometimes cause a file to be deleted when
rejecting an agent change.
2025-04-23 09:42:43 +00:00
Conrad Irwin
5e31d86f1f Fix panic in vim selection restoration (#29251)
Closes #27986

Closes #ISSUE

Release Notes:

- vim: Fixed a panic when using `gv` after `p` in visual line mode
2025-04-22 22:28:13 -06:00
Conrad Irwin
4a8f114528 Fix panic when collaborating with new multibuffers (#29245)
Before this change, when syncing a multibuffer (such as
find-all-references) to a remote, we would renumber the excerpts from 1.
This did not matter in the past because the buffers' list of excerpts
could not change. In #27876, I added the ability for excerpts to merge,
which meant that the excerpt list could change. This manifested as
people seeing "invalid excerpt id" panics when syncing.

The initial fix to this (to re-use the excerpt ids from the host) ran
into problems because `insert_excerpts_with_ids_after` assumes that you
call it in excerpt-id order. This change de-optimizes that code to
insert the excerpts 1-by-1 in excerpt-id order, but with the
insert_after set to preserve the correct UI order.

I hope to soon remove this code path and use something more like
set-excerpts-for-path for syncing, but in the meantime we should not
panic.

Release Notes:

- Fix a panic when joining a project with a multibuffer with merged
excerpts
2025-04-22 22:04:21 -06:00
Agus Zubiaga
ce1a674eba eval: Fine-grained assertions (#29246)
- Support programmatic examples
([example](17feb260a0/crates/eval/src/examples/file_search.rs))
- Combine data-driven example declarations into a single `.toml` file
([example](17feb260a0/crates/eval/src/examples/find_and_replace_diff_card.toml))
- Run judge on individual assertions (previously called "criteria")
- Report judge and programmatic assertions in one combined table

Note: We still need to work on concept naming 

<img width=400
src="https://github.com/user-attachments/assets/fc719c93-467f-412b-8d47-68821bd8a5f5">

Release Notes:

- N/A

---------

Co-authored-by: Richard Feldman <oss@rtfeldman.com>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Thomas Mickley-Doyle <tmickleydoyle@gmail.com>
2025-04-22 23:58:58 -03:00
Smit Barmase
0d3fe474db editor: Use quantize score for code completions sort + Add code completions tests (#29182)
Closes #27994, #29050, #27352, #27616

This PR implements new logic for code completions, which improve cases
where local variables, etc LSP based hints are not shown on top of code
completion menu. The new logic is explained in comment of code.

This new sort is similar to VSCode's completions sort where order of
sort is like:

Fuzzy > Snippet > LSP sort_key > LSP sort_text 

whenever two items have same value, it proceeds to use next one as tie
breaker. Changing fuzzy score from float to int based makes it possible
for two items two have same fuzzy int score, making them get sorted by
next criteria.

Release Notes:

- Improved code completions to prioritize LSP hints, such as local
variables, so they appear at the top of the list.
2025-04-23 07:23:34 +05:30
Conrad Irwin
6a009b447a debugger: Open debugger panel on session startup (#29186)
Now all debug sessions are routed through the debug panel and are
started synchronously instead of by a task that returns a session once
the initialization process is finished. A session is `Mode::Booting`
while it's starting the debug adapter process and then transitions to
`Mode::Running` once this is completed.

This PR also added new tests for the dap logger, reverse start debugging
request, and debugging over SSH.

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Anthony <anthony@zed.dev>
Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Zed AI <ai@zed.dev>
Co-authored-by: Remco Smits <djsmits12@gmail.com>
2025-04-22 19:35:47 -04:00
Ben Kunkle
75ab8ff9a1 zlog: Add default filters (#29244)
Added default filters to `zlog`, a piece that was present in our
`simple_log` setup, but was missed when switching to `zlog`, resulting
in logspam primarily on linux.

also - more explicit precedence & precedence testing

Release Notes:

- N/A
2025-04-22 18:54:56 -04:00
Mikayla Maki
3705986fac Adjust image cache APIs to enable ElementState based APIs (#29243)
cc: @sunli829 @huacnlee @probably-neb 

I really liked the earlier PR, but had an idea for how to utilize the
element state so that you don't need to construct the cache externally.
I've updated the APIs to introduce an `ImageCacheProvider` trait, and
added an example implementation of it to the image gallery :)

Release Notes:

- N/A
2025-04-22 22:08:28 +00:00
Ben Kunkle
aefb3aa2fa Fix handling of --system-specs argument so it happens before Application::new (#29240)
Fixes issue described in [description of
#28683](https://github.com/zed-industries/zed/issues/28683#issue-2992849891)

Makes sure that the `--system-specs` arg is handled before
`Application::new` is called, so that it can be used even when Zed is
panicking during app initialization (e.g. Failing to create a Vulkan
context in blade)

Release Notes:

- Fixed an issue where the `--system-specs` arg wouldn't work if Zed
panicked during app initialization (e.g. When failing to create a Vulkan
context in blade)
2025-04-22 21:32:32 +00:00
Marshall Bowers
8e7c145f20 inline_completion_button: Show the usage limits returned from the API (#29239)
This PR updates the usage meter for edit predictions to use the limits
returned from the API instead of basing it off the plan.

This will allow limits to be updated from the server rather than being
embedded in the client.

Release Notes:

- N/A
2025-04-22 21:16:54 +00:00
Marshall Bowers
a2a502f026 zed_extension_api: Release v0.4.0 (#29237)
This PR releases v0.4.0 of the Zed extension API.

Support for this version of the extension API will land in Zed v0.184.x.

Release Notes:

- N/A
2025-04-22 21:07:52 +00:00
Ben Kunkle
c231c95521 platform/blade: Improve ZED_DEVICE_ID parsing (#29235)
Closes #28533

Release Notes:

- Linux: Improved parsing of `ZED_DEVICE_ID` environment variable in an
attempt to fix some cases where it erroneously failed to parse. The
`ZED_DEVICE_ID` is now expected to always be a 4 digit hexadecimal
number (as it is in the output of `lcpci`) with an optional `0x` or `0X`
prefix.
2025-04-22 21:01:43 +00:00
Marshall Bowers
fcc6a86c90 agent: Show the usage limits returned from the API (#29236)
This PR updates the usage banners in the Agent panel to use the limits
returned from the API instead of basing it off the plan.

This will allow limits to be updated from the server rather than being
embedded in the client.

Release Notes:

- N/A
2025-04-22 21:01:23 +00:00
Peter Tripp
338a6a3b7e ci: Only run scheduled evals, not on main/release branch commits (#29238)
Release Notes:

- N/A
2025-04-22 16:55:36 -04:00
Marshall Bowers
a0eaede13d collab: Limit customers to one free trial (#29232)
This PR makes it so customers can only subscribe to the trial once.

Release Notes:

- N/A
2025-04-22 20:41:17 +00:00
Sunli
abf2b9d7d3 gpui: Add ImageCache (#27774)
Closes #27414

`ImageCache` is independent of the original image loader and can
actively release its cached images to solve the problem of images loaded
from the network or files not being released.

It has two constructors:

- `ImageCache::new`: Manually manage the cache.
- `ImageCache::max_items`: Remove the least recently used items when the
cache reaches the specified number.

When creating an `img` element, you can specify the cache object with
`Img::cache`, and the image cache will be managed by `ImageCache`.

In the example `crates\gpui\examples\image-gallery.rs`, the
`ImageCache::clear` method is actively called when switching a set of
images, and the memory will no longer continuously increase.


Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-04-22 13:30:21 -07:00
Smit Barmase
a50fbc9b5c language: Fix language_scope_at for markdown code comments (#29230)
Closes #29176

This PR fix an issue where uncommenting a code block in Markdown would
add Markdown comments instead of removing the language-specific
comments.

Why?
`language_scope_at` for comments in a code block in Markdown would
result in the language being detected as Markdown. This happens because
the smallest range, such as `//` or `#` on the Markdown layer, is
preferred over `// whole comment line` for any other language. This
results in language detection as Markdown for that point.

To fix this, we also use a depth factor and try to prefer the layer with
greater depth over one with lesser depth. In this case, the code block's
language depth would be preferred over Markdown. The smallest range is
now used as a tiebreaker.

Added test for this case.

Release Notes:

- Fixed issue where uncommenting a code block in Markdown would add
Markdown comments instead of removing the language comments.
2025-04-23 01:20:25 +05:30
Marshall Bowers
9bbc2e0fb2 collab: Set plan in LLM token based on subscription (#29231)
This PR updates the `plan` field in the LLM token to be based on the
subscription.

We weren't using this field anywhere outside of the new billing code, so
it is safe to change its meaning.

Release Notes:

- N/A
2025-04-22 19:44:16 +00:00
Matin Aniss
6caf34ab7e gpui: Align image sprites to whole pixels (#29227)
Similar to #15822, just applies the same fix to images as they are also
affected by the same issue.

Release Notes:

- N/A
2025-04-22 17:29:03 +00:00
Konstantin Podsvirov
8607c7d3ee docs: Fix mistake in Initializing the remote server section (#28641)
Fix `Initializing the remote server` section.

Release Notes:

- N/A
2025-04-22 13:15:59 -04:00
Doods
e26bb05567 docs: Update "checkOnSave" to "check" (#29212)
As-salamu alaykum,

[I recently started suffering from the same issue as this
user](https://users.rust-lang.org/t/rust-analyzer-checkonsave-command-works-but-shows-invalid-config-warning/128652),
which is caused by something the docs of Zed promote, so I decided to
help fix it.

>[anutrix](https://users.rust-lang.org/u/anutrix)
> When I add "rust-analyzer.checkOnSave.command": "clippy" I get:
> 
> invalid config value: /checkOnSave: invalid type: map, expected a
boolean;
> Extension Info: Version 0.3.2433, Server Version 0.3.2433-standalone
(66e3b5819e 2025-04-21)
> and in Language Server logs:
> 
> [Error - 3:26:22 AM] Server process exited with code 0.
> Clippy works fine but these warnings stays and extensions shows
yellow/unstable in VSCode:
> 
> Additionally, if I replace
> 
>     "rust-analyzer.checkOnSave.command": "clippy"
> with
> 
>     "rust-analyzer.checkOnSave": true,
>     "rust-analyzer.checkOnSave.command": "clippy"

> [jplatte](https://users.rust-lang.org/u/jplatte)
> From the documentation, it seems like
rust-analyzer.checkOnSave.command does not exist. It should be
rust-analyzer.check.command.
2025-04-22 16:27:47 +00:00
Marshall Bowers
b3b89c8443 collab: Don't require payment method to start a trial (#29224)
This PR makes it so a payment method is not required in order to start a
Zed Pro trial.

Release Notes:

- N/A
2025-04-22 16:24:46 +00:00
Bennet Bo Fenner
962b024248 agent: Improve the review changes UX (#29221)
Release Notes:

- agent: Improved the AI-generated changes review UX by clearly exposing
the generating state in the multibuffer tab.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-04-22 13:08:35 -03:00
Marshall Bowers
833653a3ea collab: Transfer existing usage from trial to Pro (#28884)
This PR adds support for transferring any existing usage from a trial
subscription to a Zed Pro subscription when the user upgrades.

Release Notes:

- N/A

---------

Co-authored-by: Mikayla <mikayla@zed.dev>
2025-04-22 15:25:50 +00:00
Danilo Leal
886f0b7214 agent: Add small design tweaks (#29218)
Nothing too serious over here, just spacing and other small-ish tweaks.

Release Notes:

- N/A
2025-04-22 12:17:34 -03:00
Cole Miller
207fb04969 Implement basic support for VS Code debug configurations (#29160)
- [x] Basic implementation
- [x] Match common VSC debug extension names to Zed debug adapters
- [ ] ~~`preLaunchTask` support~~ descoped for this PR

Release Notes:

- N/A
2025-04-22 14:24:09 +00:00
Max Brunsfeld
36d02de784 Rework eval to support interpretable scores and efficient repetitions (#29197)
### Problem

We want to start continuously tracking our progress on agent evals over
time. As part of this, we'd like the *score* to have a clear,
interpretable meaning. Right now, it's a number from 0 to 5, but it's
not clear what any particular number works. In addition, scores vary
widely from run to run, because the agent's output is deterministic. We
try to stabilize the score using a panel of judges, but the behavior of
the agent itself varies much more widely than the judges' scores for a
given run.

### Solution

* **explicit meanings of scores** - In this PR, we're prescribing the
diff and thread criteria files so that they *must* be unordered lists of
assertions. For both the thread and the diff, rather than providing an
abstract score, the judge's task is simply to count how many of these
assertions are satisfied. A percentage score can be derived from this
number, divided by the total number of assertions.
* **repetitions** - Rather than running each example once, and judging
it N times, we'll **run** the example N times. Right now, I'm just
judging the output once per run, because I believe that with these more
clear scoring criteria, the main source of non-determinism will be the
*agent's* behavior, not the judge's

### Questions

* **accounting for diagnostic errors** - Previously, the judge was asked
to incorporate diagnostics into their abstract scores. Now that the
"score" is determined directly from the criteria, the diagnostic will
not be captured in the score. How should the diagnostics be accounted
for in the eval? One thought is - let's simply count and report the
number of errors remaining after the agent finishes, as a separate field
of the run (along with diff score and thread score). We could consider
normalizing it using the total lines of added code (like errors per 100
lines of code added) in order to give it some semblance of stability
between examples.

* **repetitions** - How many repetitions should we run on CI? Each
repetition takes significant time, but I think running more than one
repetition will make the scores significantly less volatile.

### Todo

* [x] Fix `--concurrency` implementation so that only N tasks are
spawned
* [x] Support `--repetitions` efficiently (re-using the same worktree)
* [x] Restructure judge prompts to count passing criteria, not compute
abstract score
* [x] Report total number of diagnostics in some way
* [x] Format output nicely

Release Notes:

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

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
2025-04-22 14:00:09 +00:00
Danilo Leal
36da97935a agent: Show project name in the Agent notification (#29211)
Release Notes:

- agent: Added the project name in the Agent Panel notification.
2025-04-22 10:41:03 -03:00
Danilo Leal
19b547565d agent: Refine the web search tool call UI (#29190)
This PR refines a bit the web search tool UI by introducing a component
(`ToolCallCardHeader`) that aims to standardize the heading element of
tool calls in the thread.

In terms of next steps, I plan to evolve this component further soon
(e.g., building a full-blown "tool call card" component), and even move
it to a place where I can re-use it in the active_thread as well without
making the `assistant_tools` a dependency of it.

Release Notes:

- N/A
2025-04-22 09:51:57 -03:00
Danilo Leal
109f1d43fc agent: Simplify user message design (#29165)
Mainly removing the "You" label, which didn't add a lot of value. Still
figuring out an issue with font size Markdown rendering before merging
this PR.

Release Notes:

- N/A
2025-04-22 09:51:50 -03:00
Bennet Bo Fenner
a5852d4537 agent: Support inserting selections as context via @selection (#29045)
WIP

Release Notes:

- N/A
2025-04-22 13:56:42 +02:00
Stephan Seidt
10ded0ab75 agent: Add support for google gemini 2.5 flash preview (#29205)
Adds support for the new gemini-2.5-flash-preview-04-17

Release Notes:

- agent: Added support for gemini-2.5-flash-preview
2025-04-22 09:37:12 +00:00
Bennet Bo Fenner
b0b620af56 gemini: Add support for passing images as part of the prompt (#29203)
Release Notes:

- agent: Add support for adding images as context when using Google
Gemini
2025-04-22 09:05:46 +00:00
Bennet Bo Fenner
eca6d5a04e agent: Support pasting images as context (#29177)
https://github.com/user-attachments/assets/d6a27b05-3590-4f40-a820-f6f99f6bd581

Release Notes:

- agent: Added support for pasting images as context

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-04-22 09:01:01 +00:00
Conrad Irwin
3357736aea Fix duplicated multi-buffer excerpts (#29193)
- **add test case**
- **Merge excerpts more aggressively**
- **Randomized test for set_excerpts_for_path**

Closes #ISSUE

Release Notes:

- Fixed duplicted excerpts (and resulting panics)

---------

Co-authored-by: João Marcos <marcospb19@hotmail.com>
2025-04-22 05:25:09 +00:00
Nathan Sobo
458ffaa134 Add new action to run agent eval (#29158)
The old one wasn't linking, and
https://github.com/zed-industries/zed/pull/29081 has a bunch of merge
conflicts. Wanted to start simple/small.

## Todo

* [x] Remove low-signal examples
* [x] Make the eval run on a cron, on main, and on any PR with the
`run-eval` label
* [x] Noise in logs about failure to write settings
    ```
[2025-04-21T20:45:04Z ERROR settings] Failed to write settings to file
"/home/runner/.config/zed/settings.json"
    
       Caused by:
No such file or directory (os error 2) at path
"/home/runner/.config/zed/.tmpLewFEs"
    ```
* [x] `Agentic loop stalled`
(https://github.com/zed-industries/zed/actions/runs/14581044243/job/40897622894)
* [x] Make sure that events are recorded in snowflake
* [ ] Change judge criteria to be more explicit about meanings of scores

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Thomas Mickley-Doyle <tmickleydoyle@gmail.com>
2025-04-21 21:30:21 -07:00
Agus Zubiaga
b14356d1d3 agent: Do not add <using_tool> placeholder (#29194)
Our provider code in `language_models` filters out messages for which
`LanguageModelRequestMessage::contents_empty` returns `false`. This
doesn't seem wrong by itself, but `contents_empty` was returning `false`
for messages whose first segment didn't contain non-whitespace text even
if they contained other non-empty segments. This caused requests to fail
when a message with a tool call didn't contain any preceding text.

Release Notes:

- N/A
2025-04-22 00:41:47 -03:00
Michael Sloan
19ef56ba7c agent: Fix file context renames affecting display + simplify loading code (#29192)
Release Notes:

- N/A
2025-04-22 03:16:46 +00:00
Conrad Irwin
dfbd132d9f Update Split bindings in terminal (#29188)
Closes #29087

Release Notes:

- Changed default bindings for splitting terminals from `ctrl-k
{up,down,left,right}` to `ctrl-alt-{up,down,left,right}`. `ctrl-k` is
used by Readline to cut to the end of the line.
2025-04-21 19:48:18 -06:00
Michael Sloan
2e8ee9b64f agent: Make directory context display update on rename (#29189)
Release Notes:

- N/A
2025-04-22 01:44:31 +00:00
Gaku Kanematsu
c15382c4d8 vim: Add cursor shape settings for each vim mode (#28636)
Closes #4495

Release Notes:

- vim: add cursor shape settings for each vim mode

---

Add cursor shape settings for each vim mode to enable users to specify
them.

Example of `settings.json`:

```json
{
  "vim_mode": true,
  "vim": {
    "cursor_shape": {
      "normal": "hollow",
      "insert": "bar",
      "replace": "block",
      "visual": "underline"
    }
  }
}
```

After this change is applied,

- The cursor shape specified by the user for each mode is used.
- In insert mode, the `vim > cursor_shape > insert` setting takes
precedence over the primary `cursor_shape` setting.
- If `vim > cursor_shape > insert` is not set, the primary
`cursor_shape` will be used in insert mode.
- The cursor shape will remain unchanged before and after this update
when the user does not set the `vim > cursor_shape` setting.

Video:


[screen-record.webm](https://github.com/user-attachments/assets/b87461a1-6b3a-4a77-a607-a340f106def5)
2025-04-21 18:42:04 -06:00
Michael Sloan
70c51b513b agent eval: Default to also running typescript examples (#29185)
Release Notes:

- N/A
2025-04-21 23:59:35 +00:00
Mikayla Maki
38afae86a9 Use buffer size for markdown preview (#29172)
Note:

This is implemented in a very hacky and one-off manner. The primary
change is to pass a rem size through the markdown render tree, and scale
all sizing (rems & pixels) based on the passed in rem size manually.
This required copying in the `CheckBox` component from `ui::CheckBox` to
make it use the manual rem scaling without modifying the `CheckBox`
implementation directly as it is used elsewhere.

A better solution is required, likely involving `window.with_rem_size`
and/or _actual_ `em` units that allow text-size-relative scaling.

Release Notes:

- Made it so Markdown preview uses the _buffer_ font size instead of the
_ui_ font size.

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
Co-authored-by: Nate Butler <nate@zed.dev>
2025-04-21 19:29:21 -04:00
Michael Sloan
9249919b7a Write {result_count}.diff and last.diff eval run outputs (#29181)
These are only written when the diff has changed. `patch.diff` has been
removed as its redundant with `last.diff`.

It can be convenient to open `last.diff` and use undo/redo to navigate
its history.

Release Notes:

- N/A
2025-04-21 23:19:07 +00:00
Michael Sloan
9fe4a14f73 Add a brief description of GPUI 2->GPUI 3 changes to .rules (#29180)
Release Notes:

- N/A
2025-04-21 22:41:15 +00:00
Finn Evers
7cc3c03b08 editor: Fix hang when scrolling over single line input fields (#28471)
Closes #21684
Closes #28463
Closes #28264 

This PR fixes Zed hanging when scrolling over single line input fields
with `scroll_beyond_last_line` set to `vertical_scroll_margin`. The
change here is to fix the calculations of available lines.

The issue only arises with the setting present because with all
overscroll settings and `max_row` being 1 for single-line editors, the
calculation would still return the correct value of available lines,
which is 1. However, with overscrolling set to `vertical_scroll_margin`
and that set to any value greater than 0, the calculation would return
that the single-line editor has more than one line, which caused the
issues described above (Actually, setting `vertical_scroll_margin` to 1
works for some reason, overscrolls "properly" and does not cause a
crash. But I really did not want to investigate this buggy behavior
further).

This PR fixes this by always reporting the number of available lines as
the line number value for single line editors, which will (mostly) be 1
(for more context see the discussion in this PR).

Release Notes:

- Fixed an issue where Zed would crash when scrolling over single line
input fields and `scroll_beyond_last_line` set to
`vertical_scroll_margin`.
2025-04-22 00:37:04 +02:00
Richard Feldman
4f2f9ff762 Streaming tool calls (#29179)
https://github.com/user-attachments/assets/7854a737-ef83-414c-b397-45122e4f32e8



Release Notes:

- Create file and edit file tools now stream their tool descriptions, so
you can see what they're doing sooner.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-04-21 22:28:32 +00:00
Michael Sloan
7aa0fa1543 Add ability to attach rules as context (#29109)
Release Notes:

- agent: Added support for adding rules as context.
2025-04-21 20:16:51 +00:00
Michael Sloan
3b31860d52 Add to .rules: Avoid creating mod.rs paths (#29174)
Release Notes:

- N/A
2025-04-21 20:14:56 +00:00
Thomas Mickley-Doyle
733cd6b68c agent: Remove non-rust examples from evals (#29139)
Release Notes:

- N/A
2025-04-21 12:55:24 -07:00
Anthony Eid
e8fe0eb2e6 debugger: Fix restarting terminated child sessions (#29173)
This fixes a bug where terminated child session failed to restart
because they were using the wrong configuration/binary to start a new
session

Release Notes:

- N/A
2025-04-21 14:25:38 -04:00
Michael Sloan
0f3ac38332 Agent eval: Copy .rules file into eval worktree for examples based on Zed (#29116)
Also reverts #29108, which cherry-picked the rules file for an eval
example.

Release Notes:

- N/A
2025-04-21 12:02:44 -06:00
Conrad Irwin
32e9757a85 Fix ctrl-c in vim normal mode (#29167)
This was broken when we added helix keybindings because we populate the
menu's shortcut based on the "last" seen binding for an action ignoring
context.

Release Notes:

- Fix `ctrl-c` in vim normal mode
2025-04-21 11:19:44 -06:00
Agus Zubiaga
be76942a69 agent: Migrate tool names in settings (#29168)
Release Notes:

- agent: Add migration to rename `find_replace_file` tool to
`edit_file`, and `regex_search` to `grep`.
2025-04-21 17:03:42 +00:00
Marshall Bowers
942d4eb126 agent: Add additional fields to Agent Tool Finished telemetry event (#29163)
This PR adds additional fields to the `Agent Tool Finished` telemetry
event:

- `model`
- `model_provider`
- `thread_id`
- `prompt_id`

Release Notes:

- N/A
2025-04-21 16:12:53 +00:00
Conrad Irwin
9d35f0389d debugger: More tidy up for SSH (#28993)
Split `locator` out of DebugTaskDefinition to make it clearer when
location needs to happen.

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Anthony <anthony@zed.dev>
Co-authored-by: Cole Miller <m@cole-miller.net>
2025-04-21 16:00:03 +00:00
Ben Kunkle
d13cd007a2 zlog: Module-level configuration and other improvements (#29161)
Various improvements to `zlog` including:

- Enable filtering by module (reproducing `env_logger` behavior) both
through env and settings.
- Note: filtering by module currently does not account for parent module
configuration, but does account for crate configuration.
i.e. `crate=trace` will enable `TRACE` messages in `crate::a` and
`crate::a::b` modules, but `crate::a=trace` will not enable trace
messages in module `crate::a::b`
- Implementing the `Log` trait for `zlog::Logger` to support gradual
transition and evaluate tradeoffs of always going through `log` crate.
- Added the ability to turn off logging for a specific filter (module or
scope) completely by setting it to `off` (in env: `crate::a=off`, in
settings: `"project.foo": "off"`)
- Made it so the `zlog::scoped!` macro can be used in constant
expressions, so scoped loggers can be declared as global constants

Release Notes:

- N/A
2025-04-21 11:43:24 -04:00
Ho Chun Lau
f8ac6eef75 terminal: Add right-click in terminal to create a new selection if none is present (#29131)
This PR adds functionality to right click in terminal create new
selection if none present. The selection is identical with double click
a text in terminal, plus the logic is moved from the double-click in the
terminal::mouse_down.

Closes #28237 

Release Notes:
- Adds functionality to right click in terminal create new selection if
none present
2025-04-21 21:09:17 +05:30
redforks
6d2bdc3bac editor: Hide mouse context menu when modal is opened (#29127)
Closes #28787 

The context menu appears before the modal because it is a Deferred
element, which is always displayed above normal elements.

Release Notes:

Previously, the editor context menu appeared before the Command Palette.
This commit ensures the editor context menu is hidden when a modal,
including the Command Palette, is opened.
2025-04-21 20:13:26 +05:30
Danilo Leal
9a3434efb4 component preview: Focus search input immediately upon opening (#29155)
Just a quick quality of life improvement to make keyboard navigation in
this view a bit better. When you open the Component Preview view now,
the "filter" search input will be focused right off the bat.

Release Notes:

- N/A
2025-04-21 11:28:49 -03:00
Danilo Leal
333de5d673 agent: Update Switch color in the settings view (#29154)
Just using the color method for the Switch component added in
https://github.com/zed-industries/zed/pull/29074.

Release Notes:

- N/A
2025-04-21 11:28:44 -03:00
Antonio Scandurra
97ab0980d1 Start tracking tool failure rates in eval (#29122)
This pull request will print all the used tools and their failure rates.
The objective goal should be to minimize that failure rate.

@tmickleydoyle: this also changes the telemetry event to report
`tool_metrics` as opposed to `tool_use_counts`. Ideally I'd love to be
able to plot failure rates by tool and hopefully see that percentage go
down. Can we do that with the data we're tracking with this pull
request?

Release Notes:

- N/A
2025-04-21 16:16:43 +02:00
Agus Zubiaga
3a27e8c311 edit tool: Handle over-indentation in replace_with_flexible_indent (#29153)
Release Notes:

- agent: Correct over-indentation in search/replace strings from model
2025-04-21 11:02:08 -03:00
Danilo Leal
bfb2ed3824 ui: Add .color method to the Switch (#29074)
This allows to pass, for example, `.color(SwitchColor::Accent)` to the
Switch component and have it render differently.

<img
src="https://github.com/user-attachments/assets/c60bac8a-c5ae-4693-912a-c754e5081f45"
width="550"/>

Release Notes:

- N/A
2025-04-21 10:56:42 -03:00
Smit Barmase
9db0c4f19a editor: Hide signature popover on editor scroll (#29149)
Closes #27845

This is also how VSCode tackles this issue. I think this should be
applicable to even more popovers across the editor and context menu, but
it can be addressed later.

Release Notes:

- Fixed the signature popover not hiding on editor scroll.
2025-04-21 17:57:17 +05:30
angelrecovery
a4f5c4fef2 windows: Fix window_min_size (#29118)
Closes #29117

This makes `window_min_size` work by using the `WM_GETMINMAXINFO` window
message.


Release Notes:

- N/A

---------

Co-authored-by: 张小白 <364772080@qq.com>
2025-04-21 16:38:36 +08:00
Kirill Bulatov
4dcfe0cff9 Omit duplicate LSP data when generating completion item labels (#29137)
Before: 
<img width="875" alt="before"
src="https://github.com/user-attachments/assets/eec34f4e-3665-47e1-a224-16f1b98d5b29"
/>

After:
<img width="769" alt="after"
src="https://github.com/user-attachments/assets/4ce6a24b-6fd0-4043-b67c-c92105c1501a"
/>

Release Notes:

- N/A
2025-04-21 02:24:51 +00:00
Agus Zubiaga
4473b45c3d inline assistant: Fix model picker (#29136)
Release Notes:

- inline assistant: Fixed a bug where the default model would be used
even when a specific inline assistant model was configured
2025-04-21 01:12:57 +00:00
Agus Zubiaga
ceeae790b7 eval: Improve lang server idle detection (#29135)
Brings back #29013 after it was accidentally reverted by
e9bb15b906.

Release Notes:

- N/A
2025-04-21 00:17:28 +00:00
Nathan Sobo
107d8ca483 Rename regex search tool to grep and accept an include glob pattern (#29100)
This PR renames the `regex_search` tool to `grep` because I think it
conveys more meaning to the model, the idea of searching the filesystem
with a regular expression. It's also one word and the model seems to be
using it effectively after some additional prompt tuning.

It also takes an include pattern to filter on the specific files we try
to search. I'd like to encourage the model to scope its searches more
aggressively, as in my testing, I'm only seeing it filter on file
extension.

Release Notes:

- N/A
2025-04-20 00:53:30 +00:00
Michael Sloan
4278d894d2 Update find_and_replace_diff_card eval example to include .rules file (#29108)
Release Notes:

- N/A
2025-04-20 00:07:44 +00:00
Michael Sloan
a91948aeb4 agent: Reorder some linux keybindings to match mac keybindings (#29107)
Release Notes:

- Made keybindings for agent panel closer to the precedence order used
on Mac. This fixes use of `enter` to add context from the menu triggered
by `@` referencing.
2025-04-20 00:01:43 +00:00
Michael Sloan
2178b36cbc Add eval worktrees and repos to file_scan_exclusions in zed project settings (#29106)
Release Notes:

- N/A
2025-04-19 23:43:54 +00:00
Michael Sloan
0fb0059b5f Switch from open-codestral-mamba to codestral-latest for default mistral model (#29104)
Couldn't find mistral cloud pricing for open-codestral-mamba on the
mistral site, but codestral-latest is newer and appears to be cheaper
based on
https://sdk.vercel.ai/playground/mistral:codestral-mamba-latest,mistral:codestral-2501

Release Notes:

- N/A
2025-04-19 23:29:36 +00:00
Michael Sloan
fbf7caf93e Default to fast model for thread summaries and titles + don't include system prompt / context / thinking segments (#29102)
* Adds a fast / cheaper model to providers and defaults thread
summarization to this model. Initial motivation for this was that
https://github.com/zed-industries/zed/pull/29099 would cause these
requests to fail when used with a thinking model. It doesn't seem
correct to use a thinking model for summarization.

* Skips system prompt, context, and thinking segments.

* If tool use is happening, allows 2 tool uses + one more agent response
before summarizing.

Downside of this is that there was potential for some prefix cache reuse
before, especially for title summarization (thread summarization omitted
tool results and so would not share a prefix for those). This seems fine
as these requests should typically be fairly small. Even for full thread
summarization, skipping all tool use / context should greatly reduce the
token use.

Release Notes:

- N/A
2025-04-19 23:26:29 +00:00
Nathan Sobo
d48152d958 Don't send dummy user text with tool results (#29099)
Previously, we were including the dummy text "Here are the tool
results." whenever reporting tool call results. I'm worried this is
adding noise and confusing the model, because the user didn't actually
say anything. This inserts an empty message to be populated later. My
preference would be something less stateful, where tool results (or
batches of them requested simultaneously) would be sent to the model as
soon as they were ready, without bothering to do this message
association dance. But for now, this seems to work.

Release Notes:

- N/A
2025-04-19 20:34:49 +00:00
Bennet Bo Fenner
bafc086d27 agent: Preserve thinking blocks between requests (#29055)
Looks like the required backend component of this was deployed.

https://github.com/zed-industries/monorepo/actions/runs/14541199197

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
Co-authored-by: Nathan Sobo <nathan@zed.dev>
2025-04-19 20:12:03 +00:00
Smit Barmase
f737c4d01e editor: Improve selection highlights speed (#29097)
Before, we used to debounce selection highlight because it needed to
search the whole file to show gutter line highlights, etc. This
experience felt extremely laggy.

This PR introduces a new approach where:
1. We query only visible rows without debounce. The search function
itself is async and runs in a background thread, so it's not blocking
anything. With no debounce and such a small search space, highlights
feel realtime.
2. In parallel, we also query the whole file (still debounced, like
before). Once this query resolves, it updates highlights across the
file, making scrollbar markers visible.

This hybrid way gives the feeling of realtime, while keeping the same
functionality.


https://github.com/user-attachments/assets/432b65f1-89d2-4658-ad5e-048921b06a23

P.S. I have removed the user setting for custom debounce delay, because
(one) now it doesn't really make sense to configure that, and (two) the
whole logic is based on the assumption that the fast query will resolve
before the debounced query. A static debounce time makes sure of that.
Configuring it might lead to cases where the fast query resolves after
the debounced query, and we end up only seeing visible viewport
highlights.

Release Notes:

- Improved selection highlight speed.
2025-04-20 01:20:36 +05:30
Marko Kungla
8f308d835a Add zed to Flatpak config and data directories (#28952)
Closes #28944 

Release Notes:

- linux: Fixed incorrect config directory being used when Zed is
installed via Flatpak

Signed-off-by: Marko Kungla <marko.kungla@gmail.com>
2025-04-19 10:41:03 -07:00
Danilo Leal
703a68eedf docs: Add more examples of existing MCP extensions (#29090)
Also linking to the zed.dev site, which now includes a filter for MCP
(i.e., "Context Servers") servers in the Extensions page.

Release Notes:

- N/A
2025-04-19 12:09:57 -03:00
Danilo Leal
cc2fcb2f42 agent: Add item to add custom MCP server in the panel's menu (#29091)
This is based on user feedback that the Agent Panel menu was only
linking to extensions as a way to add MCP servers while we also support
adding "custom" servers, too, which don't go through the extensions
flow.

Release Notes:

- N/A
2025-04-19 12:09:50 -03:00
张小白
f0ef3110d3 gpui: Introduce PlatformKeyboardLayout trait for human-friendly keyboard layout names (#29049)
This PR adds a new `PlatformKeyboardLayout` trait with two methods:
`id(&self) -> &str` and `name(&self) -> &str`. The `id()` method returns
a unique identifier for the keyboard layout, while `name()` provides a
human-readable name. This distinction is especially important on
Windows, where the `id` and `name` can be quite different. For example,
the French layout has an `id` of `0000040C`, which is not
human-readable, whereas the `name` would simply be `French`. Currently,
the existing `keyboard_layout()` method returns what's essentially the
same as `id()` in this new design.

This PR implements the `name()` method for both Windows and macOS. On
Linux, for now, `name()` still returns the same value as `id()`.

Release Notes:

- N/A
2025-04-19 22:23:03 +08:00
Oleksiy Syvokon
0454e7a22e terminal: Add Alt+. keybinding passthrough for last-argument recall (#29088)
Alt+. is a useful terminal/readline feature that cycles through the last
arguments of previous commands in history. Unlike many other shortcuts,
it doesn't conflict with anything important globally, so it can be
safely enabled by default.

Release Notes:

- N/A
2025-04-19 14:03:13 +03:00
Michael Sloan
d88b06a5dc Simplify language model registry + only emit change events on change (#29086)
* Now only does default fallback logic in the registry

* Only emits change events when there is actually a change

Release Notes:

- N/A
2025-04-19 08:26:42 +00:00
Michael Sloan
98ceffe026 Pretty tool inputs in eval output markdown + numbered assistant messages (#29082)
Release Notes:

- N/A
2025-04-19 06:59:22 +00:00
Nathan Sobo
bab28560ef Systematically optimize agentic editing performance (#28961)
Now that we've established a proper eval in tree, this PR is reboots of
our agent loop back to a set of minimal tools and simpler prompts. We
should aim to get this branch feeling subjectively competitive with
what's on main and then merge it, and build from there.

Let's invest in our eval and use it to drive better performance of the
agent loop. How you can help: Pick an example, and then make the outcome
faster or better. It's fine to even use your own subjective judgment, as
our evaluation criteria likely need tuning as well at this point. Focus
on making the agent work better in your own subjective experience first.
Let's focus on simple/practical improvements to make this thing work
better, then determine how we can craft our judgment criteria to lock
those improvements in.

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Agus <agus@zed.dev>
Co-authored-by: Richard <richard@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Michael Sloan <mgsloan@gmail.com>
2025-04-19 02:47:59 +00:00
Danilo Leal
8102a16747 agent: Make copy button show while hovering the codeblock container (#29075) 2025-04-18 22:14:43 -03:00
Marshall Bowers
9875521d4e language_models: Fix passing of thread_id and prompt_id (#29071)
This PR is a follow-up to
https://github.com/zed-industries/zed/pull/29069 that fixes an issue
where the thread ID and prompt ID were not being sent up correctly.

Release Notes:

- N/A
2025-04-18 21:12:23 +00:00
Michael Sloan
8c55063417 Fix zed sometimes stopping by using setsid on interactive shells (#29070)
For some reason `SIGTTIN` sometimes gets sent to the process group,
causing it to stop when run from a terminal. This solves that issue by
putting the shell in a new session + progress group.

This allows removal of a workaround of using `exit 0;` to restore
handling of ctrl-c after exit. In testing this appears to no longer be
necessary.

Closes #27716

Release Notes:

- Fixed Zed sometimes becoming a stopped background process when run
from a terminal.
2025-04-18 15:04:26 -06:00
Marshall Bowers
7abe2c9c31 agent: Attach thread ID and prompt ID to telemetry events (#29069)
This PR attaches the thread ID and the new prompt ID to telemetry events
for completions in the Agent panel.

Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-04-18 20:41:02 +00:00
Michael Sloan
73a767fc45 Add hidden prompt_to_focus field to OpenPromptLibrary action (#29062)
Release Notes:

- N/A
2025-04-18 20:39:40 +00:00
Michael Sloan
327fee4d22 Init prompt store in agent eval (#29068)
Needed after #28915

Release Notes:

- N/A
2025-04-18 20:06:34 +00:00
Joseph T. Lyons
b1d5918fdc Fix reset_db script (#29067)
Release Notes:

- N/A
2025-04-18 19:28:14 +00:00
renovate[bot]
6f685b9f8e Update Rust crate sea-orm to v1.1.10 (#28918)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [sea-orm](https://www.sea-ql.org/SeaORM)
([source](https://redirect.github.com/SeaQL/sea-orm)) | dev-dependencies
| patch | `1.1.8` -> `1.1.10` |
| [sea-orm](https://www.sea-ql.org/SeaORM)
([source](https://redirect.github.com/SeaQL/sea-orm)) | dependencies |
patch | `1.1.8` -> `1.1.10` |

---

### Release Notes

<details>
<summary>SeaQL/sea-orm (sea-orm)</summary>

###
[`v1.1.10`](https://redirect.github.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#1110---2025-04-14)

[Compare
Source](https://redirect.github.com/SeaQL/sea-orm/compare/1.1.9...1.1.10)

##### Upgrades

- Upgrade sqlx to 0.8.4
[https://github.com/SeaQL/sea-orm/pull/2562](https://redirect.github.com/SeaQL/sea-orm/pull/2562)

###
[`v1.1.9`](https://redirect.github.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#119---2025-04-14)

[Compare
Source](https://redirect.github.com/SeaQL/sea-orm/compare/1.1.8...1.1.9)

##### Enhancements

- \[sea-orm-macros] Use fully-qualified syntax for ActiveEnum associated
type[https://github.com/SeaQL/sea-orm/pull/2552](https://redirect.github.com/SeaQL/sea-orm/pull/2552)2
- Accept `LikeExpr` in `like` and `not_like`
[https://github.com/SeaQL/sea-orm/pull/2549](https://redirect.github.com/SeaQL/sea-orm/pull/2549)

##### Bug fixes

- Check if url is well-formed before parsing
[https://github.com/SeaQL/sea-orm/pull/2558](https://redirect.github.com/SeaQL/sea-orm/pull/2558)
- `QuerySelect::column_as` method cast ActiveEnum column
[https://github.com/SeaQL/sea-orm/pull/2551](https://redirect.github.com/SeaQL/sea-orm/pull/2551)

##### House keeping

- Remove redundant `Expr::expr` from internal code
[https://github.com/SeaQL/sea-orm/pull/2554](https://redirect.github.com/SeaQL/sea-orm/pull/2554)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjI0OC40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-04-18 19:15:02 +00:00
Smit Barmase
65a7076ba8 buffer_search: Fix DeployReplace not working when buffer search is already deployed (#29066)
Closes #29000

When buffer search is already deployed:
1. If find dialog is enabled, change it to find-and-replace dialog and
focuses to it
2. If find-and-replace is enabled, focuses to it 

Release Notes:

- Fixed an issue where invoking `DeployReplace` while the Find dialog
was open did not switch to the Find & Replace dialog.
- Fixed an issue where invoking `DeployReplace` while the Find & Replace
dialog was already open did not focus it.
2025-04-19 00:26:02 +05:30
Marshall Bowers
3fe15ee915 renovate: Require dependency dashboard approval for updates (#29065)
This PR changes Renovate's behavior to require dependency dashboard
approval for updates.

> Maybe you find Renovate too noisy, and want to opt-out of getting
automatic updates whenever they're ready.
>
> In this case, you can tell Renovate to wait for your approval before
making any pull requests. This means that you have full control over
when you get updates.
>
> But vulnerability remediation PRs will still get created immediately
without requiring approval.
>
> To require manual approval for *all* updates, add the
`:dependencyDashboardApproval` presets to the `extends` array in your
Renovate configuration file:
>
> —
https://docs.renovatebot.com/key-concepts/dashboard/#require-approval-for-all-updates

Release Notes:

- N/A
2025-04-18 18:44:30 +00:00
renovate[bot]
bae3ef01c6 Update Rust crate clap to v4.5.36 (#28905)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [clap](https://redirect.github.com/clap-rs/clap) |
workspace.dependencies | patch | `4.5.35` -> `4.5.36` |

---

### Release Notes

<details>
<summary>clap-rs/clap (clap)</summary>

###
[`v4.5.36`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4536---2025-04-11)

[Compare
Source](https://redirect.github.com/clap-rs/clap/compare/v4.5.35...v4.5.36)

##### Fixes

- *(help)* Revert 4.5.35's "Don't leave space for shorts if there are
none" for now

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjI0OC40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-04-18 18:11:29 +00:00
renovate[bot]
810b39ce34 Update Rust crate aws-sdk-bedrockruntime to v1.82.0 (#28451)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[aws-sdk-bedrockruntime](https://redirect.github.com/awslabs/aws-sdk-rust)
| workspace.dependencies | minor | `1.80.0` -> `1.82.0` |

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-04-18 17:42:51 +00:00
Michael Sloan
a93aa598d6 Fix documentation of impl_action_with_deprecated_aliases (#29063)
Release Notes:

- N/A
2025-04-18 17:35:48 +00:00
renovate[bot]
28aba94369 Update actions/setup-node digest to 49933ea (#28897)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/setup-node](https://redirect.github.com/actions/setup-node) |
action | digest | `cdca736` -> `49933ea` |

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-18 11:31:02 -06:00
renovate[bot]
e147ef006c Update Rust crate libc to v0.2.172 (#28911)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [libc](https://redirect.github.com/rust-lang/libc) |
workspace.dependencies | patch | `0.2.171` -> `0.2.172` |

---

### Release Notes

<details>
<summary>rust-lang/libc (libc)</summary>

###
[`v0.2.172`](https://redirect.github.com/rust-lang/libc/releases/tag/0.2.172)

[Compare
Source](https://redirect.github.com/rust-lang/libc/compare/0.2.171...0.2.172)

##### Added

- Android: Add `getauxval` for 32-bit targets
([#&#8203;4338](https://redirect.github.com/rust-lang/libc/pull/4338))
- Android: Add `if_tun.h` ioctls
([#&#8203;4379](https://redirect.github.com/rust-lang/libc/pull/4379))
- Android: Define `SO_BINDTOIFINDEX`
([#&#8203;4391](https://redirect.github.com/rust-lang/libc/pull/4391))
- Cygwin: Add `posix_spawn_file_actions_add[f]chdir[_np]`
([#&#8203;4387](https://redirect.github.com/rust-lang/libc/pull/4387))
- Cygwin: Add new socket options
([#&#8203;4350](https://redirect.github.com/rust-lang/libc/pull/4350))
- Cygwin: Add statfs & fcntl
([#&#8203;4321](https://redirect.github.com/rust-lang/libc/pull/4321))
- FreeBSD: Add `filedesc` and `fdescenttbl`
([#&#8203;4327](https://redirect.github.com/rust-lang/libc/pull/4327))
- Glibc: Add unstable support for \_FILE_OFFSET_BITS=64
([#&#8203;4345](https://redirect.github.com/rust-lang/libc/pull/4345))
- Hermit: Add `AF_UNSPEC`
([#&#8203;4344](https://redirect.github.com/rust-lang/libc/pull/4344))
- Hermit: Add `AF_VSOCK`
([#&#8203;4344](https://redirect.github.com/rust-lang/libc/pull/4344))
- Illumos, NetBSD: Add `timerfd` APIs
([#&#8203;4333](https://redirect.github.com/rust-lang/libc/pull/4333))
- Linux: Add `_IO`, `_IOW`, `_IOR`, `_IOWR` to the exported API
([#&#8203;4325](https://redirect.github.com/rust-lang/libc/pull/4325))
- Linux: Add `tcp_info` to uClibc bindings
([#&#8203;4347](https://redirect.github.com/rust-lang/libc/pull/4347))
- Linux: Add further BPF program flags
([#&#8203;4356](https://redirect.github.com/rust-lang/libc/pull/4356))
- Linux: Add missing INPUT_PROP_XXX flags from `input-event-codes.h`
([#&#8203;4326](https://redirect.github.com/rust-lang/libc/pull/4326))
- Linux: Add missing TLS bindings
([#&#8203;4296](https://redirect.github.com/rust-lang/libc/pull/4296))
- Linux: Add more constants from `seccomp.h`
([#&#8203;4330](https://redirect.github.com/rust-lang/libc/pull/4330))
- Linux: Add more glibc `ptrace_sud_config` and related
`PTRACE_*ET_SYSCALL_USER_DISPATCH_CONFIG`.
([#&#8203;4386](https://redirect.github.com/rust-lang/libc/pull/4386))
- Linux: Add new netlink flags
([#&#8203;4288](https://redirect.github.com/rust-lang/libc/pull/4288))
- Linux: Define ioctl codes on more architectures
([#&#8203;4382](https://redirect.github.com/rust-lang/libc/pull/4382))
- Linux: Add missing `pthread_attr_setstack`
([#&#8203;4349](https://redirect.github.com/rust-lang/libc/pull/4349))
- Musl: Add missing `utmpx` API
([#&#8203;4332](https://redirect.github.com/rust-lang/libc/pull/4332))
- Musl: Enable `getrandom` on all platforms
([#&#8203;4346](https://redirect.github.com/rust-lang/libc/pull/4346))
- NuttX: Add more signal constants
([#&#8203;4353](https://redirect.github.com/rust-lang/libc/pull/4353))
- QNX: Add QNX 7.1-iosock and 8.0 to list of additional cfgs
([#&#8203;4169](https://redirect.github.com/rust-lang/libc/pull/4169))
- QNX: Add support for alternative Neutrino network stack `io-sock`
([#&#8203;4169](https://redirect.github.com/rust-lang/libc/pull/4169))
- Redox: Add more `sys/socket.h` and `sys/uio.h` definitions
([#&#8203;4388](https://redirect.github.com/rust-lang/libc/pull/4388))
- Solaris: Temporarily define `O_DIRECT` and `SIGINFO`
([#&#8203;4348](https://redirect.github.com/rust-lang/libc/pull/4348))
- Solarish: Add `secure_getenv`
([#&#8203;4342](https://redirect.github.com/rust-lang/libc/pull/4342))
- VxWorks: Add missing `d_type` member to `dirent`
([#&#8203;4352](https://redirect.github.com/rust-lang/libc/pull/4352))
- VxWorks: Add missing signal-related constsants
([#&#8203;4352](https://redirect.github.com/rust-lang/libc/pull/4352))
- VxWorks: Add more error codes
([#&#8203;4337](https://redirect.github.com/rust-lang/libc/pull/4337))

##### Deprecated

- FreeBSD: Deprecate `TCP_PCAP_OUT` and `TCP_PCAP_IN`
([#&#8203;4381](https://redirect.github.com/rust-lang/libc/pull/4381))

##### Fixed

- Cygwin: Fix member types of `statfs`
([#&#8203;4324](https://redirect.github.com/rust-lang/libc/pull/4324))
- Cygwin: Fix tests
([#&#8203;4357](https://redirect.github.com/rust-lang/libc/pull/4357))
- Hermit: Make `AF_INET = 3`
([#&#8203;4344](https://redirect.github.com/rust-lang/libc/pull/4344))
- Musl: Fix the syscall table on RISC-V-32
([#&#8203;4335](https://redirect.github.com/rust-lang/libc/pull/4335))
- Musl: Fix the value of `SA_ONSTACK` on RISC-V-32
([#&#8203;4335](https://redirect.github.com/rust-lang/libc/pull/4335))
- VxWorks: Fix a typo in the `waitpid` parameter name
([#&#8203;4334](https://redirect.github.com/rust-lang/libc/pull/4334))

##### Removed

- Musl: Remove `O_FSYNC` on RISC-V-32 (use `O_SYNC` instead)
([#&#8203;4335](https://redirect.github.com/rust-lang/libc/pull/4335))
- Musl: Remove `RTLD_DEEPBIND` on RISC-V-32
([#&#8203;4335](https://redirect.github.com/rust-lang/libc/pull/4335))

##### Other

- CI: Add matrix env variables to the environment
([#&#8203;4345](https://redirect.github.com/rust-lang/libc/pull/4345))
- CI: Always deny warnings
([#&#8203;4363](https://redirect.github.com/rust-lang/libc/pull/4363))
- CI: Always upload successfully created artifacts
([#&#8203;4345](https://redirect.github.com/rust-lang/libc/pull/4345))
- CI: Install musl from source for loongarch64
([#&#8203;4320](https://redirect.github.com/rust-lang/libc/pull/4320))
- CI: Revert "Also skip `MFD_EXEC` and `MFD_NOEXEC_SEAL` on sparc64"
([#]())
- CI: Use `$PWD` instead of `$(pwd)` in run-docker
([#&#8203;4345](https://redirect.github.com/rust-lang/libc/pull/4345))
- Solarish: Restrict `openpty` and `forkpty` polyfills to Illumos,
replace Solaris implementation with bindings
([#&#8203;4329](https://redirect.github.com/rust-lang/libc/pull/4329))
- Testing: Ensure the makedev test does not emit unused errors
([#&#8203;4363](https://redirect.github.com/rust-lang/libc/pull/4363))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-18 11:30:40 -06:00
renovate[bot]
e18d787dbc Update Rust crate proc-macro2 to v1.0.95 (#28917)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [proc-macro2](https://redirect.github.com/dtolnay/proc-macro2) |
workspace.dependencies | patch | `1.0.94` -> `1.0.95` |

---

### Release Notes

<details>
<summary>dtolnay/proc-macro2 (proc-macro2)</summary>

###
[`v1.0.95`](https://redirect.github.com/dtolnay/proc-macro2/releases/tag/1.0.95)

[Compare
Source](https://redirect.github.com/dtolnay/proc-macro2/compare/1.0.94...1.0.95)

- Update semver-exempt API under
`RUSTFLAGS=--cfg=procmacro2_semver_exempt` to that of nightly-2025-04-16
([#&#8203;497](https://redirect.github.com/dtolnay/proc-macro2/issues/497))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-18 11:30:22 -06:00
renovate[bot]
d02c8bcc02 Update Rust crate mimalloc to v0.1.46 (#28912)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [mimalloc](https://redirect.github.com/purpleprotocol/mimalloc_rust) |
dependencies | patch | `0.1.45` -> `0.1.46` |

---

### Release Notes

<details>
<summary>purpleprotocol/mimalloc_rust (mimalloc)</summary>

###
[`v0.1.46`](https://redirect.github.com/purpleprotocol/mimalloc_rust/releases/tag/v0.1.46):
Version 0.1.46

[Compare
Source](https://redirect.github.com/purpleprotocol/mimalloc_rust/compare/v0.1.45...v0.1.46)

##### Changes

-   Fixed musl builds.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-18 11:30:07 -06:00
Danilo Leal
e27f6a984f agent: Simplify design of the settings view (#29041)
Containing everything in boxes wasn't super necessary here. Want to
still improve the switch color contrast here, but will probably do that
in a separate PR.

<img
src="https://github.com/user-attachments/assets/f826a7a8-beaf-45d0-9dc2-36dc210c418e"
width="700"/>

Release Notes:

- N/A
2025-04-18 14:24:53 -03:00
Joseph T. Lyons
cce661b64b docs: Add documentation about signing in to Zed (#29054)
Release Notes:

- N/A
2025-04-18 17:09:23 +00:00
Smit Barmase
3932a6c51e pane: Fix double or invisible borders in tab bar (#29061)
Invisible borders:
<img width="349" alt="Screenshot 2025-04-18 at 3 59 03 PM"
src="https://github.com/user-attachments/assets/a3a43885-ce87-4fcf-864a-d730fea1551e"
/>
<img width="547" alt="Screenshot 2025-04-18 at 8 23 15 PM"
src="https://github.com/user-attachments/assets/1f8669a8-f893-4c58-ba30-025be1bc733f"
/>

Double borders:
<img width="295" alt="Screenshot 2025-04-18 at 3 56 48 PM"
src="https://github.com/user-attachments/assets/7b4ae42d-c7fd-478c-97ce-10abefe4a482"
/>

Release Notes:

- N/A
2025-04-18 22:32:22 +05:30
tidely
1e0ae35f69 gpui: Make MacPlatform::os_version infallible (#29008)
Core change:
```rust
fn os_version() -> Result<SemanticVersion>
```

```rust
fn os_version() -> SemanticVersion
```


Release Notes:

- N/A
2025-04-18 11:00:43 -06:00
Oleksiy Syvokon
4405ed04d0 linux: Fix cursor-related panic on Wayland (#29060)
This fixes the panic that happened in debug builds in Wayland when
focusing/defocusing window in the edit mode:

```
"Thread "main" panicked with "CursorStyle::None should be handled separately in the client" at crates/gpui/src/platform/linux/wayland.rs:40:17"
```

Full log:
[stacktrace.txt](https://github.com/user-attachments/files/19814411/stacktrace.txt)

@smitbarmase, you seem to have worked on this code. Tagging you for
visibility :)

Release Notes:

- N/A
2025-04-18 17:00:19 +00:00
Smit Barmase
c585dbd8ff git_panel: Fix amend check (#29059)
`is_some` -> `is_none` 

Release Notes:

- N/A
2025-04-18 22:24:49 +05:30
Michael Sloan
c7fc95e732 Remove .direnv from .gitignore as the correct file is .envrc (#29058)
Release Notes:

- N/A
2025-04-18 16:53:41 +00:00
chbk
f97546b6ef Improve Regex highlighting (#28183)
| Zed 0.180.2 | With this PR |
| --- | --- |
|
![Image](https://github.com/user-attachments/assets/e840bd81-25ff-4c7a-af03-bac6db11f910)
|
![Image](https://github.com/user-attachments/assets/3fd58164-8992-44e1-be01-8c6d70f9587d)
|

```js
match = "424242"
regex = /(42)+?\d{2}\1/g
```

- `/`: `operator` -> `string.regex` (like `"` for regex strings)
- `+?`: `operator.regex`
- `\d`: `string.escape` -> `string.escape.regex`
- `\1`: `keyword.operator.regex` (backreference)
- `/g`: `keyword.regex` -> `keyword.operator.regex`
- `{2}`: `number` -> `number.quantifier.regex`

Release Notes:

  - Improved Regex highlighting
2025-04-18 12:44:13 -04:00
Kirill Bulatov
7badd6053d debugger: Fix gutter tasks display for users without the debugger feature flag (#29056) 2025-04-18 10:22:26 -06:00
Michael Sloan
502a0f6535 agent: Use default prompts from prompt library in system prompt (#28915)
Related to #28490.

- Default prompts from the prompt library are now included as "user
rules" in the system prompt.
- Presence of these user rules is shown at the beginning of the thread
in the UI.
_ Now uses an `Entity<PromptStore>` instead of an `Arc<PromptStore>`.
Motivation for this is emitting a `PromptsUpdatedEvent`.
- Now disallows concurrent reloading of the system prompt. Before this
change it was possible for reloads to race.

Release Notes:

- agent: Added support for including default prompts from the Prompt
Library as "user rules" in the system prompt.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-04-18 09:32:35 -06:00
Marshall Bowers
eea6cfb383 collab: Upgrade from Zed Pro trial to Zed Pro by ending trial period early (#29052)
This PR adjusts the upgrade from a Zed Pro trial to Zed Pro to do so by
ending the trial period early.

This will transition the subscription to `active` and bill the user
without needing to send them through a Stripe Checkout flow.

Release Notes:

- N/A
2025-04-18 15:29:22 +00:00
Marshall Bowers
0dc0701967 Show edit predictions usage in status bar menu (#29046)
This PR adds an indicator for edit predictions usage in the edit
predictions menu:

| Free | Zed Pro / Trial |
|
---------------------------------------------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------------------------------
|
| <img width="235" alt="Screenshot 2025-04-18 at 9 53 47 AM"
src="https://github.com/user-attachments/assets/6da001d2-ef9c-49df-86be-03d4c615d45c"
/> | <img width="237" alt="Screenshot 2025-04-18 at 9 54 33 AM"
src="https://github.com/user-attachments/assets/31f5df04-a8e1-43ec-8af7-ebe501516abe"
/> |

Only visible to users on the new billing.

Release Notes:

- N/A
2025-04-18 14:15:19 +00:00
moaqz
62b8ef980b docs: Fix broken links (#29042)
This PR fixes some broken links in the docs.

Release Notes:

- N/A
2025-04-18 09:38:40 -04:00
Marshall Bowers
269f6403dd snippet_provider: Use proper casing of VsCode in identifiers (#29038)
This PR renames some identifiers in the `snippet_provider` to use the
correct casing of `VsCode`.

Release Notes:

- N/A
2025-04-18 12:11:54 +00:00
Bennet Bo Fenner
3538acec7c agent: Do not insert selection as context when selection is empty (#29031)
Release Notes:

- N/A
2025-04-18 09:31:26 +00:00
Bennet Bo Fenner
87512d0814 agent: Remove selections as context once message is sent (#29030)
Release Notes:

- N/A
2025-04-18 08:50:46 +00:00
5brian
6254efe39d vim: Fix character count in visual line mode (#28669)
Closes https://github.com/zed-industries/zed/issues/10727

Release Notes:

- vim: Fixed character count in visual line mode

Co-authored-by: Conrad Irwin <conrad@zed.dev>
2025-04-17 22:14:44 -06:00
redforks
72218f4a61 Make Copy and Trim ignore empty lines, and fix vim line selections (#29019)
Close #28519 

Release Notes:

Update `editor: copy and trim` command:

1. Ignore empty lines in the middle:

    ```
      Line 1

      Line 2
    ```

    Will copy text to clipboard:

    ```
    Line 1

    Line 2
    ```

    Before this commit trim not performed

1. Fix select use vim line selections, trim not works
2025-04-17 21:35:05 -06:00
AidanV
5f7189e5af vim: Change line up and change line down respect indentation (#28934)
When using 'c' with line-wise motions like j/k, operate like cc to fix
indentation issues.

Closes #28933 

Release Notes:

- `c j` and `c k` now respect indentation
2025-04-17 20:51:24 -06:00
Conrad Irwin
f6d13645ce Fix error logging (#29010)
Co-Authored-By: Ben <ben@zed.dev>

Release Notes:

- N/A

Co-authored-by: Ben <ben@zed.dev>
2025-04-17 20:58:54 -04:00
João Marcos
6ffd3f034f Don't display MacOS key symbols in Linux (#29016)
Release Notes:

- Fix MacOS key symbols being displayed in other platforms.
2025-04-17 21:48:00 -03:00
Smit Barmase
6e0732a9d7 git_ui: Fix amend not working for detached HEAD (#29017)
Closes #28736

Release Notes:

- Fixed git amend not working for detached HEAD.
2025-04-18 06:15:54 +05:30
Michael Sloan
f8d097acd6 Initial .rules file for agent with symlinks for other rules file paths (#29014)
Release Notes:

- N/A
2025-04-17 23:41:23 +00:00
Michael Sloan
7cf4926130 Misc GPUI Entity<T> cleanups (#28996)
Found these while working on a `.rules` file which explains how GPUI
works.

Release Notes:

- N/A
2025-04-17 23:29:19 +00:00
Marshall Bowers
676cc109a3 agent: Report usage from thread summarization requests (#29012)
This PR makes it so the thread summarization also reports the model
request usage, to prevent the case where the count would appear to jump
by 2 the next time a message was sent after summarization.

Release Notes:

- N/A
2025-04-17 23:05:12 +00:00
Smit Barmase
ba7f886c62 project: Show detached head commit SHA in branch pickers (#29007)
When Git is in a detached HEAD state, the branch is `None`, and we can't
get any meaningful information from it. This PR adds a `head_commit`
field to the snapshot, which is always populated with the HEAD details,
even when the branch is `None`.

This also pave path to fix:
https://github.com/zed-industries/zed/issues/28736

git panel branch picker (before, after):
<img width="197" alt="image"
src="https://github.com/user-attachments/assets/0b6abbba-2988-4890-a708-bcd8aad84f26"
/> <img width="198" alt="image"
src="https://github.com/user-attachments/assets/4b08b1a8-5e79-4aa3-a44e-932249602c18"
/>

title bar branch picker (before, after):
<img width="183" alt="image"
src="https://github.com/user-attachments/assets/d94357f8-a4da-4d60-8ddd-fdd978b99fdf"
/> <img width="228" alt="image"
src="https://github.com/user-attachments/assets/d20824a1-9279-44d6-afd1-bf9319fc50e4"
/>

Release Notes:

- Added head commit SHA information to the Git branch picker in the
title bar and Git panel.
2025-04-18 04:23:56 +05:30
Marshall Bowers
c2cd4fd7a1 agent: Show request usage in the panel (#29006)
This PR adds a banner showing request usage in the Agent panel:

<img width="640" alt="Screenshot 2025-04-17 at 5 51 46 PM"
src="https://github.com/user-attachments/assets/e0eb036c-57c1-441c-bbab-7dab1c6e56d9"
/>

Only visible to users on the new billing.

Note to Joseph: Doesn't need to be cherry-picked to Preview.

Release Notes:

- N/A

---------

Co-authored-by: Nate <nate@zed.dev>
2025-04-17 22:16:57 +00:00
Cole Miller
4095011af5 debugger_ui: Show a toast when setting breakpoints fails (#28815)
Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Anthony <anthony@zed.dev>
2025-04-17 22:10:57 +00:00
5brian
80a2f71d8e vim: Add ctrl-^ (#28648)
Alias for Ctrl-6: https://neovim.io/doc/user/editing.html#CTRL-%5E

Also removed Ctrl-6 from the ProjectPanel context, iiuc, it shouldn't
have any effect there

Release Notes:

- vim: Added `ctrl-^` as an alias for `ctrl-6` in the default vim keymap
2025-04-17 17:54:18 -04:00
Marshall Bowers
d93141bded agent: Extract usage information from response headers (#29002)
This PR updates the Agent to extract the usage information from the
response headers, if they are present.

For now we just log the information, but we'll be using this soon to
populate some UI.

Release Notes:

- N/A
2025-04-17 20:11:07 +00:00
AidanV
b402007de6 nix: Add libX11 dependency for X11 support (#28938)
Closes #28937 

Release Notes:

- N/A
2025-04-17 12:58:45 -07:00
Marshall Bowers
be63d51eb7 zeta: Extract usage information from response headers (#28999)
This PR updates the Zeta provider to extract the usage information from
the response headers, if they are present.

For now we just log the information, but we'll need to figure out where
this needs to get threaded through to in order to display it in the UI.

Release Notes:

- N/A
2025-04-17 19:07:40 +00:00
Anthony Eid
8660101b83 debugger: Configure default pane layout conditionally based on capabilities (#28991)
This fixes a debug panic that happened when closing a debug session item
through the debug panel context menu. The default layout now only
includes module list and loaded sources list if they're supported.


Release Notes:

- N/A
2025-04-17 14:46:50 -04:00
João Marcos
1aa1b2bede Fix multiline completions when surroundings don't match completion text (#28995)
Follow up to the scenarios I overlooked in
https://github.com/zed-industries/zed/pull/28586.

Release Notes:

- N/A
2025-04-17 15:37:38 -03:00
Marshall Bowers
58d8b91131 collab: Treat trialing subscriptions as active (#28992)
This PR makes it so billing subscriptions in the `trialing` state are
considered `active`.

Release Notes:

- N/A
2025-04-17 18:19:34 +00:00
Smit Barmase
ba588161d9 editor: Revert flattening of code actions in mouse context menu (#28988)
In light of making context not move dynamically, reverting back these
changes.

- Doing it async will lead to a loading state, which moves the context
menu.
- Doing it sync introduces noticeable lag in opening the context menu.
   
Future idea is to introduce fixed code actions like refactor, rewrite,
etc depending on code action kind [(see
more)](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeActionKind)
which will use submenus.
 
Release Notes:

- N/A
2025-04-17 23:48:51 +05:30
Max Brunsfeld
7e928dd615 Implement dragging external files to remote projects (#28987)
Release Notes:

- Added the ability to copy external files into remote projects by
dragging them onto the project panel.

---------

Co-authored-by: Peter Tripp <petertripp@gmail.com>
2025-04-17 11:06:56 -07:00
Marshall Bowers
fade49a11a collab: Don't use a separate product for Zed Pro trials (#28986)
This PR removes the separate product used for the Zed Pro trials, in
favor of using Stripe's trial functionality.

Release Notes:

- N/A
2025-04-17 17:44:14 +00:00
chbk
e4f692ac75 html: Improve syntax highlighting (#28184)
| Zed 0.180.2 | With this PR |
| --- | --- |
|
![Image](https://github.com/user-attachments/assets/89d70ba1-791b-462e-9a14-31c75bcebb7e)
|
![Image](https://github.com/user-attachments/assets/9199499e-071e-49b3-8536-b04b8ce5a222)
|


```html
<script>
  return <div class="main content"></div>
</script>
<div class="main content"></div>
<span></spn>
```

Changes homogenize JSX and HTML

- `"`: `string`
- `=`: `operator` -> `punctuation.delimiter` like in
[JSX](3775496b84/crates/languages/src/javascript/highlights.scm (L246)),
[VSCode](336801752d/extensions/html/syntaxes/html.tmLanguage.json (L382))
- `erroneous_end_tag_name`: `keyword` -> not a keyword

Release Notes:

  - Improved HTML highlighting
2025-04-17 13:40:56 -04:00
Noah Lemen
c21bca07e2 Correct typos in GPUI key_dispatch.rs comments (#28926)
just noticed an extra semicolon and a reference to the nonexistant
`keymap_context` function!

Release Notes:

- N/A
2025-04-17 13:33:01 -04:00
Nate Butler
acc4a5ccb3 Add example agent tool preview (#28984)
This PR adds an example of rendering previews for tools using the new
Agent ToolCard style.

![CleanShot 2025-04-17 at 13 03
12@2x](https://github.com/user-attachments/assets/d4c7d266-cc32-4038-9170-f3e070fce60e)

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-04-17 17:29:19 +00:00
Conrad Irwin
7a95c14625 Revert "git_panel: Pad end of list to avoid obscuring final entry with horizontal scrollbar (#28823)" (#28971)
This reverts commit 1d98b33ae0.

Not sure why, but seems like this breaks the binary search used to
correlate items to each other in the lists.

Release Notes:

- N/A
2025-04-17 11:15:12 -06:00
Oleksiy Syvokon
6dd622d6c3 eval: Fix git revision existence check (#28959)
This change fixes a bug in the worktree initialization.

Details: `git ref-parse --verify $HASH` just checks that $HASH is a
well-formed hash and will successfully return even if $HASH doesn't
exist.

Release Notes:

- N/A
2025-04-17 19:57:37 +03:00
Ben Kunkle
e7afbbd725 editor: Dismiss mouse context menus on selections change (#28729)
Closes #ISSUE

Adds an extra subscription for mouse context menus (i.e. right click context menu) so that when selections change in the editor while the context menu is open (e.g. with vim motions), the context menu closes.

Release Notes:

- N/A
2025-04-17 12:38:12 -04:00
Mikayla Maki
133932ed74 Add support for remote branches to the branch picker (#28978)
Release Notes:

- Added support for remote branches to the branch picker

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
2025-04-17 16:13:02 +00:00
Kirill Bulatov
3ca63584b9 Escape all runnables' cargo extra arguments coming from rust-analyzer (#28977)
Closes https://github.com/zed-industries/zed/issues/28947

Release Notes:

- Fixed certain doctests not being run properly
2025-04-17 16:05:30 +00:00
Danilo Leal
2a878ee6d0 agent: Add design tweaks (#28963)
One more batch of fine-tuning the agent panel's design.

Release Notes:

- N/A
2025-04-17 12:20:25 -03:00
Umesh Yadav
8117940aca Add support for OpenAI o3 and o4-mini models (#28881)
Release Notes:

- Add support for OpenAI o3 and o4-mini models via OpenAI API and
Copilot Chat providers.

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-04-17 10:58:41 -04:00
Bennet Bo Fenner
002235d0da agent: Support adding selection as context (#28964)
https://github.com/user-attachments/assets/42ebe911-3392-48f7-8583-caab285aca09

Release Notes:

- agent: Support adding selections via @selection or `assistant: Quote
selection` as context
2025-04-17 16:55:15 +02:00
Agus Zubiaga
f07695c4cd Remove evals crate (#28968)
Release Notes: 
- N/A
2025-04-17 14:53:22 +00:00
redforks
bdd0cbb717 Fix snippets from extensions being listed twice (#28940)
lookup_snippets() merges global snippets and extension snippets, but
global_snippets::lookup_snippets() also returns extension snippets, make
them double

Closes #28661 

Release Notes:

- Fixed a bug where extension provided snippets were being displayed in
duplicate.
2025-04-17 10:43:49 -04:00
Danilo Leal
022a110f8e agent: Fix "open thread as markdown" button (#28962)
Just now realized that the reason this button wasn't working reliably is
because we weren't passing the index to it. It's now fixed.

Release Notes:

- N/A
2025-04-17 10:20:24 -03:00
Bennet Bo Fenner
b0200c4368 agent: Show context server name in incompatible tool warning (#28954)
<img width="410" alt="image"
src="https://github.com/user-attachments/assets/e29a0ba8-3d37-4e66-b90c-398b24da0453"
/>


Release Notes:

- N/A
2025-04-17 10:18:03 +00:00
Bennet Bo Fenner
ae47829fa8 agent: Fix system instructions typo (#28949)
See #28793, the name of the field is actually `systemInstruction` not
`systemInstructions`.

Release Notes:

- Fixed an issue where Gemini requests would fail
2025-04-17 08:51:05 +00:00
Smit Barmase
5ebb18c47e editor: Fix scrolling drag interrupted on gutter hovering (#28924)
Closes #27188

This PR fixes the issue where, when you drag the scroll handle of the
editor and your mouse hovers over the gutter of the next editor,
scrolling stops. I found no good reason to stop propagation on gutter
hover.

Release Notes:

- Fixed an issue where editor scrolling would stop when the mouse
hovered over another editor's gutter.
2025-04-17 11:48:33 +05:30
Conrad Irwin
ded1c7012c Set diagnostic width based on ems (#28936)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-04-16 22:31:04 -06:00
Conrad Irwin
ad25cd09b6 Fix panic when diagnostics first opens (#28935)
Closes #ISSUE

Release Notes:

- N/A
2025-04-16 21:57:42 -06:00
Agus Zubiaga
a7a7335da4 edit prediction: Assign providers when client status changes (#28919)
There was recently a change that caused the Zed Edit Prediction provider
to only be assigned when the client was connected. However, this check
happened too early, resulting in restored buffers never getting
registered. We'll now subscribe to client status changes and reassign
providers accordingly.

Release Notes:

- edit prediction: Fixed bug disabling prediction in restored buffers
2025-04-16 21:09:25 -03:00
Ben Kunkle
cbb6c221b3 Remove netcat dependency (#28920)
Closes #28813
Closes #27749

Release Notes:

- Removed the need to have openbsd `netcat` (`nc`) installed on your
system in order to enter passwords for `git` or `ssh` (remote
development). If you previously installed `netcat` specifically for Zed,
you may uninstall it.
2025-04-16 19:46:36 -04:00
Ben Kunkle
63b4b60b79 zlog: Ensure log file is flushed (#28923)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-04-16 23:30:23 +00:00
Conrad Irwin
9ea8a9a1d3 Fix more inlay/excerpt race conditions (#28914)
Closes #ISSUE

Release Notes:

- N/A
2025-04-16 16:18:02 -06:00
Anthony Eid
19f542b8d6 debugger: Clear dap status indicator when dap update/download is complete (#28913)
Release Notes:

- N/A
2025-04-16 21:22:08 +00:00
renovate[bot]
70b3cb04bb Update Rust crate anyhow to v1.0.98 (#28904)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [anyhow](https://redirect.github.com/dtolnay/anyhow) |
workspace.dependencies | patch | `1.0.97` -> `1.0.98` |

---

### Release Notes

<details>
<summary>dtolnay/anyhow (anyhow)</summary>

###
[`v1.0.98`](https://redirect.github.com/dtolnay/anyhow/releases/tag/1.0.98)

[Compare
Source](https://redirect.github.com/dtolnay/anyhow/compare/1.0.97...1.0.98)

- Add
[`self.into_boxed_dyn_error()`](https://docs.rs/anyhow/1/anyhow/struct.Error.html#method.into_boxed_dyn_error)
and
[`self.reallocate_into_boxed_dyn_error_without_backtrace()`](https://docs.rs/anyhow/1/anyhow/struct.Error.html#method.reallocate_into_boxed_dyn_error_without_backtrace)
methods for anyhow::Error
([#&#8203;415](https://redirect.github.com/dtolnay/anyhow/issues/415))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 17:04:22 -04:00
renovate[bot]
0170f522bf Pin actions/checkout action to 11bd719 (#28896)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://redirect.github.com/actions/checkout) |
action | pinDigest | -> `11bd719` |

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 17:03:49 -04:00
Kirill Bulatov
602ae84542 Fix the buttons not working in key context view (#28910)
Release Notes:

- N/A
2025-04-16 21:02:37 +00:00
Marshall Bowers
3fef3cc392 Use more types/constants from zed_llm_client (#28909)
This PR makes it so we use more types and constants from the
`zed_llm_client` crate to avoid duplicating information.

Also updates the current usage endpoint to use limits derived from the
`Plan`.

Release Notes:

- N/A
2025-04-16 20:58:00 +00:00
Anthony Eid
78c856cb75 debugger: Enable manually restarting a session when a DAP server doesn't support restarting (#28908)
This PR also fixes the unexpected behavior of clicking restart when a
session is terminated and nothing happens.

And we fixed a small bug where `DebugClientAdapter.shutdown()` was never
called.

Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
2025-04-16 20:56:37 +00:00
Smit Barmase
21946691a4 docs: Use inline code for action (#28907)
Oops, typo.

Release Notes:

- N/A
2025-04-17 02:02:15 +05:30
Marshall Bowers
fcb1efdf21 rpc: Remove llm module in favor of zed_llm_client (#28900)
This PR removes the `llm` module of the `rpc` crate in favor of using
the types from the `zed_llm_client`.

Release Notes:

- N/A
2025-04-16 20:22:44 +00:00
Smit Barmase
54b46fdfaa docs: Add example for disabling default binding while keeping custom one active (#28906)
Release Notes:

- N/A
2025-04-17 01:44:42 +05:30
Smit Barmase
94cf1b0353 outline_panel: Rename outline_panel::Open to outline_panel::OpenSelectedEntry (#28890)
Closes #27171

The `outline_panel::Open` action seems to open the outline panel, but
instead, it moves the editor's cursor to the position of the selected
entry in the outline panel. This PR renames it to
`outline_panel::OpenSelectedEntry` for better clarity.

Meanwhile, there is an existing action, `outline_panel::ToggleFocus`,
that should be used for opening the outline panel.

Todo:
- [x] Added migration

Release Notes:

- Renamed `outline_panel::Open` to `outline_panel::OpenSelectedEntry`
for better clarity.
2025-04-17 01:44:00 +05:30
Kirill Bulatov
56856fb992 Add a way to navigate between changes (#28891)
Closes https://github.com/zed-industries/zed/issues/19731

Adds `editor::GoToPreviousChange` and `editor::GoToNextChange` that work
the same as `vim::ChangeListOlder` and `vim::ChangeListNewer` as the
common logic was extracted and reused.

Release Notes:

- Added a way to navigate between changes with
`editor::GoToPreviousChange` and `editor::GoToNextChange`
2025-04-16 14:09:17 -06:00
Conrad Irwin
64a67a1071 Remove DebugAdapterConfig (#28898)
This is unused as of recent changes to task spawning.

Release Notes:

- N/A
2025-04-16 14:02:10 -06:00
Conrad Irwin
040046ed2a Show all warnings (#28899)
Release Notes:

- (preview only) Fixes a bug where some warnings were not rendered
correctly in the Diagnostics view
2025-04-16 13:49:40 -06:00
Agus Zubiaga
0286b8ab3e agent: Fix conversation token usage and estimate unsent message (#28878)
The UI was mistakenly using the cumulative token usage for the token
counter. It will now display the last request token count, plus an
estimation of the tokens in the message editor and context entries that
haven't been sent yet.


https://github.com/user-attachments/assets/0438c501-b850-4397-9135-57214ca3c07a

Additionally, when the user edits a message, we'll display the actual
token count up to it and estimate the tokens in the new message.

Note: We don't currently estimate the delta when switching profiles. In
the future, we want to use the count tokens API to measure every part of
the request and display a breakdown.

Release Notes:

- agent: Made the token count more accurate and added back estimation of
used tokens as you type and add context.

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-04-16 16:27:36 -03:00
Thomas Mickley-Doyle
8de53bd89f agent: Add git commit ID to the eval telemetry data (#28895)
Release Notes:

- N/A
2025-04-16 14:13:43 -05:00
Marshall Bowers
10507f9a4c collab: Add plan column to subscription_usages (#28889)
This PR adds a `plan` column to the `subscription_usages` table.

These tables don't have any records in them yet, so it's fine to make
the column required without a default.

Release Notes:

- N/A
2025-04-16 18:36:49 +00:00
Conrad Irwin
7bdde8f14f Fix anchor_in_excerpt on replaced excerpts (#28880)
Release Notes:

- N/A
2025-04-16 12:35:40 -06:00
Bennet Bo Fenner
7c7f69f4c5 agent: Allow quoting selection when text thread is active (#28887)
This makes the `assistant: Quote selection` work again for text threads.
Next up is supporting this also in normal threads.

Release Notes:

- agent: Add support for inserting selections (assistant: Quote
selection) into text threads
2025-04-16 20:32:08 +02:00
Mikayla Maki
12c9526f6a Remove bottom dock layout button (#28876)
Release Notes:

- Preview: Removed the layout button from the title bar. The
`bottom_dock_layout` setting still functions.
- Added a setting, `bottom_dock_layout`, for controlling the
relationship between the bottom dock and the left and right docks.
2025-04-16 18:29:36 +00:00
Marshall Bowers
97b044acf5 proto: Add ZedProTrial to Plan (#28885)
This PR adds the `ZedProTrial` member to the `Plan` enum.

Release Notes:

- N/A
2025-04-16 18:13:00 +00:00
Bennet Bo Fenner
1e25e6b3cc agent: Improve fuzzy matching for @mentions (#28883)
Make fuzzy search in @-mention match paths and context kinds as well
(e.g., typing "sym" should let me select the "Symbols" label, as opposed
to just paths)

Release Notes:

- agent: Improve fuzzy-matching when using @mentions
2025-04-16 17:44:07 +00:00
Anthony Eid
f565994da9 debugger: Remove or move breakpoints on file deletion/rename (#28882)
Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
2025-04-16 17:41:24 +00:00
Danilo Leal
db94d6d767 agent: Add item to open Prompt Library in the panel's menu (#28877)
Release Notes:

- agent: Added a menu item to open the Prompt Library from the panel's
dropdown menu on the top right.
2025-04-16 14:31:34 -03:00
Bennet Bo Fenner
456e54b87c agent: Add websearch tool (#28621)
Staff only for now. We'll work on making this usable for non zed.dev
users later

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-04-16 19:25:00 +02:00
5brian
2b277123be vim: Fix LineUp (#27754)
Closes #27423

Release Notes:

- vim: Fixed cursor scrolling off screen with `ctrl-y`.

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-04-16 17:24:24 +00:00
Marshall Bowers
bb0b2a5b7b collab: Remove has_predict_edits_feature_flag from LlmTokenClaims (#28879)
This PR removes the `has_predict_edits_feature_flag` field from the
`LlmTokenClaims`.

We are no longer reading this anywhere.

Release Notes:

- N/A
2025-04-16 17:11:18 +00:00
Piotr Osiewicz
5c2c6d7e5e toolchain: Respect currently focused file when querying toolchains (#28875)
Closes #21743


https://github.com/user-attachments/assets/0230f233-58a4-494c-90af-28ce82f9fc1d


Release Notes:

- Virtual environment picker now looks up virtual environment based on
parent directory of active file; this enables having multiple active
virtual environments in a single worktree.
2025-04-16 19:05:57 +02:00
Danilo Leal
4f58bdee28 agent: Add small design tweaks (#28874)
Some small adjustments to simplify the agent panel's design.

Release Notes:

- N/A
2025-04-16 13:03:36 -03:00
Kirill Bulatov
486a9e4d61 Fix more panics when removing excerpts (#28836)
Release Notes:

- Fixed a panic when an excerpt removed has an edit suggestion inlay in
it

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-16 15:33:28 +00:00
Smit Barmase
0d8f77b5de editor: Expand selection to word under cursor before expanding to next enclosing syntax node (#28864)
Closes #27995

For strings in any language and Markdown, `select_larger_syntax_node`
will first select the word and then expand from there if:
- The cursor is on the word.
- The selection is inside the word.

It will not select the word and will directly proceed to expand if:
- The word is already selected.
- Multiple partial words are selected.

Todo:
- [x] Tests

Release Notes:

- Fixed `select_larger_syntax_node` to first expand to the word within a
string, and then to the larger syntax node.
2025-04-16 20:52:26 +05:30
Marshall Bowers
cb79420773 agent: Show an error when the model requests limit has been reached (#28868)
This PR adds an error message when the model requests limit has been
hit.

Release Notes:

- N/A

Co-authored-by: Oleksiy Syvokon <oleksiy.syvokon@gmail.com>
2025-04-16 15:11:35 +00:00
Thomas Mickley-Doyle
c641209341 agent: Add GitHub action for daily eval run (#28863)
Release Notes:

- N/A
2025-04-16 09:22:41 -05:00
Joseph T. Lyons
48a716fcb5 Bump Zed to v0.184 (#28861)
Release Notes:

-N/A
2025-04-16 13:06:43 +00:00
Piotr Osiewicz
25956c49c1 lsp: Register buffers with language server when querying inlay hints (#28855)
We register buffers with language servers lazily when in multi-buffer
(when the excerpt is interacted with); this does not account for inlay
hints, of which a mere presence on a screen is enough to query a
language server with a path it does not recognize. This posed a problem
with typescript-language-server, which sent a notification to the user
whenever they had a multibuffer open with inlay hints enabled.

Closes #ISSUE

Release Notes:

- Fixed annoying pop-up with typescript-language-server that happened in
multi-buffers with inlay hints enabled.
2025-04-16 11:05:53 +00:00
Anthony Eid
4efabe17dd debugger: Add Debug Panel context menu (#28847)
This PR adds a debug panel context menu that will allow a user to select
which debug session items are visible.

The context menu will add to the pane that was right clicked on.

<img width="1275" alt="Screenshot 2025-04-16 at 2 43 36 AM"
src="https://github.com/user-attachments/assets/330322ff-69db-4731-bbaf-3544d53f2f15"
/>


Release Notes:

- N/A
2025-04-16 08:36:51 +00:00
Michael Sloan
320abe9b22 Agent Eval: Check if SHA already fetched (#28846)
Release Notes:

- N/A
2025-04-16 06:54:22 +00:00
Michael Sloan
9a9f2e71ca Agent Eval: Initial support for running examples repeatedly (#28844)
Not ideal as it creates a separate worktree for each repetition

Release Notes:

- N/A
2025-04-16 06:35:55 +00:00
Michael Sloan
609895d95f Agent Eval: bounded concurrency (#28843)
Release Notes:

- N/A
2025-04-16 00:05:46 -06:00
Michael Sloan
da2d8bd845 Agent Eval: Distinguish tool successes and failures in log (#28839)
Release Notes:

- N/A
2025-04-15 22:51:33 -06:00
Conrad Irwin
6267a147ba Render error message (not pointer) (#28797)
Closes #ISSUE

Release Notes:

- N/A
2025-04-16 04:27:09 +00:00
Conrad Irwin
aceecec6bf Remove user agent from Git (#28798)
Closes #28629

Azure seems to break if this is set.

Release Notes:

- git: Stop sending a custom HTTP header on remote operations
2025-04-15 22:15:07 -06:00
Cole Miller
f3f2c6d811 Fix commondir discovery for git submodules (#28802)
The implementation of commondir discovery in #27885 was wrong, most
significantly for submodules but also for worktrees in rarer cases. The
correct procedure, implemented in this PR, is:

> If `.git` is a file, look at the `gitdir` it points to. If that
directory has a file called `commondir`, read that file to find the
commondir. (This is what happens for worktrees.) Otherwise, the
commondir is the same as the gitdir. (This is what happens for
submodules.)

Release Notes:

- N/A
2025-04-15 23:32:59 -04:00
Kirill Bulatov
41cffa64b0 Fix anchor comparison in multi buffer after expanding excerpts (#28828)
Release Notes:

- Fixed incorrect excerpt comparison when replacing them

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Conrad Irwin <conrad@zed.dev>
2025-04-15 21:30:11 -06:00
Marshall Bowers
b486e32f05 collab: Add GET /billing/usage endpoint (#28832)
This PR adds a `GET /billing/usage` endpoint for retrieving billing
usage to show on the `zed.dev/account` page.

Release Notes:

- N/A
2025-04-16 03:28:09 +00:00
Thomas Mickley-Doyle
222d4a2546 agent: Add telemetry for eval runs (#28816)
Release Notes:

- N/A

---------

Co-authored-by: Joseph <joseph@zed.dev>
2025-04-16 02:54:26 +00:00
Andy Waite
1eb948654a docs: Update Rails test task to run using name (#28574)
The author of Rails' minitest integration
[recommended](https://github.com/zed-extensions/ruby/issues/56#issuecomment-2795010202)
using the test name rather than line number.

This solves the problem in
https://github.com/zed-extensions/ruby/issues/56.

Note that everything is within `command`. I first tried using `args`:

```json
{
  "command": "bin/rails",
  "args": ["test", "$ZED_RELATIVE_FILE -n /$ZED_SYMBOL/"],
  "tags": ["ruby-test"]
}
```
but minitest receives this as:

```
Run options: -n "/\"foo bar\"/" --seed 31855
```

which doesn't match due to the escaping.

Release Notes:

- N/A
2025-04-15 21:39:32 -04:00
Finn Evers
35da1502e1 feedback: Update issue template URL (#28790)
Closes #28782 

The linked template path was updated in #28250. This PR also adds the
change to the zed action.

Since the issue template link was also referenced in workspace, I
updated that occurrence to use the `FileBugReport` action instead. For
that, I had to move the action to `zed_actions`. However, with this
change only one link has to be updated and any database related errors
will have the zed version specs attached to them automatically.

Release Notes:

- Fixed an issue where the `file bug report` action would redirect to an
outdated URL.
2025-04-15 21:36:30 -04:00
Ben Kunkle
1d98b33ae0 git_panel: Pad end of list to avoid obscuring final entry with horizontal scrollbar (#28823)
Closes #27406

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-04-16 01:02:53 +00:00
João Marcos
4e8ecfc0c4 Increase cx.condition timeout to fix flaky test (#28822)
We've been seeing `test_no_duplicated_completion_requests` fail randomly
with the error "condition timed out".

But it's always failing on MacOS, and MacOS sets a shorter timeout of
100ms, compared to 1s from other platforms, this PR increases MacOS's
timeout to match other platforms'.

Release Notes:

- N/A
2025-04-16 00:36:35 +00:00
Peter Tripp
134a0563c2 docs: Missing comma (#28780)
Release Notes:

- N/A
2025-04-16 00:30:59 +00:00
João Marcos
3f4d4af080 fix slicing crash in do_completion (#28820)
Release Notes:

- N/A
2025-04-15 23:37:37 +00:00
Marshall Bowers
68ec1d724c collab: Include subscription_period in LLM token claims (#28819)
This PR updates the LLM token claims to include the user's active
subscription period.

Release Notes:

- N/A
2025-04-15 23:25:41 +00:00
Michael Sloan
102ea6ac79 Add support for judge repetitions in eval (#28811)
Release Notes:

- N/A

---------

Co-authored-by: Thomas <thomas@zed.dev>
2025-04-15 23:18:02 +00:00
Conrad Irwin
5d3718df2d Diagnostics small fixes (#28817)
- **Clear diagnostics cache when toggling warnings**
- **Fix focus when first adding excerpts**

Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-04-15 16:42:04 -06:00
Anthony Eid
f1f5d602fc debugger: Save debug session layout when changing focus or closing session (#28788)
This fixes a bug where resizing the panes wouldn't be serialized and
persist

Release Notes:

- N/A

Co-authored-by: Piotr Osiewicz <peterosiewicz@gmail.com>
2025-04-15 18:25:50 -04:00
Marshall Bowers
60624d81ba collab: Add subscription_usages table (#28818)
This PR adds a new `subscription_usages` table to the LLM database.

We'll use this table to track usage by subscribers.

Records will be looked up using `(user_id, period_start_at,
period_end_at)` to find the record for a user's current subscription
period.

Release Notes:

- N/A
2025-04-15 18:13:29 -04:00
Danilo Leal
91755b2db1 agent: Add scrollbar to the settings view (#28814)
Release Notes:

- agent: Added a scrollbar to the panel settings view.
2025-04-15 18:25:19 -03:00
Anthony Eid
e34fee55a0 debugger: Fix Rust debugger runnable (#28801)
We ran the locator after configuring the debugger binary which cause the
binary to never use the configuration from the cargo locator. This PR
fixes this by correcting the order of configuration.


co-authored-by Anthony Eid <anthony@zed.dev>

Release Notes:

- N/A

---------

Co-authored-by: piotr <piotr@zed.dev>
2025-04-15 17:10:06 -04:00
Marshall Bowers
dad6067e18 collab: Add support for subscribing to Zed Pro trials (#28812)
This PR adds support for subscribing to Zed Pro trials (and then
upgrading from a trial to Zed Pro).

Release Notes:

- N/A
2025-04-15 20:49:16 +00:00
Smit Barmase
5619a3e618 editor: Fix bad hide_mouse_cursor call in find_all_references (#28810)
Release Notes:

- N/A
2025-04-16 02:02:54 +05:30
Antonio Scandurra
06ad45ce08 Fix rejecting multiple hunks in AgentDiff (#28806)
Release Notes:

- Fixed a bug that caused `Reject All` to not always reject _all_ the
hunks.

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-04-15 20:15:58 +00:00
Oleksiy Syvokon
7e6387052f docs: Add troubleshooting guide for Linux audio issues (#28803)
These steps solved audio issues on my system (Tuxedo OS), but should be
applicable to any PipeWire/PulseAudio system that has more than one
audio interface.

I suspect that enabling `rtc_use_pipewire` in [LiveKit SDK](0773bcec4e/webrtc-sys/libwebrtc/build_linux.sh (L105C1-L105C27))
could help as well, but I haven't tried it.

Release Notes:

- N/A
2025-04-15 18:45:25 +00:00
Agus Zubiaga
0182e09e33 eval: Do not create run files for skipped examples (#28800)
Release Notes:

- N/A
2025-04-15 18:00:04 +00:00
Smit Barmase
6f6e207eb5 editor: Move mouse context menu code actions at bottom (#28799)
Release Notes:

- N/A
2025-04-15 23:27:32 +05:30
Marshall Bowers
149cdeca29 collab: Add kind and period start/end timestamps to billing_subscriptions (#28796)
This PR updates the `billing_subscriptions` table with some new columns

- `kind` - The kind of the description (used to denote Zed Pro vs
existing)
- `stripe_current_period_start` - The Stripe timestamp of when the
subscriptions current period starts
- `stripe_current_period_end` - The Stripe timestamp of when the
subscriptions current period ends

Release Notes:

- N/A

Co-authored-by: Mikayla <mikayla@zed.dev>
2025-04-15 13:48:03 -04:00
Smit Barmase
92dc812aea git_ui: Fix commit/amend telemetry and amend click from commit modal (#28795)
Release Notes:

- N/A
2025-04-15 23:17:04 +05:30
Bennet Bo Fenner
c7e80c80c6 gemini: Pass system prompt as system instructions (#28793)
https://ai.google.dev/gemini-api/docs/text-generation#system-instructions

Release Notes:

- agent: Improve performance of Gemini models
2025-04-15 19:45:47 +02:00
Bennet Bo Fenner
c381a500f8 agent: Show a warning when some tools are incompatible with the selected model (#28755)
WIP

<img width="644" alt="image"
src="https://github.com/user-attachments/assets/b24e1a57-f82e-457c-b788-1b314ade7c84"
/>


<img width="644" alt="image"
src="https://github.com/user-attachments/assets/b158953c-2015-4cc8-b8ed-35c6fcbe162d"
/>


Release Notes:

- agent: Improve compatibility with Gemini Tool Calling APIs. When a
tool is incompatible with the Gemini APIs a warning indicator will be
displayed. Incompatible tools will be automatically excluded from the
conversation

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-04-15 16:58:11 +00:00
Agus Zubiaga
ff4334efc7 eval: Fix stalling on tool confirmation (#28786)
The `always_allow_tool_actions` setting would get overridden with the
default when we loaded each example project, leading to examples
stalling when they run a tool that needed confirmation. There's now a
separate `runner_settings.json` file where we can configure the
environment for the eval.

Release Notes:

- N/A

---------

Co-authored-by: Oleksiy <oleksiy@zed.dev>
2025-04-15 16:53:45 +00:00
Thomas Mickley-Doyle
b1e4e6048a agent: Add more Rust code examples, update TODO check (#28737)
Release Notes:

- N/A
2025-04-15 16:52:08 +00:00
Jason Lee
d0f806456c gpui: Fix snap_to_window_with_margin when window has client inset (#27330)
Release Notes:

- Fixed popup menu snap to window to leave margin on Linux.

This change to continue #17159 to fix same thing on Linux.

| Before | After |
| -- | -- |
| ![Pasted
image](https://github.com/user-attachments/assets/3129d42c-7253-4a3f-a428-86e2a3df38ff)
|
![image](https://github.com/user-attachments/assets/8dc83377-9df7-45ba-805b-1cfdea612ae0)
|
2025-04-15 18:47:00 +02:00
Marshall Bowers
b6cce1ed91 collab: Add support for launching a general-purpose billing portal session (#28785)
This PR adds a new `ManageSubscriptionIntent` that allows uses to launch
a general-purpose billing portal session to manage their subscription.

Release Notes:

- N/A
2025-04-15 16:40:22 +00:00
Piotr Osiewicz
05fc9ee396 call: Fix crash when screensharing on MacOS (#28784)
Closes #ISSUE

Release Notes:

- Fixed a crash when screensharing on MacOS

Co-authored-by: Conrad <conrad@zed.dev>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-04-15 16:36:08 +00:00
Danilo Leal
8f52bb92b6 agent: Add ability to interrupt current generation with a new message (#28762)
If you wanted to interrupt the current LLM response that's generating to
send a follow up message, you'd need to stop it first, type your new
message, and then send it. Now, you can just type your new message while
there's a response generating and send it. This will interrupt the
previous response generation and kick off a new one.

Release Notes:

- agent: Allow to send a new message while a response is generating,
interrupting the LLM to focus instead on the most recent prompt.
2025-04-15 13:34:35 -03:00
Cole Miller
144fd0b00d Fix the git panel's commit button sometimes opening the modal (#28767)
Release Notes:

- N/A
2025-04-15 12:16:24 -04:00
Cole Miller
cd4a3fd679 debugger: Skip out-of-bounds breakpoints when deserializing (#28781)
Previously we'd crash when deserializing a breakpoint whose row number
was out of bounds (could happen if the file was externally modified).
This PR fixes that code to skip such breakpoints.

An alternative would be to clip the deserialized `PointUtf16`, but I
think that would mostly result in nonsensical breakpoints.

Release Notes:

- N/A
2025-04-15 16:14:01 +00:00
Cole Miller
42c3f4e7cf debugger_ui: Preview thread state when using the dropdown (#28778)
This PR changes the thread list dropdown menu in the debugger UI to
eagerly preview the state of a thread when selecting it, instead of
waiting until confirming the selection.

Release Notes:

- N/A
2025-04-15 12:10:32 -04:00
Marshall Bowers
90dec1d451 collab: Add Zed Pro checkout flow (#28776)
This PR adds support for initiating a checkout flow for Zed Pro.

Release Notes:

- N/A
2025-04-15 15:45:51 +00:00
Conrad Irwin
afabcd1547 Update block diagnostics (#28006)
Release Notes:

- "Block" diagnostics (that show up in the diagnostics view, or when
using `f8`/`shift-f8`) are rendered more clearly
- `f8`/`shift-f8` now always go to the "next" or "prev" diagnostic,
regardless of the state of the editor

![Screenshot 2025-04-09 at 16 42
09](https://github.com/user-attachments/assets/ae6d2ff6-5183-4b74-89d0-fefee1aa11e3)

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
Co-authored-by: Julia Ryan <juliaryan3.14@gmail.com>
2025-04-15 09:35:13 -06:00
Piotr Osiewicz
ccf9aef767 debugger: Remove LLDB adapter, switch Rust tasks to CodeLLDB (#28773)
Closes #ISSUE

Release Notes:

- N/A
2025-04-15 15:29:43 +00:00
Conrad Irwin
aef78dcffd Tidy up DAP initialization (#28730)
To make DAP work over SSH we want to create the binary
at the project level (so we can wrap it in an `ssh` invocation
transparently).

This means not pushing the adapter down into the session, and resolving
more information ahead-of-time.

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Piotr <piotr@zed.dev>

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Piotr <piotr@zed.dev>
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Anthony <anthony@zed.dev>
2025-04-15 17:11:29 +02:00
Marshall Bowers
6f0951ff77 debugger_ui: Move DEBUGGER_PANEL_PREFIX out of db (#28768)
This PR moves the `DEBUGGER_PANEL_PREFIX` constant out of the `db` crate
and into `debugger_ui`, since it is specific to that.

Release Notes:

- N/A
2025-04-15 14:59:42 +00:00
Bennet Bo Fenner
5e094553fa agent: Return ToolResult from run inside Tool (#28763)
This is just a refactor which adds no functionality.
We now return a `ToolResult` from `Tool > run(...)`. For now this just
wraps the output task in a struct. We'll use this to implement custom
rendering of tools, see #28621.

Release Notes:

- N/A
2025-04-15 14:28:09 +00:00
Kirill Bulatov
32829d9f12 Use proper codenames for macOS versions (#28766)
Closes https://github.com/zed-industries/zed/issues/28765

Release Notes:

- N/A
2025-04-15 14:18:40 +00:00
Agus Zubiaga
e4cf7fe8f5 eval: Improve readability with colors and alignment (#28761)
![CleanShot 2025-04-15 at 10 35
39@2x](https://github.com/user-attachments/assets/495d96fb-fe2f-478b-a9d6-678c1184db9a)


Release Notes:

- N/A
2025-04-15 13:50:01 +00:00
Danilo Leal
2b89b97cd1 agent: Adjust markdown heading sizes (#28759)
Adjust the heading sizes for the Agent Panel so they're not aggressively
huge.

Release Notes:

- N/A
2025-04-15 10:20:35 -03:00
Bennet Bo Fenner
e26f0a331f agent: Make ToolWorkingSet an Entity (#28757)
Motivation is to emit events when enabled tools change, want to use this
in #28755

Release Notes:

- N/A
2025-04-15 14:42:31 +02:00
Danilo Leal
7e1b419243 markdown: Add ability to customize individual heading level (#28733)
This PR adds a new field in the `MarkdownStyle` struct,
`heading_level_styles`, allowing, via the newly added function
`apply_heading_style` and struct `HeadingLevelStyles` to customize each
individual heading level in Markdown rendering/styling function.

Things like this should now be possible:

```rust
    MarkdownStyle {
        heading_level_styles: Some(HeadingLevelStyles {
            h1: Some(TextStyleRefinement {
                font_size: Some(rems(1.15).into()),
                ..Default::default()
            }),
        }),
        ..Default::default()
    }
```

Release Notes:

- N/A
2025-04-15 09:35:24 -03:00
Piotr Osiewicz
98d001bad5 debugger: Always show process list in attach (#28685)
Closes #ISSUE

Release Notes:

- N/A
2025-04-15 14:13:19 +02:00
François Mockers
d4a985a6e3 Case Insensitive Unicode Text Search: Fallback To Regex (#28752)
Closes #9980

Release Notes:

- Fixed: case insensitive text search with unicode characters
2025-04-15 13:12:37 +02:00
Smit Barmase
616d17f517 git_ui: Force commit modal mode from command palette (#28745)
Depending on `git::commit` or `git::amend` action triggered, commit
modal opens up in appropriate mode, handling edge cases like if you are
already in amend mode, etc.

Release Notes:

- N/A
2025-04-15 13:30:02 +05:30
Bennet Bo Fenner
e1c42315dc gemini: Fix "invalid argument" error when request contains no tools (#28747)
When we do not have any tools, we want to set the `tools` field to
`None`

Release Notes:

- Fixed an issue where Gemini requests would sometimes return a Bad
Request ("Invalid argument...")
2025-04-15 07:57:54 +00:00
Smit Barmase
cfc848d24b git_ui: Fix commit modal dismiss on commit menu click (#28744)
Release Notes:

- N/A
2025-04-15 13:05:10 +05:30
Anthony Eid
d4761cea47 debugger: Remember pane layout from previous debugger session (#28692)
This PR makes a debugger's pane layout persistent across session's that
use the same debug adapter.

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Cole Miller <m@cole-miller.net>
2025-04-15 06:32:28 +00:00
Richard Feldman
b794919842 Add contents_tool (#28738)
This is a combination of the "read file" and "list directory contents"
tools as part of a push to reduce our quantity of builtin tools by
combining some of them.

The functionality is all there for this tool, although there's room for
improvement on the visuals side: it currently always shows the same icon
and always says "Read" - so you can't tell at a glance when it's reading
a directory vs an individual file. Changing this will require a change
to the `Tool` trait, which can be in a separate PR. (FYI @danilo-leal!)

<img width="606" alt="Screenshot 2025-04-14 at 11 56 27 PM"
src="https://github.com/user-attachments/assets/bded72af-6476-4469-97c6-2f344629b0e4"
/>

Release Notes:

- Added `contents` tool
2025-04-15 00:54:25 -04:00
Marshall Bowers
fc1252b0cd collab: Remove LLM service (#28728)
This PR removes the LLM service from collab, as it has been moved to
Cloudflare.

Release Notes:

- N/A
2025-04-14 23:47:14 +00:00
Finn Evers
12b012eab3 language: Further optimize language_for_file (#28694)
Follow-up to #28671

This primarily follows two ideas:
1. We currently take the element with the highest score which appears
last in the iterator (see
[`last_by_key`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.max_by_key)),
so we can also just reverse the iterator and take the first highest
match instead.
2. Once we have a match with a given precedence, we are not interested
in any matches with a lower or even the same priority, given what was
established in 1. Thus, we also only have to check whether any language
checked afterwards has a higher priority match.

Furthermore, once we have a match with the highest possible precedence,
there is no need to look for any more possible matches. Thus, this PR
also adds short-circuiting for that scenario.

Lastly, I also cleaned-up the custom suffix match (an empty glob-set
will never match so no need to iterate there) as well reorder the
zip-call in the content matches, as we never need the content if there
is no first line pattern present for the checked languages.

Release Notes:

- N/A
2025-04-14 23:31:45 +00:00
Danilo Leal
77f32582e2 agent: Add some design tweaks (#28726)
Fine-tuning some areas of the Agent Panel design.

Release Notes:

- N/A
2025-04-14 20:18:18 -03:00
Michael Sloan
0d6e455bf6 Agent eval: output paths to log files at the end (#28724)
Release Notes:

- N/A
2025-04-14 23:04:07 +00:00
Michael Sloan
5f897b0e00 Agent Eval: Fail example when there are no events in 2 minutes (#28725)
Release Notes:

- N/A
2025-04-14 23:01:21 +00:00
Thomas Mickley-Doyle
d74f0735c2 Add more eval examples + filtering examples by language + fix git concurrent usage (#28719)
Release Notes:

- N/A

---------

Co-authored-by: michael <michael@zed.dev>
Co-authored-by: agus <agus@zed.dev>
2025-04-14 22:05:46 +00:00
Marshall Bowers
a8b1ef3531 google_ai: Remove unused extract_text_from_events function (#28723)
This PR removes the `extract_text_from_events` function from
`google_ai`, as it was not used anywhere.

Release Notes:

- N/A
2025-04-14 22:01:21 +00:00
Michael Sloan
c8ccc472b5 Track tool use counts (#28722)
Release Notes:

- N/A
2025-04-14 21:45:36 +00:00
Ben Kunkle
26b9c32e96 python: Auto-close f-strings (#28709)
Closes #28707

Release Notes:

- Added support for auto-closing `f`, `b`, `u`, `r`, `rb` and the newly
released `t` strings in Python
2025-04-14 21:22:27 +00:00
Bennet Bo Fenner
db56254517 agent: Apply soft-wrap when message editor is expanded (#28716)
Release Notes:

- N/A
2025-04-14 20:23:42 +00:00
Joseph T. Lyons
9d91908256 Bump Zed to v0.183 (#28718)
Version was bumped to `v0.183.0` last Wednesday here:
https://github.com/zed-industries/zed/pull/28419
But was accidentally downgraded here:
https://github.com/zed-industries/zed/pull/27964

Release Notes:

- N/A
2025-04-14 16:21:19 -04:00
Michael Sloan
6b80eb556c Add judge to new eval + provide LSP diagnostics (#28713)
Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <antonio@zed.dev>
Co-authored-by: agus <agus@zed.dev>
2025-04-14 20:18:47 +00:00
Bennet Bo Fenner
2603f36737 agent: Improve compatibility when using MCP servers with Gemini models (#28700)
WIP

Release Notes:

- agent: Improve compatibility when using MCPs with Gemini models
2025-04-14 21:55:25 +02:00
Ben Kunkle
6c93d107c2 zlog: Ansi styling of zlog output to stdout (#28711)
Co-Authored-By: Zed AI <ai@zed.dev>

Closes #ISSUE

Release Notes:

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

Co-authored-by: Zed AI <ai@zed.dev>
2025-04-14 19:44:03 +00:00
Richard Hao
5b6efa4c02 copilot_chat: Add Gemini 2.5 Pro support to Copilot Chat (#28660) 2025-04-14 15:33:22 -04:00
Umesh Yadav
84aa480344 Add support for OpenAI GPT-4.1 models (#28708)
Release Notes:

- Add support for OpenAI GPT-4.1 via Copilot Chat and OpenAI API

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-04-14 16:15:59 -03:00
Cole Miller
6db29eb90a Remove debug assertions in git_store.rs (#28706)
Closes #ISSUE

Release Notes:

- N/A
2025-04-14 18:25:18 +00:00
João Marcos
ff41be30dc Fix bugs with multicursor completions (#28586)
Release Notes:

- Fixed completions with multiple cursors leaving duplicated prefixes.
- Fixed crash when accepting a completion in a multibuffer with multiple
cursors.
- Vim: improved `single-repeat` after accepting a completion, now
pressing `.` to replay the completion will re-insert the completion text
at the cursor position.
2025-04-14 15:09:28 -03:00
Peter Tripp
47b663a8df github: Add Staff-Only 'Other' Issue template (#28703)
This is because
[issues/new](https://github.com/zed-industries/zed/issues/new) now
redirects to
[issues/new/choose](https://github.com/zed-industries/zed/issues/new/choose)
(good!) so you can no longer create issues skipping templates.

Release Notes:

- N/A
2025-04-14 13:44:45 -04:00
张小白
1d9915f88a windows: Implement AutoUpdater (#25734)
Part of #24800



https://github.com/user-attachments/assets/e70d594e-3635-4f93-9073-5abf7e9d2b20



Release Notes:

- N/A
2025-04-14 10:36:31 -07:00
Evan Gibler
584fa3db53 docs: Add Yara language extension (#28693)
This PR adds a quick overview of the Yara language extension in order to
display the language on the Zed [site](https://zed.dev/docs/languages).

Release Notes:

- N/A

---------

Signed-off-by: egibs <20933572+egibs@users.noreply.github.com>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-04-14 12:40:13 -04:00
Bennet Bo Fenner
a051194195 agent: Check built-in tools schema compatibility in tests (#28691)
This ensures that we respect the `LanguageModelToolSchemaFormat` value
when we call `tool.input_schema`. This prevents us from breaking Gemini
compatibility when adding/changing built-in tools. See #28634.

The test suite will now fail with an error message like this, when
providing an incompatible input_schema:

```
thread 'tests::test_tool_schema_compatibility' panicked at crates/assistant_tools/src/assistant_tools.rs:108:17:
Tool schema for `code_actions` is not compatible with `language_model::LanguageModelToolSchemaFormat::JsonSchemaSubset` (Gemini Models).
Are you using `schema::json_schema_for<T>(format)` to generate the schema?
```


Release Notes:

- N/A
2025-04-14 17:50:01 +02:00
Smit Barmase
78ecc3cef0 git: Amend (#28187)
Adds git amend support.

- [x] Turn existing commit button into split button
- [x] Clean up + Handle shortcuts/focus cases
- [x] Test remote

Release Notes:

- Added git amend support.

---------

Co-authored-by: Cole Miller <cole@zed.dev>
2025-04-14 21:07:19 +05:30
Danilo Leal
ac8a4ba5d4 agent: Add scrollbar to the history view (#28690)
Ended up not making this one visible only upon hover or something
because the layout alignment would be weird given the list item spans
the full width. So, experimenting with this design here:

<img
src="https://github.com/user-attachments/assets/62bf661e-1aae-4644-8a89-49cefb3e8130"
width="700" />

Release Notes:

- agent: Add scrollbar to the history view.
2025-04-14 12:36:58 -03:00
Piotr Osiewicz
9863b48dd7 project/perf: Optimize BufferStore::get_by_path with an additional index (#28670)
Closes #27270

Release Notes:

- Improved performance of git panel with large # of untracked files
2025-04-14 17:06:41 +02:00
duvetfall
fddaa31655 assistant_tools: Fix code_action and rename schemas for Gemini (#28634)
Closes #28475

Updates `rename` and `code_action` `input_schema` methods to use
`json_schema_for<T>()` which transforms standard JSONSchema into the
subset required by Gemini.
Also makes `input_schema` implementations consistent.
Tested tools against Gemini 2.5 Pro Preview, Zed Claude 3.7 Sonnet
Thinking, o3-mini

Release Notes:

- Agent Beta: Fixed error 400 `INVALID_ARGUMENT` when using Gemini with
`code_actions` or `rename` tools enabled.
2025-04-14 11:01:47 -04:00
Agus Zubiaga
b45230784d agent: Handle context window exceeded errors from Anthropic (#28688)
![CleanShot 2025-04-14 at 11 15
38@2x](https://github.com/user-attachments/assets/9e803ffb-74fd-486b-bebc-2155a407a9fa)

Release Notes:

- agent: Handle context window exceeded errors from Anthropic
2025-04-14 14:39:33 +00:00
Ben Kunkle
4a57664c7f zlog: Use zlog as default log implementation (#28612)
Still TODO:

- [x] Remove old log implementations
- [x] More cleanup
- [x] Verify atomic/lock logic
- [x] More tests
- [ ] ??? Ansi coloring when logging to stdout

Release Notes:

- N/A
2025-04-14 14:17:07 +00:00
Danilo Leal
0eb0a3c7dc agent: Move focus to the message editor after going back (#28686)
When you hit the back button in the agent panel toolbar, we were
returning the focus to the buffer instead to the panel's message editor,
which is likely where you want to be after quickly checking history or
settings.

Release Notes:

- N/A
2025-04-14 11:02:55 -03:00
5brian
6278761460 agent: Fix expand message editor while not focused (#28650)
Allow expanding the message editor while the agent panel is not focused,
right now there is no effect when you use the button from another focus

Release Notes:

- N/A
2025-04-14 10:56:24 -03:00
Smit Barmase
f2ce183286 editor: Show code actions in mouse context menu (#28677)
Closes #27989

Asynchronous fetch of code actions on right-click, and shows them in
context menu.


https://github.com/user-attachments/assets/413eb0dd-cd1c-4628-a6f1-84eac813da32

Release Notes:

- Improved visibility of code actions by showing them in right-click
context menu.
2025-04-14 17:44:00 +05:30
Piotr Osiewicz
98891e4c70 language: Optimize language_for_file (#28671)
While working on #28670 this function showed up in my profiles; this PR
makes it evaluate some of it's conditions lazily + prevent constant
rebuilding of globset::Candidates.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-04-14 13:18:45 +02:00
maan2003
5e57f148ac nix: Bump rust-overlay for Rust 1.86 (#28181)
otherwise nix develop doesn't work, complains about not knowing about
rust 1.86

Release Notes:

- N/A
2025-04-14 01:14:54 -07:00
Peter Tripp
128779f615 docs: Improve Lua language documentation (#28662)
Release Notes:

- N/A
2025-04-13 14:15:57 -04:00
hrou0003
b25c3334cc Detect decorated pytest methods as runnable (#28652)
Closes #28096

Release Notes:

- Fixed decorated pytest methods not being picked up as runnable
2025-04-13 19:57:05 +02:00
loczek
77544f42b1 snippets: Fix plaintext snippets not working (#28655)
This PR fixes a minor regression introduced in #27718, where snippets
stopped working when the language was set to plaintext because
`languages_at` doesn't include plaintext, while `language_at` does.

Release Notes:

- Fixed plaintext snippets not working
2025-04-13 19:53:22 +02:00
Smit Barmase
b864a9b0ae hover_popover: Fix markdown selection for info and diagnostic popovers (#28642)
Closes #28638

This PR fixes markdown selection for the info and diagnostic popovers.

In the editor popover, after the changes in
https://github.com/zed-industries/zed/pull/28255, the markdown selection
state updates correctly, but it no longer triggers the editor element to
repaint like it used to. This is fixed by adding a subscription to
listen for markdown entity changes and triggering a repaint for the
editor.

I assume markdown selection works elsewhere because:

1. Either the `Markdown` entity is directly part of a struct that
implements the `Render` trait, causing it to repaint whenever the
markdown state changes. See
[here](d1ffda9bfe/crates/ui_prompt/src/ui_prompt.rs (L65)).
2. OR it's wrapped around component like Popover which implements
`RenderOnce` trait. See
[here](d1ffda9bfe/crates/editor/src/code_context_menus.rs (L645)).

Whereas info and diagnostic popovers does not do both. I do think we can
change it to use `Popover` component, but for now this works as quick
fix.

Extras:
- Remove unnecessary struct cloning.
- Refactor rendering logic to use `when_some`.

Release Notes:

- Fixed issue where selection wasn't working for info and diagnostic
popovers.
2025-04-13 00:32:55 +05:30
hrou0003
e4844b281d Keep .vscode folder included during initialization even if it's in .gitignore (#28631)
This fixes an issue where tasks in `.vscode/tasks.json` weren't being
loaded at startup of a project

Closes #28494

Release Notes:

- Tasks are now loaded from local `.vscode/tasks.json` files even if
they are `.gitignore`d
2025-04-12 12:54:47 +00:00
Danilo Leal
d1ffda9bfe agent: Display keybindings for "Reject All" and "Keep All" (#28620)
<img
src="https://github.com/user-attachments/assets/2cdc5121-dd7b-4f46-8d43-88d5152c77ea"
width="550" />


Release Notes:

- N/A
2025-04-11 22:36:26 -03:00
Danilo Leal
8ffa58414d agent: Increase message editor height (#28618)
It was too tiny, felt like we could use more breathing room.

Release Notes:

- N/A
2025-04-11 22:05:11 -03:00
Danilo Leal
fb78cbbd45 agent: Adjust MCP section in the settings view (#28615)
Mentioning "MCP" more prominently, adding tool descriptions in the icon
button tooltip, and other UI adjustments.

<img
src="https://github.com/user-attachments/assets/e021b3be-99b8-454c-b5fd-0221a7947a35"
width="600" />

Release Notes:

- N/A
2025-04-11 21:39:57 -03:00
Danilo Leal
17719f9f87 agent: Add "Install MCPs" to panel menu (#28616)
Also took the opportunity to rename the "Continue in New Thread" item to
a potentially clearer name.

<img
src="https://github.com/user-attachments/assets/024de07d-f215-4c41-8fbe-652a216b61d9"
width="300"/>

Release Notes:

- N/A
2025-04-11 21:39:50 -03:00
Cole Miller
055df30757 Directly parse .git when it's a file instead of using libgit2 (#27885)
Avoids building a whole git2 repository object at the worktree layer
just to watch some additional paths.

- [x] Tidy up names of the various paths
- [x] Tests for worktrees and submodules

Release Notes:

- N/A
2025-04-11 20:35:14 -04:00
Bennet Bo Fenner
429d4580cf agent: Cleanup message_editor (#28614)
This PR splits up the rendering of the message editor into multiple
functions. Previously we had a single `h_flex()...` expression which
spanned across 550 lines, `cargo fmt` stopped working.

Release Notes:

- N/A
2025-04-12 00:06:49 +00:00
Bennet Bo Fenner
62ebae96e3 agent: Only show recommended models that are actually configured (#28613)
Release Notes:

- N/A
2025-04-11 23:55:23 +00:00
Bennet Bo Fenner
0036a33263 agent: Remove unused code (#28552)
This code was used before we had a proper completion menu for
at-mentions

Release Notes:

- N/A
2025-04-11 23:45:51 +00:00
Bennet Bo Fenner
b22faf96e0 agent: Refine language model selector (#28597)
Release Notes:

- agent: Show recommended models in the agent model selector and display
the provider in the model selector's trigger.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
2025-04-11 23:02:50 +00:00
Agus Zubiaga
dafe994eef agent: Register tracked buffers with language servers (#28610)
Release Notes:

- agent: Start language servers when accessing files via tools

Co-authored-by: Michael <michael@zed.dev>
2025-04-11 22:27:24 +00:00
Conrad Irwin
5994ac5cec Use NoopTextSystem during tests (#28607)
This should allow tests to be more similar across platforms.

Release Notes:

- N/A
2025-04-11 16:26:41 -06:00
claytonrcarter
97a9a5de10 snippets: Fix snippets for PHP and ERB languages (#27718)
Closes #21541
Closes #22726

This should fix snippets in languages, like PHP, that are based on the
HTML syntax layer. To be honest, I don't totally get where HTML comes
into it, but the issues outlined in #21541 and #22726 both boil down to
"Zed only shows me HTML snippets in PHP/ERB files; I expected to see
PHP/ERB snippets". This solution is based on the comments between
@mrnugget and @osiewicz in #22726: resolve/combine snippets for all
language layers at the given position, whereas current behavior is to
resolve snippets only for the `.last()` language layer at the given
position.

- add `Buffer:languages_at()` (note the plural)
- update `snippet_completions()` in `editor.rs` to loop over each
language, gathering snippets as it goes
- the primary logic for resolving snippets within a single language has
not changed

### Verifying this change

I couldn't find tests related to snippet and currently active languages
(CI may show them to me 😆 ) but I can add some if desired and w/ perhaps
a little coaching or prompting about another test to look to for
inspiration. I have confirmed that this works for PHP, but I have not
checked ERB because I'm not familiar with it or set up for it.

To check this manually:
1. install the PHP extension
2. install at least 1 snippet for each of html, php and phpdoc. If you
don't have any, these should work:
```sh
# BEWARE these will clobber existing snippets!
echo '{"dddd":{"body":"hello from phpdoc"}}' > ~/.config/zed/snippets/phpdoc.json
echo '{"pppp":{"body":"hello from PHP"}}' > ~/.config/zed/snippets/php.json
echo '{"hhhh":{"body":"hello from HTML"}}' > ~/.config/zed/snippets/html.json
```
3. open any PHP file. If you don't have one, here's one that should
work:
```php
<?php

/**
 *
 */
function function_name()
{
}
```
4. Place your cursor in a PHPdoc comment (eg after the `/**` on line 3)
- you should be able to use the `dddd`, `pppp` and `hhhh` snippets; on
`main`, only the `dddd` snippet works here
5. Move your cursor to a non-comment PHP area (eg after the `{` on line
7)
- you should be able to use the `pppp` and `hhhh` snippets, but not
`dddd`; on `main`, only `hhhh` works here

### Performance

This adds 2 separate (not nested) loops to `snippet_completions()`, each
of which will iterate over the active language scopes at the given
location. I have not looked into the specifics of how many layers most
languages have, but I suspect that *most* users will see identical
performance as before because there will only be 1 scope active most of
the time.

In some cases, though (eg PHP, ERB, maybe template strings in JS), the
editor will be looping over more layers, possibly many in some deeply
injected/embedded cases (I'm thinking of a regex template string in a JS
heredoc string in a PHP script in an HTML file). I don't expect this to
be an issue – nor has it been in my usage and testing – but performance
of snippets could be affected in pathological cases.

### Alternate solutions

Instead of resolving snippets for *all* layers, we could just change how
we pick which language to resolve. Instead of always using `.last()`,
perhaps we could do something more clever. This feels like it could be
tricky and potentially error prone, though.

Release Notes:

- Snippets are now resolved for all languages active at the cursor
location.
- Fixed snippets in PHP, ERB and other languages whose syntax layers are
based on HTML
2025-04-12 00:20:43 +02:00
5brian
730f2e7083 vim: Add highlighting to set commands (#28600)
|Before|After|
|--|--|

|![image](https://github.com/user-attachments/assets/fb965e1f-658c-4ecd-a51f-821881b8001a)|![image](https://github.com/user-attachments/assets/f05f73bf-6661-406a-a5d6-e121e5b6fd1a)|

Release Notes:

- N/A
2025-04-11 16:03:05 -06:00
Marshall Bowers
141ad72d97 extension: Use heck instead of convert_case for snake_case check (#28608)
This PR updates the snake_case check for grammar names to use `heck`
instead of `convert_case`.

`heck` correctly handles values like `d2`.

Fixes https://github.com/zed-industries/zed/issues/28583.

Release Notes:

- Updated snake_case check for grammar names in extensions.
2025-04-11 22:01:25 +00:00
张小白
a5fe6d1e61 History manager (#26369)
While working on implementing `add_recent_documents` for Windows, I
found that the process is significantly more complex compared to macOS.
On macOS, simply registering the `add_recent_documents` function is
enough, as the system handles everything automatically.

On Windows, however, there are two cases to consider:  
- **Files opened by the app**: These appear in the "Recent" section (as
shown in the screenshot, "test.txt") and are managed automatically by
Windows (by setting windows registry), similar to macOS.

![屏幕截图 2025-03-10
230738](https://github.com/user-attachments/assets/8fc8063b-4369-43cc-aaaf-7370a7d27060)


- **Folders opened by the app**: This is more complicated because
Windows does not handle it automatically, requiring the application to
track opened folders manually.

To address this, this PR introduces a `History Manager` along with
`HistoryManagerEvent::Update` and `HistoryManagerEvent::Delete` events
to simplify the process of managing recently opened folders.



https://github.com/user-attachments/assets/a2581c15-7653-4faf-96b0-7c48ab1dcc8d



Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2025-04-11 21:34:51 +00:00
Sam Tay
5734ffbb18 Update haskell extension docs (#28603)
In https://github.com/zed-extensions/haskell/pull/2 the HLS settings
were updated to respect binary path/argument overrides. This PR just
updates the docs to demonstrate this.

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-04-11 16:37:58 -04:00
Peter Tripp
932a7c6440 keymap: Document editor::Select* actions (cmd-d, etc) (#28362)
This is a no-op change which just adds comments.

Release Notes:

- N/A
2025-04-11 16:22:36 -04:00
Peter Tripp
6a60bb189b ci: No draft releases when using 'run-bundling' (#28596)
Improve the logic in around release artifact bundling.
- Suppress a harmless "error: no such command: `about`" from
script/generate-licenes output
- Remove checks for main branch (which will never be true)
- Only run `Upload Artifacts to release` when not using `run-bundling`.
Prevents the creation of draft releases with just linux remote server binaries)

Release Notes:

- N/A
2025-04-11 15:15:22 -04:00
Cole Miller
5909d1258b Fix a panic in the git store (#28590)
Closes #ISSUE

Release Notes:

- Fixed a panic that could occur when git statuses were updated.
2025-04-11 14:05:51 -04:00
Piotr Osiewicz
78662f8fea debugger: UI refinements (#28589)
- Name of source is only used as a fallback if there's no path
- Make the frames a bit more compact.


![image](https://github.com/user-attachments/assets/74772455-c16e-477f-a962-dffd4575e557)

Release Notes:

- N/A
2025-04-11 17:40:25 +00:00
Conrad Irwin
c2e3134963 Try to weak-link ScreenCaptureKit always (#28585)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-04-11 17:38:14 +00:00
Cole Miller
66b3e03baa Fix a bug causing stale optimistic state in the git panel (#28588)
Release Notes:

- Fixed a bug that caused the staged status of files in the git panel to
be out of date in some cases.
2025-04-11 17:26:39 +00:00
Piotr Osiewicz
7caa2c2ea0 debugger: Prompt user when they try to close a running debug session (#28584)
Closes #ISSUE

Release Notes:

- N/A
2025-04-11 19:16:26 +02:00
Peter Finn
08ce230bae vim: Add some forced motion support (#27991)
Closes https://github.com/zed-industries/zed/issues/20971

Added `v` input to yank and delete to override default motion. The
global vim state tracking if the forced motion flag was passed handled
the same way that the count is. [The main chunk of code maps the motion
kind from the default to the overridden
kind](https://github.com/zed-industries/zed/pull/27991/files#diff-2dca6b7d1673c912d14e4edc74e415abbe3a4e6d6b37e0e2006d30828bf4bb9cR1249-R1254).
To handle the case of deleting a single character (dv0) at the start of
a row I had to modify the control flow
[here](https://github.com/zed-industries/zed/pull/27991/files#diff-2dca6b7d1673c912d14e4edc74e415abbe3a4e6d6b37e0e2006d30828bf4bb9cR1240-R1244).
Then to handle an exclusive delete till the end of the row (dv$) I
[saturated the endpoint with a left
bias](https://github.com/zed-industries/zed/pull/27991/files#diff-2dca6b7d1673c912d14e4edc74e415abbe3a4e6d6b37e0e2006d30828bf4bb9cR1281-R1286).

Test case: dv0


https://github.com/user-attachments/assets/613cf9fb-9732-425c-9179-025f3e107584

Test case: yvjp


https://github.com/user-attachments/assets/550b7c77-1eb8-41c3-894b-117eb50b7a5d

Release Notes:

- Added some forced motion support for delete and yank
2025-04-11 11:12:30 -06:00
Thomas Jensen
1df01eabfe workspace: Implement Extended Terminal Option (#26211)
Closes #10211 
Closes #7575 

Screenshot of feature:
![Screenshot 2025-03-06 at 1 08
13 PM](https://github.com/user-attachments/assets/73cc4519-248b-4264-9ce8-42d0980cf73c)

Screenshot of proposed menu:
![Screenshot 2025-03-06 at 1 14
30 PM](https://github.com/user-attachments/assets/efc7c18a-a2a5-491f-b3e5-5ed181f23906)

Screenshot of proposed menu closed:
![Screenshot 2025-03-06 at 1 14
57 PM](https://github.com/user-attachments/assets/0b42829c-abe3-48aa-9b81-30a0aeeac8fd)

Release Notes:

- Configuration of bottom_dock_layout in settings.json
- Layout Mode button in Title Bar
- 4 different layout modes for the bottom dock: contained (default),
full (extends below both docks), left-aligned, right-aligned (extends
only below the respective dock)

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-04-11 16:18:36 +00:00
Nate Butler
2f5c662c42 Refine component preview & add serialization (#28545)
https://github.com/user-attachments/assets/0be12a9a-f6ce-4eca-90de-6ef01eb41ff9

- Allows the active ComponentPreview page to be restored via
serialization
- Allows filtering components using a filter input
- Updates component example rendering
- Updates some components

Release Notes:

- N/A

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-04-11 11:43:57 -04:00
Andy Waite
a03fb3791e docs: Fix name for zed: open project tasks command (#28578)
There's no `zed: open local tasks`, perhaps it was called that
previously.

Release Notes:

- N/A
2025-04-11 09:31:58 -06:00
Peter Finn
dd7bc5f199 vim: Add delete keymapping to vim.json (#28551)
Closes #16511

Added test for delete in normal mode and keymapping in vim.json

Release Notes:

- Added delete mapping in normal mode
2025-04-11 08:55:43 -06:00
Piotr Osiewicz
c7d3fbcac1 debugger: Fix Debugpy spawning & session removal (#28577)
Closes #ISSUE

Release Notes:

- N/A
2025-04-11 16:36:54 +02:00
Marshall Bowers
1164829cad html: Bump to v0.2.1 (#28575)
This PR bumps the HTML extension to v0.2.1.

Changes:

- https://github.com/zed-industries/zed/pull/28542

Release Notes:

- N/A
2025-04-11 13:58:30 +00:00
Piotr Osiewicz
e09eeb7446 debugger: Style debugger tabs (#28572)
![image](https://github.com/user-attachments/assets/a88b1897-cb96-4c6c-b602-396a91ef4de8)

Release Notes:

- N/A
2025-04-11 15:33:36 +02:00
Piotr Osiewicz
cdcad708f6 task: Poll Rust subcommands on background thread (#28553)
Closes #ISSUE

Release Notes:

- Improved app responsiveness when spawning Rust tasks.
2025-04-11 11:04:10 +00:00
Smit Barmase
bd4c9b45b6 editor: Fix signature help popover goes off screen (#28566)
Closes #27731

Uses similar logic as other popovers for layouting signature help
popover.

Release Notes:

- Fixed case where signature help popover goes off the screen.
2025-04-11 14:50:42 +05:30
Anthony Eid
d4736a5427 debugger: Fix bug where deleting a breakpoint could delete multiple breakpoints (#28562)
This PR fixes a bug when deleting a breakpoint with a (log, conditional,
hit condition) message by removing the message. All breakpoints that
contain that type of message were also deleted.

Release Notes:

- N/A
2025-04-11 08:04:12 +00:00
Max Brunsfeld
353ae2335b Fix staging/unstaging hunks remotely (#28560)
Fixes a regression introduced in
https://github.com/zed-industries/zed/pull/28377 where the pending hunks
didn't get cleared properly when staging/unstaging hunks remotely. I
didn't add new tests, because the fix was to simplify some code.

Release Notes:

- N/A
2025-04-10 21:31:43 -07:00
João Marcos
ad39d3226f Add new actions editor::FindNextMatch and editor::FindPreviousMatch (#28559)
Closes #7903

Release Notes:

- Add new actions `editor::FindNextMatch` and
`editor::FindPreviousMatch` that are similar to `editor::SelectNext` and
`editor::SelectPrevious` with `"replace_newest": true`, but jumps to the
first or last selection when there are multiple selections.
2025-04-11 03:43:55 +00:00
Piotr Osiewicz
c35238bd72 debugger: Add support for setting multiple breakpoints via actions (#28437)
Allow setting multiple breakpoints with multi cursors

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Remco Smits <djsmits12@gmail.com>
Co-authored-by: Anthony <anthony@zed.dev>
2025-04-10 23:31:57 -04:00
Bennet Bo Fenner
5757e352b0 agent: Fix bug where wrong crease for @mention would be displayed (#28558)
Release Notes:

- agent: Fix a bug where an inserted @mention did not show up as the one
that was selected
2025-04-11 02:04:03 +00:00
Danilo Leal
c124838a73 agent: Fix "new text thread" action name (#28555)
Moving from "NewPromptEditor" to "NewTextThread". We recently re-named
that and this was missing.

Release Notes:

- N/A
2025-04-10 22:23:44 -03:00
Marshall Bowers
5ebac7e30c agent: Clean up thread auto-capturing (#28550)
This PR cleans up the thread auto-capturing added in #28271.

- Removed usage of `unsafe`
- Fixed feature flag check
- We were incorrectly not respecting the feature flag in release builds
- Made sure the telemetry event was being run on the background executor

Release Notes:

- N/A
2025-04-11 01:08:24 +00:00
Mikayla Maki
c143846e42 Revert buggy pr (#28554)
Earlier, I merged #24723

Before merging it, I made a change that was incorrect and fast followed
with a fix: #28548

Following that fix, @bennetbo discovered that the modals where no longer
highlighting correctly, particularly the outline modal.

So I'm going to revert it all.

Release Notes:

- N/A
2025-04-10 18:58:36 -06:00
Danilo Leal
71c2a11bd9 agent: Make the message editor expandable (#28420)
This PR allows expanding the message editor textarea to fit almost the
total height of the Agent Panel. Stylistically, I'm also changing the
font family we use in the textarea to use the buffer font; want to
experiment with this for a bit.

Release Notes:

- agent: The Agent Panel textarea can now be expanded to fill almost the
total height of the panel.

---------

Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-04-10 21:53:52 -03:00
Antonio Scandurra
2440faf4b2 Actually run the eval and fix a hang when retrieving outline (#28547)
Release Notes:

- Fixed a regression that caused the agent to hang sometimes.

---------

Co-authored-by: Thomas Mickley-Doyle <tmickleydoyle@gmail.com>
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Michael Sloan <mgsloan@gmail.com>
2025-04-11 00:01:33 +00:00
Mikayla Maki
c0262cf62f Fix bug where all editor completions would be black (#28548)
Release Notes:

- N/A
2025-04-10 17:59:10 -06:00
Jason Lee
fd256d159d gpui: Keep drag cursor style when dragging (#24797)
Release Notes:

- Improve to keep drag cursor style on dragging resize handles.

---

### Before


https://github.com/user-attachments/assets/d4100d01-ac02-42b8-b923-9f2b4633c458

### After


https://github.com/user-attachments/assets/b5a450cd-c6de-4b39-a79c-2d73fcbad209

With example:

```
cargo run -p gpui --example drag_drop
```


https://github.com/user-attachments/assets/4cba1966-1578-40ce-a435-64ec11bcace5
2025-04-10 23:54:12 +00:00
Danilo Leal
a2a3d1a4bd editor: Refactor EditorMode::Full (#28546)
This PR lightly refactors the `EditorMode::Full` exposing two new
methods: `is_full` and `set_mode`.

Motivation is to expose fields that modify the behavior when the editor
is in `Full` mode. By using is `mode.is_full()` instead of
`EditorMode::Full` we can introduce new fields without breaking other
places in the code.

Release Notes:

- N/A

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-04-10 23:22:27 +00:00
Max Brunsfeld
294a1b63c0 Fix diff recalculation hang (#28377)
Fixes https://github.com/zed-industries/zed/issues/26039

Release Notes:

- Fixed an issue where diffs stopped updating closing and reopening them
after staging hunks.
- Fixed a bug where staging a hunk while the cursor was in a deleted
line would move the cursor erroneously.

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: João Marcos <marcospb19@hotmail.com>
2025-04-10 22:58:41 +00:00
Jason Lee
ffdf725f32 gpui: Fix text hover & active style (#24723)
Release Notes:

- N/A

---

Fix this long-standing issue so that we can support Link hover colors.

And renamed `text_layout` example to `text_style`.

---

I spent some time studying the process of this text style change and
found it a bit complicated.

At first, I thought there was a problem with refine and it was not
passed properly. After changing it, I found that it was not the problem.

Then I found that it was because `TextRun` had already stored the
`color`, `background`, `underline`, `strikethrough` in TextRun in the
`request_layout` stage. They area calculate at the `request_layout`
stage, but request_layout stage there was no `hitbox`, so the hover
state was not obtained.

```bash
cargo run -p gpui --example text_style
```


https://github.com/user-attachments/assets/24f88f73-775e-41d3-a502-75a7a39ac82b

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-04-10 22:34:47 +00:00
vipex
8ee6a2b454 html: Fix leading slash on Windows paths (#28542)
This PR builds on the fix proposed in
[zed-extensions/astro#5](https://github.com/zed-extensions/astro/pull/5)
and serves as a workaround for certain LSPs affected by
[zed-industries/zed#20559](https://github.com/zed-industries/zed/issues/20559)—specifically,
the HTML language server in this case.

Credit to @maxdeviant for identifying and implementing the original fix.
This PR extends that solution to other areas where it may be beneficial.

Release Notes:

- N/A
2025-04-10 18:34:22 -04:00
Anthony Eid
cf65d9437a debugger: Add console indicator and resolve debug configs from NewSessionModal (#28489)
The debug console will now show an indicator when it's unopened and
there's unread messages.

`NewSessionModal` attempts to resolve debug configurations before using
the config to start debugging. This allows users to use zed's task
variables in the modal prompt.

I had to invert tasks_ui dependency on debugger_ui so `NewSessionModal`
could get the correct `TaskContexts` by calling tasks_ui functions. A
consequence of this workspace has a new event `ShowAttachModal` that I'm
not a big fan of. @osiewicz if you have time could you please take a
look to see if there's a way around adding the event. I'm open to pair
on it too.

Release Notes:

- N/A
2025-04-10 22:29:03 +00:00
Ben Kunkle
66dd6726df zlog: Support configuring log levels with env var (#28544)
Reimplemented logic from `env_logger` to parse log configuration from
environment variables.

Had to re-implement instead of using `env_filter` crate that
`env_logger` uses, as it does not export the information required to
integrate it.


Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-04-10 22:00:44 +00:00
Bennet Bo Fenner
44cb8e582b markdown: Track code block metadata in parser (#28543)
This allows us to not scan the codeblock content for newlines on every
frame in `active_thread`

Release Notes:

- N/A
2025-04-10 21:49:08 +00:00
Danilo Leal
73305ce45e Change zed.dev's default model to Claude 3.7 Sonnet (#28541)
From Claude 3.5 Sonnet to **Claude 3.7 Sonnet**.

Release Notes:

- Change the default model of Zed's hosted LLM service to Claude 3.7
Sonnet.
2025-04-10 18:34:04 -03:00
James Tucker
94b75f3ad9 gpui: Enable per-pixel, GPU composited transparency on Windows (#26645)
Move the SetLayeredWindowAttributes call to immediately after window
construction, and initialize it with per-pixel transparency settings, no
color key and no global blending. The render pipeline will perform alpha
blending during compositing.

Cleaned up the DWM acrylic API calls some, to explicitly set to the
three appropriate modes depending on opaque, transparent or blurred
settings. The API internally hides versioning concerns from the caller.

Set the window class background color to black, this prevents a
flashbang on slow startup, e.g. debug builds on a heavily loaded system.

The outcome is that the window no longer receives paint demands for
underlying window updates, while also having per-pixel transparency -
opaque theme elements are now correctly opaque. The transparency
settings are now portable across windows and macOS having mostly similar
outcomes (modulo palette differences). Small fonts may still appear to
be alpha blended - this seems to be in the glyph atlas, their pixels are
not actually opaque. Larger fonts (or higher DPIs) don't suffer this and
are as opaque as expected. Layering the window atop one that is
rendering at 120fps, the editor window can drop to its 8fps idle state,
while still being composited with 120fps alpha blend in the background,
in both blur and transparent modes.

Updates #20400

Release Notes:

- Improved transparency on Windows to be more efficient, support fully
opaque elements and more closely match other platforms.
2025-04-10 21:27:19 +00:00
Marko Kungla
384868e597 Add --user-data-dir CLI flag and propose renaming support_dir to data_dir (#26886)
This PR introduces support for a `--user-data-dir` CLI flag to override
Zed's data directory and proposes renaming `support_dir` to `data_dir`
for better cross-platform clarity. It builds on the discussion in #25349
about custom data directories, aiming to provide a flexible
cross-platform solution.

### Changes

The PR is split into two commits:
1. **[feat(cli): add --user-data-dir to override data
directory](28e8889105)**
2. **[refactor(paths): rename support_dir to data_dir for cross-platform
clarity](affd2fc606)**


### Context
Inspired by the need for custom data directories discussed in #25349,
this PR provides an immediate implementation in the first commit, while
the second commit suggests a naming improvement for broader appeal.
@mikayla-maki, I’d appreciate your feedback, especially on the rename
proposal, given your involvement in the original discussion!

### Testing
- `cargo build `
- `./target/debug/zed --user-data-dir ~/custom-data-dir`

Release Notes:
- Added --user-data-dir CLI flag

---------

Signed-off-by: Marko Kungla <marko.kungla@gmail.com>
2025-04-10 21:16:43 +00:00
Marshall Bowers
d88694f8da language_models: Fix non-streaming Copilot Chat models (#28537)
This PR fixes usage of non-streaming Copilot Chat models.

Closes https://github.com/zed-industries/zed/issues/28528.

Release Notes:

- Fixed an issue with using non-streaming Copilot Chat models (e.g., o1,
o3-mini).
2025-04-10 20:48:08 +00:00
Terminal
90f30b5c20 gpui: Allow DisplayId to be compared to u32 (#27895)
allow DisplayId to be compared to u32. This is handy since gpui doesn't
provide a method to detect current active display of the user. So when
using mouse location to get the active display we need to then compare
that display u32 to DisplayID

Release Notes:
- added From to allow u32 comparison
2025-04-10 14:41:10 -06:00
tidely
24d4f8ca18 gpui: Optimize coalesce float sign checking (#28072)
Optimize away a multiplication during in the `coalesce` function. Our
goal is to check whether the sign of two floats is the same.

Instead of multiplying each `.signum()` and checking that the result is
positive, we can simply check that the signum's are the same. This
removes a float multiplication.

```rust
a.signum() * b.signum() >= 0.0
```

turns into

```rust
a.signum() == b.signum()
```



Release Notes:

- Fix documentation for `Pixels::signum`
2025-04-10 14:39:50 -06:00
Kirill Bulatov
804066a047 Do not query for LSP tasks buffers that do not belong to the position given (#28536)
Follow-up of https://github.com/zed-industries/zed/pull/28359

Release Notes:

- Fixed a panic when LSP tasks are queried in certain multi buffer
excerpts
2025-04-10 20:37:21 +00:00
Niklas Eicker
4a356466b1 rust: Enable required features when executing main functions in tasks (#27312)
Closes #13344

This PR causes required features to be read from `cargo metadata` and
enabled when executing an example/bin in Rust.

Release Notes:

- Added enabling required features when executing a Rust example or bin
through a task
2025-04-10 20:29:07 +00:00
Smit Barmase
0921762b59 install_cli: Show feedback when installing CLI from welcome screen (#28532)
Closes #28408

Release Notes:

- Fixed no feedback provided when installing CLI from welcome page.
2025-04-11 01:47:40 +05:30
Thomas Mickley-Doyle
46b1df2e2d agent: Auto-capture telemetry feature flag (#28271)
Release Notes:

- N/A
2025-04-10 15:07:48 -05:00
Conrad Irwin
986da332db Bump rustls (#28531)
Closes #26699

Release Notes:

- Fixed a panic when enabling or disabling a VPN on macOS
2025-04-10 14:04:34 -06:00
Austin Merrick
dad33f7cc2 Fix code action selection bug while using vim visual mode (#27817)
## Problem

Code actions do not handle vim line mode correctly. See this video where
`Extract to function` doesn't extract both selected lines:


https://github.com/user-attachments/assets/8fa0fb28-0403-44f6-9e55-a59b6713dffd

## Solution

Use `selections.newest_adjusted` instead of `selections.newest_anchor`
so code actions consider the full selection.

Correct behavior:


https://github.com/user-attachments/assets/174d5a34-3873-4d20-b67d-103edec4cdbe

---

Release Notes:

- vim: Fixed code actions in visual line mode
2025-04-10 13:53:00 -06:00
Smit Barmase
64241f7d2f editor: Fix extra characters were being written at the end of an HTML tag (#28529)
Closes #25586

It is caused due to assumption all character being typed are word
characters and linked edit ranges can be used even when first non-word
character is typed. Because next character passes all the criteria like
being word character, anchor matching the previous range before typing
started, wrong edit take place.

This PR fixes it by clearing linked edit ranges when non-word character
is typed.

Before:

`<div cx^></div>cx` when typing fast.

After:

`<div cx^></div>` always.


Release Notes:

- Fixed a case where extra characters were being written at the end of
an HTML tag.
2025-04-11 00:17:34 +05:30
Ben Kunkle
fbbc23bec3 editor: Restore selections to positions after last edit (#28527)
Closes #22692

Makes it so when undoing a format operation, the selections are set to
where they were at the last edit.

Release Notes:

- Made it so the cursor position is reset to where it was after the last
edit when undoing a format operation. This will only result in different
behavior when you make an edit, scroll away, initiate formatting (either
by saving or manually) and then undo the format.

---------

Co-authored-by: Zed AI <ai@zed.dev>
2025-04-10 18:33:49 +00:00
Piotr Osiewicz
26f4705198 debugger: Add breakpoint list (#28496)
![image](https://github.com/user-attachments/assets/2cbe60cc-bf04-4233-a7bc-32affff8eef5)
Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-04-10 18:18:58 +00:00
Nate Butler
3abf95216c Add progress bar component (#28518)
- Adds the progress bar component

Release Notes:

- N/A
2025-04-10 12:11:58 -06:00
Andy Waite
b0b52f299c docs: Prefer bin/rails when running Rails tests (#28167) 2025-04-10 17:46:12 +00:00
Ben Kunkle
53cde329da Clean up formatting code and add testing for formatting with multiple formatters (including code actions!) (#28457)
Release Notes:

- N/A

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-04-10 15:32:43 +00:00
Cole Miller
b55b310ad0 Downgrade environment-related logging (#28509)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-04-10 14:38:29 +00:00
Cole Miller
8ab25e2bac Fix merge conflicts jumping (#28508)
This regressed in #27568, oops.

Release Notes:

- Fixed a bug causing conflicted files in the git panel to jump to the
"Tracked" section as soon as they were staged.
2025-04-10 14:29:36 +00:00
Peter Tripp
c10b1f7c61 docs: Update system requirements (#28504)
Explicitly note that macOS 12.x Monterey is required for screen sharing.

Release Notes:

- N/A
2025-04-10 14:18:12 +00:00
Thomas Mickley-Doyle
cb1ee01a66 agent: Add selected tool names to agent panel telemetry (#28247)
Release Notes:

- N/A
2025-04-10 08:43:52 -05:00
Agus Zubiaga
90bcde116f agent: Use current shell (#28470)
Release Notes:

- agent: Replace `bash` tool with `terminal` tool which uses the current
shell

---------

Co-authored-by: Bennet <bennet@zed.dev>
Co-authored-by: Antonio <antonio@zed.dev>
2025-04-09 23:38:36 -06:00
Antonio Scandurra
8ac378b86e Lay the groundwork for a Rust-based eval (#28488)
Also, we moved the logic for driving the agentic loop into `Thread` so
that we don't have to re-implement it.

Release Notes:

- N/A

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
2025-04-10 04:45:27 +00:00
Bennet Bo Fenner
55760295d9 agent: Optimize render_markdown_block function (#28487)
Co-Authored-by: Agus <agus@zed.dev>

Closes #ISSUE

Release Notes:

- N/A

Co-authored-by: Agus <agus@zed.dev>
2025-04-10 04:29:47 +00:00
Antonio Scandurra
9dfb907f97 Revert "Add reminder message about system prompt" (#28482)
This breaks the agentic loop.
2025-04-09 22:12:33 -06:00
Danilo Leal
e20daa7639 agent: Fix toolbar spacing (#28485)
Release Notes:

- N/A
2025-04-10 01:08:07 -03:00
Danilo Leal
b46ab367ef agent: Add button to open thread as markdown (#28481)
<img
src="https://github.com/user-attachments/assets/92ca8f64-a949-4cc1-a657-3978a2c65839"
width="600"/>

Release Notes:

- agent: The action to open the current active thread in Markdown is now
exposed in the UI.
2025-04-10 00:09:34 -03:00
5brian
12212dc329 agent: Prevent sending whitespace only messages (#28409)
Prevent this from happening when sending a prompt with only spaces and
newlines:


![image](https://github.com/user-attachments/assets/b275f4c5-c013-4695-8fb4-e3ad75d41750)

Release Notes:

- agent: Prevent from sending messages containing only white space.
2025-04-09 23:57:42 -03:00
Conrad Irwin
324e4658ba Reset modifiers when the window active state changes (#28348)
Closes #23449

Release Notes:

- Fixed a bug causing shift to get stuck down when the window focus
changes

---------

Co-authored-by: Dino <dinojoaocosta@gmail.com>
2025-04-09 20:55:19 -06:00
Conrad Irwin
ed500dacb6 Fix typo in symbolicate script (#28456)
Fix silly typo in symbolicate script

Release Notes:

- N/A
2025-04-09 20:55:10 -06:00
Danilo Leal
2f4b48129b agent: Collapse code blocks in the active thread (#28467)
Release Notes:

- N/A

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-04-09 23:44:02 -03:00
Dino
ed7c55a04e vim: Reset search range after substitute (#28403)
Update the `Vim::replace_command` method so as to reset the range in the
`BufferSearchBar` after running the replacement in order to fix the
issue where the number of matches in the search bar would be incorrect
after the replacement was done, as it would only take into consideration
the range in which the replacement happened, instead of the whole
buffer.

In order to get this working a new
`BufferSearchBar::clear_search_within_ranges` method is introduced in
these changes.

Release Notes:

- Fixed the number of matches displayed in the search bar after running
vim's substitute command.
2025-04-09 20:43:53 -06:00
Richard Feldman
6db4ab381c Add code action tool and rename tool (#28453)
Having a separate rename tool seems to make the agent more likely to use
it compared to having it be part of the code actions tool.

Release Notes:

- Added code action tool and rename tool.
2025-04-09 22:38:01 -04:00
renovate[bot]
0e72a7e6ce Update Rust crate smallvec to v1.15.0 (#28469)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [smallvec](https://redirect.github.com/servo/rust-smallvec) |
workspace.dependencies | minor | `1.14.0` -> `1.15.0` |

---

### Release Notes

<details>
<summary>servo/rust-smallvec (smallvec)</summary>

###
[`v1.15.0`](https://redirect.github.com/servo/rust-smallvec/releases/tag/v1.15.0)

[Compare
Source](https://redirect.github.com/servo/rust-smallvec/compare/v1.14.0...v1.15.0)

#### What's Changed

- Fix typos by
[@&#8203;waywardmonkeys](https://redirect.github.com/waywardmonkeys) in
[https://github.com/servo/rust-smallvec/pull/373](https://redirect.github.com/servo/rust-smallvec/pull/373)
- Implement bincode2 encode/decode support for smallvec v1 by
[@&#8203;markbt](https://redirect.github.com/markbt) in
[https://github.com/servo/rust-smallvec/pull/375](https://redirect.github.com/servo/rust-smallvec/pull/375)

#### New Contributors

- [@&#8203;markbt](https://redirect.github.com/markbt) made their first
contribution in
[https://github.com/servo/rust-smallvec/pull/375](https://redirect.github.com/servo/rust-smallvec/pull/375)

**Full Changelog**:
https://github.com/servo/rust-smallvec/compare/v1.14.0...v1.15.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-09 17:37:32 -06:00
renovate[bot]
3dc3ab062d Update Rust crate prometheus to 0.14 (#28468)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [prometheus](https://redirect.github.com/tikv/rust-prometheus) |
dependencies | minor | `0.13` -> `0.14` |

---

### Release Notes

<details>
<summary>tikv/rust-prometheus (prometheus)</summary>

###
[`v0.14.0`](https://redirect.github.com/tikv/rust-prometheus/blob/HEAD/CHANGELOG.md#0140)

[Compare
Source](https://redirect.github.com/tikv/rust-prometheus/compare/v0.13.4...v0.14.0)

- API change: Use `AsRef<str>` for owned label values
([#&#8203;537](https://redirect.github.com/tikv/rust-prometheus/issues/537))

- Improvement: Hashing improvements
([#&#8203;532](https://redirect.github.com/tikv/rust-prometheus/issues/532))

- Dependency upgrade: Update `hyper` to 1.6
([#&#8203;524](https://redirect.github.com/tikv/rust-prometheus/issues/524))

- Dependency upgrade: Update `procfs` to 0.17
([#&#8203;543](https://redirect.github.com/tikv/rust-prometheus/issues/543))

- Dependency upgrade: Update `protobuf` to 3.7.2 for RUSTSEC-2024-0437
([#&#8203;541](https://redirect.github.com/tikv/rust-prometheus/issues/541))

- Dependency upgrade: Update `thiserror` to 2.0
([#&#8203;534](https://redirect.github.com/tikv/rust-prometheus/issues/534))

- Internal change: Fix LSP and Clippy warnings
([#&#8203;540](https://redirect.github.com/tikv/rust-prometheus/issues/540))

- Internal change: Bump MSRV to 1.81
([#&#8203;539](https://redirect.github.com/tikv/rust-prometheus/issues/539))

- Documentation: Fix `register_histogram_vec_with_registry` docstring
([#&#8203;528](https://redirect.github.com/tikv/rust-prometheus/issues/528))

- Documentation: Fix typos in static-metric docstrings
([#&#8203;479](https://redirect.github.com/tikv/rust-prometheus/issues/479))

- Documentation: Add missing `protobuf` feature to README list
([#&#8203;531](https://redirect.github.com/tikv/rust-prometheus/issues/531))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-09 17:37:15 -06:00
Michael Sloan
ed63f216e3 Remove "use_key_equivalents" from linux keymap as it does nothing (#28464)
Release Notes:

- N/A
2025-04-09 23:02:45 +00:00
Michael Sloan
ba767a1998 Fix directory context paths (#28459)
Release Notes:

- N/A
2025-04-09 21:40:46 +00:00
renovate[bot]
23c3f5f410 Update Rust crate indexmap to v2.9.0 (#28455)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [indexmap](https://redirect.github.com/indexmap-rs/indexmap) |
workspace.dependencies | minor | `2.8.0` -> `2.9.0` |

---

### Release Notes

<details>
<summary>indexmap-rs/indexmap (indexmap)</summary>

###
[`v2.9.0`](https://redirect.github.com/indexmap-rs/indexmap/blob/HEAD/RELEASES.md#290-2025-04-04)

[Compare
Source](https://redirect.github.com/indexmap-rs/indexmap/compare/2.8.0...2.9.0)

- Added a `get_disjoint_mut` method to `IndexMap`, matching Rust 1.86's
    `HashMap` method.
- Added a `get_disjoint_indices_mut` method to `IndexMap` and
`map::Slice`,
    matching Rust 1.86's `get_disjoint_mut` method on slices.
- Deprecated the `borsh` feature in favor of their own `indexmap`
feature,
    solving a cyclic dependency that occured via `borsh-derive`.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-09 14:52:40 -06:00
Vitaly Slobodin
b3be294c90 lsp_store: Preserve environment variables from ExtensionLspAdapter (#28173)
## Description

In https://github.com/zed-industries/zed/pull/27213 the new feature for
setting env variables for LSPs was added but env vars passed from an
instance of `ExtensionLspAdapter` are lost now. This means if an
extension returns any env variable like this:

```rust
zed::Command {
  command: some_command,
  args: some_args,
  env: vec![("A", "value_for_a")],
}
```

The env variable `A` will never be used by `LspStore`. This commit
preserves env variables passed from an instance of
`ExtensionLspAdapter`.

After this change overwriting of env variables
happens in the following order:

```plaintext
shell <- variables from an extension <- variables from settings
```

## How to reproduce

Allow any extension to return a `zed::Command` with environment
variables to Zed. You can use [this
branch](https://github.com/zed-extensions/ruby/pull/48) for the Ruby
extension:

1. Check out the branch and install the dev version of the Ruby
extension.
2. Ensure you have the `solargraph` LSP configured and enabled for the
Ruby extension. This LSP is enabled by default in Zed and in the Ruby
extension.
3. Make sure you don’t have `solargraph` installed in your user gemset.
4. Open any Ruby project, such as [this
one](https://github.com/vitallium/stimulus-lsp-error-zed).
5. Open a Ruby file and wait for the error message about failing to
start `solargraph`. It should look like this or something similar:

```
[2025-04-05T23:17:26+02:00 ERROR project::lsp_store] server stderr: "/Users/vslobodin/.local/share/mise/installs/ruby/3.4.1/lib/ruby/site_ruby/3.4.0/rubygems.rb:262:in 'Gem.find_spec_for_exe': can't find gem solargraph (>= 0.a) with executable solargraph (Gem::GemNotFoundException)\n\tfrom /Users/vslobodin/.local/share/mise/installs/ruby/3.4.1/lib/ruby/site_ruby/3.4.0/rubygems.rb:281:in 'Gem.activate_bin_path'\n"
```

This error occurs because the Ruby extension passes the `GEM_PATH`
environment variable to specify the location of Ruby gems. Without it,
Zed tries to spawn the `solargraph` gem in the user's gemset scope. Ruby
fails to start it because the `solargraph` gem is not installed in the
user gemset but in the extension directory. By setting the `GEM_PATH`
environment variable, Ruby searches additional locations to start the
`solargraph` LSP.

I hope I've described it correctly. Please let me know if you need more
information. Thanks!

Release Notes:

- Fixed the issue where environment variables from `ExtensionLspAdapter`
were lost
2025-04-09 14:50:50 -06:00
Dino
af5318df98 Update default vim substitute command behavior and add support for 'g' flag (#28138)
This Pull Request updates the default behavior of the substitute (`s`)
command in vim mode to only replace the next match by default, instead
of all, and replace all matches only when the `g` flag is provided,
making it more similar to NeoVim's behavior.

In order to achieve this, the following changes were introduced:

- Update `BufferSearchBar::replace_next` to be a public method, so it
can be called from `Vim::replace_command` .
- Update the `Replacement::parse` to set the `should_replace_all` field
to `false` by default, and only set it to `true` if the `'g'` flag is
present in the query.
- Add support for when the `Replacement.should_replace_all` is set to
`false` in `Vim::replace_command`, so as to have it only replace the
next occurrence instead of all occurrences in the line.
- Introduce `BufferSearchBar::select_first_match` so as to activate the
first match on the line under the cursor.

Closes #24450 

Release Notes:

- Improved vim's substitute command so as to only replace the first
match by default, and replace all matches if the `'g'` flag is provided

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-09 14:34:51 -06:00
5brian
60c420a2da docs: Update vim features (#28360)
Follow up:
https://github.com/zed-industries/zed/pull/28044#issuecomment-2786769520

Adds
- Indent wise motions
- :ls
- :set

Release Notes:

- vim: Added documentation for indent-wise motions, `:ls`, and `:set`
2025-04-09 16:30:50 -04:00
5brian
ee6c33ffb3 Fix vim test keystroke (#28406)
I wrote the test wrongly in
https://github.com/zed-industries/zed/pull/28005:

It should be `$` instead of `shift-4`, so it was just yanking from the
middle of the line instead of the newline character. Fixed it and
regenerated it.

Release Notes:

- N/A
2025-04-09 14:29:03 -06:00
tidely
9ae4f4b158 gpui: Use BoolExt trait in more places (#28052)
Use the `BoolExt` trait which converts rust booleans to their objc
equivalent when applicable.


Release Notes:

- N/A
2025-04-09 14:28:15 -06:00
renovate[bot]
915a1cb116 Update actions/dependency-review-action digest to 67d4f4b (#28450)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/dependency-review-action](https://redirect.github.com/actions/dependency-review-action)
| action | digest | `3b139cf` -> `67d4f4b` |

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-09 14:23:48 -06:00
renovate[bot]
aead0e11ff Update Rust crate mimalloc to v0.1.46 (#27964)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [mimalloc](https://redirect.github.com/purpleprotocol/mimalloc_rust) |
dependencies | patch | `0.1.45` -> `0.1.46` |

---

### Release Notes

<details>
<summary>purpleprotocol/mimalloc_rust (mimalloc)</summary>

###
[`v0.1.46`](https://redirect.github.com/purpleprotocol/mimalloc_rust/releases/tag/v0.1.46):
Version 0.1.46

[Compare
Source](https://redirect.github.com/purpleprotocol/mimalloc_rust/compare/v0.1.45...v0.1.46)

##### Changes

-   Fixed musl builds.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 3pm on Wednesday" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMjcuMyIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-09 14:23:19 -06:00
Anthony Eid
2752c08810 debugger: Add run to cursor and evaluate selected text actions (#28405)
## Summary

### Actions

This PR implements actions that allow a user to "run to cursor" and
"evaluate selected text" while there's an active debug session and
exposes the functionality to the UI as well.

- Run to cursor: Can be accessed by right clicking on the gutter
- Evaluate selected text: Can be accessed by selecting text then right
clicking in the editor

### Bug fixes

I also fixed these bugs as well

- Panic when using debugger: Stop action
- Debugger actions command palette filter not working properly in all
cases
- We stopped displaying the correct label in the session's context menu
when a session was terminated

Release Notes:

- N/A

---------

Co-authored-by: Max Brunsfeld <max@zed.dev>
Co-authored-by: Remco Smits <djsmits12@gmail.com>
2025-04-09 19:57:29 +00:00
Bennet Bo Fenner
780143298a agent: Fuzzy match on paths and symbols when typing @ (#28357)
Release Notes:

- agent: Improve fuzzy matching when using @-mentions
2025-04-09 19:00:23 +00:00
João Marcos
088d7c1342 Add sublime keybinding for git::Restore (#28444)
Release Notes:

- Sublime Keymap: Added `git::Restore` compatibility bind (revert_hunk).
Mac: `cmd-k cmd-z` and Linux: `ctrl-k ctrl-z`.
2025-04-09 14:57:15 -03:00
neunato
64de6bd2a8 Don't scroll the editor on select all matches (#28435)
Part of https://github.com/zed-industries/zed/issues/9309

Release Notes:

- Improved scroll behavior of `editor: select all matches`

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-04-09 17:50:14 +00:00
Finn Evers
6aa0248ab3 docs: Update outdated keybind for opening extensions page (#28443)
This PR updates an outdated keybind for opening the extensions page (the
shown keybind opens the project panel instead) on the `Configuring
Languages` page.

It also updates a nearby keybind to use the preprocessor syntax instead.

Release Notes:

- N/A
2025-04-09 13:46:12 -04:00
Thomas Mickley-Doyle
342134fbab agent: Add reactions at the response level (#27958)
Release Notes:

- Added the user reaction (👍 or 👎) to each agent response.
- 👎 will trigger a comment box linked to the response

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-04-09 14:21:07 -03:00
João Marcos
b47aa33459 Remove actions UnfoldAt and FoldAt (#28442)
`UnfoldAt` and `FoldAt` are used internally, and don't really work
when users try to trigger them, they do however appear in the command
palette and keybindings, misleading users to try using them.

Release Notes:

- Remove unused actions `UnfoldAt` and `FoldAt` (prefer `Fold` and
`Unfold`).
2025-04-09 17:13:41 +00:00
Michael Sloan
9f6c5e2877 Reapply "Use Project instead of Workspace in ContextStore (#28402)" (#28441)
Motivation for this change is to use `ContextStore` in headless
assistant, which requires it to not depend on UI entities like
`Workspace`.

This reapplies a change that was revert was in #28428, and fixes the panic.

Release Notes:

- N/A
2025-04-09 16:56:14 +00:00
Cole Miller
7bf6cd4ccf Fix ancestor git repositories going missing (#28436)
Closes #ISSUE

Release Notes:

- Fixed a bug that caused Zed to sometimes not discover git repositories
above a worktree root.
2025-04-09 12:44:29 -04:00
Peter Tripp
c7963c8a93 ci: Require workspace_hack for PR merge (#28431)
Release Notes:

- N/A
2025-04-09 16:43:38 +00:00
renovate[bot]
dd4629433b Update cachix/install-nix-action digest to d1ca217 (#27951)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[cachix/install-nix-action](https://redirect.github.com/cachix/install-nix-action)
| action | digest | `02a151a` -> `d1ca217` |

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMjcuMyIsInVwZGF0ZWRJblZlciI6IjM5LjIyNy4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-09 09:31:13 -07:00
Rodrigo Freire
2e56935997 Fix invalid number of space characters inserted for tab (#27336)
Closes #25941 

Release Notes:

- Corrected SoftTab indentation handling for lines with mixed spaces and
tabs across .go files and other file types.
- Renamed the editor test `test_tab_with_mixed_whitespace` to
`test_tab_with_mixed_whitespace_rust` as it only tested this behavior
for Rust buffers, which have auto-indentation support. This change
clarifies that the test does not cover default files without
language-specific features.
- Added a new editor test `test_tab_with_mixed_whitespace_txt` to ensure
proper coverage for files with no associated language.

While investigating the issue — initially thought to be Go-related — I
discovered that the underlying problem was how soft tabs were calculated
in `Editor::tab`, given that the problem could also be observed on
`.txt` files

The correct soft tab indentation is now determined by treating all `\t`
characters before the cursor (on the same row) as new indentation
levels, resetting the remainder counter accordingly.


https://github.com/user-attachments/assets/78192e98-2b81-43cb-ae6f-7c48cd17d168
2025-04-09 16:22:14 +00:00
Richard Feldman
e43a397f1d Make regex search tool optionally case-sensitive (#28427)
Release Notes:

- The agent panel's regex search tool is now optionally case-sensitive.
2025-04-09 16:21:21 +00:00
Richard Feldman
9d0fe164a7 Revert to fix panic in inline assistant (#28428)
This reverts commit f12a554f86, which
introduced a panic in inline assistant (cc @mgsloan) - I'm not sure what
the motivation was for that change, but I figure we can revert to fix
the inline assistant now and deal with that later. 😄

Panic was:

> Thread "main" panicked with "cannot read workspace::Workspace while it
is already being updated" at
/Users/rtfeldman/code/zed/crates/gpui/src/app/entity_map.rs:139:32


Release Notes:

- N/A
2025-04-09 11:24:53 -04:00
Kainoa Kanter
6d7fef6fd3 Add icon for Vyper files (#28307)
Release Notes:

- Added icon for Vyper (`.vy`, `.vyi`) files
2025-04-09 10:49:39 -04:00
5brian
b67d3fd21b git_ui: Show disabled states in context menu (#28288)
Other elements in the git panel are shown as disabled when an action is
not actionable (For example: stage all, commit). Updating the context
menu to match this behavior when an action does nothing.

|Before|After|
|--|--|

|![image](https://github.com/user-attachments/assets/e517f758-216f-4451-911b-7121dce0c53b)|![image](https://github.com/user-attachments/assets/a85905c1-2f42-44c3-8b11-2f93c8a6f686)|





Release Notes:

- Git: Improved the Git panel context menu to show actions with no
effect as disabled.
2025-04-09 10:46:21 -04:00
Agus Zubiaga
1cb4f8288d Fix bash tool output (#28391) 2025-04-09 08:20:24 -06:00
Richard Feldman
3a8fe4d973 Add reminder message about system prompt (#28344)
Trying out sending the model a reminder message about code blocks in the
system prompt. If this seems to work well, we can include more specific
reminder messages, e.g. tool-specific ones.

Release Notes:

- N/A
2025-04-09 10:09:48 -04:00
Joseph T. Lyons
9d6d152918 Bump Zed to v0.183 (#28419)
Release Notes:

-N/A
2025-04-09 09:11:25 -04:00
Joseph T. Lyons
31034f8296 Add toggle case command (#28415)
A small addition for those coming from JetBrain's IDEs. A behavioral
detail: when any upper case character is detected, the command defaults
to toggling to lower case.

> Note that when you apply the toggle case action to the CamelCase name
format, IntelliJ IDEA converts the name to the lower case.


https://www.jetbrains.com/help/idea/working-with-source-code.html#edit_code_fragments

Release Notes:

- Added an `editor: toggle case` command. Use `cmd-shift-u` for macOS
and `ctrl-shift-u` for Linux, when using the `JetBrains` keymap.
2025-04-09 08:44:53 -04:00
Piotr Osiewicz
c441b651fa debugger: Add support for CodeLLDB (#28376)
Closes #ISSUE

Release Notes:

- N/A
2025-04-09 12:57:24 +02:00
Piotr Osiewicz
61ddcd516f chore: Add workspace-hack dependency to agent_rules (#28412)
Closes #ISSUE

Release Notes:

- N/A
2025-04-09 10:19:54 +00:00
Michael Sloan
f12a554f86 Use Project instead of Workspace in ContextStore (#28402)
Release Notes:

- N/A
2025-04-09 05:05:24 +00:00
Cole Miller
9dae4d8c59 Remove references to SSH remoting beta (#28399)
Release Notes:

- N/A
2025-04-09 03:26:22 +00:00
Cole Miller
f0b7f355a2 Clean up environment loading a bit (#28356)
Closes #ISSUE

Release Notes:

- N/A
2025-04-08 22:16:35 -04:00
Cole Miller
b687a5e56d git: Always reload current branch after pushing (#28327)
Closes #27347 

Release Notes:

- Fixed a bug causing the git panel to not update after pushing to a
remote
2025-04-08 22:16:03 -04:00
Ben Kunkle
e66a24edcf format: Re-implement support for formatting with code actions that contain commands (#28392)
Closes #27692
Closes #27935

Release Notes:

- Fixed a regression where code-actions used when formatting on save
were rejected if they contained commands
2025-04-09 01:53:54 +00:00
Michael Sloan
301fc7cd7b Pull out plain rules file loading code into a new agent_rules crate (#28383)
Also renames for rules file templated into the system prompt

Release Notes:

- N/A
2025-04-09 01:31:56 +00:00
Mikayla Maki
020a1071d5 Add the project search as an item in the status bar (#28388)
Was chatting with @wilhelmklopp, he pointed out that our current
UI-accessible way to access the project search was pretty obscure.


<img width="393" alt="Screenshot 2025-04-08 at 6 57 51 PM"
src="https://github.com/user-attachments/assets/636053cd-5a88-4a5e-8155-6d41d189b7db"
/>

Release Notes:

- Added a button to open the project search to the status bar
2025-04-09 01:13:48 +00:00
Bennet Bo Fenner
38d2487630 agent: Polish Generating... animation (#28379)
https://github.com/user-attachments/assets/9e798a50-9403-4e1c-a3df-2931e748b77d



Release Notes:

- N/A
2025-04-08 18:14:30 -06:00
0x2CA
79c9f2bbd9 editor: Fix invalid read-only with split pane (#28012)
Closes #28004

Release Notes:

- Fixed invalid read-only with split pane
2025-04-08 18:09:34 -06:00
Danilo Leal
c8caae03df agent: Change the reject changes keybinding (#28381)
This PR makes the reject keybinding, in the Review Changes mutlbuffer,
`cmd-n`.

Release Notes:

- N/A
2025-04-08 21:09:05 -03:00
Danilo Leal
dabc4d8ff5 agent: Remove type of item in the panel history view (#28382)
This PR removes the labels displaying whether a certain item in the
Agent Panel's history is a thread or prompt editor.

Release Notes:

- N/A
2025-04-08 21:08:56 -03:00
Antonio Scandurra
c0ad3e8183 Introduce a telemetry event for when a tool finishes (#28380)
This should help us understand which tools fail the most.

Release Notes:

- N/A
2025-04-09 00:07:06 +00:00
Kirill Bulatov
afde25a5cb Fix a docs typo (#28384)
Closes https://github.com/zed-industries/zed/pull/28053

Release Notes:

- N/A
2025-04-09 00:05:58 +00:00
Michael Sloan
9f708ee789 Fix refactoring bug in dashes around rounded corners (#28378)
Accidentally introduced in #28341

Release Notes:

- N/A
2025-04-09 00:00:30 +00:00
Michael Sloan
58731e2fd1 Remove log when pulldown_cmark produces long substituted text (#28375)
Turns out that consecutive dashes are substituted with half the number
of input dashes. Extended the test with this case as well

Release Notes:

- N/A
2025-04-08 23:45:49 +00:00
Antonio Scandurra
d0632a5332 Fix truncation of bash output (#28374)
Release Notes:

- Fixed a regression that caused the bash tool to not include all of the
output.

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-04-08 23:41:20 +00:00
Danilo Leal
64cea2f1f1 agent: Refine toolbar spacing (#28373)
Release Notes:

- N/A
2025-04-08 23:28:25 +00:00
Antonio Scandurra
ac958d4a2d Encourage agent to edit files it just created (#28372)
Release Notes:

- Fixed a problem that would cause the agent to keep recreating a file
instead of editing it.
2025-04-08 23:18:34 +00:00
Danilo Leal
2df06cd2e4 agent: Improve thinking design display (#28186)
Release Notes:

- N/A
2025-04-08 20:13:49 -03:00
Danilo Leal
0d4ca71e68 agent: Change "prompt editor" to "text thread" (#28370)
Release Notes:

- N/A
2025-04-08 19:56:01 -03:00
Danilo Leal
e2d6505d12 agent: Make the copy button in the codeblock visible on hover (#28371)
This simplifies the UI a little bit.

Release Notes:

- N/A
2025-04-08 19:55:53 -03:00
Kirill Bulatov
f7c3c533a3 Update task defaults (#28368)
Follow-up of https://github.com/zed-industries/zed/pull/28359

Release Notes:

- N/A
2025-04-08 22:20:00 +00:00
Nate Butler
c05bf096f8 Merge Component and ComponentPreview trait (#28365)
- Merge `Component` and `ComponentPreview` trait
- Adds a number of component previews
- Removes a number of stories

Release Notes:

- N/A
2025-04-08 16:09:06 -06:00
João Marcos
b15ee1b1cc Add dedicated actions for LSP completions insertion mode (#28121)
Adds actions so you can have customized keybindings for `insert` and
`replace` modes.

And add `shift-enter` as a default for `replace`, this will override the
default setting
`completions.lsp_insert_mode` which is set to `replace_suffix`, which
tries to "smartly"
decide whether to replace or insert based on the surrounding text.

For those who come from VSCode, if you want to mimic their behavior, you
only have to
set `completions.lsp_insert_mode` to `insert`.

If you want `tab` and `enter` to do different things, you need to remap
them, here is
an example:

```jsonc
[
  // ...
  {
    "context": "Editor && showing_completions",
    "bindings": {
      "enter": "editor::ConfirmCompletionInsert",
      "tab": "editor::ConfirmCompletionReplace"
    }
  },
]
```

Closes #24577

- [x] Make LSP completion insertion mode decision in guest's machine
(host is currently deciding it and not allowing guests to have their own
setting for it)
- [x] Add shift-enter as a hotkey for `replace` by default.
- [x] Test actions.
- [x] Respect the setting being specified per language, instead of using
the "defaults".
- [x] Move `insert_range` of `Completion` to the Lsp variant of
`.source`.
- [x] Fix broken default, forgotten after
https://github.com/zed-industries/zed/pull/27453#pullrequestreview-2736906628,
should be `replace_suffix` and not `insert`.

Release Notes:

- LSP completions: added actions `ConfirmCompletionInsert` and
`ConfirmCompletionReplace` that control how completions are inserted,
these override `completions.lsp_insert_mode`, by default, `shift-enter`
triggers `ConfirmCompletionReplace` which replaces the whole word.
2025-04-08 22:03:03 +00:00
Cole Miller
0459b1d303 Fix panic when a file in a path-based multibuffer excerpt is renamed (#28364)
Closes #ISSUE

Release Notes:

- Fixed a panic that could occur when paths changed in the project diff.

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-08 22:01:40 +00:00
5brian
246013cfc2 tab_switcher: Add keybind to close tab tooltip (#27212)
| prev | new |
|--|--|
|<img width="619" alt="image"
src="https://github.com/user-attachments/assets/53b14fd4-17ee-4336-81ca-30324d918e15"
/>|<img width="620" alt="image"
src="https://github.com/user-attachments/assets/316699b3-295b-4f83-9fb1-b799f7c71d7f"
/>|


Release Notes:

- N/A
2025-04-08 15:57:36 -06:00
Bennet Bo Fenner
47eaf274d6 agent: Only require confirmation for batch tool when subset of tool calls require confirmation (#28363)
Release Notes:

- agent: Only require confirmation for batch tool when subset of tool
calls require confirmation
2025-04-08 21:37:10 +00:00
Peter Tripp
ef4b5b0698 script: Ignore feature/meta issues from issue_response nag (#28332)
Release Notes:

- N/A
2025-04-08 17:14:07 -04:00
Kirill Bulatov
39c98ce882 Support tasks from rust-analyzer (#28359)
(and any other LSP server in theory, if it exposes any LSP-ext endpoint
for the same)

Closes https://github.com/zed-industries/zed/issues/16160

* adds a way to disable tree-sitter tasks (the ones from the plugins,
enabled by default) with
```json5
"languages": {
  "Rust": "tasks": {
      "enabled": false
    }
  }
}
```
language settings

* adds a way to disable LSP tasks (the ones from the rust-analyzer
language server, enabled by default) with
```json5
"lsp": {
  "rust-analyzer": {
    "enable_lsp_tasks": false,
  }
}
```

* adds rust-analyzer tasks into tasks modal and gutter:

<img width="1728" alt="modal"
src="https://github.com/user-attachments/assets/22b9cee1-4ffb-4c9e-b1f1-d01e80e72508"
/>

<img width="396" alt="gutter"
src="https://github.com/user-attachments/assets/bd818079-e247-4332-bdb5-1b7cb1cce768"
/>


Release Notes:

- Added tasks from rust-analyzer
2025-04-08 15:07:56 -06:00
Joseph T. Lyons
763cc6dba3 Tell the model not to act on TODO type comments (#28358)
Release Notes:

- Adjusted system prompt to direct it to never act on TODO-type comments
it encounters, unless the user directly asked it to do so or they relate
to the current task at hand.
2025-04-08 21:00:02 +00:00
Piotr Osiewicz
0b75c13034 chore: Replace as_any functions with trait upcasting (#28221)
Closes #ISSUE

Release Notes:

- N/A
2025-04-08 22:16:27 +02:00
Ben Kunkle
38ec45008c project: Workaround invalid code action edits from pyright (#28354)
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Piotr Osiewicz <piotr@zed.dev>

fixes issue where:

In a two line python file like so
```

Path()
```

If the user asks for code actions on `Path` and they select (`From
pathlib import path`)
the result they get is
```

Pathfrom pathlib import Path


Path()
```
Instead of 

```

from pathlib import Path



Path()
```

This is due to a non-lsp-spec-compliant response from pyright below

```json
{"jsonrpc":"2.0","id":40,"result":[{"title":"from pathlib import Path","edit":{"changes":{"file:///Users/neb/Zed/example-project/pyright-project/main.py":[{"range":{"start":{"line":2,"character":0},"end":{"line":2,"character":4}},"newText":"Path"},{"range":{"start":{"line":2,"character":0},"end":{"line":2,"character":0}},"newText":"from pathlib import Path\n\n\n"}]}},"kind":"quickfix"}]}
```

Release Notes:

- Fixed an issue when using auto-import code actions provided by pyright
(or basedpyright) where the import would be jumbled with the scoped
import resulting in an invalid result

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-04-08 20:13:44 +00:00
Antonio Scandurra
97641c3298 Use tree-sitter when returning symbols to the model for a given file (#28352)
This also increases the threshold for when we return an outline during
`read_file`.

Release Notes:

- Fixed an issue that caused the agent to fail reading large files if
the LSP hadn't started yet.
2025-04-08 16:11:05 -04:00
Joseph T. Lyons
ca8f6e8a3f Tell the model not to remove tests (#28349)
Release Notes:

- Adjusted system prompt to direct it to never remove tests as a way to
have the test suite pass, unless the user directly asks for test
removal.
2025-04-08 19:26:43 +00:00
Piotr Osiewicz
db53da49e1 debugger: Respect initialize_args from user profiles (#28347)
Closes #ISSUE

Release Notes:

- N/A

Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com>
2025-04-08 21:15:05 +02:00
Peter Tripp
df94dcdea6 ci: Only run workspace_hack when tests run (#28346)
Skip `workspace_hack` for PRs that don't trigger tests (docs-only,
.github issue templates, etc).

Release Notes:

- N/A
2025-04-08 18:55:54 +00:00
Richard Feldman
1c85901440 Tell the model not to create .bak files (#28244)
Release Notes:

- Adjusted system prompt to avoid having the agent create backup files
unnecessarily.
2025-04-08 18:45:35 +00:00
Peter Tripp
9fb77ad176 Refine GitHub Issue templates (#28345)
Make various improvements to our github issue templates.

- Adjust line lengths to not wrap in constrained new issue view (85
cols) not just full screen view (95 columns)
- Remove reference to drag/drop logs to upload (recently multiple issues
with dead upload links)
- Cleanup list view

Release Notes:

- N/A
2025-04-08 14:41:55 -04:00
Michael Sloan
feafad2f9d Improve comments on shader code for dashed borders (#28341)
Improvements from going over the code with @as-cii 

Release Notes:

- N/A
2025-04-08 18:08:22 +00:00
Piotr Osiewicz
86ef00054b pylsp: Upgrade existing installation if possible (#28338)
Closes #ISSUE

Release Notes:

- Zed-managed pylsp installations will now correctly upgrade themselves
2025-04-08 20:01:09 +02:00
Conrad Irwin
9e8afa8daa Fix local task dropped on the wrong thread (#28290)
Release Notes:

- Fixed a panic during shutdown of the remote server
2025-04-08 11:54:51 -06:00
Danilo Leal
698cdc4d1a agent: Display "generating" label in the active thread (#28297)
Release Notes:

- N/A

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-04-08 14:31:32 -03:00
Agus Zubiaga
85c5d8af3a agent: Truncate bash tool output (#28291)
The bash tool will now truncate its output to 8192 bytes (or the last
newline before that).

We also added a global limit for any tool that produces a clearly large
output that wouldn't fit the context window.

Release Notes:

- agent: Truncate bash tool output

---------

Co-authored-by: Michael Sloan <mgsloan@gmail.com>
2025-04-08 16:55:35 +00:00
Anthony Eid
1774cad933 debugger: Add close button and coloring to debug panel session's menu (#28310)
This PR adds colors to debug panel's session menu that indicate the
state of each respective session. It also adds a close button to each
entry.

green - running
yellow - stopped
red - terminated/ended 


Release Notes:

- N/A
2025-04-08 16:35:33 +00:00
Conrad Irwin
ee7b1ec7f2 Fix deafening new participants (#28330)
Release Notes:

- Fixed an issue where new participants were not muted when the room was
deafened
2025-04-08 16:01:27 +00:00
Bennet Bo Fenner
14b43d573c agent: Navigate to line when clicking on filepath in markdown codeblock (#28329)
Release Notes:

- N/A
2025-04-08 15:41:34 +00:00
Max Brunsfeld
d39e1e03b8 Demote buffer-diff to a dev dependency of collab (#28295)
This will speed up our collab deployments a little

Release Notes:

- N/A
2025-04-08 08:36:44 -07:00
Peter Tripp
9e504a1ed9 git: Fix logging FromUtf8Error when diffing (#28276)
If you attempt to load a git diff which includes a non utf-8 file,
previously
(1) the entire contents of the file was logged as ordinals and
(2) a second spurious error was logged

```
2025-04-07T16:21:28.392845-04:00 [ERROR] FromUtf8Error { bytes: [0, 1, 0, 0, 0, 19, 1, 0, 0, 4, 0, 48, 68, 83, 73, 71, 0, 0, 0, 1, 0, 2, 241, 204, 0, 0, 0, 8, 71, 68, 69, 70, 164, 172, 164, ...

[2025-04-07T17:12:16-04:00 ERROR git::repository] Error loading index text: invalid utf-8 sequence of 1 bytes from index 35
```

Having 1MB binary file in a commit would generate ~3MB-5MB of log
output.

Discovered while investigating
https://github.com/zed-industries/zed/issues/28241

Release Notes:

- git: Fixed an issue where non-UTF8 files in a git diff would generate
log spam.
2025-04-08 11:28:34 -04:00
Conrad Irwin
ca4cc4764b Upgrade async-tungstenite to tokio (#26193)
We're seeing panics caused by a buggy implementation of AsyncWrite
that is being passed to rustls: 

https://github.com/rustls/rustls/issues/2316#issuecomment-2662838186

One hypothesis was that we're using (comparatively) non-standard async
tools for connecting over websockets; so this attempts to make us be
(comparitvely) more standard.

Release Notes:

- N/A
2025-04-08 09:17:08 -06:00
5brian
ea33d78ae4 vim: Fix visual line yank on newline char (#28005)
Problem:
When yanking in visual line on the newline char, the next line gets
yanked as well:


https://github.com/user-attachments/assets/40f332dd-19f5-445f-a30f-39d50167c46f

Changes:
Similar to visual delete, exclude the newline char from the selection in
line mode.

Release Notes:

- vim: Fixed visual line yank while on the newline character yanking
following line
2025-04-08 09:15:34 -06:00
5brian
e36a2f2739 vim: Add indent-wise motions (#28044)
Taken from:
https://github.com/jeetsukumaran/vim-indentwise?tab=readme-ov-file#movements-by-relative-indent-depth



> [- : Move to previous line of lesser indent than the current line.
> [+ : Move to previous line of greater indent than the current line.
> [= : Move to previous line of same indent as the current line that is
separated from the current line by lines of different indents.
> ]- : Move to next line of lesser indent than the current line.
> ]+ : Move to next line of greater indent than the current line.
> ]= : Move to next line of same indent as the current line that is
separated from the current line by lines of different indents.



Release Notes:

- vim: Added indent-wise motions `] -/+/=`
2025-04-08 09:07:37 -06:00
jneem
cbd9b4cc39 Switch back to the default mode after paste (#28304)
Now that the flaky tests are disabled, this should work...

Release Notes:

- Fixed vim paste action to switch back to the configured default mode.
2025-04-08 09:03:55 -06:00
James Falade
64f8b1e739 docs: Add missing comma in the Tasks page (#28324)
Added a missing command in the same tasks.json file

Release Notes:

- N/A
2025-04-08 15:03:42 +00:00
5brian
4f936d8100 vim: Fix exchange showing ccx in pending keys (#28303)
|Before|After|
|--|--|

|![image](https://github.com/user-attachments/assets/cac97981-518e-46d2-8540-a22496bc948e)|![image](https://github.com/user-attachments/assets/5158b17f-d14e-42a2-8a94-ad98d1b1c33c)|

Changes:
Add vim exchange to the clear stack

Release Notes:

- N/A
2025-04-08 09:03:24 -06:00
Bennet Bo Fenner
97abf21a28 agent: Add support for Google Gemini 2.5 preview (#28326)
Adds support for the new `gemini-2.5-pro-preview-03-25`

Release Notes:

- Added support for `gemini-2.5-pro-preview-03-25` in the assistant
2025-04-08 15:00:23 +00:00
Piotr Osiewicz
5fb1411e4d debugger: Add scrollbars, fix papercuts with completions menu and jumps (#28321)
Closes #ISSUE

Release Notes:

- N/A
2025-04-08 16:55:18 +02:00
0x2CA
b04f7a4c7c vim: Fix visual object expands (#28301)
Closes #28198

Release Notes:

- Fixed visual object expands
2025-04-08 08:51:41 -06:00
Marshall Bowers
61b7a05792 language_models: Allow overriding Zed completions URL via environment variable (#28323)
This PR adds support for overriding the Zed completions URL via the
`ZED_COMPLETIONS_URL` environment variable.

Release Notes:

- N/A
2025-04-08 14:46:15 +00:00
Peter Tripp
cc15598e09 keymap: Document conflicting macos ctrl-shift-space shortcut (#28325)
Closes https://github.com/zed-industries/zed/issues/26261

Release Notes:

- N/A
2025-04-08 14:46:03 +00:00
Smit Barmase
7ee9109ade project_panel: Do not allow creating empty file/dir or file/dir with only whitespaces (#28240)
- Do not allow creating empty file or empty directory.
- Do not allow creating file or directory with just whitespace.
- Show error only in case whitespace.

<img width="352" alt="image"
src="https://github.com/user-attachments/assets/f6040332-59a6-4d09-bf07-2b4b1b8b9e03"
/>

Release Notes:

- N/A
2025-04-08 18:00:01 +05:30
Piotr Osiewicz
c21fdd212b python: Bump PET version (#28319)
Speculative fix for #27518


Release Notes:

- N/A
2025-04-08 11:04:57 +00:00
tidely
a28929592e gpui: Depend on workspace image crate (#28313)
Make gpui depend on the image crate on the workspace level

Release Notes:

- N/A
2025-04-08 12:21:24 +02:00
Smit Barmase
3b787e85a4 Fix scrolling too fast on selection for editor and terminal (#28309) 2025-04-08 12:16:18 +05:30
Kirill Bulatov
1264e7a200 Properly store editor restoration data (#28296)
We cannot compare versions and anchors between different `Buffer`s with
different `BufferId`s.

Release Notes:

- Fixed Zed panicking on editor reopen

Co-authored-by: Conrad Irwin <conrad@zed.dev>
2025-04-08 01:25:43 +00:00
Richard Feldman
bfe08e449f Tell the system prompt not to write incomplete code (#28245)
Sometimes agents do this. I've had some success responding by telling it
not to do this, so trying out having it in the system prompt.

Release Notes:

- Adjusted the system prompt to avoid incomplete code generation.
2025-04-07 20:59:52 -04:00
Piotr Osiewicz
df3c7a73b5 debugger: Pick best candidate binary for debugging cargo-located tasks (#28289)
Closes #ISSUE

Release Notes:

- N/A
2025-04-08 02:30:21 +02:00
Max Brunsfeld
0dc3dffe38 Use insert_id as partition key for crash events (#28293)
Release Notes:

- N/A
2025-04-07 17:24:31 -07:00
Bennet Bo Fenner
b306a0221b agent: Add headers for code blocks (#28253)
<img width="639" alt="image"
src="https://github.com/user-attachments/assets/1fd51387-cbdc-474d-b1a3-3d0201f3735a"
/>


Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
2025-04-07 23:56:24 +00:00
Max Brunsfeld
d385a60ed1 Fix phrasing of crash/panic event names 2025-04-07 16:31:29 -07:00
Danilo Leal
b27922129c agent: Refine individual file item design in the edit disclosure (#28283)
<img
src="https://github.com/user-attachments/assets/f1ad0598-d864-407f-8b81-6ca29e2ffae3"
width="650"/>

Release Notes:

- N/A
2025-04-07 19:58:49 -03:00
Max Brunsfeld
6220b86f94 Write panics and crashes to snowflake (#28284)
This will let us create a better crashes dashboard, using Hex.

Release Notes:

- N/A
2025-04-07 15:50:16 -07:00
Conrad Irwin
448db20eaa Fix bad unicode calculations in do_completion (#28259)
Co-authored-by: João Marcos <marcospb19@hotmail.com>

Release Notes:

- Fixed a panic with completions around non-ASCII code

---------

Co-authored-by: João Marcos <marcospb19@hotmail.com>
2025-04-07 22:45:29 +00:00
renovate[bot]
e4a6943c76 Update Rust crate tokio to v1.44.2 [SECURITY] (#28277)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [tokio](https://tokio.rs)
([source](https://redirect.github.com/tokio-rs/tokio)) | dependencies |
patch | `1.44.1` -> `1.44.2` |
| [tokio](https://tokio.rs)
([source](https://redirect.github.com/tokio-rs/tokio)) |
workspace.dependencies | patch | `1.44.1` -> `1.44.2` |

### GitHub Vulnerability Alerts

####
[GHSA-rr8g-9fpq-6wmg](https://redirect.github.com/tokio-rs/tokio/pull/7232)

The broadcast channel internally calls `clone` on the stored value when
receiving it, and only requires `T:Send`. This means that using the
broadcast channel with values that are `Send` but not `Sync` can trigger
unsoundness if the `clone` implementation makes use of the value being
`!Sync`.

Thank you to Austin Bonander for finding and reporting this issue.

---

### Release Notes

<details>
<summary>tokio-rs/tokio (tokio)</summary>

###
[`v1.44.2`](https://redirect.github.com/tokio-rs/tokio/releases/tag/tokio-1.44.2):
Tokio v1.44.2

[Compare
Source](https://redirect.github.com/tokio-rs/tokio/compare/tokio-1.44.1...tokio-1.44.2)

This release fixes a soundness issue in the broadcast channel. The
channel
accepts values that are `Send` but `!Sync`. Previously, the channel
called
`clone()` on these values without synchronizing. This release fixes the
channel
by synchronizing calls to `.clone()` (Thanks Austin Bonander for finding
and
reporting the issue).

##### Fixed

- sync: synchronize `clone()` call in broadcast channel ([#&#8203;7232])

[#&#8203;7232]: https://redirect.github.com/tokio-rs/tokio/pull/7232

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "" in timezone America/New_York,
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMjcuMyIsInVwZGF0ZWRJblZlciI6IjM5LjIyNy4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 18:38:32 -04:00
Conrad Irwin
f03efeda73 Try to identify dSYMs by UUID not channel (#28268)
This should make it possible to more reliably symbolicate crash reports
from nightly, and from users with pending auto-updates.


Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com>
2025-04-07 16:08:38 -06:00
Anthony Eid
862d0c07ca debugger: Fix gdb adapter and logger (#28280)
There were two bugs that caused the gdb adapter not to work properly,
one on our end and one their end.

The bug on our end was sending `stopOnEntry: null` in our launch request
when stop on entry had no value. I fixed that bug across all dap
adapters

The other bug had to do with python's "great" type system and how we
serialized our unit structs to json; mainly,
`ConfigurationDoneArguments` and `ThreadsArguments`. Gdb seems to follow
a pattern for handling requests where they pass `**args` to a function,
this errors out when the equivalent json is `"arguments": null`.

```py
@capability("supportsConfigurationDoneRequest")
@request("configurationDone", on_dap_thread=True)
def config_done(**args): ### BUG!!
    ...
```

Release Notes:

- N/A
2025-04-07 22:02:13 +00:00
Danilo Leal
56ed5dcc89 agent: Add the history button back in the toolbar and make it a toggle (#28275)
Release Notes:

- agent: The history view is now more easily accessible via the icon
button in the Agent Panel toolbar.

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-04-07 18:58:49 -03:00
Conrad Irwin
b3f47dc5e0 Temporarily disable helix tests (#28279)
Not sure why, but recent changes to helix have made these flakey.

We can re-enable when we understand.

Release Notes:

- N/A
2025-04-07 21:50:35 +00:00
Marshall Bowers
fe1ae1860e agent: Copy text as Markdown (#28272)
Release Notes:

- agent: Copying text in the Agent Panel will now copy it as Markdown.

Co-authored-by: Antonio Scandurra <me@as-cii.com>
2025-04-07 17:42:11 -04:00
Bennet Bo Fenner
c165729b3f agent: Add a way to go back to thread from settings/history (#28273)
Release Notes:

- N/A
2025-04-07 21:25:40 +00:00
Piotr Osiewicz
22b937f27f Debugger UI: Dynamic session contents (#28033)
Closes #ISSUE

Release Notes:

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

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Anthony <anthony@zed.dev>
2025-04-07 23:22:09 +02:00
Danilo Leal
fdaf2a27bf agent: Adjust the thread generation design (#28193)
This PR simplifies the button to send a new message as well as the
"generation" display design.

Release Notes:

- N/A

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-04-07 18:09:38 -03:00
Marshall Bowers
0414908c4a markdown: Move open_url to the MarkdownElement as on_url_click (#28269)
Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
2025-04-07 20:43:00 +00:00
5brian
d3abc61728 breadcrumbs: Update multibuffer to match singleton (#28267)
Before:


https://github.com/user-attachments/assets/a2c8fe84-14f6-4cda-b51a-5ada3e2523b6

After:


https://github.com/user-attachments/assets/559bcfe8-a40f-44cc-a626-b0544b6cea68



Release Notes:

- N/A
2025-04-07 20:26:55 +00:00
Hourann
e7a0f0e876 terminal: Fix misaligned mouse selection when inline assist is active (#26112)
This PR fixes an issue where mouse selection in the terminal would be
offset when the Terminal Inline Assistant was active. The problem was
caused by incorrect coordinate translation when handling mouse events
with an active inline assistant.

The fix adjusts mouse event coordinates by properly accounting for the
terminal view's `scroll_top` value when the inline assistant is present,
ensuring that text selection precisely follows the mouse cursor
position.

Closes #26111 

Release Notes:

- Fixed text selection misalignment in terminal when the inline
assistant is active

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-04-07 20:10:14 +00:00
Thorben Kröger
5996c58452 node_runtime: Update to Node 20 (#27912)
Require a newer Node version to make Copilot work

Closes #27908

Release Notes:

- Breaking Change: If using system node Zed now requires Node >= v20.
Previously Node >= v18 was required. (Node v18 EOL date is 2025-04-30;
Node v19 EOL since 2023-06-01). Note: This does not change the Zed
bundled Node runtime version (still v23).
2025-04-07 15:47:04 -04:00
Marshall Bowers
b6ee367ee0 markdown: Don't retain MarkdownStyle in favor of using MarkdownElement directly (#28255)
This PR removes the retained `MarkdownStyle` on the `Markdown` entity in
favor of using the `MarkdownElement` directly and passing the
`MarkdownStyle` to it.

This makes it so switching themes will be reflected live in the code
block styles.

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-04-07 19:03:24 +00:00
tidely
aa026156f2 chore: Make objc a workspace level crate (#28258)
Make objc a workspace level crate to unify version control

Release Notes:

- N/A
2025-04-07 18:46:09 +00:00
Cole Miller
d5cc576b0c Downgrade some logs (#28257)
Closes #ISSUE

Release Notes:

- N/A
2025-04-07 18:41:58 +00:00
Thomas Mickley-Doyle
f3274851d9 Move assistant_evals to agent_evals and remove Judge logic (#28233)
Release Notes:

- N/A
2025-04-07 13:28:06 -05:00
Dallin Huff
500d8f2943 theme: Make Gruvbox terminal ANSI magenta more vibrant (#27166)
Closes #27119

Release Notes:

- Improved contrast of terminal ANSI colors in Gruvbox theme(s)
2025-04-07 18:25:24 +00:00
Julia Ryan
e3830d2ef5 Git activity indicator (#28204)
Closes #26182

Release Notes:

- Added an activity indicator for long-running git commands.

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-04-07 18:10:01 +00:00
Danilo Leal
4f9f443452 agent: Remove duplicated keybinding for creating new thread in Linux (#28254)
Release Notes:

- N/A
2025-04-07 18:09:01 +00:00
Joseph T. Lyons
1556b446e7 Fix titles in issue templates (#28252)
Release Notes:

- N/A
2025-04-07 13:48:31 -04:00
Joseph T. Lyons
5ca8a3e342 Add issue templates for newer flagship features (#28250)
Release Notes:

- N/A
2025-04-07 13:47:12 -04:00
Richard Feldman
aeea3645ff Fix typo in system prompt (#28246)
Release Notes:

- N/A
2025-04-07 17:29:56 +00:00
Conrad Irwin
a577a72f69 Add support for insert_text_mode of a completion (#28171)
I wanted this for CONL (https://conl.dev )'s nascent langauge server,
and it seems like most of the support was already wired up on the LSP
side, so this surfaces it into the editor.

Release Notes:

- Added support for the `insert_text_mode` field of completions from the
language server protocol.
2025-04-07 10:35:11 -06:00
Neo Nie
5a7222edc5 prompt_store: Remove additional code for /project (#27981)
Found leftover from https://github.com/zed-industries/zed/pull/27660

Release Notes:

- N/A
2025-04-07 12:11:14 -04:00
Danilo Leal
097aefeac4 agent: Display keybinding to delete Prompt Editor item (#28168)
This PR makes the keybinding to remove Prompt Editor items visible in
the icon button tooltip.

Release Notes:

- N/A
2025-04-07 13:10:48 -03:00
Smit Barmase
99a9647b78 editor: Fix excerpt down scroll behavior to only scroll when there are enough lines (#28231)
Follow up for https://github.com/zed-industries/zed/pull/27058

Improves excerpt down button to only scroll when there exists lines more
than equal to `expand_excerpt_lines`. This prevents weird shift at end
of the file.

Before:


https://github.com/user-attachments/assets/244a3bd6-d813-4cc8-9dcb-3addba2b652f

After:


https://github.com/user-attachments/assets/a9a9ba62-a454-4b56-9c8a-d8e6931b270b


Release Notes:

- N/A
2025-04-07 21:15:30 +05:30
Richard Feldman
fa90b3a986 Link to cited code blocks (#28217)
<img width="612" alt="Screenshot 2025-04-06 at 9 59 41 PM"
src="https://github.com/user-attachments/assets/3a996b4a-ef5c-4ca6-bd16-3b180b364a3a"
/>

Release Notes:

- Agent panel now shows links to relevant source code files above code
blocks.
2025-04-07 12:01:34 -03:00
Joseph T. Lyons
8049fc1038 Add ai label to agent beta issue template (#28227)
Release Notes:

- N/A
2025-04-07 14:19:09 +00:00
Joseph T. Lyons
85b811a783 Add Agent Panel bug report template (#28226)
Release Notes:

- N/A
2025-04-07 10:10:15 -04:00
张小白
d60dbbc791 windows: Add update-workspace-hack.ps1 script (#28219)
Release Notes:

- N/A
2025-04-07 21:26:26 +08:00
Julia Ryan
656302ee4c Stop centering when selecting larger syntax nodes (#28172)
With #27295, the cursor would center upon running
`SelectLargerSyntaxNode`. This was done to provide more context when
making large selections, but when making small selections (such as a
single parameter in an argument list) it was confusing that the scroll
position jumped.

This change makes that behavior slightly more conservative: now when the
selection is small enough to fit on the screen scrolling will only occur
to keep the cursor position on the screen (including respecting
`vertical_scroll_margin`).

Release Notes:

- N/A

Co-authored-by: João Marcos <marcospb19@hotmail.com>
2025-04-07 06:06:15 -07:00
Smit Barmase
956f359045 project_panel: Add warning error for leading or trailing whitespace when creating file or directory (#28215)
- Show yellow warning (instead or error) for leading/trailing
whitespace.
- Do not block user from creating it.
- If you rename existing file/dir which contains leading/trailing
whitespace, it will show error right away.

<img width="250" alt="image"
src="https://github.com/user-attachments/assets/562895ee-3a86-4ecd-bb38-703d1d8b8599"
/>

Release Notes:

- Added warning for leading or trailing whitespace while renaming or
creating new file or directory in Project Panel.
2025-04-07 17:47:54 +05:30
Smit Barmase
3b46fca64c project_panel: Fix validation error style alignment (#28214)
Use px over rem for positioning as rem is dependent on font
size.

Release Notes:

- N/A
2025-04-07 17:03:21 +05:30
Smit Barmase
d6d9c383cb project_panel: Show error when file or directory already exists while renaming or creating new one (#28177)
Closes #14425

<img width="289" alt="image"
src="https://github.com/user-attachments/assets/2994c401-23e3-419a-90fc-1a83959fdf21"
/>

Release Notes:

- Improved the project panel to show an error when a file or directory
already exists while renaming or creating a new one.
2025-04-07 08:14:22 +05:30
Michael Sloan
8cfb9beb17 Reapply support for X11 screenshare (#28160)
Reapplies #27807 after [revert due to not building on
ARM](https://github.com/zed-industries/zed/pull/28141) by updating scap
to include [a fix to its build on
ARM](08f0a01417)

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-04-06 11:25:29 -06:00
chbk
0708d476ca Improve Bash heredoc highlighting (#28185)
Release Notes:

  - Improved Bash heredoc highlighting

| Zed 0.180.2 | With this PR |
| --- | --- |
|
![Image](https://github.com/user-attachments/assets/aa2534af-53df-4f01-988e-f18ec52a2b62)
|
![Image](https://github.com/user-attachments/assets/8fc92113-41f2-4249-ab81-6beb0a1469ca)
|

```bash
cat << EOT >> hello.txt
hello world
EOT
```

- `<<`: `operator`
- `EOT`: `string`
2025-04-06 11:14:05 -04:00
Agus Zubiaga
57669b4908 agent: Refresh UI when context or thread history changes (#28188)
I found a few more cases where the UI wasn't updated immediately after
an interaction.

Release Notes:

- agent: Fixed delay after removing threads from "Past Interactions"
- agent: Fixed delay after adding/remove context via keyboard
2025-04-06 09:35:15 -05:00
Agus Zubiaga
b1f7133a7b agent: Refresh UI when sending first message (#28180)
Release Notes:

- Agent Beta: Fixed a delay when sending the first message in a new
thread
2025-04-06 08:36:10 -05:00
Richard Feldman
ac9e2f30bb Try to improve behavior when agent is stuck (#28169)
Currently, it's pretty common that when the agent gets stuck, it deletes
whatever it's stuck on and replaces it with a TODO comment, then
cheerfully reports that it has "simpified" the implementation. This is
worse than leaving the broken code, because at least a human could take
over and try to get it across the finish line.

This system prompt adjustment attempts to make the agent do something
more useful when in this situation: report that it's stuck, explain why
it's stuck, and ask the user what to do.

Release Notes:

- N/A
2025-04-05 23:52:28 -04:00
Richard Feldman
a2fbe82c42 If file is too big, provide the outline and suggest a follow-up tool (#28158)
<img width="622" alt="Screenshot 2025-04-05 at 5 48 14 PM"
src="https://github.com/user-attachments/assets/24b9c7d4-d3e2-4929-bca8-79db5b4e5748"
/>

Release Notes:

- The `read_files` tool now reads only the symbol outline files above a
certain size, to conserve context window space. Then it suggests that
the agent call `read_files` again with the relevant line ranges it saw
in the outline.
2025-04-05 18:52:52 -04:00
Richard Hao
57d8c99473 copilot: Create Copilot directory if it does not exist (#28157)
Closes https://github.com/zed-industries/zed/issues/27966

Issue:

- Copilot is failing to launch because the copilot directory is missing

<img width="497" alt="image"
src="https://github.com/user-attachments/assets/af35eb66-7e91-4dc6-a862-d1575da33b5b"
/>


<img width="943" alt="image"
src="https://github.com/user-attachments/assets/0b195c8c-52eb-42b9-bf36-40086398cc3f"
/>


Release Notes:

- copilot: Fixed an issue where GitHub Copilot would not install
properly if the directory was not present.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-04-05 16:06:14 -04:00
Marshall Bowers
41827372fe Revert "Add a next_mode to vim::Paste instead of hard-coding Normal mode (#27897) (#28162)
This PR reverts #27897, as it is causing a number of Helix-related tests
to fail:

```
     Summary [  84.324s] 1796 tests run: 1793 passed, 3 failed, 0 skipped
        FAIL [   0.434s] vim helix::test::test_delete
        FAIL [   0.562s] vim helix::test::test_delete_character_end_of_buffer
        FAIL [   0.537s] vim helix::test::test_delete_character_end_of_line
```

This reverts commit 9949512b64.

Release Notes:

- Community: Reverted https://github.com/zed-industries/zed/pull/27897.
2025-04-05 19:52:56 +00:00
jneem
9949512b64 Add a next_mode to vim::Paste instead of hard-coding Normal mode (#27897)
This adds a `next_mode` parameter to the `vim::Paste` action. My main
use-case for this is for helix users, who will want to switch into
`HelixNormal` mode instead of `Normal` mode.

I'm not sure if this is the best approach -- another possibility would
be to have a global vim-vs-helix configuration, and then have every
invocation of "normal" mode choose vim or helix based on that global
configuration. But the approach in this PR is much less invasive.

Release Notes:

- vim: switch to the configured default mode after paste instead of
hard-coding Normal mode
2025-04-05 12:55:23 -06:00
Kamil Jakubus
b9051e65d4 extension: Bump wasi-sdk to version 25 (#27906)
This bumps `wasi-sdk` to version 25 and adds target architecture
conditionals.

Closes #18492

Release Notes:

- Fixed compiling dev extensions with Tree-sitter grammars on Linux
aarch64.
2025-04-05 13:20:36 -04:00
Marshall Bowers
adbebb28dc assistant: Fix assistant: open prompt library not opening the prompt library (#28156)
This PR fixes the `assistant: open prompt library` action in the command
palette not opening the prompt library when the Assistant Panel did not
have focus.

Fixes https://github.com/zed-industries/zed/issues/28058.

Release Notes:

- assistant: Fixed `assistant: open prompt library` not opening the
prompt library when the Assistant Panel was not focused.
2025-04-05 17:10:39 +00:00
Marshall Bowers
caf0d6c5fa agent: Fix opening configuration view from the model selector (#28154)
This PR fixes an issue where opening the configuration view from the
model selector in the Agent (or inline assist) was not working properly.

Fixes https://github.com/zed-industries/zed/issues/28078.

Release Notes:

- Agent Beta: Fixed an issue where selecting "Configure" in the model
selector would not bring up the configuration view.
2025-04-05 16:32:16 +00:00
Shardul Vaidya
525755c28e bedrock: Add support for tool use, cross-region inference, and Claude 3.7 Thinking (#28137)
Closes #27223
Merges: #27996, #26734, #27949 

Release Notes:

- AWS Bedrock: Added advanced authentication strategies with:
  - Short lived credentials with Session Tokens 
  - AWS Named Profile
  - EC2 Identity, Pod Identity, Web Identity
- AWS Bedrock: Added Claude 3.7 Thinking support.
- AWS Bedrock: Adding Cross Region Inference for all combinations of
regions and model availability.
- Agent Beta: Added support for AWS Bedrock.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-04-05 11:16:26 -04:00
Finn Evers
ea0f5144c9 title_bar: Ensure git onboarding banner dismissal is properly respected (#28147)
A user reported this issue [on
Discord](https://discord.com/channels/869392257814519848/873292398204170290/1357879959422636185).

The issue here only arises for users which recently installed Zed or had
previously not dismissed the Git Onboarding component. It was introduced
by https://github.com/zed-industries/zed/pull/27412, which made the
banner component reusable.

For every banner, there is a value stored in the KVP store when it was
first dismissed. For the git onboarding banner, this was
`zed_git_banner_dismissed_at` initially, but this key would have been
changed by the linked PR. A change would have resulted in the banner
being shown again for users who already dismissed the panel, so for the
special case of `Git Onboarding`, a check was added which ensured this
would not happen.

However, this check was only added for reading from the key from the DB
but not on writing the git onboarding dismissal it to the DB. Thus, if a
user who had not previously dismissed the panel opened Zed, we would
check for the old key to be present in the DB. Since that would not be
the case, the banner would be shown. If the user dismissed the panel, it
would be stored in the database with the new key. Thus, on a reopen of
Zed, the banner would again be shown since for the old key there would
still be no value present and users are unable to dismiss the panel.


This PR fixes this behavior by moving the check into the method that
generates the key. With this, users which were unaffected by the bug
will still not see the panel again. Users who would install Zed with
this change present will be able to properly dismiss the panel aswell.
Users which were affected by the bug need to dismiss the banner one more
time. That happens because I did not want to modify the dismissal check
to check for two keys (the original one and the new one), as it would
clutter the logic even more for this special case. If this would be
preferred, feel free to let me know.

Release Notes:

- Fixed an issue where dismissing the git onboarding banner would not be
persisted across sessions.
2025-04-05 14:33:46 +00:00
Agus Zubiaga
b78ac5410f agent: Fix tool use output rendering (#28146)
Tool use output wouldn't get rendered in some states.

Release Notes:

- N/A
2025-04-05 13:16:24 +00:00
Agus Zubiaga
2462b949bc agent: Add missing notify in ThreadHistory::delete_thread (#28144)
This would cause the history view not to get refreshed immediately when
a thread was deleted

Release Notes:

- agent: Fixed a bug where the history view wouldn't refresh after
deleting a thread
2025-04-05 12:48:10 +00:00
Agus Zubiaga
ec7d28648a agent: Fix thread summary generation (#28143)
#28102 introduced a bug where thread summaries wouldn't get generated
because they would get set to the default title instead of `None`.

Not adding a release note because the bug didn't make it to Preview.

Release Notes:

- N/A
2025-04-05 09:34:23 -03:00
Michael Sloan
c1259c136e Revert "Use scap library to implement screensharing on X11 (#27807)" (#28141)
This reverts commit c2afc2271b.

Build on ARM if failing, likely because `c_char` is `u8` on arm and `i8`
on x86:

```
error[E0308]: mismatched types
   --> /home/runner/.cargo/git/checkouts/scap-40ad33e1dd47aaea/5715067/src/targets/linux/mod.rs:75:74
    |
75  |     let result = unsafe { XmbTextPropertyToTextList(display, &mut xname, &mut list, &mut count) };
    |                           -------------------------                      ^^^^^^^^^ expected `*mut *mut *mut u8`, found `&mut *mut *mut i8`
    |                           |
    |                           arguments to this function are incorrect
    |
    = note:    expected raw pointer `*mut *mut *mut u8`
            found mutable reference `&mut *mut *mut i8`
note: function defined here
   --> /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/x11-2.21.0/src/xlib.rs:552:10
    |
552 |   pub fn XmbTextPropertyToTextList (_4: *mut Display, _3: *const XTextProperty, _2: *mut *mut *mut c_char, _1: *mut c_int) -> c_int,
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^
```

Release Notes:

- N/A
2025-04-05 06:01:27 +00:00
Julia Ryan
6ddad64af1 Add actions for calls (#28048)
Add the following actions for use while calling: `Mute`, `Deafen`,
`ShareProject`, `ScreenShare`, `LeaveCall`

We were also interested in adding push-to-talk functionality for mute,
but that will go in a followup PR

Release Notes:

- Call actions (mute/screenshare/etc.) can now be bound to keys and run from the command palette.

---------

Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com>
Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-04-04 19:32:41 -07:00
Smit Barmase
69d7ea7b60 buffer: Fix broken auto indent when pasting code starting with new line (#28134)
Closes #26907

Currently, in case of new line to find delta, it is comparing old first
line indent with new second line indent. This results into incorrect
indentation. This PR fixes this delta calculation by passing correct
second line indent in that particular case.

- [X] Add Test

Before:


https://github.com/user-attachments/assets/065deba0-be19-4643-a784-d248a8e7c891

After:


https://github.com/user-attachments/assets/a0037043-4bd8-460f-b8ba-b7da7bdbe1ea

Release Notes:

- Fixed issue where pasting code starting with new line resulted
incorrect auto indent.
2025-04-05 05:14:15 +05:30
Conrad Irwin
d0e82b0538 Introduce "Near" block type (#28032)
A "Near" block acts similarly to a "Below" block, but can (if it's
height is <= one line height) be shown on the end of the preceding line
instead of adding an entire blank line to the editor.

You can test it out by pasting this into `go_to_diagnostic_impl` and
then press `F8`
```
        let buffer = self.buffer.read(cx).snapshot(cx);
        let selection = self.selections.newest_anchor();

        self.display_map.update(cx, |display_map, cx| {
            display_map.insert_blocks(
                [BlockProperties {
                    placement: BlockPlacement::Near(selection.start),
                    height: Some(1),
                    style: BlockStyle::Flex,
                    render: Arc::new(|_| {
                        div()
                            .w(px(100.))
                            .h(px(16.))
                            .bg(gpui::hsla(0., 0., 1., 0.5))
                            .into_any_element()
                    }),
                    priority: 0,
                }],
                cx,
            )
        });
        return;
```

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
2025-04-04 17:37:42 -06:00
Smit Barmase
10821aae2c file_finder: Fix filename matching to require contiguous characters (#28093)
Improves https://github.com/zed-industries/zed/pull/27937 to only
prioritize file name if it's contiguous character match.

Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-05 05:01:56 +05:30
Marshall Bowers
03aadb4e5b telemetry_events: Rename AssistantEvent to AssistantEventData (#28133)
This PR renames the `AssistantEvent` type to `AssistantEventData`, as it
no longer represents the event itself, just the data needed to construct
it.

Pulling out of https://github.com/zed-industries/zed/pull/25179.

Release Notes:

- N/A
2025-04-04 19:28:32 -04:00
Max Brunsfeld
8ab252c42d Split protobufs into separate files (#28130)
The one big protobuf file was getting a bit difficult to navigate. I
split it into separate topic-specific files that import each other.

Release Notes:

- N/A
2025-04-04 16:15:49 -07:00
Michael Sloan
e74af03065 Restore direct use of the input text for Markdown Text (#27620)
PR #24388 changed the markdown parsing to copy parsed text in order to
handle markdown escaping, removing the optimization to instead reuse
text from the input.

Another issue with that change was that handling of finding links within
`Text` intermixed use of `text` and `parsed`, relying on the offsets
matching up (which I believe was true in practice).

The solution is to distinguish pulldown_cmark `Text` nodes that share
bytes with the input and those that do not.

Release Notes:

- N/A
2025-04-04 23:12:32 +00:00
Marshall Bowers
4bcd37a537 agent: Fix panic when opening Agent diff from the workspace (#28132)
This PR fixes a panic that could occur when opening the Agent diff from
the workspace (with the agent panel closed).

Release Notes:

- agent: Fixed a panic when running the `agent: open agent diff` command
with the Agent Panel closed.
2025-04-04 23:09:52 +00:00
Marshall Bowers
e3d212ac60 debugger_ui: Don't .unwrap debug panel access (#28131)
This PR removes replaces the `.unwrap`s when accessing the debug panel
with `if let Some`s.

These `.unwrap`s are not locally verifiable, and thus are not safe.

Release Notes:

- N/A
2025-04-04 22:54:40 +00:00
Finn Evers
8b077f0c41 gpui: Avoid dereferencing null pointer in MacWindow::update_ime_position (#28110)
Seems to be very similar to
https://github.com/zed-industries/zed/pull/28059

Edit: Updated the reproduction-steps as I missed something.

The method without a check currently causes my debug-builds to crash on
the regular if I:
- Run a debug build and open it fullscreen in a dedicated space on my
Mac.
- Work on any of the built-in languages (e.g. remove some content from
any `highlights.scm`)
- Reopen the workspace with the debug-build.
- Crash.

~~We might actually be able to revert the changes made in
https://github.com/zed-industries/zed/pull/21510 and just add the
null-check. Then again, I am not at all sure whether that would work.­~~
See comment below.

Release Notes:

- N/A
2025-04-04 18:20:06 -04:00
Danilo Leal
288da0f072 agent: Use Markdown to render tool input and output content (#28127)
Release Notes:

- agent: Tool call's input and output content are now rendered with
Markdown, which allows them to be selected and copied.

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-04-04 18:53:21 -03:00
Danilo Leal
b8d05bb641 agent: Hide the scrollbar if there's no mouse movement (#28129)
Release Notes:

- agent: The scrollbar now automatically hides if there's no mouse
movement on the thread list.

---------

Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-04-04 18:53:11 -03:00
Bennet Bo Fenner
02e4267bc6 Add tool calling support for GitHub Copilot Chat (#28035)
This PR adds tool calling support for GitHub Copilot Chat models.

Currently only supports the Claude family of models.

Release Notes:

- agent: Added tool calling support for Claude models in GitHub Copilot
Chat.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-04-04 21:41:07 +00:00
Michael Sloan
c2afc2271b Use scap library to implement screensharing on X11 (#27807)
While `scap` does have support for Wayland and Windows, but haven't seen
screensharing work properly there yet. So for now just adding support
for X11 screensharing.

WIP branches for enabling wayland and windows support:

* https://github.com/zed-industries/zed/tree/wayland-screenshare
* https://github.com/zed-industries/zed/tree/windows-screenshare


Release Notes:

- Added support for screensharing on X11 (Linux)

---------

Co-authored-by: Conrad <conrad@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Junkui Zhang <364772080@qq.com>
2025-04-04 21:31:03 +00:00
Aaron Feickert
7bc62de267 Use consistent ordering for popup menus (#27765)
Before this change, the editor completion menu and gutter menus reversed their order so that the initial selection is near the user's focus.  This change instead displays these menus in a consistent top-to-bottom order because the following benefits outweigh that benefit:

* Matches behavior of some other editors (Neovim and VSCode).
* Looks better for lexicographic lists.
* Keeps the meaning of keyboard interaction consistent, if the user is anticipating the order of the menu's contents.

Could consider making this configurable in the future if desired.

Closes #25066.

Release Notes:

- N/A
2025-04-04 14:57:09 -06:00
Agus Zubiaga
f3adf41c25 agent: Fix deleting threads in history via keyboard (#28113)
Using `shift-backspace` now because we need `backspace` for search

Release Notes:
- agent: Fix deleting threads in history via keyboard
2025-04-04 17:45:44 -03:00
Kirill Bulatov
6162d9942d Properly query remote ssh server for language servers by name (#28124)
Follow-up of https://github.com/zed-industries/zed/pull/27775

Release Notes:

- N/A
2025-04-04 20:03:51 +00:00
Max Brunsfeld
156dd32a35 Fix panic or bad hunks when expanding hunks w/ multiple ranges in 1 hunk (#28117)
Release Notes:

- Fixed a crash that could happen when expanding diff hunks with
multiple cursors in one hunk.
2025-04-04 12:22:02 -07:00
Ben Kunkle
2747915569 jsx-tag-auto-close: Remove potential source of bugs and panics (#28119)
Switch to using anchors for storing edited ranges rather than offsets as
they have to be used with multiple buffer snapshots

Release Notes:

- N/A
2025-04-04 19:01:08 +00:00
Agus Zubiaga
75b9a3b6a8 agent: Disable redundant tools (might delete later) (#28114)
Release Notes:

- agent: Disable tools that are redundant in the presence of the bash
tool
2025-04-04 18:59:21 +00:00
Marshall Bowers
9bd3dbcf28 collab: Include more information on some LLM usage log lines (#28116)
This PR updates the `user rate limit` and `user usage` log lines to
include some more information that will be useful for graphing in Axiom.

Release Notes:

- N/A
2025-04-04 18:33:23 +00:00
jneem
435fff94bd Flesh out helix bindings (#28103)
This brings in a bunch of helix bindings (many of them from
infogulch/zed-helix-keymap) and implements helix-style delete.

Release Notes:

- vim: Expanded default helix-style keybindings in HelixNormal mode
2025-04-04 12:21:15 -06:00
Marshall Bowers
558d61b907 collab: Adjust rate-limiting measures for Claude 3.7 Sonnet (#28111)
This PR updates the usage measures used for rate limiting when using
Claude 3.7 Sonnet.

Instead of using the combined `tokens_per_minute` measure we now rate
limit individually on `input_tokens_per_minute` (which exclude cache
reads) and `output_tokens_per_minute`.

Release Notes:

- N/A
2025-04-04 13:37:24 -04:00
Bennet Bo Fenner
02a8ece074 agent: Fix invalid tool names in batch tool description (#28109)
The description of the Batch Tool was still referring using `-` as a
seperator for tool names

Release Notes:

- N/A
2025-04-04 17:15:39 +00:00
Marshall Bowers
1a899fda60 collab: Capture upstream input/output rate limits from Anthropic (#28106)
This PR makes it so we capture the upstream rate limit information from
Anthropic for input and output tokens.

Release Notes:

- N/A
2025-04-04 17:09:00 +00:00
Marshall Bowers
183f57f318 collab: Include max input/output tokens per minute on "Language Model Rate Limited" event (#28108)
This PR adds the max input/output tokens per minute on the "Language
Model Rate Limited" event.

Missed this in https://github.com/zed-industries/zed/pull/28097.

Release Notes:

- N/A
2025-04-04 16:57:43 +00:00
Agus Zubiaga
cc9cc12f7b agent: Remove edit_files tool (#28041)
Release Notes:

- agent: Remove `edit_files` tool  in favor of `find_replace`
2025-04-04 16:37:14 +00:00
Agus Zubiaga
1bc5618f61 agent: Allow renaming threads (#28102)
Release Notes:

- agent: Add support for renaming threads

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
2025-04-04 13:24:33 -03:00
Richard Feldman
ef8fe52877 Try adding beta token-efficient tool use for 3.7 Sonnet (#28100)
Release Notes:

- Enabled [token-efficient tool use
(beta)](https://docs.anthropic.com/en/docs/build-with-claude/tool-use/token-efficient-tool-use)
for Claude 3.7 Sonnet models
2025-04-04 11:05:41 -05:00
Marshall Bowers
982196343f Fix script/zed-local on non-Windows platforms (#28098)
This PR fixes the `script/zed-local` script, which was no longer working
properly after https://github.com/zed-industries/zed/pull/23117.

Release Notes:

- N/A
2025-04-04 16:03:57 +00:00
Danilo Leal
cfe5620a2a docs: Adjust assistant configuration docs table of contents (#28099)
Follow up https://github.com/zed-industries/zed/pull/28088

The "Feature-specific models" was under the LM Studio section, which was
incorrect.

Release Notes:

- N/A
2025-04-04 12:47:52 -03:00
Marshall Bowers
5fe86f7e70 collab: Track input and output tokens per minute separately (#28097)
This PR adds tracking for input and output tokens per minute separately
from the current aggregate tokens per minute.

We are not yet rate-limiting based on these measures.

Release Notes:

- N/A
2025-04-04 15:37:06 +00:00
Finn Evers
c94b587e1a squawk: Specify PostgreSQL version in config (#28094)
This PR adds the PostgreSQL version to the squawk config, see
https://squawkhq.com/docs/cli#specifying-postgres-version for reference.

The specified version matches the PostgreSQL version in the compose-file


43cb925a59/compose.yml (L3)

and prevents false positives like
https://github.com/zed-industries/zed/pull/28090#issuecomment-2778871346
from happening (tested it locally with that commit).

Release Notes:

- N/A
2025-04-04 09:32:30 -06:00
Agus Zubiaga
43cb925a59 ai: Separate model settings for each feature (#28088)
Closes: https://github.com/zed-industries/zed/issues/20582

Allows users to select a specific model for each AI-powered feature:
- Agent panel
- Inline assistant
- Thread summarization
- Commit message generation

If unspecified for a given feature, it will use the `default_model`
setting.

Release Notes:

- Added support for configuring a specific model for each AI-powered
feature

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-04-04 11:40:55 -03:00
Marshall Bowers
cf0d1e4229 collab: Add granular tokens per minute columns to models table (#28090)
This PR adds new granular tokens per minute columns to the `models`
table in preparation for more fine-grained rate limits.

The following columns have been added:

- `max_input_tokens_per_minute`
- `max_output_tokens_per_minute`

These mirror the "Maximum input tokens per minute (ITPM)" and "Maximum
output tokens per minute (OTPM)" [rate limits from
Anthropic](https://docs.anthropic.com/en/api/rate-limits#rate-limits).

Release Notes:

- N/A
2025-04-04 14:33:15 +00:00
Artem Evsikov
2f5a4f7e80 tasks: Add spawn option by tag (#25650)
Closes #19497
Fixed conflicts from https://github.com/zed-industries/zed/pull/19498
Added tags to tasks selector

Release Notes:

- Added ability to spawn tasks by tag with key bindings
- Added tags to tasks selector


https://github.com/user-attachments/assets/0eefea21-ec4e-407c-9d4f-2a0a4a0f74df

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2025-04-04 14:20:09 +00:00
tidely
80441f675b gpui: Use NSOperatingSystemVersion provided by cocoa (#28055)
Use the `NSOperatingSystemVersion` struct provided by the cocoa crate
instead of our own. Additionally we can directly use
`isOperatingSystemAtLeastVersion` instead of manually implementing
version comparison logic.

The `isOperatingSystemAtLeastVersion` instance method has been available
since MacOS 10.10, which released a decade ago.

Documentation for `isOperatingSystemAtLeastVersion `:
https://developer.apple.com/documentation/foundation/nsprocessinfo/1414876-isoperatingsystematleastversion

Release Notes:

- N/A
2025-04-04 09:33:25 -04:00
Peter Tripp
393d6560a3 Make CloseAll keybindings more closely match VS Code (#28060)
Changes default keymaps to more closely match the behavior of VSCode.

New Zed behavior:
`cmd-k w` / `ctrl-k w` -- Closes all buffers in the current pane
`cmd-k cmd-w` / `ctrl-k ctrl-w` -- Closes all buffers in all panes

VScode:
`cmd-k cmd-w` is workbench.action.closeAllEditors (close all buffers in
all splits)
`cmd-k w` is workbench.action.closeEditorsInGroup (close all buffers in
current split)

Both leave pinned tabs untouched.

Release Notes:

- Improved keybindings for close all tabs to better match VSCode
behavior
2025-04-04 09:15:41 -04:00
Agus Zubiaga
3d48efad67 agent: Add search to Thread History (#28085)
![CleanShot 2025-04-04 at 09 45
47@2x](https://github.com/user-attachments/assets/a8ec4086-f71e-4ff4-a5b3-4eb5d4c48294)


Release Notes:

- agent: Add search box to thread history

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-04-04 10:09:21 -03:00
Antonio Scandurra
277a3f8d6f Implement edit rejection in ActionLog (#28080)
Release Notes:

- Fixed a bug that would prevent rejecting certain agent edits.
2025-04-04 11:20:18 +00:00
Jake
5e286897d3 Escape carets (^) in Go test regex (#27746)
This is a follow up to https://github.com/zed-industries/zed/pull/14821,
which escaped `$` but not `^`.

This is fine for `bash`, but causes issues with `zsh`. This change
escapes the `^`. I tested this against `bash`, `zsh` and `fish`

I suspect such escaping would probably need to be done at some
shell-specific layer of the code, but for now it seems like the tasks
provided by the `ContextProvider` are supposed to be shell agnostic.

To reproduce the original issue:
1. Create a Go test file in a module that just contains a single test
`TestABC`.
2. Run `zsh -i -c "go test -run ^TestABC\$"` which is what Zed tries to
run when the task for a specific Go test is executed.
3. An error that there are no tests to run will be produced even though
there is a test.
4. Run `zsh -i -c "go test -run \^TestABC\$"` (note the backslash before
^).
5. The test will run successfully.

Example:
``` go
package bar

import "testing"

func TestABC(t *testing.T) {}
```

Release Notes:

- fix: Escape the ^ in the Go test -run regex to improve shell
compatibility (notably with zsh).
2025-04-04 12:04:38 +02:00
Bennet Bo Fenner
9e38c45a9b agent: Show which lines were read when using read_file tool (#28077)
This makes sure that we specify which lines the agent actually read,
avoids confusing scenarios such as:

<img width="642" alt="Screenshot 2025-04-04 at 10 22 10"
src="https://github.com/user-attachments/assets/2680c313-4f77-4971-8743-8e3f5327c18d"
/>

Here the agent starts out by actually only reading a certain amount of
lines when the first tool call happens, then it does a second tool call
to read the whole file. To the user this looks like to identical tool
calls.

Now:
<img width="621" alt="image"
src="https://github.com/user-attachments/assets/76222258-9cc8-4b7c-98c0-6d5cffb282f2"
/>
<img width="362" alt="image"
src="https://github.com/user-attachments/assets/293f2fc0-365d-4b84-8400-4c11474caeb8"
/>
<img width="420" alt="image"
src="https://github.com/user-attachments/assets/ca92493e-67ce-4d45-8f83-0168df575326"
/>



Release Notes:

- N/A
2025-04-04 09:40:05 +00:00
Bennet Bo Fenner
1db3d92066 agent: Differentiate @mentions from markdown links (#28073)
This ensures that we display @mentions and normal markdown links
differently:

<img width="670" alt="Screenshot 2025-04-04 at 11 07 51"
src="https://github.com/user-attachments/assets/0a4d0881-abb9-42a8-b3fa-912cd6873ae0"
/>


Release Notes:

- N/A
2025-04-04 09:39:48 +00:00
Antonio Scandurra
a7674d3edc Scroll to first hunk when clicking on a file to review in Agent Panel (#28075)
Release Notes:

- Added the ability to scroll to a file when clicking on it in the Agent
Panel review section.
2025-04-04 09:30:35 +00:00
Conrad Irwin
ee4b6a8db4 Listen for changes to the configuration of the attached device too (#28045)
Release Notes:

- Fixed an issue causing "robot voice" when enabling the microphone on
some bluetooth headphones (hopefully).

Co-authored-by: Zed AI <ai+claude-3.7@zed.dev>
2025-04-03 21:05:54 -06:00
Rahul Butani
c04c5812b6 nix: Fix the cargo-bundle override (#28061)
With the recent deprecation of `rustPlatform.fetchCargoTarball` +
migration to using `fetchCargoVendor` by default in `buildRustPackage`
(NixOS/nixpkgs#394012), the `cargo-bundle` override strategy used here,
as prescribed by the
[nixos asia wiki](https://nixos.asia/en/buildRustPackage) no longer
works:

c6e2d20a02/nix/build.nix (L100-L116)

[`fetchCargoTarball` produced a single derivation][tarball-drv] but
`fetchCargoVendor` [produces two][vendor-drvs]:
  - `${name}-vendor-staging` (inner; FoD)
  - `${name}-vendor` (outer)

[tarball-drv]:
36fd87baa9/pkgs/build-support/rust/fetch-cargo-tarball/default.nix (L79)
[vendor-drvs]:
10214747f5/pkgs/build-support/rust/fetch-cargo-vendor.nix (L52-L103)

`overrideAttrs` here is setting `outputHash` on the latter (which isn't
a fixed-output-derivation and does not have `outputHashMode` set which
implies `outputHashMode = "flat"`) instead of the inner; this results in
errors like this:
```console
❯ nix develop
error: output path '/nix/store/cb57w05zvsqxshqjl789kmsy9pbqjn06-cargo-bundle-0.6.1-zed-vendor.tar.gz' should be a non-executable regular file since recursive hashing is not enabled (outputHashMode=flat)
error: 1 dependencies of derivation '/nix/store/k3azmxljgjn26hqyhg9m1y3lhx32y939-cargo-bundle-0.6.1-zed.drv' failed to build
error: 1 dependencies of derivation '/nix/store/8ag4v0m90m4kcaq1ypp7f85pp8s6fxgc-nix-shell-env.drv' failed to build
```

> [!NOTE]
> you will need to remove
`/nix/store/cb57w05zvsqxshqjl789kmsy9pbqjn06-cargo-bundle-0.6.1-zed-vendor.tar.gz`
> from your nix store in order to be able to reproduce this

We want to be setting `outputHash` on the [first derivation][first-drv]
instead. This change has us just do the call to `fetchCargoTarball`
manually instead of using overrides.

[first-drv]:
10214747f5/pkgs/build-support/rust/fetch-cargo-vendor.nix (L85)

---

I suspect CI/other machines didn't catch this due to a store path
matching the name + `outputHash` already being present but I'm not
entirely sure how this happened...

`sha256-Q49FnXNHWhvbH1LtMUpXFcvGKu9VHwqOXXd+MjswO64=` is actually a
`fetchCargoTarball` hash, not a `fetchCargoVendor` hash (and upstream
`cargo-about`'s `cargoDeps` [has been using `cargoVendor`][ups] since
before the nixpkgs bump in 50ad71a630)

[ups]:
1d09c579c1/pkgs/by-name/ca/cargo-about/package.nix (L22)

---

> [!NOTE]
> eventually we'll be able to just have `.overrideAttrs (_: { cargoHash
= "..."; })` work as expected [^2]

---

Release Notes:

- N/A

[^2]:
[now that
`buildRustPackage`](https://github.com/NixOS/nixpkgs/pull/382550) uses
[`lib.extendMkDerivation`](bbdf8601bc/doc/build-helpers/fixed-point-arguments.chapter.md)
(NixOS/nixpkgs/#234651) the groundwork is in place; a follow PR [needs
to use `cargoHash` and friends from
`finalAttrs`](10214747f5/pkgs/build-support/rust/build-rust-package/default.nix (L104))
2025-04-03 23:15:49 +00:00
Peter Tripp
cba96b5a38 ci: Prettier GitHub Actions display (#28062)
Skipped nix builds were ugly, showing raw template when being skipped. Make prettier.

Release Notes:

- N/A
2025-04-03 23:12:33 +00:00
Nathan Sobo
8b5ea05163 Fix panic calling blocks_intersecting_buffer_range with an empty range (#28049)
Previously, when comparing a block with an empty range to an empty query
range in non-inclusive mode, our binary search logic could end up
computing an inverted range, causing a panic.

This commit adds special casing when comparing empty blocks with empty
ranges.

cc @as-cii: I'm realizing that the approach to searching for the
intersecting replacement blocks makes some invalid assumptions about the
ordering of replace decorations. They aren't ordered at all by their end
range. @maxbrunsfeld and I are wondering if long term, we should remove
replace decorations and find another solution for folding buffers in
multi buffers.

Release Notes:

- Fixed an occasional panic that would occur when navigating to the next
change hunk with a pending inline transformation present.

Co-authored-by: Peter Tripp <petertripp@gmail.com>
2025-04-03 16:50:49 -06:00
Marshall Bowers
ec40e2d85c gpui: Avoid dereferencing null pointer in MacWindow::active_window (#28059)
This PR adds a check to avoid dereferencing a null pointer in
`MacWindow::active_window`.

Rust 1.86 now has a [debug assertion for dereferencing null
pointers](https://blog.rust-lang.org/2025/04/03/Rust-1.86.0.html#debug-assertions-that-pointers-are-non-null-when-required-for-soundness),
which means that losing focus of the window would cause a null pointer
to be dereferenced and panic.

Release Notes:

- N/A
2025-04-03 22:47:13 +00:00
Marshall Bowers
819bb8fffb open_ai: Disable parallel_tool_calls (#28056)
This PR disables `parallel_tool_calls` for the models that support it,
as the Agent currently expects at most one tool use per turn.

It was a bit of trial and error to figure this out. OpenAI's API
annoyingly will return an error if passing `parallel_tool_calls` to a
model that doesn't support it.

Release Notes:

- N/A
2025-04-03 22:07:37 +00:00
Piotr Osiewicz
c6e2d20a02 chore: Bump Rust version to 1.86 (#28021)
Closes #ISSUE

Release Notes:

- N/A
2025-04-03 23:32:50 +02:00
Marshall Bowers
7492ec3f67 Add tool use support for OpenAI models (#28051)
This PR adds support for using tools to the OpenAI models.

Release Notes:

- agent: Added support for tool use with OpenAI models (Preview only).
2025-04-03 20:55:11 +00:00
Julia Ryan
4d8df0a00b Add nix CI (#28036)
This adds a nix CI job to build the flake in debug mode for
aarch64-darwin and x86-linux. For now this job will only run when the
`run-nix` label is added to a PR.

The CI job doesn't push to cachix for now, so every build is a clean
build.

I also added a condition to the garbage collection step so it only runs
when the nix store is >50GB.

Release Notes:

- N/A
2025-04-03 12:55:18 -07:00
Kirill Bulatov
3f71ae9897 Use more appropriate action for Vim word completions (#28043)
Follow-up of https://github.com/zed-industries/zed/pull/26410

The action does not sort the items the way Vim does, but still better
than the previous state.

Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-03 19:32:24 +00:00
Nate Butler
2086f7d85b ui_input: TextField -> SingleLineInput (#28031)
- Rename `TextField` -> `SingleLineInput`
- Add a component preview for `SingleLineInput`
- Apply `SingleLineInput` to the AddContextServerModal

Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
2025-04-03 16:00:43 -03:00
Agus Zubiaga
315f1bf168 agent: Snapshot context in user message instead of recreating it (#27967)
This makes context essentially work the same way as `read-file`,
increasing the likelihood of cache hits.

Just like with `read-file`, we'll notify the model when the user makes
an edit to one of the tracked files. In the future, we want to send a
diff instead of just a list of files, but that's an orthogonal change.


Release Notes:
- agent: Improved caching of files in context

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
2025-04-03 15:52:28 -03:00
Kirill Bulatov
0c82541f0a Allow to temporarily stop LSP servers (#28034)
Same as `editor::RestartLanguageServer`, now there's an
`editor::StopLanguageServer` action that stops all language servers,
related to the currently opened editor.

Opening another singleton editor with the same language or changing
selections in a multi buffer will bring the servers back up.

Release Notes:

- Added a way to temporarily stop LSP servers

---------

Co-authored-by: Michael Sloan <mgsloan@gmail.com>
2025-04-03 12:50:43 -06:00
Danilo Leal
b9724d9cbe agent: Add token count in the thread view (#28037)
This PR adds the token count to the active thread view. It doesn't
behaves quite like Assistant 1 where it updates as you type, though; it
updates after you submit the message.

<img
src="https://github.com/user-attachments/assets/82d2a180-554a-43ee-b776-3743359b609b"
width="700" />

---

Release Notes:

- agent: Add token count in the thread view

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-04-03 15:43:58 -03:00
Marshall Bowers
e5b347b03a Remove unused extract_tool_args_from_events functions (#28038)
This PR removes the unused `extract_tool_args_from_events` functions
that were defined in some of the LLM provider crates.

Release Notes:

- N/A
2025-04-03 18:38:35 +00:00
Antonio Scandurra
e123c4bced Fix soft-wrapping with fold creases (#28029)
Release Notes:

- Fixed a rendering bug that caused context in the agent to not wrap
properly.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Zed AI <ai+claude-3.7@zed.dev>
2025-04-03 17:33:08 +00:00
Agus Zubiaga
ed3722023e agent: Handle tool use without text (#28030)
### Context 

The Anthropic API fails if a request message contains a tool use and no
`Text` segments or it only contains empty `Text` segments. These are
cases that the model itself produces, but the API doesn't support
sending them back.

#27917 fixed this by appending "Using tool..." in the thread's message,
but this causes the actual conversation to include it, so it would
appear in the UI (we would actually display a gap because we never
rendered its markdown, but "Using tool..." would show up when the thread
was restored).

### Solution

We'll now only append this placeholder when we build the request, so the
API still sees it, but the UI/Thread doesn't.

Another issue we found is that the model starts mimicking these
placeholders in later tool uses which is undesirable. So unfortunately,
we had to add logic to filter them out.

Release Notes:

- agent: Improved rendering of tool uses without text

---------

Co-authored-by: Bennet <bennet@zed.dev>
2025-04-03 14:22:59 -03:00
Piotr Osiewicz
ece4a1cd7c debugger: Start on tabless design (#27837)
![image](https://github.com/user-attachments/assets/1cd54b70-5457-4c64-95bd-45a7055ea165)

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Anthony <anthony@zed.dev>
2025-04-03 16:11:14 +00:00
Danilo Leal
9986a21970 agent: Add button to continue iterating once all reviews are done (#28027)
This PR adds a button on the review tab empty state that toggles the
focus back to the agent panel so that users can keep iterating on the
thread that's active in the panel.

<img
src="https://github.com/user-attachments/assets/ace5cf93-8869-49bb-8106-e03a9e3c90f2"
width="700"/>

Release Notes:

- N/A
2025-04-03 12:32:02 -03:00
Kirill Bulatov
c674e8d62d Clear path-based excerpt data properly (#28026)
Follow-up of https://github.com/zed-industries/zed/pull/27893

Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-03 15:17:10 +00:00
Finn Evers
e5e3e9ac8c rust: Improve runnable detection for test modules (#28024)
Closes #28002

This PR updates the `runnabless.scm` for Rust to improve detection of
test modules with non-standard names. Instead of matching on the module
name, we now check for the `#[cfg(test)]`-attribute above test modules.
This allows for generic matching whilst not regressing the previous
behaviour.

| `main` | <img width="922" alt="main"
src="https://github.com/user-attachments/assets/34fc4443-13a2-4e18-b806-7e14771c3df4"
/> |
| --- | --- |
| This PR | <img width="922" alt="PR"
src="https://github.com/user-attachments/assets/13e6a6d8-e177-4a83-89ab-24c0a69ade27"
/> |

Release Notes:

- Improved runnable detection for test modules in Rust.
2025-04-03 14:56:28 +00:00
Cole Miller
399d19231b Temporarily disable flaky conflicted-cherry-pick test (#27950)
Closes #ISSUE

Release Notes:

- N/A
2025-04-03 10:51:23 -04:00
Bennet Bo Fenner
c98bcc72b8 agent: Fix thinking step showing up as pending when completion is cancelled (#28019)
Previously the "Thinking..." step would show up as pending, even though
the user cancelled the generation:
<img width="672" alt="image"
src="https://github.com/user-attachments/assets/c9cdce0a-d827-4e23-96f5-b150465911a7"
/>


Release Notes:

- Fixed an issue where the thinking step would show up as pending even
when the generation was cancelled
2025-04-03 13:35:06 +00:00
Agus Zubiaga
fe27d11f08 agent: Include active file in recent history (#27914)
This happened because of two reasons:

- `Workspace::recent_navigation_history` didn't include the current file
- The context picker added the current file to a exclude list

The latter was actually intentional because we already show the file in
the suggested context, but now that we actually have mentions, it's just
inconvenient not to have it there.

Release Notes:

- N/A
2025-04-03 13:29:41 +00:00
Finn Evers
9693eab098 editor: Fix active line number highlighting regression (#28015)
This PR resolves a small regression introduced by the
debugger-introduction, which causes the active line number to no longer
be highlighted in the gutter as long as it is not part of a selection. A
user reported this issue [on
Discord](https://discord.com/channels/869392257814519848/995403703894954060/1357153291913662567).

Prior to the debugger-commit, an active line number was highlighted if
it was part of the editor active line numbers:

ed4e654fdf/crates/editor/src/element.rs (L4295-L4303)

With the debugger-introduction, the code was changed to only highlight
lines which are part of a selection:

e2aaf9b704/crates/editor/src/element.rs (L2411-L2422)

However, the check whether it is within a selection is not neccesary, as
the line is an active line as long as it is within the map of active
lines.

This PR restores the previous behavior.

| `main` | <img width="922" alt="main"
src="https://github.com/user-attachments/assets/486a548d-fe09-450e-922e-1feb4366fb4f"
/> |
| --- | --- |
| This PR | <img width="922" alt="PR"
src="https://github.com/user-attachments/assets/80517880-14b5-4861-bf83-8364f7831c46"
/> |

Release Notes:

- Fixed an issue where the active line number in the editor was not
always highlighted.
2025-04-03 11:32:15 +00:00
Piotr Osiewicz
e2aaf9b704 chore: Remove stray eprintln (#28014)
Closes #ISSUE

Release Notes:

- N/A
2025-04-03 10:28:27 +00:00
张小白
9abfbdff43 Fix test_peers_following_each_other for Windows (#28008)
Release Notes:

- N/A
2025-04-03 15:47:33 +08:00
Thomas Mickley-Doyle
cd85b430e4 assistant_eval: Add ACE framework (#27181)
Release Notes:

- N/A

---------

Co-authored-by: Michael Sloan <michael@zed.dev>
2025-04-02 23:02:06 -05:00
Julia Ryan
d3e4de7c72 workspace-hack: remove openssl from remote_server (#27990)
This was accidentally getting added due to increased feature
unification. We've manually excluded reqwest to go back to the desired
behavior: remote_server, doesn't depend on openssl.

Release Notes:

- N/A

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-04-03 00:49:07 +00:00
Piotr Osiewicz
ee950f5bc4 Debugger: Add pretty printers for Cargo-located tasks (#27979)
Closes #ISSUE

Release Notes:

- N/A
2025-04-03 01:40:08 +02:00
Smit Barmase
501b539286 gpui: Fix background for WrappedLine (#27980)
https://github.com/zed-industries/zed/pull/26454 In this PR, we
separated painting for text line into two parts: `paint` and
`paint_background`. This allows selections to appear in front of the
text background but behind the text itself in the editor.

The `paint_background` method was implemented for `ShapedLine` but not
for `WrappedLine`. This PR adds that, fixing the background rendering
for inline code blocks in Markdown, as they use `WrappedLine`.

Before:
<img width="160" alt="image"
src="https://github.com/user-attachments/assets/81466c63-6835-4128-ba22-1b63f5fd7b1f"
/>

After:
<img width="160" alt="image"
src="https://github.com/user-attachments/assets/3b7044d7-265b-45db-904c-3b70fdf421fe"
/>

Release Notes:

- Fixed missing background for inline code blocks in the editor hover
tooltip.
2025-04-03 05:09:42 +05:30
Marshall Bowers
444b7b8acb renovate: Ignore Cargo.toml for workspace-hack (#27976)
This PR adds the `Cargo.toml` for the `workspace-hack` crate to the
ignore list for Renovate, as it is opening a number of PRs against it
that will interfere with it.

Release Notes:

- N/A
2025-04-02 23:24:46 +00:00
Kirill Bulatov
8a6ed4a2ca Use new multibuffer excerpts in project search (#27893)
Follow-up of https://github.com/zed-industries/zed/pull/27876
Closes https://github.com/zed-industries/zed/issues/13513

Release Notes:

- Improved multi buffer excerpts to merge when expanded

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-02 22:57:40 +00:00
5brian
b4af5b2ce0 agent: Update thread label to use plural form (#27971)
Update thread label to match the other contexts.

|Before|After|
|--|--|

|![image](https://github.com/user-attachments/assets/6e02808e-50d7-480f-a9ca-251e9519a71d)|![image](https://github.com/user-attachments/assets/174aad84-9e55-4531-bb4a-1a1adaa46418)|

Release Notes:

- N/A
2025-04-02 18:33:05 -04:00
Marshall Bowers
ee33d313e2 agent: Allow editing previous messages (#27965)
This PR adds the ability to edit previous user messages in the thread.

Release Notes:

- Agent: Added the ability to edit previous user messages
(Preview-only).
2025-04-02 21:05:49 +00:00
Danilo Leal
0a132779a1 agent: Change loading label if command is waiting on permission (#27955)
If there's a command pending confirmation, the label changes from
"Generating" to "Waiting for confirmation".

<img
src="https://github.com/user-attachments/assets/d804e382-5315-40b0-9588-c257cca2430c"
width="600"/>

Release Notes:

- N/A
2025-04-02 17:41:16 -03:00
Danilo Leal
d23c2d4b02 agent: Refine feedback message input (#27948)
<img
src="https://github.com/user-attachments/assets/cde37a88-9973-4c27-80b7-459f5e986c74"
width="650" />

Release Notes:

- N/A

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-04-02 17:41:07 -03:00
Max Brunsfeld
b9f10c0adb Fix redundant FS file watches due to LSP path watching (#27957)
Release Notes:

- Fixed a bug where Zed sometimes added multiple redundant FS watchers
when language servers requested to watch paths. This could cause saves
and git operations to fail if Zed exceeded the file descriptor limit.

---------

Co-authored-by: Piotr <piotr@zed.dev>
2025-04-02 13:36:28 -07:00
Smit Barmase
9f9746872e editor: Fix typing closing bracket skips it even when use_autoclose is disabled (#27960)
Closes #27769

When adding snippet we were not respecting autoclose setting, before
creating AutocloseRegion. This leads to cursor to skip over instead of
typing that character. This PR fixes it.

Release Notes:

- Fixed certain case where typing closing bracket would skip it when
auto close setting is turned off.
2025-04-03 02:00:44 +05:30
Julia Ryan
01ec6e0f77 Add workspace-hack (#27277)
This adds a "workspace-hack" crate, see
[mozilla's](https://hg.mozilla.org/mozilla-central/file/3a265fdc9f33e5946f0ca0a04af73acd7e6d1a39/build/workspace-hack/Cargo.toml#l7)
for a concise explanation of why this is useful. For us in practice this
means that if I were to run all the tests (`cargo nextest r
--workspace`) and then `cargo r`, all the deps from the previous cargo
command will be reused. Before this PR it would rebuild many deps due to
resolving different sets of features for them. For me this frequently
caused long rebuilds when things "should" already be cached.

To avoid manually maintaining our workspace-hack crate, we will use
[cargo hakari](https://docs.rs/cargo-hakari) to update the build files
when there's a necessary change. I've added a step to CI that checks
whether the workspace-hack crate is up to date, and instructs you to
re-run `script/update-workspace-hack` when it fails.

Finally, to make sure that people can still depend on crates in our
workspace without pulling in all the workspace deps, we use a `[patch]`
section following [hakari's
instructions](https://docs.rs/cargo-hakari/0.9.36/cargo_hakari/patch_directive/index.html)

One possible followup task would be making guppy use our
`rust-toolchain.toml` instead of having to duplicate that list in its
config, I opened an issue for that upstream: guppy-rs/guppy#481.

TODO:
- [x] Fix the extension test failure
- [x] Ensure the dev dependencies aren't being unified by Hakari into
the main dependencies
- [x] Ensure that the remote-server binary continues to not depend on
LibSSL

Release Notes:

- N/A

---------

Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-04-02 13:26:34 -07:00
frederik-uni
07a77792c5 Add completions.lsp_insert_mode setting to control what ranges are replaced when a completion is inserted (#27453)
This PR adds `completions.lsp_insert_mode` and effectively changes the
default from `"replace"` to `"replace_suffix"`, which automatically
detects whether to use the LSP `replace` range instead of `insert`
range.

`"replace_suffix"` was chosen as a default because it's more
conservative than `"replace_subsequence"`, considering that deleting
text is usually faster and less disruptive than having to rewrite a long
replaced word.

Fixes #27197
Fixes #23395 (again)
Fixes #4816 (again)

Release Notes:

- Added new setting `completions.lsp_insert_mode` that changes what will
be replaced when an LSP completion is accepted. The default is
`"replace_suffix"`, but it accepts 4 values: `"insert"` for replacing
only the text before the cursor, `"replace"` for replacing the whole
text, `"replace_suffix"` that acts like `"replace"` when the text after
the cursor is a suffix of the completion, and `"replace_subsequence"`
that acts like `"replace"` when the text around your cursor is a
subsequence of the completion (similiar to a fuzzy match). Check [the
documentation](https://zed.dev/docs/configuring-zed#LSP-Insert-Mode) for
more information.

---------

Co-authored-by: João Marcos <marcospb19@hotmail.com>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-04-02 16:55:03 -03:00
Anthony Eid
108ae0b5b0 debugger: Add args argument to debugger launch config (#27953)
This also fixes a bug where debug cargo test code actions would debug
all tests in a mod instead of a specific test

Release Notes:

- N/A
2025-04-02 15:37:12 -04:00
Andy Waite
500964a6fa docs: Add example of Ruby plain minitest task (#27607)
Via
https://github.com/zed-industries/zed/issues/12579#issuecomment-2143972765

Release Notes:

- N/A
2025-04-02 15:24:59 -04:00
Shardul Vaidya
0a58e54477 aws_http_client: Copy response headers (#27941)
Preemptive fixes required for #26734

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-04-02 15:00:44 -04:00
Ben Kunkle
8539e23018 zed: Include full debug info in debug builds (#27924)
Closes #ISSUE

Release Notes:

- N/A
2025-04-02 18:39:30 +00:00
Marshall Bowers
c7d27753ee agent: Do some cleanup of feedback comments submission (#27940)
This PR does some stylistic cleanup of the feedback comments submission
code.

Release Notes:

- N/A
2025-04-02 18:03:18 +00:00
Michael Sloan
b7b7f1ccdd Use worktree qualified paths in agent file context + some code cleanup (#27943)
Release Notes:

- N/A
2025-04-02 18:00:32 +00:00
Michael Sloan
142f9917d0 Fix clippy lints that don't currently appear in CI (#27944)
I may have a newer version of clippy than CI. Also removes some unused
code in `livekit_client.rs`

Release Notes:

- N/A
2025-04-02 18:00:16 +00:00
Joseph T. Lyons
f8092bf0d2 Bump Zed to v0.182 (#27945)
Release Notes:

-N/A
2025-04-02 13:45:42 -04:00
Anthony Eid
0ba8432b0b Debugger: Add stop on entry support to debug adapter configs (#27942)
This PR adds passing in `stop_on_entry` to debug configs in debug.json
instead of going through initialization args.

This has two benefits:

1. It's more streamlined to a user since every internal adapter supports
`stop_on_entry` for launch requests and Go's adapter supports it for
attach requests too.
2. It will allow @osiewicz `NewSesssionModal` PR to use this field for
the stop on entry checkbox.

Release Notes:

- N/A
2025-04-02 13:45:26 -04:00
Cole Miller
e1a8a31fa4 Fix flickering when staging and unstaging files (#27931)
This fixes a bug in #27568 that caused flickering in the git panel's
checkbox state when staging and unstaging entire files. The problem is
that that stage/unstage action first saves the target path (if it's open
as a buffer), and we do a targeted git status scan in response to that
filesystem event, which makes its way to the git panel and causes it to
clear its pending state before the actual stage or unstage has gone
through.

The fix is to not clear the panel's pending state for git repository
events that originated from a targeted scan (i.e. one that was triggered
by FS events for repo paths, as opposed to events inside `.git` which
cause all statuses to be recomputed).

Release Notes:

- N/A
2025-04-02 17:43:48 +00:00
Danilo Leal
0be8bf1b12 agent: Improve action confirmation UX (#27932)
This PR makes the command permission prompt part of the tool card and
allow users to straight away change the `always_allow_tool_actions`
setting via the "Always Allow" button from that card. If that button is
clicked, that setting is turned on, and any command that requires
permission from that point on will auto-run.

Additionally, if a bash command spans multiple lines, we show the line
count at the end of the command string. (Note: this is not perfect yet
because it can likely be not visible by default, but we didn't think
this was a major blocker for now. We'll work on improving this next).

### Thread View

<img
src="https://github.com/user-attachments/assets/00f93c39-990f-4b79-84ec-0427b997167f"
width="500"/>

### Settings View

<img
src="https://github.com/user-attachments/assets/52d32435-7c8d-4ab4-a319-6cabc007267b"
width="500"/>


Release Notes:

- N/A

---------

Co-authored-by: Thomas Mickley-Doyle <tmickleydoyle@gmail.com>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
2025-04-02 17:40:49 +00:00
Cole Miller
3e2ac3e7bc Log error and proceed when failing to load repo environment (#27938)
Closes #ISSUE

Release Notes:

- N/A
2025-04-02 13:15:35 -04:00
Marshall Bowers
646f65511c agent: Add newtype for profile IDs (#27939)
This PR adds an `AgentProfileId` newtype for profile IDs that we can use
instead of `Arc<str>` everywhere.

Release Notes:

- N/A
2025-04-02 17:12:52 +00:00
Smit Barmase
b2904e5d9f file_finder: Prioritize file name matches over path matches (#27937)
Closes https://github.com/zed-industries/zed/issues/27936

Adds additional comparison after the existing history-based comparison.

This comparison checks whether the first position matched via fuzzy
search is in the file name. If so, we prioritize this match over one in
the path. If both items match either the file name or the path, the
existing comparison logic is used.

- [x] Tests

Before:
<img width="580" alt="image"
src="https://github.com/user-attachments/assets/011fdd01-3dfa-4950-abb1-dfda10885664"
/>

After:
<img width="614" alt="image"
src="https://github.com/user-attachments/assets/9c4944b0-83dc-4611-94bb-aae1758fab23"
/>


Release Notes:

- Fixed an issue where fuzzy matching in file finder did not properly
prioritize matches in file names.
2025-04-02 22:36:46 +05:30
Thomas Mickley-Doyle
45e7cd1638 Add comment box to negative feedback reaction (#27934)
Release Notes:

- N/A
2025-04-02 18:54:25 +02:00
Marshall Bowers
889bc13b7d language_model: Remove use_any_tool method from LanguageModel (#27930)
This PR removes the `use_any_tool` method from the `LanguageModel`
trait.

It was not being used anywhere, and doesn't really fit in our new tool
use story.

Release Notes:

- N/A
2025-04-02 15:49:21 +00:00
Bennet Bo Fenner
da3383b10e agent: Fix thinking step showing up as pending even though it is completed (#27926)
Release Notes:

- N/A
2025-04-02 15:43:39 +00:00
Agus Zubiaga
9c4e61eae1 agent: Add more guidelines to system prompt (#27927)
Release Notes:

- N/A
2025-04-02 12:41:51 -03:00
Antonio Scandurra
b413605f6b Fix bug that prevented deletion-only hunks from being kept correctly (#27921)
Release Notes:

- N/A
2025-04-02 17:38:58 +02:00
Marshall Bowers
961bfadad9 agent: Return an error to the model when trying to use a tool that is disabled (#27928)
This PR makes it so we return an error to the model if it tries to use a
tool that is not currently enabled:

<img width="628" alt="Screenshot 2025-04-02 at 11 10 55 AM"
src="https://github.com/user-attachments/assets/e4bdf01c-f0ea-4c9c-805a-11868bd9c771"
/>

This allows the model to adapt based on that:

<img width="637" alt="Screenshot 2025-04-02 at 11 08 38 AM"
src="https://github.com/user-attachments/assets/41016b47-933c-4dcb-b791-847be0548c8a"
/>

Release Notes:

- N/A
2025-04-02 15:24:37 +00:00
Agus Zubiaga
2eed94ff23 agent: Include context with first message that introduced it (#27925)
We were including the context at the end which meant it never got
cached. We'll now include it with the first message that introduced it
so it's cached as long as it doesn't change.

This is an improvement, but we probably still need to think of ways to
optimize caching for cases where files in context change.

Release Notes:

- N/A
2025-04-02 12:14:48 -03:00
Antonio Scandurra
7a54dd7190 Avoid requiring confirmation for tools that edit buffers (#27923)
It's super easy to undo those changes. In a future PR, we should also
avoid requiring confirmation in the batch tool if all the underlying
tools don't require confirmation.

Release Notes:

- N/A
2025-04-02 15:13:16 +00:00
Cole Miller
8ac4cbcbb9 Don't create repos for invisible worktrees (#27894)
Closes #ISSUE

Release Notes:

- Fixed git repositories being added for files outside the project
2025-04-02 15:11:12 +00:00
Marshall Bowers
d82bf132ca agent: Remove unneeded branch in use_pending_tools (#27922)
This PR removes an unneeded `else if` branch that was the same as the
previous branch in the conditional.

Doesn't seem necessary to run this twice.

Release Notes:

- N/A
2025-04-02 14:50:24 +00:00
Danilo Leal
0f58d4f533 agent: Remove duplicate OpenConfiguration action registration (#27918)
I had introduced the same thing a few days ago without noticing there
was one in place already!

Release Notes:

- N/A
2025-04-02 10:16:59 -04:00
Antonio Scandurra
d340670fd4 Ensure rejecting a hunk dismisses the diff (#27919)
Release Notes:

- N/A
2025-04-02 14:06:36 +00:00
Danilo Leal
ad42a8ad9b agent: Adjust permission card design (#27911)
<img
src="https://github.com/user-attachments/assets/76ae380c-6d5b-4ed6-9b47-9a4c44b00042"
width="500" />

Release Notes:

- N/A
2025-04-02 10:03:43 -03:00
Marshall Bowers
2846c55cde agent: Account for tool use messages without any corresponding text (#27917)
This PR makes it so if we receive a tool use from the model without any
corresponding text, we'll insert some placeholder text to keep the
conversation in a well-structured format.

This aims to fix an error that Antonio was seeing:


![image](https://github.com/user-attachments/assets/eeba95ef-9b67-41d7-bce3-afe84bcdc7b1)

Release Notes:

- N/A
2025-04-02 12:56:50 +00:00
Marshall Bowers
5dcd0d37a6 language_models: Denote Copilot Chat as not supporting tools (#27909)
This PR updates the Copilot Chat language model to indicate it does not
yet support tool use in Zed.

Release Notes:

- N/A
2025-04-02 12:12:09 +00:00
Marshall Bowers
a70daa4310 agent: Update tool names in default profiles to reflect snake_cased rename (#27907)
This PR updates the tool names in the default profiles, as they were not
updated after the tool names were changed to snake_case in #27903.

Release Notes:

- N/A
2025-04-02 12:03:07 +00:00
Bennet Bo Fenner
b158ee60ca Fix Gemini function calling (#27905)
This seems to improve the performance of `gemini-2.5-pro-exp-03-25`
significantly.
We know define a single `Tool` that has multiple `FunctionDeclaration`s,
instead of defining multiple `Tool`s with a single
`FunctionDeclaration`.
Oddly enough the `flash` models seemed to work perfectly fine with the
multiple `Tool { ... }` definitions

Release Notes:

- N/A
2025-04-02 10:35:17 +00:00
Bennet Bo Fenner
9b7b4a9d2d assistant2: Use snake_case for tool names (#27903)
The [Gemini
docs](https://ai.google.dev/gemini-api/docs/function-calling?example=weather#function_declarations)
state that function names should be in `snake_case` or `camelCase`.

Release Notes:

- N/A
2025-04-02 12:17:58 +02:00
Antonio Scandurra
c1d6dfd832 Update selection when keeping/rejecting hunks (#27902)
Release Notes:

- N/A
2025-04-02 08:38:26 +00:00
0x2CA
57d7bc23ae vim: Add g? convert to Rot13/Rot47 (#27824)
Release Notes:

- Added `g?` convert to `Rot13`/`Rot47`

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-02 02:17:00 +00:00
Conrad Irwin
7bc0dd1bf6 Don't unwrap on bad zstd data (#27891)
Closes #ISSUE

Release Notes:

- N/A
2025-04-01 20:09:18 -06:00
Marshall Bowers
dc83f1ad38 Rename assistant2 to agent (#27887)
This PR renames the `assistant2` crate to `agent`.

Release Notes:

- N/A
2025-04-02 00:40:47 +00:00
Marshall Bowers
8e0f70f3c7 Bump revision for livekit-rust-sdks (#27886)
This PR bumps the revision of the `livekit-rust-sdks` crate.

I was running into issue where Cargo was stuck on "Updating git
submodule `https://chromium.googlesource.com/libyuv/libyuv`".

I resolved the issue by forking `libyuv` to
https://github.com/zed-industries/libyuv and updating our
`livekit-rust-sdks` fork to use it in
https://github.com/zed-industries/livekit-rust-sdks/pull/4.

Release Notes:

- N/A
2025-04-02 00:24:08 +00:00
Cole Miller
6262a4638b Fix a bug that prevented repositories from being deduplicated (#27884)
Closes #ISSUE

Release Notes:

- Ensure that only one repository is shown in the git UI when two
subdirectories of a repository root are open in Zed
2025-04-01 22:41:01 +00:00
Max Brunsfeld
8321e65561 Bump tree-sitter-rust to 0.24 (#27875)
Release Notes:

- Added correct syntax highlighting for use bounds and async closures in
Rust.
2025-04-01 15:35:31 -07:00
Julia Ryan
4110928314 nix: Clean up build (#27881)
- bump our livekit version to include a fix for a crane bug (TODO: add
link when an issue is filed on crane)
- switch to a clang stdenv for both linux and macos
- manually unify versions of our notify crate
- remove old linker flags which were only needed for livekit
- fix an issue where RUSTFLAGS shadowed the rustflags from cargo configs

Release Notes:

- N/A
2025-04-01 22:35:15 +00:00
Marshall Bowers
a1b53e91e7 git: Use doc comments for ResetMode variants (#27882)
This PR updates the `ResetMode` enum to use doc comments for its
variants instead of line comments.

Release Notes:

- N/A
2025-04-01 22:19:29 +00:00
Antonio Scandurra
4a252515b1 Improve tracking for agent edits (#27857)
Release Notes:

- N/A

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-04-01 22:13:28 +00:00
Agus Zubiaga
d26c477d86 assistant2: Summarize threads in context and continue long ones (#27851)
We'll now prompt the user to start a new thread when the active one gets
too long.

<img width=500
src="https://github.com/user-attachments/assets/91445bc0-3e81-422f-aa4a-b8f0741f9d9a"></img>


When they click "Start New Thread", will create a new one with the
previous one added as context.

<img width=500
src="https://github.com/user-attachments/assets/c3b4223f-5bdd-4ba4-956f-5a5880d5e2c3"></img>

Instead of including the full thread text, we'll now add summarized
versions of threads to the context, allowing you to continue the
conversation even if it was near the token limit.

- Thread summaries are cached and persisted. 
- A cached summary is invalidated if the thread is continued.
- We start generating the thread summary as soon as it's selected from
the picker. Most times, the summary will be ready by the time the user
sends the message.
- If the summary isn't ready by the time a message is sent, the user
message will be displayed in the thread immediately, and a "Summarizing
context..." indicator will appear. After the summaries are ready, we'll
start generating the response and show the usual "Generating..."
indicator.

Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-04-01 21:48:56 +00:00
KyleBarton
16f625bd07 Add persistence to command palette history (#26948)
Closes #20391

### Summary
This adds a persistence layer to the command palette so that usages can
persist after Zed is closed and re-opened.

The current "usage" algorithm is unchanged, e.g.:
- Sorts by number of usages descending (no recency preference)
- Once a user's query is active, removes these suggestions in favor of
fuzzy matching

There are some additional considerations in order to keep the DB from
growing uncontrollably (and to make long-term use ergonomic):
- The "invocations" count handles max values (though at u16, it seems
unlikely a user will deal with this)
- If a command is un-invoked for more than a month, it stops being
considered a recent usage, and its next update will update its usages
back to 1

### Future Considerations
- Could make the "command expiry" configurable in settings, so the user
can decide how long to hold onto recent usages
- Could make a more sophisticated algorithm which balances recency and
total invocations - e.g. if I've used COMMAND_A 100 times in the last
month, but COMMAND_B 10 times today, should COMMAND_B actually be
preferred?
- Could do preferential fuzzy-matching against these matches once the
user starts a query.

Release Notes:

- Added persistent history of command palette usages.

---------

Co-authored-by: Peter Finn <mastion11@gmail.com>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-01 15:46:35 -06:00
Conrad Irwin
9bc4697a33 Use new multibuffer excerpts in find-all-references and friends (#27876)
Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2025-04-01 15:42:32 -06:00
Cole Miller
e7290df02b Finish removing git repository state and scanning logic from worktrees (#27568)
This PR completes the process of moving git repository state storage and
scanning logic from the worktree crate to `project::git_store`.

Release Notes:

- N/A

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Conrad <conrad@zed.dev>
2025-04-01 17:41:20 -04:00
Marshall Bowers
8f25251faf assistant2: Rename assistant2 actions to agent (#27877)
This PR renames the `assistant2` actions to `agent`.

Note that any `assistant` actions have been left as-is for now so that
there aren't any changes to users not in the feature flag.

Release Notes:

- N/A
2025-04-01 21:25:30 +00:00
Julia Ryan
1567967309 nix: Separate debug output (#27871)
This makes it easier to iterate on the nix flake by adding a package
which builds zed in debug mode rather than release

Release Notes:

- N/A
2025-04-01 14:19:10 -07:00
Agus Zubiaga
2d05537b59 assistant2: Fix notification max width (#27872)
<img width=400
src="https://github.com/user-attachments/assets/4132e075-3f2a-4c65-8c06-c3428f3ad477">

<img width=400
src="https://github.com/user-attachments/assets/95908b32-a9b9-45c3-9db9-dcbeedffc5e7">

Release Notes:

- N/A
2025-04-01 18:16:53 -03:00
5brian
95b963c87b vim: Add :Git (#27874)
Just adding to the existing https://zed.dev/docs/vim#ex-commands

Captial G is not used by vim commands.

Release Notes:

- N/A
2025-04-01 15:12:36 -06:00
Marshall Bowers
5d1695ed1d assistant2: Add affordances for when the selected model does not support tools (#27870)
This PR adds some affordances for when the currently-selected model does
not support tools.

We disable the profile selector and put it into a "No Tools" state:

<img width="1394" alt="Screenshot 2025-04-01 at 3 58 00 PM"
src="https://github.com/user-attachments/assets/de6ecb0f-7657-4e16-9d5d-7bbfbc2b0a5c"
/>

We will also only attach tools to the request to the model if the model
supports it.

Release Notes:

- N/A
2025-04-01 20:13:06 +00:00
Danilo Leal
192097f58f assistant2: Ensure errors are also displayed in populated new thread view (#27869)
Follow-up to https://github.com/zed-industries/zed/pull/27812

This PR makes sure these errors cases also show up in the panel's empty
state even when there is past data.

| No ToS | Missing Provider |
|--------|--------|
| ![CleanShot 2025-04-01 at 4  49
36@2x](https://github.com/user-attachments/assets/6da6bdc9-daa6-4a7b-a224-989eb845e205)
| ![CleanShot 2025-04-01 at 4  50
04@2x](https://github.com/user-attachments/assets/bddf62cb-3727-44b5-b115-9a88313c6d85)
|

Release Notes:

- N/A
2025-04-01 17:06:34 -03:00
Agus Zubiaga
92059803fb find-replace tool: Return diff in output (#27868)
This should help the model maintain an accurate picture of the file as
it makes changes

Release Notes:

- N/A
2025-04-01 19:57:32 +00:00
Marshall Bowers
5880271b11 language_model: Add supports_tools method to LanguageModel (#27867)
This PR adds a new `supports_tools` method to the `LanguageModel` trait
to indicate whether a given model supports tool use.

Release Notes:

- N/A
2025-04-01 19:56:05 +00:00
Cole Miller
4ff07bf789 Fix restore button (#27866)
ID collision!

Release Notes:

- N/A
2025-04-01 19:33:30 +00:00
Max Brunsfeld
b59967bbdf Don't allow open excerpts in commit view for now (#27862)
Follow-up to https://github.com/zed-industries/zed/pull/27636

Release Notes:

- N/A
2025-04-01 11:40:45 -07:00
Marshall Bowers
133d3f052f assistant2: Update some text to use "agent" nomenclature (#27864)
This PR updates some text in the Agent Panel to use the "agent"
nomenclature.

Release Notes:

- N/A
2025-04-01 18:33:28 +00:00
Cole Miller
0934cb58c4 Don't prompt twice when trashing added files from git panel (#27863)
Closes #ISSUE

Release Notes:

- N/A
2025-04-01 18:25:58 +00:00
Peter Finn
bda33ec436 vim: Fix space forward bug with non-ASCII characters at EOL (#27860)
Closes https://github.com/zed-industries/zed/issues/27619

Fixes issue with right wrapped movement when a multi-byte character is
at the end of the line. This is done by grabbing the last character on
the current row and using that characters size to calculate the
`max_column` variable, which is used to decide if the next right
movement should move down the line or not.

We did notice a bit of code that could be an issue that we wanted to
call out.
[Here](https://github.com/zed-industries/zed/blob/main/crates/editor/src/display_map.rs#L1070)
inside of `clip_at_line_end` it also does a saturating_sub(1), assuming
a single byte character. We didn't run into any issues due to this line
but felt like a similar bug. We can apply a similar fix if wanted to
pose the question first.

Test case: Moving to next line when eol is a multi-byte character


https://github.com/user-attachments/assets/1021ab1f-f49d-4986-8f9a-8cfc7e5c91bc


Release Notes:

- Fixed issue in vim forward spacing when a multi-byte character is at
the eol

---------

Co-authored-by: KyleBarton <kjbarton4@gmail.com>
2025-04-01 18:21:41 +00:00
Danilo Leal
64ef3ab09d ui: Introduce Banner component (#27853)
This PR adds a new, generic `Banner` component so that we can
potentially replace the multiple, isolated implementations of it
throughout some places of the app.

<img
src="https://github.com/user-attachments/assets/a268f745-1747-48e6-9461-2732eb7c0be4"
width="750"/>

Release Notes:

- N/A
2025-04-01 14:36:38 -03:00
Elvis Pranskevichus
ac5a2b2122 python: Add recognition of worktree -> venv links (#26759)
Python envs created with virtualenvwrapper have the ability to link to
the project directory (via the `.project` file in the virtualenv).
`python-environment-tools` supports this and reports the project path.

Additionally, some tools (e.g virtualfish) recognize special
"environment activation" files in project directories (`.venv` by
default)
[1].

Use the above information to sort reported Python toolchains so that the
correct env for a given worktree is at the top.

[1]
https://virtualfish.readthedocs.io/en/latest/plugins.html#auto-activation-auto-activation

Release Notes:

- python: Improved detection of virtualenvwrapper environments in work
trees
2025-04-01 19:12:16 +02:00
Max Brunsfeld
ada8b0f822 Show commit author, not committer (#27856)
Release Notes:

- Fixed a bug where the git panel displayed a commit's committer in
place of its author.
2025-04-01 09:55:56 -07:00
Antonio Scandurra
76871056f5 Preserve cursor position when resetting excerpts (#27850)
Release Notes:

- N/A

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-04-01 16:55:10 +00:00
Marshall Bowers
f859b328f0 assistant2: Use "Agent Panel" nomenclature for panel tooltip (#27858)
This PR updates the Agent Panel tooltip in the status to use "Agent
Panel" instead of "Assistant Panel".

Also changes the name we use in workspace serialization.

Release Notes:

- N/A
2025-04-01 16:54:40 +00:00
Marshall Bowers
e63df58adc assistant2: Rename key context to AgentPanel (#27859)
This PR renames the key context for the Agent Panel from
"AssistantPanel2" to "AgentPanel".

Release Notes:

- N/A
2025-04-01 16:42:31 +00:00
Marshall Bowers
0ad4656489 assistant_settings: Show JSON schema for profile settings (#27855)
This PR makes it so we include the `default_profile` and `profiles`
settings in the JSON schema.

This provides completions when editing the `settings.json`.

Release Notes:

- N/A
2025-04-01 16:34:48 +00:00
Piotr Osiewicz
992831ceb6 workspace: Do not call set_active when deserializing a dock if that dock was not visible (#27852)
This unblocks work on new debugger UI, where we don't want the
set_active function to be called unconditionally.

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-04-01 16:33:52 +00:00
Junseong Park
7257053df4 Update docs for some settings (#27474)
Fix incorrect defaults or descriptions and add missing content.

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-04-01 11:46:36 -04:00
Bennet Bo Fenner
feb1d37798 assistant2: Allow clicking on @mentions (#27846)
https://github.com/user-attachments/assets/f6f7c115-5c40-48f9-a099-2b691993967b

Release Notes:

- N/A
2025-04-01 15:44:20 +00:00
Marshall Bowers
12037dc2c6 assistant2: Allow profiles to enable all context servers (#27847)
This PR adds a new `enable_all_context_servers` field to agent profiles
to allow them to enable all context servers without having to opt into
them individually.

The "Write" profile will now have all context servers enabled out of the
box.

Release Notes:

- N/A
2025-04-01 15:25:23 +00:00
1064 changed files with 90780 additions and 51912 deletions

View File

@@ -14,10 +14,10 @@ linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-args=-Objc -all_load"]
rustflags = ["-C", "link-args=-all_load"]
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-args=-Objc -all_load"]
rustflags = ["-C", "link-args=-all_load"]
[target.'cfg(target_os = "windows")']
rustflags = [

1
.clinerules Symbolic link
View File

@@ -0,0 +1 @@
.rules

43
.config/hakari.toml Normal file
View File

@@ -0,0 +1,43 @@
# This file contains settings for `cargo hakari`.
# See https://docs.rs/cargo-hakari/latest/cargo_hakari/config for a full list of options.
hakari-package = "workspace-hack"
resolver = "2"
dep-format-version = "4"
workspace-hack-line-style = "workspace-dotted"
# this should be the same list as "targets" in ../rust-toolchain.toml
platforms = [
"x86_64-apple-darwin",
"aarch64-apple-darwin",
"x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-gnu",
"x86_64-pc-windows-msvc",
"x86_64-unknown-linux-musl", # remote server
]
[traversal-excludes]
workspace-members = [
"remote_server",
]
third-party = [
{ name = "reqwest", version = "0.11.27" },
]
[final-excludes]
workspace-members = [
"zed_extension_api",
# exclude all extensions
"zed_emmet",
"zed_glsl",
"zed_html",
"perplexity",
"zed_proto",
"zed_ruff",
"slash_commands_example",
"zed_snippets",
"zed_test_extension",
"zed_toml",
]

1
.cursorrules Symbolic link
View File

@@ -0,0 +1 @@
.rules

36
.github/ISSUE_TEMPLATE/01_bug_agent.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Bug Report (Agent Panel)
description: Zed Agent Panel Bugs
type: "Bug"
labels: ["agent", "ai"]
title: "Agent Panel: <a short description of the Agent Panel 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. -->
<!-- Please include the LLM provider and model name you are using -->
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"'
placeholder: |
Output of "zed: Copy System Specs Into Clipboard"
validations:
required: true

View File

@@ -0,0 +1,36 @@
name: Bug Report (Edit Predictions)
description: Zed Edit Predictions bugs
type: "Bug"
labels: ["ai", "inline completion", "zeta"]
title: "Edit Predictions: <a short description of the Edit Prediction 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. -->
<!-- Please include the LLM provider and model name you are using -->
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"'
placeholder: |
Output of "zed: Copy System Specs Into Clipboard"
validations:
required: true

35
.github/ISSUE_TEMPLATE/03_bug_git.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
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.
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"'
placeholder: |
Output of "zed: Copy System Specs Into Clipboard"
validations:
required: true

View File

@@ -0,0 +1,56 @@
name: Bug Report (Other)
description: |
Something else is broken in Zed (exclude crashing).
type: "Bug"
body:
- 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!)
- Code must as text, not just as a screenshot.
- Issues with insufficient detail may be summarily closed.
-->
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>
-->
validations:
required: true
- type: textarea
id: environment
attributes:
label: Zed Version and System Specs
description: |
Open Zed, from the command palette select "zed: Copy System Specs Into Clipboard"
placeholder: |
Output of "zed: Copy System Specs Into Clipboard"
validations:
required: true

View File

@@ -5,10 +5,12 @@ body:
- type: textarea
attributes:
label: Summary
description: Describe the bug with a one line summary, and provide detailed reproduction steps
description: Summarize the issue with detailed reproduction steps
value: |
<!-- Please insert a one line summary of the issue below -->
<!-- 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.
@@ -16,7 +18,6 @@ body:
3.
Actual Behavior:
Expected Behavior:
validations:
@@ -40,10 +41,11 @@ body:
value: |
<details><summary>Zed.log</summary>
<!-- Click below this line and paste or drag-and-drop your log-->
```
<!-- Paste your log inside the code block. -->
```log
```
<!-- Click above this line and paste or drag-and-drop your log--></details>
</details>
validations:
required: false

View File

@@ -1,57 +0,0 @@
name: Bug Report
description: |
Something is broken in Zed (exclude crashing).
type: "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
<!-- Be verbose: Include all steps necessary to reproduce from a clean Zed installation. -->
<!-- Code snippets are better than images, a repository link that reproduces the issue is ideal. -->
Steps to trigger the problem:
1.
2.
3.
4.
Actual Behavior:
Expected Behavior:
<!--
Is there anything additional necessary to reproduce this issue?
- settings.json, keymap.json, .editorconfig etc?
- Does it happen intermittently or only with specific projects / file types?
- Have you found a workaround?
Did you check your Zed.log to see if there is any relevant details there?
- When including large items (videos, screenshots, logs, configs) please wrap with:
<details><summary>See inside for XXXXYYY</summary>
```shell
code
```
</details>
-->
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

19
.github/ISSUE_TEMPLATE/99_other.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
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

@@ -4,9 +4,6 @@ contact_links:
- 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 Discussion Forum
url: https://github.com/zed-industries/zed/discussions
about: A community discussion forum
- name: "Zed Discord: #Support Channel"
- name: "Zed Discord"
url: https://zed.dev/community-links
about: Real-time discussion and user support

View File

@@ -10,7 +10,7 @@ runs:
cargo install cargo-nextest --locked
- name: Install Node
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "18"

View File

@@ -16,11 +16,11 @@ runs:
run: cargo install cargo-nextest --locked
- name: Install Node
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "18"
- name: Run tests
shell: pwsh
working-directory: ${{ inputs.working-directory }}
run: cargo nextest run --workspace --no-fail-fast
run: cargo nextest run --workspace --no-fail-fast --config='profile.dev.debug="limited"'

View File

@@ -110,6 +110,39 @@ jobs:
input: "crates/proto/proto/"
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=${BUF_BASE_BRANCH},subdir=crates/proto/proto/"
workspace_hack:
timeout-minutes: 60
name: Check workspace-hack crate
needs: [job_spec]
if: |
github.repository_owner == 'zed-industries' &&
needs.job_spec.outputs.run_tests == 'true'
runs-on:
- buildjet-8vcpu-ubuntu-2204
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Add Rust to the PATH
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install cargo-hakari
uses: clechasseur/rs-cargo@8435b10f6e71c2e3d4d3b7573003a8ce4bfc6386 # v2
with:
command: install
args: cargo-hakari@0.9.35
- name: Check workspace-hack Cargo.toml is up-to-date
run: |
cargo hakari generate --diff || {
echo "To fix, run script/update-workspace-hack or script/update-workspace-hack.ps1";
false
}
- name: Check all crates depend on workspace-hack
run: |
cargo hakari manage-deps --dry-run || {
echo "To fix, run script/update-workspace-hack or script/update-workspace-hack.ps1"
false
}
style:
timeout-minutes: 60
name: Check formatting and spelling
@@ -192,7 +225,7 @@ jobs:
- name: Check for new vulnerable dependencies
if: github.event_name == 'pull_request'
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4
uses: actions/dependency-review-action@67d4f4bd7a9b17a0db54d2a7519187c65e339de8 # v4
with:
license-check: false
@@ -432,6 +465,8 @@ jobs:
- job_spec
- style
- migration_checks
# run_tests: If adding required tests, add them here and to script below.
- workspace_hack
- linux_tests
- build_remote_server
- macos_tests
@@ -448,11 +483,14 @@ jobs:
# Only check test jobs if they were supposed to run
if [[ "${{ needs.job_spec.outputs.run_tests }}" == "true" ]]; then
[[ "${{ needs.workspace_hack.result }}" != 'success' ]] && { RET_CODE=1; echo "Workspace Hack failed"; }
[[ "${{ needs.macos_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "macOS tests failed"; }
[[ "${{ needs.linux_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "Linux tests failed"; }
[[ "${{ needs.windows_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "Windows tests failed"; }
[[ "${{ needs.windows_clippy.result }}" != 'success' ]] && { RET_CODE=1; echo "Windows clippy failed"; }
[[ "${{ needs.build_remote_server.result }}" != 'success' ]] && { RET_CODE=1; echo "Remote server build failed"; }
# This check is intentionally disabled. See: https://github.com/zed-industries/zed/pull/28431
# [[ "${{ needs.migration_checks.result }}" != 'success' ]] && { RET_CODE=1; echo "Migration Checks failed"; }
fi
if [[ "$RET_CODE" -eq 0 ]]; then
echo "All tests passed successfully!"
@@ -481,7 +519,7 @@ jobs:
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
steps:
- name: Install Node
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "18"
@@ -556,7 +594,7 @@ jobs:
timeout-minutes: 60
name: Linux x86_x64 release bundle
runs-on:
- buildjet-16vcpu-ubuntu-2004
- buildjet-16vcpu-ubuntu-2004 # ubuntu 20.04 for minimal glibc
if: |
startsWith(github.ref, 'refs/tags/v')
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
@@ -584,26 +622,23 @@ jobs:
- name: Create Linux .tar.gz bundle
run: script/bundle-linux
- name: Upload Linux bundle to workflow run if main branch or specific label
- name: Upload Artifact to Workflow - zed (run-bundling)
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: |
github.ref == 'refs/heads/main'
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
with:
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
path: target/release/zed-*.tar.gz
- name: Upload Linux remote server to workflow run if main branch or specific label
- name: Upload Artifact to Workflow - zed-remote-server (run-bundling)
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: |
github.ref == 'refs/heads/main'
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
with:
name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.gz
path: target/zed-remote-server-linux-x86_64.gz
- name: Upload app bundle to release
- name: Upload Artifacts to release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) }}
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
@@ -642,29 +677,26 @@ jobs:
# This exports RELEASE_CHANNEL into env (GITHUB_ENV)
script/determine-release-channel
- name: Create and upload Linux .tar.gz bundle
- name: Create and upload Linux .tar.gz bundles
run: script/bundle-linux
- name: Upload Linux bundle to workflow run if main branch or specific label
- name: Upload Artifact to Workflow - zed (run-bundling)
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: |
github.ref == 'refs/heads/main'
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
with:
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
path: target/release/zed-*.tar.gz
- name: Upload Linux remote server to workflow run if main branch or specific label
- name: Upload Artifact to Workflow - zed-remote-server (run-bundling)
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: |
github.ref == 'refs/heads/main'
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
with:
name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.gz
path: target/zed-remote-server-linux-aarch64.gz
- name: Upload app bundle to release
- name: Upload Artifacts to release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) }}
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
@@ -674,6 +706,51 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
nix-build:
timeout-minutes: 60
name: Nix Build
continue-on-error: true
if: github.repository_owner == 'zed-industries' && contains(github.event.pull_request.labels.*.name, 'run-nix')
strategy:
fail-fast: false
matrix:
system:
- os: x86 Linux
runner: buildjet-16vcpu-ubuntu-2204
install_nix: true
- os: arm Mac
runner: [macOS, ARM64, test]
install_nix: false
runs-on: ${{ matrix.system.runner }}
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
GIT_LFS_SKIP_SMUDGE: 1 # breaks the livekit rust sdk examples which we don't actually depend on
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
- name: Set path
if: ${{ ! matrix.system.install_nix }}
run: |
echo "/nix/var/nix/profiles/default/bin" >> $GITHUB_PATH
echo "/Users/administrator/.nix-profile/bin" >> $GITHUB_PATH
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
if: ${{ matrix.system.install_nix }}
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
with:
name: zed-industries
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
skipPush: true
- run: nix build .#debug
- name: Limit /nix/store to 50GB
run: "[ $(du -sm /nix/store | cut -f1) -gt 50000 ] && nix-collect-garbage -d"
auto-release-preview:
name: Auto release preview
if: |

View File

@@ -22,7 +22,7 @@ jobs:
version: 9
- name: Setup Node
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "20"
cache: "pnpm"

View File

@@ -117,12 +117,10 @@ jobs:
export ZED_KUBE_NAMESPACE=production
export ZED_COLLAB_LOAD_BALANCER_SIZE_UNIT=10
export ZED_API_LOAD_BALANCER_SIZE_UNIT=2
export ZED_LLM_LOAD_BALANCER_SIZE_UNIT=2
elif [[ $GITHUB_REF_NAME = "collab-staging" ]]; then
export ZED_KUBE_NAMESPACE=staging
export ZED_COLLAB_LOAD_BALANCER_SIZE_UNIT=1
export ZED_API_LOAD_BALANCER_SIZE_UNIT=1
export ZED_LLM_LOAD_BALANCER_SIZE_UNIT=1
else
echo "cowardly refusing to deploy from an unknown branch"
exit 1
@@ -147,9 +145,3 @@ jobs:
envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f -
kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch
echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}"
export ZED_SERVICE_NAME=llm
export ZED_LOAD_BALANCER_SIZE_UNIT=$ZED_LLM_LOAD_BALANCER_SIZE_UNIT
envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f -
kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch
echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}"

80
.github/workflows/eval.yml vendored Normal file
View File

@@ -0,0 +1,80 @@
name: Run Agent Eval
on:
schedule:
- cron: "0 0 * * *"
pull_request:
branches:
- "**"
types: [opened, synchronize, reopened, labeled]
workflow_dispatch:
concurrency:
# Allow only one workflow per any non-`main` branch.
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_EVAL_TELEMETRY: 1
jobs:
# This is a no-op job that we run to prevent GitHub from marking the workflow
# as failed for PRs that don't have the `run-eval` label.
noop:
name: No-op
runs-on: ubuntu-latest
steps:
- name: No-op
run: echo "Nothing to do"
run_eval:
timeout-minutes: 60
name: Run Agent Eval
if: >
github.repository_owner == 'zed-industries' &&
(github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-eval'))
runs-on:
- buildjet-16vcpu-ubuntu-2204
steps:
- name: Add Rust to the PATH
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
- name: Cache dependencies
uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-provider: "buildjet"
- name: Install Linux dependencies
run: ./script/linux
- name: Configure CI
run: |
mkdir -p ./../.cargo
cp ./.cargo/ci-config.toml ./../.cargo/config.toml
- name: Compile eval
run: cargo build --package=eval
- name: Run eval
run: cargo run --package=eval -- --repetitions=3 --concurrency=1
# Even the Linux runner is not stateful, in theory there is no need to do this cleanup.
# But, to avoid potential issues in the future if we choose to use a stateful Linux runner and forget to add code
# to clean up the config file, Ive included the cleanup code here as a precaution.
# While its not strictly necessary at this moment, I believe its better to err on the side of caution.
- name: Clean CI config file
if: always()
run: rm -rf ./../.cargo

View File

@@ -18,7 +18,7 @@ jobs:
version: 9
- name: Setup Node
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "20"
cache: "pnpm"

View File

@@ -23,7 +23,7 @@ jobs:
- buildjet-16vcpu-ubuntu-2204
steps:
- name: Install Node
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "18"

View File

@@ -71,7 +71,7 @@ jobs:
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
steps:
- name: Install Node
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "18"
@@ -184,9 +184,6 @@ jobs:
- os: arm Mac
runner: [macOS, ARM64, test]
install_nix: false
- os: arm Linux
runner: buildjet-16vcpu-ubuntu-2204-arm
install_nix: true
if: github.repository_owner == 'zed-industries'
runs-on: ${{ matrix.system.runner }}
needs: tests
@@ -209,7 +206,7 @@ jobs:
echo "/nix/var/nix/profiles/default/bin" >> $GITHUB_PATH
echo "/Users/administrator/.nix-profile/bin" >> $GITHUB_PATH
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
if: ${{ matrix.system.install_nix }}
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
@@ -219,7 +216,8 @@ jobs:
name: zed-industries
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- run: nix build
- run: nix-collect-garbage -d
- name: Limit /nix/store to 50GB
run: '[ $(du -sm /nix/store | cut -f1) -gt 50000 ] && nix-collect-garbage -d'
update-nightly-tag:
name: Update nightly tag

3
.gitignore vendored
View File

@@ -18,12 +18,12 @@
.venv
.vscode
.wrangler
/.direnv
/assets/*licenses.*
/crates/collab/seed.json
/crates/theme/schemas/theme.json
/crates/zed/resources/flatpak/flatpak-cargo-sources.json
/dev.zed.Zed*.json
/node_modules/
/plugins/bin
/script/node_modules
/snap
@@ -33,4 +33,5 @@ Packages
xcuserdata/
# Don't commit any secrets to the repo.
.env
.env.secret.toml

126
.rules Normal file
View File

@@ -0,0 +1,126 @@
# Rust coding guidelines
* Prioritize code correctness and clarity. Speed and efficiency are secondary priorities unless otherwise specified.
* Do not write organizational or comments that summarize the code. Comments should only be written in order to explain "why" the code is written in some way in the case there is a reason that is tricky / non-obvious.
* Prefer implementing functionality in existing files unless it is a new logical component. Avoid creating many small files.
* Avoid using functions that panic like `unwrap()`, instead use mechanisms like `?` to propagate errors.
* Be careful with operations like indexing which may panic if the indexes are out of bounds.
* Never create files with `mod.rs` paths - prefer `src/some_module.rs` instead of `src/some_module/mod.rs`.
# GPUI
GPUI is a UI framework which also provides primitives for state and concurrency management.
## Context
Context types allow interaction with global state, windows, entities, and system services. They are typically passed to functions as the argument named `cx`. When a function takes callbacks they come after the `cx` parameter.
* `App` is the root context type, providing access to global state and read and update of entities.
* `Context<T>` is provided when updating an `Entity<T>`. This context dereferences into `App`, so functions which take `&App` can also take `&Context<T>`.
* `AsyncApp` and `AsyncWindowContext` are provided by `cx.spawn` and `cx.spawn_in`. These can be held across await points.
## `Window`
`Window` provides access to the state of an application window. It is passed to functions as an argument named `window` and comes before `cx` when present. It is used for managing focus, dispatching actions, directly drawing, getting user input state, etc.
## Entities
An `Entity<T>` is a handle to state of type `T`. With `thing: Entity<T>`:
* `thing.entity_id()` returns `EntityId`
* `thing.downgrade()` returns `WeakEntity<T>`
* `thing.read(cx: &App)` returns `&T`.
* `thing.read_with(cx, |thing: &T, cx: &App| ...)` returns the closure's return value.
* `thing.update(cx, |thing: &mut T, cx: &mut Context<T>| ...)` allows the closure to mutate the state, and provides a `Context<T>` for interacting with the entity. It returns the closure's return value.
* `thing.update_in(cx, |thing: &mut T, window: &mut Window, cx: &mut Context<T>| ...)` takes a `AsyncWindowContext` or `VisualTestContext`. It's the same as `update` while also providing the `Window`.
Within the closures, the inner `cx` provided to the closure must be used instead of the outer `cx` to avoid issues with multiple borrows.
Trying to update an entity while it's already being updated must be avoided as this will cause a panic.
When `read_with`, `update`, or `update_in` are used with an async context, the closure's return value is wrapped in an `anyhow::Result`.
`WeakEntity<T>` is a weak handle. It has `read_with`, `update`, and `update_in` methods that work the same, but always return an `anyhow::Result` so that they can fail if the entity no longer exists. This can be useful to avoid memory leaks - if entities have mutually recursive handles to eachother they will never be dropped.
## Concurrency
All use of entities and UI rendering occurs on a single foreground thread.
`cx.spawn(async move |cx| ...)` runs an async closure on the foreground thread. Within the closure, `cx` is an async context like `AsyncApp` or `AsyncWindowContext`.
When the outer cx is a `Context<T>`, the use of `spawn` instead looks like `cx.spawn(async move |handle, cx| ...)`, where `handle: WeakEntity<T>`.
To do work on other threads, `cx.background_spawn(async move { ... })` is used. Often this background task is awaited on by a foreground task which uses the results to update state.
Both `cx.spawn` and `cx.background_spawn` return a `Task<R>`, which is a future that can be awaited upon. If this task is dropped, then its work is cancelled. To prevent this one of the following must be done:
* Awaiting the task in some other async context.
* Detaching the task via `task.detach()` or `task.detach_and_log_err(cx)`, allowing it to run indefinitely.
* Storing the task in a field, if the work should be halted when the struct is dropped.
A task which doesn't do anything but provide a value can be created with `Task::ready(value)`.
## Elements
The `Render` trait is used to render some state into an element tree that is laid out using flexbox layout. An `Entity<T>` where `T` implements `Render` is sometimes called a "view".
Example:
```
struct TextWithBorder(SharedString);
impl Render for TextWithBorder {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
div().border_1().child(self.0.clone())
}
}
```
Since `impl IntoElement for SharedString` exists, it can be used as an argument to `child`. `SharedString` is used to avoid copying strings, and is either an `&'static str` or `Arc<str>`.
UI components that are constructed just to be turned into elements can instead implement the `RenderOnce` trait, which is similar to `Render`, but its `render` method takes ownership of `self`. Types that implement this trait can use `#[derive(IntoElement)]` to use them directly as children.
The style methods on elements are similar to those used by Tailwind CSS.
If some attributes or children of an element tree are conditional, `.when(condition, |this| ...)` can be used to run the closure only when `condition` is true. Similarly, `.when_some(option, |this, value| ...)` runs the closure when the `Option` has a value.
## Input events
Input event handlers can be registered on an element via methods like `.on_click(|event, window, cx: &mut App| ...)`.
Often event handlers will want to update the entity that's in the current `Context<T>`. The `cx.listener` method provides this - its use looks like `.on_click(cx.listener(|this: &mut T, event, window, cx: &mut Context<T>| ...)`.
## Actions
Actions are dispatched via user keyboard interaction or in code via `window.dispatch_action(SomeAction.boxed_clone(), cx)` or `focus_handle.dispatch_action(&SomeAction, window, cx)`.
Actions which have no data inside are created and registered with the `actions!(some_namespace, [SomeAction, AnotherAction])` macro call.
Actions that do have data must implement `Clone, Default, PartialEq, Deserialize, JsonSchema` and can be registered with an `impl_actions!(some_namespace, [SomeActionWithData])` macro call.
Action handlers can be registered on an element via the event handler `.on_action(|action, window, cx| ...)`. Like other event handlers, this is often used with `cx.listener`.
## Notify
When a view's state has changed in a way that may affect its rendering, it should call `cx.notify()`. This will cause the view to be rerendered. It will also cause any observe callbacks registered for the entity with `cx.observe` to be called.
## Entity events
While updating an entity (`cx: Context<T>`), it can emit an event using `cx.emit(event)`. Entities register which events they can emit by declaring `impl EventEmittor<EventType> for EntityType {}`.
Other entities can then register a callback to handle these events by doing `cx.subscribe(other_entity, |this, other_entity, event, cx| ...)`. This will return a `Subscription` which deregisters the callback when dropped. Typically `cx.subscribe` happens when creating a new entity and the subscriptions are stored in a `_subscriptions: Vec<Subscription>` field.
## Recent API changes
GPUI has had some changes to its APIs. Always write code using the new APIs:
* `spawn` methods now take async closures (`AsyncFn`), and so should be called like `cx.spawn(async move |cx| ...)`.
* Use `Entity<T>`. This replaces `Model<T>` and `View<T>` which longer exists and should NEVER be used.
* Use `App` references. This replaces `AppContext` which no longer exists and should NEVER be used.
* Use `Context<T>` references. This replaces `ModelContext<T>` which no longer exists and should NEVER be used.
* `Window` is now passed around explicitly. The new interface adds a `Window` reference parameter to some methods, and adds some new "*_in" methods for plumbing `Window`. The old types `WindowContext` and `ViewContext<T>` should NEVER be used.
## General guidelines
- Use `./script/clippy` instead of `cargo clippy`

1
.windsurfrules Symbolic link
View File

@@ -0,0 +1 @@
.rules

View File

@@ -1,14 +1,14 @@
[
{
"label": "Debug Zed with LLDB",
"adapter": "lldb",
"label": "Debug Zed (CodeLLDB)",
"adapter": "CodeLLDB",
"program": "$ZED_WORKTREE_ROOT/target/debug/zed",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT"
},
{
"label": "Debug Zed with GDB",
"adapter": "gdb",
"label": "Debug Zed (GDB)",
"adapter": "GDB",
"program": "$ZED_WORKTREE_ROOT/target/debug/zed",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT",

View File

@@ -45,5 +45,6 @@
"hard_tabs": false,
"formatter": "auto",
"remove_trailing_whitespace_on_save": true,
"ensure_final_newline_on_save": true
"ensure_final_newline_on_save": true,
"file_scan_exclusions": ["crates/eval/worktrees/", "crates/eval/repos/"]
}

1
CLAUDE.md Symbolic link
View File

@@ -0,0 +1 @@
.rules

3270
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,13 +2,12 @@
resolver = "2"
members = [
"crates/activity_indicator",
"crates/agent",
"crates/anthropic",
"crates/askpass",
"crates/assets",
"crates/assistant",
"crates/assistant2",
"crates/assistant_context_editor",
"crates/assistant_eval",
"crates/assistant_settings",
"crates/assistant_slash_command",
"crates/assistant_slash_commands",
@@ -16,6 +15,7 @@ members = [
"crates/assistant_tools",
"crates/audio",
"crates/auto_update",
"crates/auto_update_helper",
"crates/auto_update_ui",
"crates/aws_http_client",
"crates/bedrock",
@@ -33,20 +33,21 @@ members = [
"crates/command_palette_hooks",
"crates/component",
"crates/component_preview",
"crates/component_state",
"crates/context_server",
"crates/context_server_settings",
"crates/copilot",
"crates/credentials_provider",
"crates/dap",
"crates/dap_adapters",
"crates/db",
"crates/debugger_tools",
"crates/debugger_ui",
"crates/db",
"crates/deepseek",
"crates/diagnostics",
"crates/docs_preprocessor",
"crates/editor",
"crates/evals",
"crates/eval",
"crates/extension",
"crates/extension_api",
"crates/extension_cli",
@@ -109,7 +110,6 @@ members = [
"crates/project",
"crates/project_panel",
"crates/project_symbols",
"crates/prompt_library",
"crates/prompt_store",
"crates/proto",
"crates/recent_projects",
@@ -123,6 +123,7 @@ members = [
"crates/rich_text",
"crates/rope",
"crates/rpc",
"crates/rules_library",
"crates/schema_generator",
"crates/search",
"crates/semantic_index",
@@ -164,6 +165,8 @@ members = [
"crates/util_macros",
"crates/vim",
"crates/vim_mode_setting",
"crates/web_search",
"crates/web_search_providers",
"crates/welcome",
"crates/workspace",
"crates/worktree",
@@ -192,6 +195,7 @@ members = [
# Tooling
#
"tooling/workspace-hack",
"tooling/xtask",
]
default-members = ["crates/zed"]
@@ -207,14 +211,13 @@ edition = "2024"
#
activity_indicator = { path = "crates/activity_indicator" }
agent = { path = "crates/agent" }
ai = { path = "crates/ai" }
anthropic = { path = "crates/anthropic" }
askpass = { path = "crates/askpass" }
assets = { path = "crates/assets" }
assistant = { path = "crates/assistant" }
assistant2 = { path = "crates/assistant2" }
assistant_context_editor = { path = "crates/assistant_context_editor" }
assistant_eval = { path = "crates/assistant_eval" }
assistant_settings = { path = "crates/assistant_settings" }
assistant_slash_command = { path = "crates/assistant_slash_command" }
assistant_slash_commands = { path = "crates/assistant_slash_commands" }
@@ -222,10 +225,12 @@ assistant_tool = { path = "crates/assistant_tool" }
assistant_tools = { path = "crates/assistant_tools" }
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" }
breadcrumbs = { path = "crates/breadcrumbs" }
buffer_diff = { path = "crates/buffer_diff" }
call = { path = "crates/call" }
channel = { path = "crates/channel" }
cli = { path = "crates/cli" }
@@ -238,6 +243,7 @@ command_palette = { path = "crates/command_palette" }
command_palette_hooks = { path = "crates/command_palette_hooks" }
component = { path = "crates/component" }
component_preview = { path = "crates/component_preview" }
component_state = { path = "crates/component_state" }
context_server = { path = "crates/context_server" }
context_server_settings = { path = "crates/context_server_settings" }
copilot = { path = "crates/copilot" }
@@ -245,11 +251,10 @@ credentials_provider = { path = "crates/credentials_provider" }
dap = { path = "crates/dap" }
dap_adapters = { path = "crates/dap_adapters" }
db = { path = "crates/db" }
debugger_ui = { path = "crates/debugger_ui" }
debugger_tools = { path = "crates/debugger_tools" }
debugger_ui = { path = "crates/debugger_ui" }
deepseek = { path = "crates/deepseek" }
diagnostics = { path = "crates/diagnostics" }
buffer_diff = { path = "crates/buffer_diff" }
editor = { path = "crates/editor" }
extension = { path = "crates/extension" }
extension_host = { path = "crates/extension_host" }
@@ -306,8 +311,8 @@ ollama = { path = "crates/ollama" }
open_ai = { path = "crates/open_ai" }
outline = { path = "crates/outline" }
outline_panel = { path = "crates/outline_panel" }
paths = { path = "crates/paths" }
panel = { path = "crates/panel" }
paths = { path = "crates/paths" }
picker = { path = "crates/picker" }
plugin = { path = "crates/plugin" }
plugin_macros = { path = "crates/plugin_macros" }
@@ -315,7 +320,6 @@ prettier = { path = "crates/prettier" }
project = { path = "crates/project" }
project_panel = { path = "crates/project_panel" }
project_symbols = { path = "crates/project_symbols" }
prompt_library = { path = "crates/prompt_library" }
prompt_store = { path = "crates/prompt_store" }
proto = { path = "crates/proto" }
recent_projects = { path = "crates/recent_projects" }
@@ -328,6 +332,7 @@ reqwest_client = { path = "crates/reqwest_client" }
rich_text = { path = "crates/rich_text" }
rope = { path = "crates/rope" }
rpc = { path = "crates/rpc" }
rules_library = { path = "crates/rules_library" }
search = { path = "crates/search" }
semantic_index = { path = "crates/semantic_index" }
semantic_version = { path = "crates/semantic_version" }
@@ -368,6 +373,8 @@ util = { path = "crates/util" }
util_macros = { path = "crates/util_macros" }
vim = { path = "crates/vim" }
vim_mode_setting = { path = "crates/vim_mode_setting" }
web_search = { path = "crates/web_search" }
web_search_providers = { path = "crates/web_search_providers" }
welcome = { path = "crates/welcome" }
workspace = { path = "crates/workspace" }
worktree = { path = "crates/worktree" }
@@ -395,20 +402,23 @@ async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "8
async-recursion = "1.0.0"
async-tar = "0.5.0"
async-trait = "0.1"
async-tungstenite = "0.28"
async-tungstenite = "0.29.1"
async-watch = "0.3.1"
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
aws-config = { version = "1.5.16", features = ["behavior-version-latest"] }
aws-credential-types = { version = "1.2.1", features = ["hardcoded-credentials"] }
aws-sdk-bedrockruntime = { version = "1.73.0", features = ["behavior-version-latest"] }
aws-smithy-runtime-api = { version = "1.7.3", features = ["http-1x", "client"] }
aws-smithy-types = { version = "1.2.13", features = ["http-body-1-x"] }
aws-config = { version = "1.6.1", features = ["behavior-version-latest"] }
aws-credential-types = { version = "1.2.2", features = [
"hardcoded-credentials",
] }
aws-sdk-bedrockruntime = { version = "1.80.0", features = [
"behavior-version-latest",
] }
aws-smithy-runtime-api = { version = "1.7.4", features = ["http-1x", "client"] }
aws-smithy-types = { version = "1.3.0", features = ["http-body-1-x"] }
base64 = "0.22"
bitflags = "2.6.0"
blade-graphics = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
blade-util = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
naga = { version = "23.1.0", features = ["wgsl-in"] }
blake3 = "1.5.3"
bytes = "1.0"
cargo_metadata = "0.19"
@@ -418,15 +428,16 @@ circular-buffer = "1.0"
clap = { version = "4.4", features = ["derive"] }
cocoa = "0.26"
cocoa-foundation = "0.2.0"
core-video = { version = "0.4.3", features = ["metal"] }
convert_case = "0.8.0"
core-foundation = "0.10.0"
core-foundation-sys = "0.8.6"
core-video = { version = "0.4.3", features = ["metal"] }
ctor = "0.4.0"
dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "be69a016ba710191b9fdded28c8b042af4b617f7" }
dashmap = "6.0"
dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "bfd4af0" }
derive_more = "0.99.17"
dirs = "4.0"
dotenv = "0.15.0"
ec4rs = "1.1"
emojis = "0.6.1"
env_logger = "0.11"
@@ -439,11 +450,12 @@ futures-lite = "1.13"
git2 = { version = "0.20.1", default-features = false }
globset = "0.4"
handlebars = "4.3"
heck = "0.5"
heed = { version = "0.21.0", features = ["read-txn-no-tls"] }
hex = "0.4.3"
html5ever = "0.27.0"
hyper = "0.14"
http = "1.1"
hyper = "0.14"
ignore = "0.4.22"
image = "0.25.1"
imara-diff = "0.1.8"
@@ -452,31 +464,36 @@ indoc = "2"
inventory = "0.3.19"
itertools = "0.14.0"
jsonwebtoken = "9.3"
jupyter-protocol = { version = "0.6.0" }
jupyter-websocket-client = { version = "0.9.0" }
jupyter-protocol = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
jupyter-websocket-client = { git = "https://github.com/ConradIrwin/runtimed" ,rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
libc = "0.2"
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
linkify = "0.10.0"
linkme = "0.3.31"
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "c9c189f1c5dd53c624a419ce35bc77ad6a908d18" }
markup5ever_rcdom = "0.3.0"
metal = "0.29"
mlua = { version = "0.10", features = ["lua54", "vendored", "async", "send"] }
naga = { version = "23.1.0", features = ["wgsl-in"] }
nanoid = "0.4"
nbformat = { version = "0.10.0" }
nbformat = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734" }
nix = "0.29"
open = "5.0.0"
num-format = "0.4.4"
objc = "0.2"
open = "5.0.0"
ordered-float = "2.1.1"
palette = { version = "0.7.5", default-features = false, features = ["std"] }
parking_lot = "0.12.1"
partial-json-fixer = "0.5.3"
pathdiff = "0.2"
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
pet-pixi = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" }
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-pixi = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
postage = { version = "0.5", features = ["futures-traits"] }
pretty_assertions = { version = "1.3.0", features = ["unstable"] }
proc-macro2 = "1.0.93"
@@ -488,9 +505,10 @@ pulldown-cmark = { version = "0.12.0", default-features = false }
quote = "1.0.9"
rand = "0.8.5"
rayon = "1.8"
ref-cast = "1.0.24"
regex = "1.5"
repair_json = "0.1.0"
reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "fd110f6998da16bbca97b6dddda9be7827c50e29", default-features = false, features = [
reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "951c770a32f1998d6e999cef3e59e0013e6c4415", default-features = false, features = [
"charset",
"http2",
"macos-system-configuration",
@@ -499,14 +517,15 @@ reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "fd110f
"stream",
] }
rsa = "0.9.6"
runtimelib = { version = "0.25.0", default-features = false, features = [
runtimelib = { git = "https://github.com/ConradIrwin/runtimed", rev = "7130c804216b6914355d15d0b91ea91f6babd734", default-features = false, features = [
"async-dispatcher-runtime",
] }
rustc-demangle = "0.1.23"
rust-embed = { version = "8.4", features = ["include-exclude"] }
rustc-demangle = "0.1.23"
rustc-hash = "2.1.0"
rustls = { version = "0.23.22" }
rustls = { version = "0.23.26" }
rustls-platform-verifier = "0.5.0"
scap = { git = "https://github.com/zed-industries/scap", rev = "08f0a01417505cc0990b9931a37e5120db92e0d0", default-features = false }
schemars = { version = "0.8", features = ["impl_json_schema", "indexmap2"] }
semver = "1.0"
serde = { version = "1.0", features = ["derive", "rc"] }
@@ -527,7 +546,7 @@ smol = "2.0"
sqlformat = "0.2"
streaming-iterator = "0.1"
strsim = "0.11"
strum = { version = "0.26.0", features = ["derive"] }
strum = { version = "0.27.0", features = ["derive"] }
subtle = "2.5.0"
syn = { version = "1.0.72", features = ["full", "extra-traits"] }
sys-locale = "0.3.1"
@@ -544,15 +563,16 @@ time = { version = "0.3", features = [
"formatting",
] }
tiny_http = "0.8"
toml = "0.8"
tokio = { version = "1" }
tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"]}
tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"] }
toml = "0.8"
tower-http = "0.4.4"
tree-sitter = { version = "0.25.3", features = ["wasm"] }
tree-sitter-bash = "0.23"
tree-sitter-c = "0.23"
tree-sitter-cpp = "0.23"
tree-sitter-css = "0.23"
tree-sitter-diff = "0.1.0"
tree-sitter-elixir = "0.3"
tree-sitter-embedded-template = "0.23.0"
tree-sitter-gitcommit = { git = "https://github.com/zed-industries/tree-sitter-git-commit", rev = "88309716a69dd13ab83443721ba6e0b491d37ee9" }
@@ -560,7 +580,6 @@ tree-sitter-go = "0.23"
tree-sitter-go-mod = { git = "https://github.com/camdencheek/tree-sitter-go-mod", rev = "6efb59652d30e0e9cd5f3b3a669afd6f1a926d3c", package = "tree-sitter-gomod" }
tree-sitter-gowork = { git = "https://github.com/zed-industries/tree-sitter-go-work", rev = "acb0617bf7f4fda02c6217676cc64acb89536dc7" }
tree-sitter-heex = { git = "https://github.com/zed-industries/tree-sitter-heex", rev = "1dd45142fbb05562e35b2040c6129c9bca346592" }
tree-sitter-diff = "0.1.0"
tree-sitter-html = "0.23"
tree-sitter-jsdoc = "0.23"
tree-sitter-json = "0.24"
@@ -568,18 +587,19 @@ tree-sitter-md = { git = "https://github.com/tree-sitter-grammars/tree-sitter-ma
tree-sitter-python = "0.23"
tree-sitter-regex = "0.24"
tree-sitter-ruby = "0.23"
tree-sitter-rust = "0.23"
tree-sitter-rust = "0.24"
tree-sitter-typescript = "0.23"
tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "baff0b51c64ef6a1fb1f8390f3ad6015b83ec13a" }
unicase = "2.6"
unindent = "0.2.0"
unicode-segmentation = "1.10"
unicode-script = "0.5.7"
unicode-segmentation = "1.10"
unindent = "0.2.0"
url = "2.2"
urlencoding = "2.1.2"
uuid = { version = "1.1.2", features = ["v4", "v5", "v7", "serde"] }
wasmparser = "0.221"
walkdir = "2.3"
wasm-encoder = "0.221"
wasmparser = "0.221"
wasmtime = { version = "29", default-features = false, features = [
"async",
"demangle",
@@ -590,9 +610,9 @@ wasmtime = { version = "29", default-features = false, features = [
wasmtime-wasi = "29"
which = "6.0.0"
wit-component = "0.221"
zed_llm_client = "0.4"
workspace-hack = "0.1.0"
zed_llm_client = "0.7.1"
zstd = "0.11"
metal = "0.29"
[workspace.dependencies.async-stripe]
git = "https://github.com/zed-industries/async-stripe"
@@ -611,12 +631,10 @@ features = [
[workspace.dependencies.windows]
version = "0.61"
features = [
"Foundation_Collections",
"Foundation_Numerics",
"Storage_Search",
"Storage_Streams",
"System_Threading",
"UI_StartScreen",
"UI_ViewManagement",
"Wdk_System_SystemServices",
"Win32_Globalization",
@@ -643,6 +661,7 @@ features = [
"Win32_System_SystemInformation",
"Win32_System_SystemServices",
"Win32_System_Threading",
"Win32_System_Variant",
"Win32_System_WinRT",
"Win32_UI_Controls",
"Win32_UI_HiDpi",
@@ -650,17 +669,21 @@ features = [
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Shell",
"Win32_UI_Shell_Common",
"Win32_UI_Shell_PropertiesSystem",
"Win32_UI_WindowsAndMessaging",
]
# TODO livekit https://github.com/RustAudio/cpal/pull/891
[patch.crates-io]
cpal = { git = "https://github.com/zed-industries/cpal", rev = "fd8bc2fd39f1f5fdee5a0690656caff9a26d9d50" }
real-async-tls = { git = "https://github.com/zed-industries/async-tls", rev = "1e759a4b5e370f87dc15e40756ac4f8815b61d9d", package = "async-tls" }
notify = { git = "https://github.com/zed-industries/notify.git", rev = "bbb9ea5ae52b253e095737847e367c30653a2e96" }
notify-types = { git = "https://github.com/zed-industries/notify.git", rev = "bbb9ea5ae52b253e095737847e367c30653a2e96" }
# Makes the workspace hack crate refer to the local one, but only when you're building locally
workspace-hack = { path = "tooling/workspace-hack" }
[profile.dev]
split-debuginfo = "unpacked"
debug = "limited"
codegen-units = 16
[profile.dev.package]
@@ -678,7 +701,6 @@ breadcrumbs = { codegen-units = 1 }
collections = { codegen-units = 1 }
command_palette = { codegen-units = 1 }
command_palette_hooks = { codegen-units = 1 }
evals = { codegen-units = 1 }
extension_cli = { codegen-units = 1 }
feature_flags = { codegen-units = 1 }
file_icons = { codegen-units = 1 }
@@ -770,4 +792,12 @@ let_underscore_future = "allow"
too_many_arguments = "allow"
[workspace.metadata.cargo-machete]
ignored = ["bindgen", "cbindgen", "prost_build", "serde", "component", "linkme"]
ignored = [
"bindgen",
"cbindgen",
"prost_build",
"serde",
"component",
"linkme",
"workspace-hack",
]

View File

@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.2
FROM rust:1.81-bookworm as builder
FROM rust:1.86-bookworm as builder
WORKDIR app
COPY . .

View File

@@ -1,12 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" rx="2" fill="black" fill-opacity="0.2"/>
<g clip-path="url(#clip0_1916_18)">
<path d="M10.652 3.79999H8.816L12.164 12.2H14L10.652 3.79999Z" fill="#1F1F1E"/>
<path d="M5.348 3.79999L2 12.2H3.872L4.55672 10.436H8.05927L8.744 12.2H10.616L7.268 3.79999H5.348ZM5.16224 8.87599L6.308 5.92399L7.45374 8.87599H5.16224Z" fill="#1F1F1E"/>
</g>
<defs>
<clipPath id="clip0_1916_18">
<rect width="12" height="8.4" fill="white" transform="translate(2 3.79999)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 601 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-right-icon lucide-arrow-down-right"><path d="m7 7 10 10"/><path d="M17 7v10H7"/></svg>

After

Width:  |  Height:  |  Size: 300 B

View File

@@ -1,3 +1 @@
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 2H6.5C6.5 1.86739 6.44732 1.74021 6.35355 1.64645C6.25979 1.55268 6.13261 1.5 6 1.5V2ZM2 1.5C1.72386 1.5 1.5 1.72386 1.5 2C1.5 2.27614 1.72386 2.5 2 2.5L2 1.5ZM5.5 6C5.5 6.27614 5.72386 6.5 6 6.5C6.27614 6.5 6.5 6.27614 6.5 6H5.5ZM1.64645 5.64645C1.45118 5.84171 1.45118 6.15829 1.64645 6.35355C1.84171 6.54882 2.15829 6.54882 2.35355 6.35355L1.64645 5.64645ZM6 1.5H2L2 2.5H6V1.5ZM5.5 2V6H6.5V2H5.5ZM5.64645 1.64645L1.64645 5.64645L2.35355 6.35355L6.35355 2.35355L5.64645 1.64645Z" fill="white"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right-icon lucide-arrow-up-right"><path d="M7 7h10v10"/><path d="M7 17 17 7"/></svg>

Before

Width:  |  Height:  |  Size: 608 B

After

Width:  |  Height:  |  Size: 296 B

1
assets/icons/binary.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-binary-icon lucide-binary"><rect x="14" y="14" width="4" height="6" rx="2"/><rect x="6" y="4" width="4" height="6" rx="2"/><path d="M6 20h4"/><path d="M14 10h4"/><path d="M6 14h2v6"/><path d="M14 4h2v6"/></svg>

After

Width:  |  Height:  |  Size: 413 B

1
assets/icons/bug_off.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bug-off-icon lucide-bug-off"><path d="M15 7.13V6a3 3 0 0 0-5.14-2.1L8 2"/><path d="M14.12 3.88 16 2"/><path d="M22 13h-4v-2a4 4 0 0 0-4-4h-1.3"/><path d="M20.97 5c0 2.1-1.6 3.8-3.5 4"/><path d="m2 2 20 20"/><path d="M7.7 7.7A4 4 0 0 0 6 11v3a6 6 0 0 0 11.13 3.13"/><path d="M12 20v-8"/><path d="M6 13H2"/><path d="M3 21c0-2.1 1.7-3.9 3.8-4"/></svg>

After

Width:  |  Height:  |  Size: 551 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-check-icon lucide-check-check"><path d="M18 6 7 17l-5-5"/><path d="m22 10-7.5 7.5L13 16"/></svg>

After

Width:  |  Height:  |  Size: 305 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-off-icon lucide-circle-off"><path d="m2 2 20 20"/><path d="M8.35 2.69A10 10 0 0 1 21.3 15.65"/><path d="M19.08 19.08A10 10 0 1 1 4.92 4.92"/></svg>

After

Width:  |  Height:  |  Size: 357 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" fill="none" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g style="fill:#000;fill-opacity:1" fill="#180c25"><path d="m-116.1-101.4-28.9-28.9a6.7 6.7 0 0 1-1.8-4.7v-41.2c0-2.4-2.4-4.8-4.8-4.8h-9.6a5.2 5.2 0 0 0-4.8 4.8v48c0 2.5 1 5 2.7 6.8l33.6 33.6a9.6 9.6 0 0 0 6.8 2.8h4.8c2.7 0 4.8-2.2 4.8-4.8v-4.8c0-2.5-1-5-2.8-6.8zM-79.6-176.2c0-2.4-2.4-4.8-4.8-4.8h-9.7a5.2 5.2 0 0 0-4.7 4.8v41.2c0 1.8-.8 3.5-2 4.7l-9.6 9.7a9.5 9.5 0 0 0-2.8 6.8v4.8c0 2.6 2.1 4.7 4.8 4.7h4.8c2.4 0 4.9-.9 6.7-2.8l14.4-14.3a9.6 9.6 0 0 0 2.8-6.8v-48z" style="fill:#000;fill-opacity:1;stroke-width:.255894" transform="translate(21.6 22.7) scale(.11067)"/></g></svg>

After

Width:  |  Height:  |  Size: 677 B

1
assets/icons/flame.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-flame-icon lucide-flame"><path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/></svg>

After

Width:  |  Height:  |  Size: 415 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-forward-icon lucide-forward"><polyline points="15 17 20 12 15 7"/><path d="M4 18v-2a4 4 0 0 1 4-4h12"/></svg>

After

Width:  |  Height:  |  Size: 312 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-function-icon lucide-square-function"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><path d="M9 17c2 0 2.8-1 2.8-2.8V10c0-2 1-3.3 3.2-3"/><path d="M9 11.2h5.7"/></svg>

After

Width:  |  Height:  |  Size: 387 B

1
assets/icons/image.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-image-icon lucide-image"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>

After

Width:  |  Height:  |  Size: 372 B

5
assets/icons/layout.svg Normal file
View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 14H4C3.44772 14 3 14.4477 3 15V20C3 20.5523 3.44772 21 4 21H20C20.5523 21 21 20.5523 21 20V15C21 14.4477 20.5523 14 20 14Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 3H4C3.44772 3 3 3.44772 3 4V9C3 9.55228 3.44772 10 4 10H11C11.5523 10 12 9.55228 12 9V4C12 3.44772 11.5523 3 11 3Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M20 3H17C16.4477 3 16 3.44772 16 4V9C16 9.55228 16.4477 10 17 10H20C20.5523 10 21 9.55228 21 9V4C21 3.44772 20.5523 3 20 3Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 746 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.1331 11.3776C10.2754 10.6665 10.1331 9.78593 11.1998 8.53327C11.82 7.80489 12.2664 6.96894 12.2664 6.04456C12.2664 4.91305 11.8169 3.82788 11.0168 3.02778C10.2167 2.22769 9.13152 1.7782 8.00001 1.7782C6.8685 1.7782 5.78334 2.22769 4.98324 3.02778C4.18314 3.82788 3.73364 4.91305 3.73364 6.04456C3.73364 6.75562 3.87586 7.6089 4.80024 8.53327C5.86683 9.80679 5.72462 10.6665 5.86683 11.3776M10.1331 11.3776V12.8821C10.1331 13.622 9.53341 14.2218 8.79353 14.2218H7.2065C6.46662 14.2218 5.86683 13.622 5.86683 12.8821V11.3776M10.1331 11.3776H5.86683" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 751 B

1
assets/icons/power.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-power-icon lucide-power"><path d="M12 2v10"/><path d="M18.4 6.6a9 9 0 1 1-12.77.04"/></svg>

After

Width:  |  Height:  |  Size: 294 B

4
assets/icons/send.svg Normal file
View File

@@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.09666 3.02263C3.0567 3.00312 3.01178 2.9961 2.96778 3.0025C2.92377 3.00889 2.88271 3.02839 2.84995 3.05847C2.8172 3.08854 2.79426 3.12778 2.78413 3.17108C2.77401 3.21439 2.77716 3.25973 2.79319 3.30121L4.05638 6.69C4.13088 6.89005 4.13088 7.11022 4.05638 7.31027L2.79363 10.6991C2.77769 10.7405 2.77457 10.7858 2.78469 10.829C2.79481 10.8722 2.8177 10.9114 2.85038 10.9414C2.88306 10.9715 2.92402 10.991 2.96794 10.9975C3.01186 11.0039 3.05671 10.997 3.09666 10.9776L11.0943 7.20097C11.1324 7.18297 11.1645 7.15455 11.187 7.11899C11.2096 7.08344 11.2215 7.04222 11.2215 7.00014C11.2215 6.95805 11.2096 6.91683 11.187 6.88128C11.1645 6.84573 11.1324 6.8173 11.0943 6.79931L3.09666 3.02263Z" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.11255 7.00014H11.2216" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1014 B

View File

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 9.8V4.2C4 4.08954 4.08954 4 4.2 4H9.8C9.91046 4 10 4.08954 10 4.2V9.8C10 9.91046 9.91046 10 9.8 10H4.2C4.08954 10 4 9.91046 4 9.8Z" fill="#C56757" stroke="#C56757" stroke-width="1.25" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 325 B

View File

@@ -49,15 +49,6 @@
"down": "menu::SelectNext"
}
},
{
"context": "Prompt",
"bindings": {
"left": "menu::SelectPrevious",
"right": "menu::SelectNext",
"h": "menu::SelectPrevious",
"l": "menu::SelectNext"
}
},
{
"context": "Editor",
"bindings": {
@@ -134,23 +125,9 @@
"shift-f10": "editor::OpenContextMenu",
"ctrl-shift-e": "editor::ToggleEditPrediction",
"f9": "editor::ToggleBreakpoint",
"shift-f9": "editor::EditLogBreakpoint"
}
},
{
"context": "Editor && !assistant_diff",
"bindings": {
"ctrl-k ctrl-r": "git::Restore",
"ctrl-alt-y": "git::ToggleStaged",
"alt-y": "git::StageAndNext",
"alt-shift-y": "git::UnstageAndNext"
}
},
{
"context": "AssistantDiff",
"bindings": {
"ctrl-y": "assistant2::ToggleKeep",
"ctrl-k ctrl-r": "assistant2::Reject"
"shift-f9": "editor::EditLogBreakpoint",
"ctrl-shift-backspace": "editor::GoToPreviousChange",
"ctrl-shift-alt-backspace": "editor::GoToNextChange"
}
},
{
@@ -201,6 +178,31 @@
"ctrl-c": "markdown::Copy"
}
},
{
"context": "Editor && jupyter && !ContextEditor",
"bindings": {
"ctrl-shift-enter": "repl::Run",
"ctrl-alt-enter": "repl::RunInPlace"
}
},
{
"context": "Editor && !agent_diff",
"bindings": {
"ctrl-k ctrl-r": "git::Restore",
"ctrl-alt-y": "git::ToggleStaged",
"alt-y": "git::StageAndNext",
"alt-shift-y": "git::UnstageAndNext"
}
},
{
"context": "AgentDiff",
"bindings": {
"ctrl-y": "agent::Keep",
"ctrl-n": "agent::Reject",
"ctrl-shift-y": "agent::KeepAll",
"ctrl-shift-n": "agent::RejectAll"
}
},
{
"context": "AssistantPanel",
"bindings": {
@@ -210,18 +212,105 @@
"ctrl-shift-g": "search::SelectPreviousMatch",
"ctrl-alt-/": "assistant::ToggleModelSelector",
"ctrl-k h": "assistant::DeployHistory",
"ctrl-k l": "assistant::OpenPromptLibrary",
"ctrl-k l": "assistant::OpenRulesLibrary",
"new": "assistant::NewChat",
"ctrl-t": "assistant::NewChat",
"ctrl-n": "assistant::NewChat"
}
},
{
"context": "ContextEditor > Editor",
"bindings": {
"ctrl-enter": "assistant::Assist",
"ctrl-shift-enter": "assistant::Edit",
"ctrl-s": "workspace::Save",
"save": "workspace::Save",
"ctrl->": "assistant::QuoteSelection",
"ctrl-<": "assistant::InsertIntoEditor",
"ctrl-alt-/": "assistant::ToggleModelSelector",
"shift-enter": "assistant::Split",
"ctrl-r": "assistant::CycleMessageRole",
"enter": "assistant::ConfirmCommand",
"alt-enter": "editor::Newline"
}
},
{
"context": "AgentPanel",
"bindings": {
"ctrl-n": "agent::NewThread",
"ctrl-alt-n": "agent::NewTextThread",
"ctrl-shift-h": "agent::OpenHistory",
"ctrl-alt-c": "agent::OpenConfiguration",
"ctrl-alt-p": "assistant::OpenRulesLibrary",
"ctrl-i": "agent::ToggleProfileSelector",
"ctrl-alt-/": "assistant::ToggleModelSelector",
"ctrl-shift-a": "agent::ToggleContextPicker",
"shift-escape": "agent::ExpandMessageEditor",
"ctrl-e": "agent::ChatMode",
"ctrl-alt-e": "agent::RemoveAllContext"
}
},
{
"context": "AgentPanel > Markdown",
"bindings": {
"copy": "markdown::CopyAsMarkdown",
"ctrl-c": "markdown::CopyAsMarkdown"
}
},
{
"context": "AgentPanel && prompt_editor",
"bindings": {
"cmd-n": "agent::NewTextThread",
"cmd-alt-t": "agent::NewThread"
}
},
{
"context": "MessageEditor > Editor",
"bindings": {
"enter": "agent::Chat",
"ctrl-i": "agent::ToggleProfileSelector",
"shift-ctrl-r": "agent::OpenAgentDiff"
}
},
{
"context": "EditMessageEditor > Editor",
"bindings": {
"escape": "menu::Cancel",
"enter": "menu::Confirm",
"alt-enter": "editor::Newline"
}
},
{
"context": "AgentFeedbackMessageEditor > Editor",
"bindings": {
"escape": "menu::Cancel",
"enter": "menu::Confirm",
"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": "ThreadHistory",
"bindings": {
"backspace": "agent::RemoveSelectedThread"
}
},
{
"context": "PromptLibrary",
"bindings": {
"new": "prompt_library::NewPrompt",
"ctrl-n": "prompt_library::NewPrompt",
"ctrl-shift-s": "prompt_library::ToggleDefaultPrompt"
"new": "rules_library::NewRule",
"ctrl-n": "rules_library::NewRule",
"ctrl-shift-s": "rules_library::ToggleDefaultRule"
}
},
{
@@ -311,6 +400,7 @@
"ctrl-k t": ["pane::CloseItemsToTheRight", { "close_pinned": false }],
"ctrl-k u": ["pane::CloseCleanItems", { "close_pinned": false }],
"ctrl-k w": ["pane::CloseAllItems", { "close_pinned": false }],
"ctrl-k ctrl-w": "workspace::CloseAllItemsAndPanes",
"back": "pane::GoBack",
"ctrl-alt--": "pane::GoBack",
"ctrl-alt-_": "pane::GoForward",
@@ -351,11 +441,11 @@
"alt-shift-left": "editor::SelectSmallerSyntaxNode", // Shrink Selection
"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 }],
"ctrl-shift-down": ["editor::SelectNext", { "replace_newest": false }], // Add selection to Next Find Match
"ctrl-shift-up": ["editor::SelectPrevious", { "replace_newest": false }],
"ctrl-k ctrl-d": ["editor::SelectNext", { "replace_newest": true }],
"ctrl-k ctrl-shift-d": ["editor::SelectPrevious", { "replace_newest": true }],
"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-/": ["editor::ToggleComments", { "advance_downwards": false }],
"ctrl-u": "editor::UndoSelection",
@@ -481,6 +571,8 @@
"alt-shift-r": ["task::Spawn", { "reveal_target": "center" }]
// also possible to spawn tasks by name:
// "foo-bar": ["task::Spawn", { "task_name": "MyTask", "reveal_target": "dock" }]
// or by tag:
// "foo-bar": ["task::Spawn", { "task_tag": "MyTag" }],
}
},
{
@@ -529,6 +621,7 @@
"context": "Editor && showing_completions",
"bindings": {
"enter": "editor::ConfirmCompletion",
"shift-enter": "editor::ConfirmCompletionReplace",
"tab": "editor::ComposeCompletion"
}
},
@@ -582,7 +675,7 @@
}
},
{
"context": "Editor && mode == full",
"context": "!ContextEditor > Editor && mode == full",
"bindings": {
"alt-enter": "editor::OpenExcerpts",
"shift-enter": "editor::ExpandExcerpts",
@@ -594,99 +687,21 @@
"ctrl-:": "editor::ToggleInlayHints"
}
},
{
"context": "ProposedChangesEditor",
"bindings": {
"ctrl-shift-y": "editor::ApplyDiffHunk",
"ctrl-alt-a": "editor::ApplyAllDiffHunks"
}
},
{
"context": "Editor && jupyter && !ContextEditor",
"bindings": {
"ctrl-shift-enter": "repl::Run",
"ctrl-alt-enter": "repl::RunInPlace"
}
},
{
"context": "ContextEditor > Editor",
"bindings": {
"ctrl-enter": "assistant::Assist",
"ctrl-shift-enter": "assistant::Edit",
"ctrl-s": "workspace::Save",
"save": "workspace::Save",
"ctrl->": "assistant::QuoteSelection",
"ctrl-<": "assistant::InsertIntoEditor",
"ctrl-alt-/": "assistant::ToggleModelSelector",
"shift-enter": "assistant::Split",
"ctrl-r": "assistant::CycleMessageRole",
"enter": "assistant::ConfirmCommand",
"alt-enter": "editor::Newline"
}
},
{
"context": "AssistantPanel2",
"bindings": {
"ctrl-n": "assistant2::NewThread",
"new": "assistant2::NewThread",
"ctrl-alt-n": "assistant2::NewPromptEditor",
"ctrl-shift-h": "assistant2::OpenHistory",
"ctrl-alt-c": "assistant2::OpenConfiguration",
"ctrl-i": "assistant2::ToggleProfileSelector",
"ctrl-alt-/": "assistant::ToggleModelSelector",
"ctrl-shift-a": "assistant2::ToggleContextPicker",
"ctrl-e": "assistant2::ChatMode",
"ctrl-alt-e": "assistant2::RemoveAllContext"
}
},
{
"context": "AssistantPanel2 && prompt_editor",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "assistant2::NewPromptEditor",
"cmd-alt-t": "assistant2::NewThread"
}
},
{
"context": "MessageEditor > Editor",
"bindings": {
"enter": "assistant2::Chat",
"ctrl-i": "assistant2::ToggleProfileSelector",
"shift-ctrl-r": "assistant2::OpenAssistantDiff"
}
},
{
"context": "EditMessageEditor > Editor",
"use_key_equivalents": true,
"bindings": {
"escape": "menu::Cancel",
"enter": "menu::Confirm",
"alt-enter": "editor::Newline"
}
},
{
"context": "ContextStrip",
"bindings": {
"up": "assistant2::FocusUp",
"right": "assistant2::FocusRight",
"left": "assistant2::FocusLeft",
"down": "assistant2::FocusDown",
"backspace": "assistant2::RemoveFocusedContext",
"enter": "assistant2::AcceptSuggestedContext"
}
},
{
"context": "ThreadHistory",
"bindings": {
"backspace": "assistant2::RemoveSelectedThread"
}
},
{
"context": "PromptEditor",
"bindings": {
"ctrl-[": "assistant::CyclePreviousInlineAssist",
"ctrl-]": "assistant::CycleNextInlineAssist",
"ctrl-alt-e": "assistant2::RemoveAllContext"
"ctrl-alt-e": "agent::RemoveAllContext"
}
},
{
"context": "Prompt",
"bindings": {
"left": "menu::SelectPrevious",
"right": "menu::SelectNext",
"h": "menu::SelectPrevious",
"l": "menu::SelectNext"
}
},
{
@@ -706,7 +721,7 @@
"alt-shift-copy": "workspace::CopyRelativePath",
"alt-ctrl-shift-c": "workspace::CopyRelativePath",
"alt-ctrl-r": "outline_panel::RevealInFileManager",
"space": "outline_panel::Open",
"space": "outline_panel::OpenSelectedEntry",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrevious",
"alt-enter": "editor::OpenExcerpts",
@@ -770,6 +785,7 @@
"shift-tab": "git_panel::FocusEditor",
"escape": "git_panel::ToggleFocus",
"ctrl-enter": "git::Commit",
"ctrl-shift-enter": "git::Amend",
"alt-enter": "menu::SecondaryConfirm",
"delete": ["git::RestoreFile", { "skip_prompt": false }],
"backspace": ["git::RestoreFile", { "skip_prompt": false }],
@@ -778,18 +794,25 @@
"ctrl-delete": ["git::RestoreFile", { "skip_prompt": false }]
}
},
{
"context": "GitPanel && CommitEditor",
"use_key_equivalents": true,
"bindings": {
"escape": "git::Cancel"
}
},
{
"context": "GitCommit > Editor",
"bindings": {
"escape": "menu::Cancel",
"enter": "editor::Newline",
"ctrl-enter": "git::Commit",
"ctrl-shift-enter": "git::Amend",
"alt-l": "git::GenerateCommitMessage"
}
},
{
"context": "GitPanel",
"use_key_equivalents": true,
"bindings": {
"ctrl-g ctrl-g": "git::Fetch",
"ctrl-g up": "git::Push",
@@ -806,6 +829,7 @@
"context": "GitDiff > Editor",
"bindings": {
"ctrl-enter": "git::Commit",
"ctrl-shift-enter": "git::Amend",
"ctrl-space": "git::StageAll",
"ctrl-shift-space": "git::UnstageAll"
}
@@ -824,6 +848,7 @@
"shift-tab": "git_panel::FocusChanges",
"enter": "editor::Newline",
"ctrl-enter": "git::Commit",
"ctrl-shift-enter": "git::Amend",
"alt-up": "git_panel::FocusChanges",
"alt-l": "git::GenerateCommitMessage"
}
@@ -895,6 +920,7 @@
"ctrl-enter": "assistant::InlineAssist",
"alt-b": ["terminal::SendText", "\u001bb"],
"alt-f": ["terminal::SendText", "\u001bf"],
"alt-.": ["terminal::SendText", "\u001b."],
// Overrides for conflicting keybindings
"ctrl-b": ["terminal::SendKeystroke", "ctrl-b"],
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],

View File

@@ -5,8 +5,8 @@
"context": "PromptLibrary",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "prompt_library::NewPrompt",
"cmd-shift-s": "prompt_library::ToggleDefaultPrompt",
"cmd-n": "rules_library::NewRule",
"cmd-shift-s": "rules_library::ToggleDefaultRule",
"cmd-w": "workspace::CloseWindow"
}
},
@@ -228,7 +228,7 @@
}
},
{
"context": "Editor && !assistant_diff",
"context": "Editor && !agent_diff",
"use_key_equivalents": true,
"bindings": {
"cmd-alt-z": "git::Restore",
@@ -238,11 +238,13 @@
}
},
{
"context": "AssistantDiff",
"context": "AgentDiff",
"use_key_equivalents": true,
"bindings": {
"cmd-y": "assistant2::ToggleKeep",
"cmd-alt-z": "assistant2::Reject"
"cmd-y": "agent::Keep",
"cmd-n": "agent::Reject",
"cmd-shift-y": "agent::KeepAll",
"cmd-shift-n": "agent::RejectAll"
}
},
{
@@ -255,7 +257,7 @@
"cmd-shift-g": "search::SelectPreviousMatch",
"cmd-alt-/": "assistant::ToggleModelSelector",
"cmd-k h": "assistant::DeployHistory",
"cmd-k l": "assistant::OpenPromptLibrary",
"cmd-k l": "assistant::OpenRulesLibrary",
"cmd-t": "assistant::NewChat",
"cmd-n": "assistant::NewChat"
}
@@ -277,35 +279,44 @@
}
},
{
"context": "AssistantPanel2",
"context": "AgentPanel",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "assistant2::NewThread",
"cmd-alt-n": "assistant2::NewPromptEditor",
"cmd-shift-h": "assistant2::OpenHistory",
"cmd-alt-c": "assistant2::OpenConfiguration",
"cmd-i": "assistant2::ToggleProfileSelector",
"cmd-n": "agent::NewThread",
"cmd-alt-n": "agent::NewTextThread",
"cmd-shift-h": "agent::OpenHistory",
"cmd-alt-c": "agent::OpenConfiguration",
"cmd-alt-p": "assistant::OpenRulesLibrary",
"cmd-i": "agent::ToggleProfileSelector",
"cmd-alt-/": "assistant::ToggleModelSelector",
"cmd-shift-a": "assistant2::ToggleContextPicker",
"cmd-e": "assistant2::ChatMode",
"cmd-alt-e": "assistant2::RemoveAllContext"
"cmd-shift-a": "agent::ToggleContextPicker",
"shift-escape": "agent::ExpandMessageEditor",
"cmd-e": "agent::ChatMode",
"cmd-alt-e": "agent::RemoveAllContext"
}
},
{
"context": "AssistantPanel2 && prompt_editor",
"context": "AgentPanel > Markdown",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "assistant2::NewPromptEditor",
"cmd-alt-t": "assistant2::NewThread"
"cmd-c": "markdown::CopyAsMarkdown"
}
},
{
"context": "AgentPanel && prompt_editor",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "agent::NewTextThread",
"cmd-alt-t": "agent::NewThread"
}
},
{
"context": "MessageEditor > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "assistant2::Chat",
"cmd-i": "assistant2::ToggleProfileSelector",
"shift-ctrl-r": "assistant2::OpenAssistantDiff"
"enter": "agent::Chat",
"cmd-i": "agent::ToggleProfileSelector",
"shift-ctrl-r": "agent::OpenAgentDiff"
}
},
{
@@ -317,22 +328,49 @@
"alt-enter": "editor::Newline"
}
},
{
"context": "AgentFeedbackMessageEditor > Editor",
"use_key_equivalents": true,
"bindings": {
"escape": "menu::Cancel",
"enter": "menu::Confirm",
"alt-enter": "editor::Newline"
}
},
{
"context": "ContextStrip",
"use_key_equivalents": true,
"bindings": {
"up": "assistant2::FocusUp",
"right": "assistant2::FocusRight",
"left": "assistant2::FocusLeft",
"down": "assistant2::FocusDown",
"backspace": "assistant2::RemoveFocusedContext",
"enter": "assistant2::AcceptSuggestedContext"
"up": "agent::FocusUp",
"right": "agent::FocusRight",
"left": "agent::FocusLeft",
"down": "agent::FocusDown",
"backspace": "agent::RemoveFocusedContext",
"enter": "agent::AcceptSuggestedContext"
}
},
{
"context": "AgentConfiguration",
"bindings": {
"ctrl--": "pane::GoBack"
}
},
{
"context": "ThreadHistory",
"bindings": {
"backspace": "assistant2::RemoveSelectedThread"
"ctrl--": "pane::GoBack"
}
},
{
"context": "ThreadHistory",
"bindings": {
"ctrl--": "pane::GoBack"
}
},
{
"context": "ThreadHistory > Editor",
"bindings": {
"shift-backspace": "agent::RemoveSelectedThread"
}
},
{
@@ -421,7 +459,8 @@
"cmd-k e": ["pane::CloseItemsToTheLeft", { "close_pinned": false }],
"cmd-k t": ["pane::CloseItemsToTheRight", { "close_pinned": false }],
"cmd-k u": ["pane::CloseCleanItems", { "close_pinned": false }],
"cmd-k cmd-w": ["pane::CloseAllItems", { "close_pinned": false }],
"cmd-k w": ["pane::CloseAllItems", { "close_pinned": false }],
"cmd-k cmd-w": "workspace::CloseAllItemsAndPanes",
"cmd-f": "project_search::ToggleFocus",
"cmd-g": "search::SelectNextMatch",
"cmd-shift-g": "search::SelectPreviousMatch",
@@ -453,12 +492,15 @@
"alt-shift-down": "editor::DuplicateLineDown",
"ctrl-shift-right": "editor::SelectLargerSyntaxNode", // Expand Selection
"ctrl-shift-left": "editor::SelectSmallerSyntaxNode", // Shrink Selection
"cmd-d": ["editor::SelectNext", { "replace_newest": false }], // Add selection to Next Find Match
"cmd-d": ["editor::SelectNext", { "replace_newest": false }], // editor.action.addSelectionToNextFindMatch / find_under_expand
"cmd-shift-l": "editor::SelectAllMatches", // Select all occurrences of current selection
"cmd-f2": "editor::SelectAllMatches", // Select all occurrences of current word
"ctrl-cmd-d": ["editor::SelectPrevious", { "replace_newest": false }],
"cmd-k cmd-d": ["editor::SelectNext", { "replace_newest": true }],
"cmd-k ctrl-cmd-d": ["editor::SelectPrevious", { "replace_newest": true }],
"cmd-k cmd-d": ["editor::SelectNext", { "replace_newest": true }], // editor.action.moveSelectionToNextFindMatch / find_under_expand_skip
// macOS binds `ctrl-cmd-d` to Show Dictionary which breaks these two binds
// To use `ctrl-cmd-d` or `ctrl-k ctrl-cmd-d` in Zed you must execute this command and then restart:
// defaults write com.apple.symbolichotkeys AppleSymbolicHotKeys -dict-add 70 '<dict><key>enabled</key><false/></dict>'
"ctrl-cmd-d": ["editor::SelectPrevious", { "replace_newest": false }], // editor.action.addSelectionToPreviousFindMatch
"cmd-k ctrl-cmd-d": ["editor::SelectPrevious", { "replace_newest": true }], // editor.action.moveSelectionToPreviousFindMatch
"cmd-k cmd-i": "editor::Hover",
"cmd-/": ["editor::ToggleComments", { "advance_downwards": false }],
"cmd-u": "editor::UndoSelection",
@@ -490,7 +532,7 @@
"cmd-k cmd-9": ["editor::FoldAtLevel", 9],
"cmd-k cmd-0": "editor::FoldAll",
"cmd-k cmd-j": "editor::UnfoldAll",
// Using `ctrl-space` in Zed requires disabling the macOS global shortcut.
// Using `ctrl-space` / `ctrl-shift-space` in Zed requires disabling the macOS global shortcut.
// System Preferences->Keyboard->Keyboard Shortcuts->Input Sources->Select the previous input source (uncheck)
"ctrl-space": "editor::ShowCompletions",
"ctrl-shift-space": "editor::ShowWordCompletions",
@@ -500,7 +542,9 @@
"cmd-\\": "pane::SplitRight",
"cmd-k v": "markdown::OpenPreviewToTheSide",
"cmd-shift-v": "markdown::OpenPreview",
"ctrl-cmd-c": "editor::DisplayCursorNames"
"ctrl-cmd-c": "editor::DisplayCursorNames",
"cmd-shift-backspace": "editor::GoToPreviousChange",
"cmd-shift-alt-backspace": "editor::GoToNextChange"
}
},
{
@@ -598,6 +642,8 @@
"ctrl-alt-shift-r": ["task::Spawn", { "reveal_target": "center" }]
// also possible to spawn tasks by name:
// "foo-bar": ["task::Spawn", { "task_name": "MyTask", "reveal_target": "dock" }]
// or by tag:
// "foo-bar": ["task::Spawn", { "task_tag": "MyTag" }],
}
},
// Bindings from Sublime Text
@@ -644,6 +690,7 @@
"use_key_equivalents": true,
"bindings": {
"enter": "editor::ConfirmCompletion",
"shift-enter": "editor::ConfirmCompletionReplace",
"tab": "editor::ComposeCompletion"
}
},
@@ -691,7 +738,7 @@
}
},
{
"context": "Editor && mode == full",
"context": "!ContextEditor > Editor && mode == full",
"use_key_equivalents": true,
"bindings": {
"alt-enter": "editor::OpenExcerpts",
@@ -704,21 +751,13 @@
"ctrl-:": "editor::ToggleInlayHints"
}
},
{
"context": "ProposedChangesEditor",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-y": "editor::ApplyDiffHunk",
"cmd-shift-a": "editor::ApplyAllDiffHunks"
}
},
{
"context": "PromptEditor",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-a": "assistant2::ToggleContextPicker",
"cmd-shift-a": "agent::ToggleContextPicker",
"cmd-alt-/": "assistant::ToggleModelSelector",
"cmd-alt-e": "assistant2::RemoveAllContext",
"cmd-alt-e": "agent::RemoveAllContext",
"ctrl-[": "assistant::CyclePreviousInlineAssist",
"ctrl-]": "assistant::CycleNextInlineAssist"
}
@@ -750,7 +789,7 @@
"cmd-alt-c": "workspace::CopyPath",
"alt-cmd-shift-c": "workspace::CopyRelativePath",
"alt-cmd-r": "outline_panel::RevealInFileManager",
"space": "outline_panel::Open",
"space": "outline_panel::OpenSelectedEntry",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrevious",
"alt-enter": "editor::OpenExcerpts",
@@ -819,17 +858,26 @@
"shift-tab": "git_panel::FocusEditor",
"escape": "git_panel::ToggleFocus",
"cmd-enter": "git::Commit",
"cmd-shift-enter": "git::Amend",
"backspace": ["git::RestoreFile", { "skip_prompt": false }],
"delete": ["git::RestoreFile", { "skip_prompt": false }],
"cmd-backspace": ["git::RestoreFile", { "skip_prompt": true }],
"cmd-delete": ["git::RestoreFile", { "skip_prompt": true }]
}
},
{
"context": "GitPanel && CommitEditor",
"use_key_equivalents": true,
"bindings": {
"escape": "git::Cancel"
}
},
{
"context": "GitDiff > Editor",
"use_key_equivalents": true,
"bindings": {
"cmd-enter": "git::Commit",
"cmd-shift-enter": "git::Amend",
"cmd-ctrl-y": "git::StageAll",
"cmd-ctrl-shift-y": "git::UnstageAll"
}
@@ -840,6 +888,7 @@
"bindings": {
"enter": "editor::Newline",
"cmd-enter": "git::Commit",
"cmd-shift-enter": "git::Amend",
"tab": "git_panel::FocusChanges",
"shift-tab": "git_panel::FocusChanges",
"alt-up": "git_panel::FocusChanges",
@@ -869,6 +918,7 @@
"enter": "editor::Newline",
"escape": "menu::Cancel",
"cmd-enter": "git::Commit",
"cmd-shift-enter": "git::Amend",
"alt-tab": "git::GenerateCommitMessage"
}
},
@@ -955,6 +1005,7 @@
"alt-right": ["terminal::SendText", "\u001bf"],
"alt-b": ["terminal::SendText", "\u001bb"],
"alt-f": ["terminal::SendText", "\u001bf"],
"alt-.": ["terminal::SendText", "\u001b."],
// There are conflicting bindings for these keys in the global context.
// these bindings override them, remove at your own risk:
"up": ["terminal::SendKeystroke", "up"],
@@ -974,11 +1025,13 @@
"cmd-home": "terminal::ScrollToTop",
"shift-end": "terminal::ScrollToBottom",
"cmd-end": "terminal::ScrollToBottom",
// Using `ctrl-shift-space` in Zed requires disabling the macOS global shortcut.
// System Preferences->Keyboard->Keyboard Shortcuts->Input Sources->Select the previous input source (uncheck)
"ctrl-shift-space": "terminal::ToggleViMode",
"ctrl-k up": "pane::SplitUp",
"ctrl-k down": "pane::SplitDown",
"ctrl-k left": "pane::SplitLeft",
"ctrl-k right": "pane::SplitRight"
"ctrl-alt-up": "pane::SplitUp",
"ctrl-alt-down": "pane::SplitDown",
"ctrl-alt-left": "pane::SplitLeft",
"ctrl-alt-right": "pane::SplitRight"
}
},
{

View File

@@ -58,7 +58,8 @@
"ctrl-shift-home": "editor::SelectToBeginning",
"ctrl-shift-end": "editor::SelectToEnd",
"ctrl-f8": "editor::ToggleBreakpoint",
"ctrl-shift-f8": "editor::EditLogBreakpoint"
"ctrl-shift-f8": "editor::EditLogBreakpoint",
"ctrl-shift-u": "editor::ToggleCase"
}
},
{

View File

@@ -37,6 +37,8 @@
"ctrl-shift-a": "editor::SelectLargerSyntaxNode",
"ctrl-shift-d": "editor::DuplicateSelection",
"alt-f3": "editor::SelectAllMatches", // find_all_under
// "ctrl-f3": "", // find_under (cancels any selections)
// "cmd-alt-shift-g": "" // find_under_prev (cancels any selections)
"f9": "editor::SortLinesCaseSensitive",
"ctrl-f9": "editor::SortLinesCaseInsensitive",
"f12": "editor::GoToDefinition",
@@ -49,7 +51,9 @@
"ctrl-k ctrl-l": "editor::ConvertToLowerCase",
"shift-alt-m": "markdown::OpenPreviewToTheSide",
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
"ctrl-delete": "editor::DeleteToNextWordEnd"
"ctrl-delete": "editor::DeleteToNextWordEnd",
"f3": "editor::FindNextMatch",
"shift-f3": "editor::FindPreviousMatch"
}
},
{
@@ -58,6 +62,12 @@
"ctrl-r": "outline::Toggle"
}
},
{
"context": "Editor && !agent_diff",
"bindings": {
"ctrl-k ctrl-z": "git::Restore"
}
},
{
"context": "Pane",
"bindings": {

View File

@@ -55,7 +55,8 @@
"cmd-shift-home": "editor::SelectToBeginning",
"cmd-shift-end": "editor::SelectToEnd",
"ctrl-f8": "editor::ToggleBreakpoint",
"ctrl-shift-f8": "editor::EditLogBreakpoint"
"ctrl-shift-f8": "editor::EditLogBreakpoint",
"cmd-shift-u": "editor::ToggleCase"
}
},
{

View File

@@ -38,6 +38,8 @@
"cmd-shift-a": "editor::SelectLargerSyntaxNode",
"cmd-shift-d": "editor::DuplicateSelection",
"ctrl-cmd-g": "editor::SelectAllMatches", // find_all_under
// "cmd-alt-g": "", // find_under (cancels any selections)
// "cmd-alt-shift-g": "" // find_under_prev (cancels any selections)
"f5": "editor::SortLinesCaseSensitive",
"ctrl-f5": "editor::SortLinesCaseInsensitive",
"shift-f12": "editor::FindAllReferences",
@@ -51,7 +53,9 @@
"cmd-shift-j": "editor::JoinLines",
"shift-alt-m": "markdown::OpenPreviewToTheSide",
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
"ctrl-delete": "editor::DeleteToNextWordEnd"
"ctrl-delete": "editor::DeleteToNextWordEnd",
"cmd-g": "editor::FindNextMatch",
"cmd-shift-g": "editor::FindPreviousMatch"
}
},
{
@@ -60,6 +64,12 @@
"cmd-r": "outline::Toggle"
}
},
{
"context": "Editor && !agent_diff",
"bindings": {
"cmd-k cmd-z": "git::Restore"
}
},
{
"context": "Pane",
"bindings": {

View File

@@ -44,6 +44,12 @@
"[ /": "vim::PreviousComment",
"] *": "vim::NextComment",
"] /": "vim::NextComment",
"[ -": "vim::PreviousLesserIndent",
"[ +": "vim::PreviousGreaterIndent",
"[ =": "vim::PreviousSameIndent",
"] -": "vim::NextLesserIndent",
"] +": "vim::NextGreaterIndent",
"] =": "vim::NextSameIndent",
// Word motions
"w": "vim::NextWordStart",
"e": "vim::NextWordEnd",
@@ -184,7 +190,8 @@
"ctrl-w g shift-d": "editor::GoToTypeDefinitionSplit",
"ctrl-w space": "editor::OpenExcerptsSplit",
"ctrl-w g space": "editor::OpenExcerptsSplit",
"ctrl-6": "pane::AlternateFile"
"ctrl-6": "pane::AlternateFile",
"ctrl-^": "pane::AlternateFile"
}
},
{
@@ -197,6 +204,7 @@
"c": "vim::PushChange",
"shift-c": "vim::ChangeToEndOfLine",
"d": "vim::PushDelete",
"delete": "vim::DeleteRight",
"shift-d": "vim::DeleteToEndOfLine",
"shift-j": "vim::JoinLines",
"g shift-j": "vim::JoinLinesNoWhitespace",
@@ -227,6 +235,8 @@
"g u": "vim::PushLowercase",
"g shift-u": "vim::PushUppercase",
"g ~": "vim::PushOppositeCase",
"g ?": "vim::PushRot13",
// "g ?": "vim::PushRot47",
"\"": "vim::PushRegister",
"g w": "vim::PushRewrap",
"g q": "vim::PushRewrap",
@@ -298,6 +308,8 @@
"g r": ["vim::Paste", { "preserve_clipboard": true }],
"g c": "vim::ToggleComments",
"g q": "vim::Rewrap",
"g ?": "vim::ConvertToRot13",
// "g ?": "vim::ConvertToRot47",
"\"": "vim::PushRegister",
// tree-sitter related commands
"[ x": "editor::SelectLargerSyntaxNode",
@@ -331,31 +343,106 @@
}
},
{
"context": "vim_mode == helix_normal",
"context": "vim_mode == helix_normal && !menu",
"bindings": {
"escape": "editor::Cancel",
"ctrl-[": "editor::Cancel",
":": "command_palette::Toggle",
"shift-d": "vim::DeleteToEndOfLine",
"shift-j": "vim::JoinLines",
"y": "editor::Copy",
"shift-y": "vim::YankLine",
"i": "vim::InsertBefore",
"shift-i": "vim::InsertFirstNonWhitespace",
"a": "vim::InsertAfter",
"d": "vim::HelixDelete",
"w": "vim::NextWordStart",
"e": "vim::NextWordEnd",
"b": "vim::PreviousWordStart",
"x": "vim::CurrentLine",
"X": "vim::CurrentLine",
"y": "vim::HelixYank",
"p": "vim::HelixPaste",
"shift-a": "vim::InsertEndOfLine",
"o": "vim::InsertLineBelow",
"shift-o": "vim::InsertLineAbove",
"~": "vim::ChangeCase",
"ctrl-a": "vim::Increment",
"ctrl-x": "vim::Decrement",
"p": "vim::Paste",
"shift-p": ["vim::Paste", { "before": true }],
"u": "vim::Undo",
"ctrl-r": "vim::Redo",
"r": "vim::PushReplace",
"s": "vim::Substitute",
"shift-s": "vim::SubstituteLine",
">": "vim::Indent",
"<": "vim::Outdent",
"=": "vim::AutoIndent",
"g u": "vim::PushLowercase",
"g shift-u": "vim::PushUppercase",
"g ~": "vim::PushOppositeCase",
"\"": "vim::PushRegister",
"g q": "vim::PushRewrap",
"g w": "vim::PushRewrap",
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-pageup": "pane::ActivatePreviousItem",
"insert": "vim::InsertBefore",
// tree-sitter related commands
"[ x": "editor::SelectLargerSyntaxNode",
"] x": "editor::SelectSmallerSyntaxNode",
"] d": "editor::GoToDiagnostic",
"[ d": "editor::GoToPreviousDiagnostic",
"] c": "editor::GoToHunk",
"[ c": "editor::GoToPreviousHunk",
// Goto mode
"g n": "pane::ActivateNextItem",
"g p": "pane::ActivatePreviousItem",
// "tab": "pane::ActivateNextItem",
// "shift-tab": "pane::ActivatePrevItem",
"shift-h": "pane::ActivatePreviousItem",
"shift-l": "pane::ActivateNextItem",
"g l": "vim::EndOfLine",
"g h": "vim::StartOfLine",
"g s": "vim::FirstNonWhitespace", // "g s" default behavior is "space s"
"g e": "vim::EndOfDocument",
"g y": "editor::GoToTypeDefinition",
"g r": "editor::FindAllReferences", // zed specific
"g t": "vim::WindowTop",
"g c": "vim::WindowMiddle",
"g b": "vim::WindowBottom",
"h": "vim::Left",
"j": "vim::Down",
"k": "vim::Up",
"l": "vim::Right"
"x": "editor::SelectLine",
"shift-x": "editor::SelectLine",
// Window mode
"space w h": "workspace::ActivatePaneLeft",
"space w l": "workspace::ActivatePaneRight",
"space w k": "workspace::ActivatePaneUp",
"space w j": "workspace::ActivatePaneDown",
"space w q": "pane::CloseActiveItem",
"space w s": "pane::SplitRight",
"space w r": "pane::SplitRight",
"space w v": "pane::SplitDown",
"space w d": "pane::SplitDown",
// Space mode
"space f": "file_finder::Toggle",
"space k": "editor::Hover",
"space s": "outline::Toggle",
"space shift-s": "project_symbols::Toggle",
"space d": "editor::GoToDiagnostic",
"space r": "editor::Rename",
"space a": "editor::ToggleCodeActions",
"space h": "editor::SelectAllMatches",
"space c": "editor::ToggleComments",
"space y": "editor::Copy",
"space p": "editor::Paste",
// Match mode
"m m": "vim::Matching",
"m i w": ["workspace::SendKeystrokes", "v i w"],
"shift-u": "editor::Redo",
"ctrl-c": "editor::ToggleComments",
"d": "vim::HelixDelete",
"c": "vim::Substitute",
"shift-c": "editor::AddSelectionBelow"
}
},
{
"context": "vim_mode == insert && !(showing_code_actions || showing_completions)",
"bindings": {
"ctrl-p": "editor::ShowCompletions",
"ctrl-n": "editor::ShowCompletions"
"ctrl-p": "editor::ShowWordCompletions",
"ctrl-n": "editor::ShowWordCompletions"
}
},
{
@@ -453,6 +540,7 @@
"bindings": {
"d": "vim::CurrentLine",
"s": "vim::PushDeleteSurrounds",
"v": "vim::PushForcedMotion", // "d v"
"o": "editor::ToggleSelectedDiffHunks", // "d o"
"shift-o": "git::ToggleStaged",
"p": "git::Restore", // "d p"
@@ -481,6 +569,13 @@
"~": "vim::CurrentLine"
}
},
{
"context": "vim_operator == g?",
"bindings": {
"g ?": "vim::CurrentLine",
"?": "vim::CurrentLine"
}
},
{
"context": "vim_operator == gq",
"bindings": {
@@ -494,6 +589,7 @@
"context": "vim_operator == y",
"bindings": {
"y": "vim::CurrentLine",
"v": "vim::PushForcedMotion",
"s": ["vim::PushAddSurrounds", {}]
}
},
@@ -597,7 +693,7 @@
}
},
{
"context": "GitPanel || ProjectPanel || CollabPanel || OutlinePanel || ChatPanel || VimControl || EmptyPane || SharedScreen || MarkdownPreview || KeyContextView",
"context": "GitPanel || ProjectPanel || CollabPanel || OutlinePanel || ChatPanel || VimControl || EmptyPane || SharedScreen || MarkdownPreview || KeyContextView || DebugPanel",
"bindings": {
// window related commands (ctrl-w X)
"ctrl-w": null,
@@ -691,8 +787,7 @@
"{": "project_panel::SelectPrevDirectory",
"shift-g": "menu::SelectLast",
"g g": "menu::SelectFirst",
"-": "project_panel::SelectParent",
"ctrl-6": "pane::AlternateFile"
"-": "project_panel::SelectParent"
}
},
{
@@ -735,5 +830,13 @@
// and Windows.
"alt-l": "editor::AcceptEditPrediction"
}
},
{
// Fixes https://github.com/zed-industries/zed/issues/29095 by ensuring that
// the last binding for editor::ToggleComments is not ctrl-c.
"context": "hack_to_fix_ctrl-c",
"bindings": {
"g c": "editor::ToggleComments"
}
}
]

View File

@@ -1,34 +1,91 @@
You are an AI assistant integrated into a code editor. You have the programming ability of an expert programmer who takes pride in writing high-quality code and is driven to the point of obsession about solving problems effectively. Your goal is to do one of the following two things:
You are a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.
1. Help users answer questions and perform tasks related to their codebase.
2. Answer general-purpose questions unrelated to their particular codebase.
## Communication
It will be up to you to decide which of these you are doing based on what the user has told you. When unclear, ask clarifying questions to understand the user's intent before proceeding.
1. Be conversational but professional.
2. Refer to the USER in the second person and yourself in the first person.
3. Format your responses in markdown. Use backticks to format file, directory, function, and class names.
4. NEVER lie or make things up.
5. Refrain from apologizing all the time when results are unexpected. Instead, just try your best to proceed or explain the circumstances to the user without apologizing.
You should only perform actions that modify the user's system if explicitly requested by the user:
- If the user asks a question about how to accomplish a task, provide guidance or information, and use read-only tools (e.g., search) to assist. You may suggest potential actions, but do not directly modify the users system without explicit instruction.
- If the user clearly requests that you perform an action, carry out the action directly without explaining why you are doing so.
- The editing actions you perform might produce errors or warnings. At the end of your changes, check whether you introduced any problems, and fix them before providing a summary of the changes you made. You may only attempt to fix these up to 3 times. If you have tried 3 times to fix them, and there are still problems remaining, you must not continue trying to fix them, and must instead tell the user that there are problems remaining - and ask if the user would like you to attempt to solve them further.
- Do not fix errors unrelated to your changes unless the user explicitly asks you to do so.
## Tool Use
Be concise and direct in your responses. Never apologize or thank the user. Don't comment that you have just realized or understood something. When you are going to make a tool call, tersely explain your reasoning for choosing to use that tool, with no flourishes or commentary beyond that information. For example, rather than saying "You're absolutely right! Thank you for providing that context. Now I understand that we're missing a dependency, and I need to add it:" say "I'll add that missing dependency:" instead. Also, don't restate what a tool call is about to do (or just did). For example, don't say "Now I'm going to check diagnostics to see if there are any warnings or errors," followed by running a tool which checks diagnostics and reports warnings or errors; instead, just request the tool call without saying anything.
1. Make sure to adhere to the tools schema.
2. Provide every required argument.
3. DO NOT use tools to access items that are already available in the context section.
4. Use only the tools that are currently available.
5. DO NOT use a tool that is not available just because it appears in the conversation. This means the user turned it off.
The user has opened a project that contains the following root directories/files. Whenever you specify a path in the project, it must be a relative path which begins with one of these root directories/files:
## Searching and Reading
If you are unsure how to fulfill the user's request, gather more information with tool calls and/or clarifying questions.
{{! TODO: If there are files, we should mention it but otherwise omit that fact }}
If appropriate, use tool calls to explore the current project, which contains the following root directories:
{{#each worktrees}}
- `{{root_name}}` (absolute path: `{{abs_path}}`)
- `{{root_name}}`
{{/each}}
{{#if has_rules}}
There are rules that apply to these root directories:
- When providing paths to tools, the path should always begin with a path that starts with a project root directory listed above.
- When looking for symbols in the project, prefer the `grep` tool.
- As you learn about the structure of the project, use that information to scope `grep` searches to targeted subtrees of the project.
- Bias towards not asking the user for help if you can find the answer yourself.
{{! TODO: Only mention tools if they are enabled }}
- The user might specify a partial file path. If you don't know the full path, use `find_path` (not `grep`) before you read the file.
- Before you read or edit a file, you must first find the full path. DO NOT ever guess a file path!
## Fixing Diagnostics
1. Make 1-2 attempts at fixing diagnostics, then defer to the user.
2. Never simplify code you've written just to solve diagnostics. Complete, mostly correct code is more valuable than perfect code that doesn't solve the problem.
## Debugging
When debugging, only make code changes if you are certain that you can solve the problem.
Otherwise, follow debugging best practices:
1. Address the root cause instead of the symptoms.
2. Add descriptive logging statements and error messages to track variable and code state.
3. Add test functions and statements to isolate the problem.
## Calling External APIs
1. Unless explicitly requested by the user, use the best suited external APIs and packages to solve the task. There is no need to ask the user for permission.
2. When selecting which version of an API or package to use, choose one that is compatible with the user's dependency management file. If no such file exists or if the package is not present, use the latest version that is in your training data.
3. If an external API requires an API Key, be sure to point this out to the user. Adhere to best security practices (e.g. DO NOT hardcode an API key in a place where it can be exposed)
## System Information
Operating System: {{os}}
Default Shell: {{shell}}
{{#if (or has_rules has_default_user_rules)}}
## User's Custom Instructions
The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the tool use guidelines.
{{#if has_rules}}
There are project rules that apply to these root directories:
{{#each worktrees}}
{{#if rules_file}}
`{{root_name}}/{{rules_file.rel_path}}`:
`{{root_name}}/{{rules_file.path_in_worktree}}`:
``````
{{{rules_file.text}}}
``````
{{/if}}
{{/each}}
{{/if}}
{{#if has_user_rules}}
The user has specified the following rules that should be applied:
{{#each user_rules}}
{{#if title}}
Rules title: {{title}}
{{/if}}
``````
{{contents}}}
``````
{{/each}}
{{/if}}
{{/if}}

View File

@@ -1,8 +0,0 @@
A software developer is asking a question about their project. The source files in their project have been indexed into a database of semantic text embeddings.
Your task is to generate a list of 4 diverse search queries that can be run on this embedding database, in order to retrieve a list of code snippets
that are relevant to the developer's question. Redundant search queries will be heavily penalized, so only include another query if it's sufficiently
distinct from previous ones.
Here is the question that's been asked, together with context that the developer has added manually:
{{{context_buffer}}}

View File

@@ -80,6 +80,8 @@
// Values are clamped to the [0.0, 1.0] range.
"inactive_opacity": 1.0
},
// Layout mode of the bottom dock. Defaults to "contained"
"bottom_dock_layout": "contained",
// The direction that you want to split panes horizontally. Defaults to "up"
"pane_split_direction_horizontal": "up",
// The direction that you want to split panes horizontally. Defaults to "left"
@@ -165,7 +167,23 @@
// Default: not set, defaults to "bar"
"cursor_shape": null,
// Determines when the mouse cursor should be hidden in an editor or input box.
//
// 1. Never hide the mouse cursor:
// "never"
// 2. Hide only when typing:
// "on_typing"
// 3. Hide on both typing and cursor movement:
// "on_typing_and_movement"
"hide_mouse": "on_typing_and_movement",
// Determines how snippets are sorted relative to other completion items.
//
// 1. Place snippets at the top of the completion list:
// "top"
// 2. Place snippets normally without any preference:
// "inline"
// 3. Place snippets at the bottom of the completion list:
// "bottom"
"snippet_sort_order": "inline",
// How to highlight the current line in the editor.
//
// 1. Don't highlight the current line:
@@ -179,8 +197,6 @@
"current_line_highlight": "all",
// Whether to highlight all occurrences of the selected text in an editor.
"selection_highlight": true,
// The debounce delay before querying highlights based on the selected text.
"selection_highlight_debounce": 50,
// The debounce delay before querying highlights from the language
// server based on the current cursor location.
"lsp_highlight_debounce": 75,
@@ -585,7 +601,6 @@
//
// Default: main
"fallback_branch_name": "main",
"scrollbar": {
// When to show the scrollbar in the git panel.
//
@@ -624,51 +639,61 @@
// The provider to use.
"provider": "zed.dev",
// The model to use.
"model": "claude-3-5-sonnet-latest"
"model": "claude-3-7-sonnet-latest"
},
// The model to use when applying edits from the assistant.
"editor_model": {
// The provider to use.
"provider": "zed.dev",
// The model to use.
"model": "claude-3-5-sonnet-latest"
"model": "claude-3-7-sonnet-latest"
},
// When enabled, the agent can run potentially destructive actions without asking for your confirmation.
"always_allow_tool_actions": false,
"default_profile": "write",
"profiles": {
"ask": {
"name": "Ask",
// We don't know which of the context server tools are safe for the "Ask" profile, so we don't enable them by default.
// "enable_all_context_servers": true,
"tools": {
"contents": true,
"diagnostics": true,
"fetch": true,
"list-directory": true,
"list_directory": false,
"now": true,
"path-search": true,
"read-file": true,
"regex-search": true,
"thinking": true
"find_path": true,
"read_file": true,
"grep": true,
"thinking": true,
"web_search": true
}
},
"write": {
"name": "Write",
"enable_all_context_servers": true,
"tools": {
"bash": true,
"batch-tool": true,
"code-symbols": true,
"copy-path": true,
"create-file": true,
"delete-path": true,
"batch_tool": false,
"code_actions": false,
"code_symbols": false,
"contents": false,
"copy_path": false,
"create_file": true,
"delete_path": false,
"diagnostics": true,
"find-replace-file": true,
"edit-files": false,
"edit_file": true,
"fetch": true,
"list-directory": true,
"move-path": true,
"now": true,
"path-search": true,
"read-file": true,
"regex-search": true,
"symbol-info": true,
"thinking": true
"list_directory": true,
"move_path": false,
"now": false,
"find_path": true,
"read_file": true,
"grep": true,
"rename": false,
"symbol_info": false,
"terminal": true,
"thinking": true,
"web_search": true
}
}
},
@@ -1132,7 +1157,8 @@
"code_actions_on_format": {},
// Settings related to running tasks.
"tasks": {
"variables": {}
"variables": {},
"enabled": true
},
// An object whose keys are language names, and whose values
// are arrays of filenames or extensions of files that should
@@ -1196,7 +1222,27 @@
// When set to 0, waits indefinitely.
//
// Default: 0
"lsp_fetch_timeout_ms": 0
"lsp_fetch_timeout_ms": 0,
// Controls what range to replace when accepting LSP completions.
//
// When LSP servers give an `InsertReplaceEdit` completion, they provides two ranges: `insert` and `replace`. Usually, `insert`
// contains the word prefix before your cursor and `replace` contains the whole word.
//
// Effectively, this setting just changes whether Zed will use the received range for `insert` or `replace`, so the results may
// differ depending on the underlying LSP server.
//
// Possible values:
// 1. "insert"
// Replaces text before the cursor, using the `insert` range described in the LSP specification.
// 2. "replace"
// Replaces text before and after the cursor, using the `replace` range described in the LSP specification.
// 3. "replace_subsequence"
// Behaves like `"replace"` if the text that would be replaced is a subsequence of the completion text,
// and like `"insert"` otherwise.
// 4. "replace_suffix"
// Behaves like `"replace"` if the text after the cursor is a suffix of the completion, and like
// `"insert"` otherwise.
"lsp_insert_mode": "replace_suffix"
},
// Different settings for specific languages.
"languages": {
@@ -1432,6 +1478,8 @@
"lsp": {
// Specify the LSP name as a key here.
// "rust-analyzer": {
// // A special flag for rust-analyzer integration, to use server-provided tasks
// enable_lsp_tasks": true,
// // These initialization options are merged into Zed's defaults
// "initialization_options": {
// "check": {
@@ -1457,7 +1505,12 @@
"use_multiline_find": false,
"use_smartcase_find": false,
"highlight_on_yank_duration": 200,
"custom_digraphs": {}
"custom_digraphs": {},
// Cursor shape for the each mode.
// Specify the mode as the key and the shape as the value.
// The mode can be one of the following: "normal", "replace", "insert", "visual".
// The shape can be one of the following: "block", "bar", "underline", "hollow".
"cursor_shape": {}
},
// The server to connect to. If the environment variable
// ZED_SERVER_URL is set, it will override this setting.
@@ -1527,7 +1580,6 @@
// }
// ]
"ssh_connections": [],
// Configures context servers for use in the Assistant.
"context_servers": {},
"debugger": {

View File

@@ -43,6 +43,8 @@
// "args": ["--login"]
// }
// }
"shell": "system"
"shell": "system",
// Represents the tags for inline runnable indicators, or spawning multiple tasks at once.
"tags": []
}
]

View File

@@ -87,9 +87,9 @@
"terminal.ansi.blue": "#83a598ff",
"terminal.ansi.bright_blue": "#414f4aff",
"terminal.ansi.dim_blue": "#c0d2cbff",
"terminal.ansi.magenta": "#a89984ff",
"terminal.ansi.bright_magenta": "#514a41ff",
"terminal.ansi.dim_magenta": "#d2cabfff",
"terminal.ansi.magenta": "#d3869bff",
"terminal.ansi.bright_magenta": "#8e5868ff",
"terminal.ansi.dim_magenta": "#ff9ebbff",
"terminal.ansi.cyan": "#8ec07cff",
"terminal.ansi.bright_cyan": "#45603eff",
"terminal.ansi.dim_cyan": "#c7dfbdff",
@@ -472,9 +472,9 @@
"terminal.ansi.blue": "#83a598ff",
"terminal.ansi.bright_blue": "#414f4aff",
"terminal.ansi.dim_blue": "#c0d2cbff",
"terminal.ansi.magenta": "#a89984ff",
"terminal.ansi.bright_magenta": "#514a41ff",
"terminal.ansi.dim_magenta": "#d2cabfff",
"terminal.ansi.magenta": "#d3869bff",
"terminal.ansi.bright_magenta": "#8e5868ff",
"terminal.ansi.dim_magenta": "#ff9ebbff",
"terminal.ansi.cyan": "#8ec07cff",
"terminal.ansi.bright_cyan": "#45603eff",
"terminal.ansi.dim_cyan": "#c7dfbdff",
@@ -857,9 +857,9 @@
"terminal.ansi.blue": "#83a598ff",
"terminal.ansi.bright_blue": "#414f4aff",
"terminal.ansi.dim_blue": "#c0d2cbff",
"terminal.ansi.magenta": "#a89984ff",
"terminal.ansi.bright_magenta": "#514a41ff",
"terminal.ansi.dim_magenta": "#d2cabfff",
"terminal.ansi.magenta": "#d3869bff",
"terminal.ansi.bright_magenta": "#8e5868ff",
"terminal.ansi.dim_magenta": "#ff9ebbff",
"terminal.ansi.cyan": "#8ec07cff",
"terminal.ansi.bright_cyan": "#45603eff",
"terminal.ansi.dim_cyan": "#c7dfbdff",
@@ -1242,9 +1242,9 @@
"terminal.ansi.blue": "#0b6678ff",
"terminal.ansi.bright_blue": "#8fb0baff",
"terminal.ansi.dim_blue": "#14333bff",
"terminal.ansi.magenta": "#7c6f64ff",
"terminal.ansi.bright_magenta": "#bcb5afff",
"terminal.ansi.dim_magenta": "#3e3833ff",
"terminal.ansi.magenta": "#8f3e71ff",
"terminal.ansi.bright_magenta": "#c76da0ff",
"terminal.ansi.dim_magenta": "#5c2848ff",
"terminal.ansi.cyan": "#437b59ff",
"terminal.ansi.bright_cyan": "#9fbca8ff",
"terminal.ansi.dim_cyan": "#253e2eff",
@@ -1627,9 +1627,9 @@
"terminal.ansi.blue": "#0b6678ff",
"terminal.ansi.bright_blue": "#8fb0baff",
"terminal.ansi.dim_blue": "#14333bff",
"terminal.ansi.magenta": "#7c6f64ff",
"terminal.ansi.bright_magenta": "#bcb5afff",
"terminal.ansi.dim_magenta": "#3e3833ff",
"terminal.ansi.magenta": "#8f3e71ff",
"terminal.ansi.bright_magenta": "#c76da0ff",
"terminal.ansi.dim_magenta": "#5c2848ff",
"terminal.ansi.cyan": "#437b59ff",
"terminal.ansi.bright_cyan": "#9fbca8ff",
"terminal.ansi.dim_cyan": "#253e2eff",
@@ -2012,9 +2012,9 @@
"terminal.ansi.blue": "#0b6678ff",
"terminal.ansi.bright_blue": "#8fb0baff",
"terminal.ansi.dim_blue": "#14333bff",
"terminal.ansi.magenta": "#7c6f64ff",
"terminal.ansi.bright_magenta": "#bcb5afff",
"terminal.ansi.dim_magenta": "#3e3833ff",
"terminal.ansi.magenta": "#8f3e71ff",
"terminal.ansi.bright_magenta": "#c76da0ff",
"terminal.ansi.dim_magenta": "#5c2848ff",
"terminal.ansi.cyan": "#437b59ff",
"terminal.ansi.bright_cyan": "#9fbca8ff",
"terminal.ansi.dim_cyan": "#253e2eff",

View File

@@ -1,2 +1,7 @@
allow-private-module-inception = true
avoid-breaking-exported-api = false
ignore-interior-mutability = [
# Suppresses clippy::mutable_key_type, which is a false positive as the Eq
# and Hash impls do not use fields with interior mutability.
"agent::context::AgentContextKey"
]

View File

@@ -25,6 +25,7 @@ smallvec.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
workspace-hack.workspace = true
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }

View File

@@ -10,14 +10,23 @@ use gpui::{
use language::{BinaryStatus, LanguageRegistry, LanguageServerId};
use project::{
EnvironmentErrorMessage, LanguageServerProgress, LspStoreEvent, Project,
ProjectEnvironmentEvent, WorktreeId,
ProjectEnvironmentEvent,
git_store::{GitStoreEvent, Repository},
};
use smallvec::SmallVec;
use std::{cmp::Reverse, fmt::Write, sync::Arc, time::Duration};
use std::{
cmp::Reverse,
fmt::Write,
path::Path,
sync::Arc,
time::{Duration, Instant},
};
use ui::{ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*};
use util::truncate_and_trailoff;
use workspace::{StatusItemView, Workspace, item::ItemHandle};
const GIT_OPERATION_DELAY: Duration = Duration::from_millis(0);
actions!(activity_indicator, [ShowErrorMessage]);
pub enum Event {
@@ -105,6 +114,15 @@ impl ActivityIndicator {
)
.detach();
cx.subscribe(
&project.read(cx).git_store().clone(),
|_, _, event: &GitStoreEvent, cx| match event {
project::git_store::GitStoreEvent::JobsUpdated => cx.notify(),
_ => {}
},
)
.detach();
if let Some(auto_updater) = auto_updater.as_ref() {
cx.observe(auto_updater, |_, _, cx| cx.notify()).detach();
}
@@ -218,13 +236,14 @@ impl ActivityIndicator {
fn pending_environment_errors<'a>(
&'a self,
cx: &'a App,
) -> impl Iterator<Item = (&'a WorktreeId, &'a EnvironmentErrorMessage)> {
) -> impl Iterator<Item = (&'a Arc<Path>, &'a EnvironmentErrorMessage)> {
self.project.read(cx).shell_environment_errors(cx)
}
fn content_to_render(&mut self, cx: &mut Context<Self>) -> Option<Content> {
// Show if any direnv calls failed
if let Some((&worktree_id, error)) = self.pending_environment_errors(cx).next() {
if let Some((abs_path, error)) = self.pending_environment_errors(cx).next() {
let abs_path = abs_path.clone();
return Some(Content {
icon: Some(
Icon::new(IconName::Warning)
@@ -234,7 +253,7 @@ impl ActivityIndicator {
message: error.0.clone(),
on_click: Some(Arc::new(move |this, window, cx| {
this.project.update(cx, |project, cx| {
project.remove_environment_error(worktree_id, cx);
project.remove_environment_error(&abs_path, cx);
});
window.dispatch_action(Box::new(workspace::OpenLog), cx);
})),
@@ -284,6 +303,34 @@ impl ActivityIndicator {
});
}
let current_job = self
.project
.read(cx)
.active_repository(cx)
.map(|r| r.read(cx))
.and_then(Repository::current_job);
// Show any long-running git command
if let Some(job_info) = current_job {
if Instant::now() - job_info.start >= GIT_OPERATION_DELAY {
return Some(Content {
icon: Some(
Icon::new(IconName::ArrowCircle)
.size(IconSize::Small)
.with_animation(
"arrow-circle",
Animation::new(Duration::from_secs(2)).repeat(),
|icon, delta| {
icon.transform(Transformation::rotate(percentage(delta)))
},
)
.into_any_element(),
),
message: job_info.message.into(),
on_click: None,
});
}
}
// Show any language server installation info.
let mut downloading = SmallVec::<[_; 3]>::new();
let mut checking_for_update = SmallVec::<[_; 3]>::new();

View File

@@ -1,5 +1,5 @@
[package]
name = "assistant2"
name = "agent"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
@@ -28,9 +28,9 @@ async-watch.workspace = true
buffer_diff.workspace = true
chrono.workspace = true
client.workspace = true
clock.workspace = true
collections.workspace = true
command_palette_hooks.workspace = true
component.workspace = true
context_server.workspace = true
convert_case.workspace = true
db.workspace = true
@@ -50,6 +50,7 @@ itertools.workspace = true
language.workspace = true
language_model.workspace = true
language_model_selector.workspace = true
linkme.workspace = true
log.workspace = true
lsp.workspace = true
markdown.workspace = true
@@ -60,9 +61,10 @@ parking_lot.workspace = true
paths.workspace = true
picker.workspace = true
project.workspace = true
prompt_library.workspace = true
rules_library.workspace = true
prompt_store.workspace = true
proto.workspace = true
ref-cast.workspace = true
release_channel.workspace = true
rope.workspace = true
schemars.workspace = true
@@ -78,14 +80,17 @@ terminal.workspace = true
terminal_view.workspace = true
text.workspace = true
theme.workspace = true
thiserror.workspace = true
time.workspace = true
time_format.workspace = true
ui.workspace = true
ui_input.workspace = true
util.workspace = true
uuid.workspace = true
vim_mode_setting.workspace = true
workspace-hack.workspace = true
workspace.workspace = true
zed_actions.workspace = true
zed_llm_client.workspace = true
[dev-dependencies]
buffer_diff = { workspace = true, features = ["test-support"] }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
mod active_thread;
mod agent_diff;
mod assistant_configuration;
mod assistant_diff;
mod assistant_model_selector;
mod assistant_panel;
mod buffer_codegen;
@@ -18,12 +18,13 @@ mod terminal_inline_assistant;
mod thread;
mod thread_history;
mod thread_store;
mod tool_compatibility;
mod tool_use;
mod ui;
use std::sync::Arc;
use assistant_settings::AssistantSettings;
use assistant_settings::{AgentProfileId, AssistantSettings};
use client::Client;
use command_palette_hooks::CommandPaletteFilter;
use feature_flags::{Assistant2FeatureFlag, FeatureFlagAppExt};
@@ -33,25 +34,26 @@ use prompt_store::PromptBuilder;
use schemars::JsonSchema;
use serde::Deserialize;
use settings::Settings as _;
use thread::ThreadId;
pub use crate::active_thread::ActiveThread;
use crate::assistant_configuration::{AddContextServerModal, ManageProfilesModal};
pub use crate::assistant_panel::{AssistantPanel, ConcreteAssistantPanelDelegate};
pub use crate::context::{ContextLoadResult, LoadedContext};
pub use crate::inline_assistant::InlineAssistant;
pub use crate::thread::{Message, RequestKind, Thread, ThreadEvent};
pub use crate::thread::{Message, Thread, ThreadEvent};
pub use crate::thread_store::ThreadStore;
pub use assistant_diff::{AssistantDiff, AssistantDiffToolbar};
pub use agent_diff::{AgentDiff, AgentDiffToolbar};
actions!(
assistant2,
agent,
[
NewThread,
NewPromptEditor,
NewTextThread,
ToggleContextPicker,
ToggleProfileSelector,
RemoveAllContext,
ExpandMessageEditor,
OpenHistory,
OpenConfiguration,
AddContextServer,
RemoveSelectedThread,
Chat,
@@ -65,33 +67,39 @@ actions!(
RemoveFocusedContext,
AcceptSuggestedContext,
OpenActiveThreadAsMarkdown,
OpenAssistantDiff,
ToggleKeep,
OpenAgentDiff,
Keep,
Reject,
RejectAll,
KeepAll
]
);
#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema)]
pub struct NewThread {
#[serde(default)]
from_thread_id: Option<ThreadId>,
}
#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema)]
pub struct ManageProfiles {
#[serde(default)]
pub customize_tools: Option<Arc<str>>,
pub customize_tools: Option<AgentProfileId>,
}
impl ManageProfiles {
pub fn customize_tools(profile_id: Arc<str>) -> Self {
pub fn customize_tools(profile_id: AgentProfileId) -> Self {
Self {
customize_tools: Some(profile_id),
}
}
}
impl_actions!(assistant, [ManageProfiles]);
impl_actions!(agent, [NewThread, ManageProfiles]);
const NAMESPACE: &str = "assistant2";
const NAMESPACE: &str = "agent";
/// Initializes the `assistant2` crate.
/// Initializes the `agent` crate.
pub fn init(
fs: Arc<dyn Fs>,
client: Arc<Client>,
@@ -117,10 +125,10 @@ pub fn init(
cx.observe_new(AddContextServerModal::register).detach();
cx.observe_new(ManageProfilesModal::register).detach();
feature_gate_assistant2_actions(cx);
feature_gate_agent_actions(cx);
}
fn feature_gate_assistant2_actions(cx: &mut App) {
fn feature_gate_agent_actions(cx: &mut App) {
CommandPaletteFilter::update_global(cx, |filter, _cx| {
filter.hide_namespace(NAMESPACE);
});

View File

@@ -4,12 +4,20 @@ mod tool_picker;
use std::sync::Arc;
use assistant_settings::AssistantSettings;
use assistant_tool::{ToolSource, ToolWorkingSet};
use collections::HashMap;
use context_server::manager::ContextServerManager;
use gpui::{Action, AnyView, App, Entity, EventEmitter, FocusHandle, Focusable, Subscription};
use fs::Fs;
use gpui::{
Action, AnyView, App, Entity, EventEmitter, FocusHandle, Focusable, ScrollHandle, Subscription,
};
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
use ui::{Disclosure, Divider, DividerColor, ElevationIndex, Indicator, Switch, prelude::*};
use settings::{Settings, update_settings_file};
use ui::{
Disclosure, Divider, DividerColor, ElevationIndex, Indicator, Scrollbar, ScrollbarState,
Switch, SwitchColor, Tooltip, prelude::*,
};
use util::ResultExt as _;
use zed_actions::ExtensionCategoryFilter;
@@ -19,18 +27,22 @@ pub(crate) use manage_profiles_modal::ManageProfilesModal;
use crate::AddContextServer;
pub struct AssistantConfiguration {
fs: Arc<dyn Fs>,
focus_handle: FocusHandle,
configuration_views_by_provider: HashMap<LanguageModelProviderId, AnyView>,
context_server_manager: Entity<ContextServerManager>,
expanded_context_server_tools: HashMap<Arc<str>, bool>,
tools: Arc<ToolWorkingSet>,
tools: Entity<ToolWorkingSet>,
_registry_subscription: Subscription,
scroll_handle: ScrollHandle,
scrollbar_state: ScrollbarState,
}
impl AssistantConfiguration {
pub fn new(
fs: Arc<dyn Fs>,
context_server_manager: Entity<ContextServerManager>,
tools: Arc<ToolWorkingSet>,
tools: Entity<ToolWorkingSet>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
@@ -53,13 +65,19 @@ impl AssistantConfiguration {
},
);
let scroll_handle = ScrollHandle::new();
let scrollbar_state = ScrollbarState::new(scroll_handle.clone());
let mut this = Self {
fs,
focus_handle,
configuration_views_by_provider: HashMap::default(),
context_server_manager,
expanded_context_server_tools: HashMap::default(),
tools,
_registry_subscription: registry_subscription,
scroll_handle,
scrollbar_state,
};
this.build_provider_configuration_views(window, cx);
this
@@ -101,7 +119,7 @@ pub enum AssistantConfigurationEvent {
impl EventEmitter<AssistantConfigurationEvent> for AssistantConfiguration {}
impl AssistantConfiguration {
fn render_provider_configuration(
fn render_provider_configuration_block(
&mut self,
provider: &Arc<dyn LanguageModelProvider>,
cx: &mut Context<Self>,
@@ -114,7 +132,11 @@ impl AssistantConfiguration {
.cloned();
v_flex()
.pt_3()
.pb_1()
.gap_1p5()
.border_t_1()
.border_color(cx.theme().colors().border.opacity(0.6))
.child(
h_flex()
.justify_between()
@@ -126,7 +148,7 @@ impl AssistantConfiguration {
.size(IconSize::Small)
.color(Color::Muted),
)
.child(Label::new(provider_name.clone())),
.child(Label::new(provider_name.clone()).size(LabelSize::Large)),
)
.when(provider.is_authenticated(cx), |parent| {
parent.child(
@@ -151,37 +173,103 @@ impl AssistantConfiguration {
)
}),
)
.map(|parent| match configuration_view {
Some(configuration_view) => parent.child(configuration_view),
None => parent.child(div().child(Label::new(format!(
"No configuration view for {provider_name}",
)))),
})
}
fn render_provider_configuration_section(
&mut self,
cx: &mut Context<Self>,
) -> impl IntoElement {
let providers = LanguageModelRegistry::read_global(cx).providers();
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.pr(DynamicSpacing::Base20.rems(cx))
.gap_4()
.flex_1()
.child(
div()
.p(DynamicSpacing::Base08.rems(cx))
.bg(cx.theme().colors().editor_background)
.border_1()
.border_color(cx.theme().colors().border_variant)
.rounded_sm()
.map(|parent| match configuration_view {
Some(configuration_view) => parent.child(configuration_view),
None => parent.child(div().child(Label::new(format!(
"No configuration view for {provider_name}",
)))),
}),
v_flex()
.gap_0p5()
.child(Headline::new("LLM Providers"))
.child(
Label::new("Add at least one provider to use AI-powered features.")
.color(Color::Muted),
),
)
.children(
providers
.into_iter()
.map(|provider| self.render_provider_configuration_block(&provider, cx)),
)
}
fn render_command_permission(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
let always_allow_tool_actions = AssistantSettings::get_global(cx).always_allow_tool_actions;
const HEADING: &str = "Allow running editing tools without asking for confirmation";
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.pr(DynamicSpacing::Base20.rems(cx))
.gap_2()
.flex_1()
.child(Headline::new("General Settings"))
.child(
h_flex()
.gap_4()
.justify_between()
.flex_wrap()
.child(
v_flex()
.gap_0p5()
.max_w_5_6()
.child(Label::new(HEADING))
.child(Label::new("When enabled, the agent can perform potentially destructive actions without asking for your confirmation.").color(Color::Muted)),
)
.child(
Switch::new(
"always-allow-tool-actions-switch",
always_allow_tool_actions.into(),
)
.color(SwitchColor::Accent)
.on_click({
let fs = self.fs.clone();
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _| {
settings.set_always_allow_tool_actions(allow);
},
);
}
}),
),
)
}
fn render_context_servers_section(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
let context_servers = self.context_server_manager.read(cx).all_servers().clone();
let tools_by_source = self.tools.tools_by_source(cx);
let tools_by_source = self.tools.read(cx).tools_by_source(cx);
let empty = Vec::new();
const SUBHEADING: &str = "Connect to context servers via the Model Context Protocol either via Zed extensions or directly.";
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.pr(DynamicSpacing::Base20.rems(cx))
.gap_2()
.flex_1()
.child(
v_flex()
.gap_0p5()
.child(Headline::new("Context Servers (MCP)").size(HeadlineSize::Small))
.child(Headline::new("Model Context Protocol (MCP) Servers"))
.child(Label::new(SUBHEADING).color(Color::Muted)),
)
.children(context_servers.into_iter().map(|context_server| {
@@ -202,15 +290,14 @@ impl AssistantConfiguration {
v_flex()
.id(SharedString::from(context_server.id()))
.border_1()
.rounded_sm()
.rounded_md()
.border_color(cx.theme().colors().border)
.bg(cx.theme().colors().editor_background)
.bg(cx.theme().colors().background.opacity(0.25))
.child(
h_flex()
.p_1()
.justify_between()
.px_2()
.py_1()
.when(are_tools_expanded, |element| {
.when(are_tools_expanded && tool_count > 1, |element| {
element
.border_b_1()
.border_color(cx.theme().colors().border)
@@ -220,6 +307,7 @@ impl AssistantConfiguration {
.gap_2()
.child(
Disclosure::new("tool-list-disclosure", are_tools_expanded)
.disabled(tool_count == 0)
.on_click(cx.listener({
let context_server_id = context_server.id();
move |this, _event, _window, _cx| {
@@ -240,65 +328,78 @@ impl AssistantConfiguration {
.child(Label::new(context_server.id()))
.child(
Label::new(format!("{tool_count} tools"))
.color(Color::Muted),
.color(Color::Muted)
.size(LabelSize::Small),
),
)
.child(h_flex().child(
Switch::new("context-server-switch", is_running.into()).on_click({
let context_server_manager =
self.context_server_manager.clone();
let context_server = context_server.clone();
move |state, _window, cx| match state {
ToggleState::Unselected | ToggleState::Indeterminate => {
context_server_manager.update(cx, |this, cx| {
this.stop_server(context_server.clone(), cx)
.log_err();
});
}
ToggleState::Selected => {
cx.spawn({
let context_server_manager =
context_server_manager.clone();
let context_server = context_server.clone();
async move |cx| {
if let Some(start_server_task) =
context_server_manager
.update(cx, |this, cx| {
this.start_server(
context_server,
cx,
)
})
.log_err()
{
start_server_task.await.log_err();
.child(
Switch::new("context-server-switch", is_running.into())
.color(SwitchColor::Accent)
.on_click({
let context_server_manager =
self.context_server_manager.clone();
let context_server = context_server.clone();
move |state, _window, cx| match state {
ToggleState::Unselected
| ToggleState::Indeterminate => {
context_server_manager.update(cx, |this, cx| {
this.stop_server(context_server.clone(), cx)
.log_err();
});
}
ToggleState::Selected => {
cx.spawn({
let context_server_manager =
context_server_manager.clone();
let context_server = context_server.clone();
async move |cx| {
if let Some(start_server_task) =
context_server_manager
.update(cx, |this, cx| {
this.start_server(
context_server,
cx,
)
})
.log_err()
{
start_server_task.await.log_err();
}
}
}
})
.detach();
})
.detach();
}
}
}
}),
)),
}),
),
)
.map(|parent| {
if !are_tools_expanded {
return parent;
}
parent.child(v_flex().children(tools.into_iter().enumerate().map(
|(ix, tool)| {
parent.child(v_flex().py_1p5().px_1().gap_1().children(
tools.into_iter().enumerate().map(|(ix, tool)| {
h_flex()
.px_2()
.py_1()
.when(ix < tool_count - 1, |element| {
element
.border_b_1()
.border_color(cx.theme().colors().border)
})
.child(Label::new(tool.name()))
},
)))
.id(("tool-item", ix))
.px_1()
.gap_2()
.justify_between()
.hover(|style| style.bg(cx.theme().colors().element_hover))
.rounded_sm()
.child(
Label::new(tool.name())
.buffer_font(cx)
.size(LabelSize::Small),
)
.child(
Icon::new(IconName::Info)
.size(IconSize::Small)
.color(Color::Ignored),
)
.tooltip(Tooltip::text(tool.description()))
}),
))
})
}))
.child(
@@ -307,7 +408,7 @@ impl AssistantConfiguration {
.gap_2()
.child(
h_flex().w_full().child(
Button::new("add-context-server", "Add Context Server")
Button::new("add-context-server", "Add Custom Server")
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.full_width()
@@ -323,7 +424,7 @@ impl AssistantConfiguration {
h_flex().w_full().child(
Button::new(
"install-context-server-extensions",
"Install Context Server Extensions",
"Install MCP Extensions",
)
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
@@ -350,36 +451,51 @@ impl AssistantConfiguration {
impl Render for AssistantConfiguration {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let providers = LanguageModelRegistry::read_global(cx).providers();
v_flex()
.id("assistant-configuration")
.key_context("AgentConfiguration")
.track_focus(&self.focus_handle(cx))
.bg(cx.theme().colors().panel_background)
.relative()
.size_full()
.overflow_y_scroll()
.child(self.render_context_servers_section(cx))
.child(Divider::horizontal().color(DividerColor::Border))
.pb_8()
.bg(cx.theme().colors().panel_background)
.child(
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.mt_1()
.gap_6()
.flex_1()
.child(
v_flex()
.gap_0p5()
.child(Headline::new("LLM Providers").size(HeadlineSize::Small))
.child(
Label::new("Add at least one provider to use AI-powered features.")
.color(Color::Muted),
),
)
.children(
providers
.into_iter()
.map(|provider| self.render_provider_configuration(&provider, cx)),
),
.id("assistant-configuration-content")
.track_scroll(&self.scroll_handle)
.size_full()
.overflow_y_scroll()
.child(self.render_command_permission(cx))
.child(Divider::horizontal().color(DividerColor::Border))
.child(self.render_context_servers_section(cx))
.child(Divider::horizontal().color(DividerColor::Border))
.child(self.render_provider_configuration_section(cx)),
)
.child(
div()
.id("assistant-configuration-scrollbar")
.occlude()
.absolute()
.right(px(3.))
.top_0()
.bottom_0()
.pb_6()
.w(px(12.))
.cursor_default()
.on_mouse_move(cx.listener(|_, _, _window, cx| {
cx.notify();
cx.stop_propagation()
}))
.on_hover(|_, _window, cx| {
cx.stop_propagation();
})
.on_any_mouse_down(|_, _window, cx| {
cx.stop_propagation();
})
.on_scroll_wheel(cx.listener(|_, _, _window, cx| {
cx.notify();
}))
.children(Scrollbar::vertical(self.scrollbar_state.clone())),
)
}
}

View File

@@ -1,17 +1,17 @@
use context_server::{ContextServerSettings, ServerCommand, ServerConfig};
use editor::Editor;
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, WeakEntity, prelude::*};
use serde_json::json;
use settings::update_settings_file;
use ui::{Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*};
use ui::{KeyBinding, Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*};
use ui_input::SingleLineInput;
use workspace::{ModalView, Workspace};
use crate::AddContextServer;
pub struct AddContextServerModal {
workspace: WeakEntity<Workspace>,
name_editor: Entity<Editor>,
command_editor: Entity<Editor>,
name_editor: Entity<SingleLineInput>,
command_editor: Entity<SingleLineInput>,
}
impl AddContextServerModal {
@@ -33,15 +33,10 @@ impl AddContextServerModal {
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let name_editor = cx.new(|cx| Editor::single_line(window, cx));
let command_editor = cx.new(|cx| Editor::single_line(window, cx));
name_editor.update(cx, |editor, cx| {
editor.set_placeholder_text("Context server name", cx);
});
command_editor.update(cx, |editor, cx| {
editor.set_placeholder_text("Command to run the context server", cx);
let name_editor =
cx.new(|cx| SingleLineInput::new(window, cx, "my-custom-server").label("Name"));
let command_editor = cx.new(|cx| {
SingleLineInput::new(window, cx, "Command").label("Command to run the MCP server")
});
Self {
@@ -51,9 +46,23 @@ impl AddContextServerModal {
}
}
fn confirm(&mut self, cx: &mut Context<Self>) {
let name = self.name_editor.read(cx).text(cx).trim().to_string();
let command = self.command_editor.read(cx).text(cx).trim().to_string();
fn confirm(&mut self, _: &menu::Confirm, cx: &mut Context<Self>) {
let name = self
.name_editor
.read(cx)
.editor()
.read(cx)
.text(cx)
.trim()
.to_string();
let command = self
.command_editor
.read(cx)
.editor()
.read(cx)
.text(cx)
.trim()
.to_string();
if name.is_empty() || command.is_empty() {
return;
@@ -87,7 +96,7 @@ impl AddContextServerModal {
cx.emit(DismissEvent);
}
fn cancel(&mut self, cx: &mut Context<Self>) {
fn cancel(&mut self, _: &menu::Cancel, cx: &mut Context<Self>) {
cx.emit(DismissEvent);
}
}
@@ -103,48 +112,68 @@ impl Focusable for AddContextServerModal {
impl EventEmitter<DismissEvent> for AddContextServerModal {}
impl Render for AddContextServerModal {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let is_name_empty = self.name_editor.read(cx).text(cx).trim().is_empty();
let is_command_empty = self.command_editor.read(cx).text(cx).trim().is_empty();
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let is_name_empty = self.name_editor.read(cx).is_empty(cx);
let is_command_empty = self.command_editor.read(cx).is_empty(cx);
let focus_handle = self.focus_handle(cx);
div()
.elevation_3(cx)
.w(rems(34.))
.key_context("AddContextServerModal")
.on_action(cx.listener(|this, _: &menu::Cancel, _window, cx| this.cancel(cx)))
.on_action(cx.listener(|this, _: &menu::Confirm, _window, cx| this.confirm(cx)))
.on_action(
cx.listener(|this, _: &menu::Cancel, _window, cx| this.cancel(&menu::Cancel, cx)),
)
.on_action(
cx.listener(|this, _: &menu::Confirm, _window, cx| {
this.confirm(&menu::Confirm, cx)
}),
)
.capture_any_mouse_down(cx.listener(|this, _, window, cx| {
this.focus_handle(cx).focus(window);
}))
.on_mouse_down_out(cx.listener(|_this, _, _, cx| cx.emit(DismissEvent)))
.child(
Modal::new("add-context-server", None)
.header(ModalHeader::new().headline("Add Context Server"))
.header(ModalHeader::new().headline("Add MCP Server"))
.section(
Section::new()
.child(
v_flex()
.gap_1()
.child(Label::new("Name"))
.child(self.name_editor.clone()),
)
.child(
v_flex()
.gap_1()
.child(Label::new("Command"))
.child(self.command_editor.clone()),
),
Section::new().child(
v_flex()
.gap_2()
.child(self.name_editor.clone())
.child(self.command_editor.clone()),
),
)
.footer(
ModalFooter::new()
.start_slot(
Button::new("cancel", "Cancel").on_click(
cx.listener(|this, _event, _window, cx| this.cancel(cx)),
),
Button::new("cancel", "Cancel")
.key_binding(
KeyBinding::for_action_in(
&menu::Cancel,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click(cx.listener(|this, _event, _window, cx| {
this.cancel(&menu::Cancel, cx)
})),
)
.end_slot(
Button::new("add-server", "Add Server")
.disabled(is_name_empty || is_command_empty)
.key_binding(
KeyBinding::for_action_in(
&menu::Confirm,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(12.))),
)
.map(|button| {
if is_name_empty {
button.tooltip(Tooltip::text("Name is required"))
@@ -154,9 +183,9 @@ impl Render for AddContextServerModal {
button
}
})
.on_click(
cx.listener(|this, _event, _window, cx| this.confirm(cx)),
),
.on_click(cx.listener(|this, _event, _window, cx| {
this.confirm(&menu::Confirm, cx)
})),
),
),
)

View File

@@ -2,7 +2,7 @@ mod profile_modal_header;
use std::sync::Arc;
use assistant_settings::{AgentProfile, AssistantSettings};
use assistant_settings::{AgentProfile, AgentProfileId, AssistantSettings};
use assistant_tool::ToolWorkingSet;
use convert_case::{Case, Casing as _};
use editor::Editor;
@@ -27,7 +27,7 @@ enum Mode {
NewProfile(NewProfileMode),
ViewProfile(ViewProfileMode),
ConfigureTools {
profile_id: Arc<str>,
profile_id: AgentProfileId,
tool_picker: Entity<ToolPicker>,
_subscription: Subscription,
},
@@ -58,7 +58,7 @@ impl Mode {
#[derive(Clone)]
struct ProfileEntry {
pub id: Arc<str>,
pub id: AgentProfileId,
pub name: SharedString,
pub navigation: NavigableEntry,
}
@@ -71,7 +71,7 @@ pub struct ChooseProfileMode {
#[derive(Clone)]
pub struct ViewProfileMode {
profile_id: Arc<str>,
profile_id: AgentProfileId,
fork_profile: NavigableEntry,
configure_tools: NavigableEntry,
}
@@ -79,12 +79,12 @@ pub struct ViewProfileMode {
#[derive(Clone)]
pub struct NewProfileMode {
name_editor: Entity<Editor>,
base_profile_id: Option<Arc<str>>,
base_profile_id: Option<AgentProfileId>,
}
pub struct ManageProfilesModal {
fs: Arc<dyn Fs>,
tools: Arc<ToolWorkingSet>,
tools: Entity<ToolWorkingSet>,
thread_store: WeakEntity<ThreadStore>,
focus_handle: FocusHandle,
mode: Mode,
@@ -117,7 +117,7 @@ impl ManageProfilesModal {
pub fn new(
fs: Arc<dyn Fs>,
tools: Arc<ToolWorkingSet>,
tools: Entity<ToolWorkingSet>,
thread_store: WeakEntity<ThreadStore>,
window: &mut Window,
cx: &mut Context<Self>,
@@ -140,7 +140,7 @@ impl ManageProfilesModal {
fn new_profile(
&mut self,
base_profile_id: Option<Arc<str>>,
base_profile_id: Option<AgentProfileId>,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -158,7 +158,7 @@ impl ManageProfilesModal {
pub fn view_profile(
&mut self,
profile_id: Arc<str>,
profile_id: AgentProfileId,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -172,7 +172,7 @@ impl ManageProfilesModal {
fn configure_tools(
&mut self,
profile_id: Arc<str>,
profile_id: AgentProfileId,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -219,7 +219,7 @@ impl ManageProfilesModal {
.and_then(|profile_id| settings.profiles.get(profile_id).cloned());
let name = mode.name_editor.read(cx).text(cx);
let profile_id: Arc<str> = name.to_case(Case::Kebab).into();
let profile_id = AgentProfileId(name.to_case(Case::Kebab).into());
let profile = AgentProfile {
name: name.into(),
@@ -227,6 +227,10 @@ impl ManageProfilesModal {
.as_ref()
.map(|profile| profile.tools.clone())
.unwrap_or_default(),
enable_all_context_servers: base_profile
.as_ref()
.map(|profile| profile.enable_all_context_servers)
.unwrap_or_default(),
context_servers: base_profile
.map(|profile| profile.context_servers)
.unwrap_or_default(),
@@ -257,7 +261,12 @@ impl ManageProfilesModal {
}
}
fn create_profile(&self, profile_id: Arc<str>, profile: AgentProfile, cx: &mut Context<Self>) {
fn create_profile(
&self,
profile_id: AgentProfileId,
profile: AgentProfile,
cx: &mut Context<Self>,
) {
update_settings_file::<AssistantSettings>(self.fs.clone(), cx, {
move |settings, _cx| {
settings.create_profile(profile_id, profile).log_err();

View File

@@ -1,8 +1,8 @@
use std::sync::Arc;
use assistant_settings::{
AgentProfile, AgentProfileContent, AssistantSettings, AssistantSettingsContent,
ContextServerPresetContent, VersionedAssistantSettingsContent,
AgentProfile, AgentProfileContent, AgentProfileId, AssistantSettings, AssistantSettingsContent,
ContextServerPresetContent,
};
use assistant_tool::{ToolSource, ToolWorkingSet};
use fs::Fs;
@@ -51,7 +51,7 @@ pub struct ToolPickerDelegate {
thread_store: WeakEntity<ThreadStore>,
fs: Arc<dyn Fs>,
tools: Vec<ToolEntry>,
profile_id: Arc<str>,
profile_id: AgentProfileId,
profile: AgentProfile,
matches: Vec<StringMatch>,
selected_index: usize,
@@ -60,15 +60,15 @@ pub struct ToolPickerDelegate {
impl ToolPickerDelegate {
pub fn new(
fs: Arc<dyn Fs>,
tool_set: Arc<ToolWorkingSet>,
tool_set: Entity<ToolWorkingSet>,
thread_store: WeakEntity<ThreadStore>,
profile_id: Arc<str>,
profile_id: AgentProfileId,
profile: AgentProfile,
cx: &mut Context<ToolPicker>,
) -> Self {
let mut tool_entries = Vec::new();
for (source, tools) in tool_set.tools_by_source(cx) {
for (source, tools) in tool_set.read(cx).tools_by_source(cx) {
tool_entries.extend(tools.into_iter().map(|tool| ToolEntry {
name: tool.name().into(),
source: source.clone(),
@@ -191,8 +191,8 @@ impl PickerDelegate for ToolPickerDelegate {
let active_profile_id = &AssistantSettings::get_global(cx).default_profile;
if active_profile_id == &self.profile_id {
self.thread_store
.update(cx, |this, _cx| {
this.load_profile(&self.profile);
.update(cx, |this, cx| {
this.load_profile(self.profile.clone(), cx);
})
.log_err();
}
@@ -201,45 +201,49 @@ impl PickerDelegate for ToolPickerDelegate {
let profile_id = self.profile_id.clone();
let default_profile = self.profile.clone();
let tool = tool.clone();
move |settings, _cx| match settings {
AssistantSettingsContent::Versioned(VersionedAssistantSettingsContent::V2(
settings,
)) => {
let profiles = settings.profiles.get_or_insert_default();
let profile =
profiles
.entry(profile_id)
.or_insert_with(|| AgentProfileContent {
name: default_profile.name.into(),
tools: default_profile.tools,
context_servers: default_profile
.context_servers
.into_iter()
.map(|(server_id, preset)| {
(
server_id,
ContextServerPresetContent {
tools: preset.tools,
},
)
})
.collect(),
});
move |settings: &mut AssistantSettingsContent, _cx| {
settings
.v2_setting(|v2_settings| {
let profiles = v2_settings.profiles.get_or_insert_default();
let profile =
profiles
.entry(profile_id)
.or_insert_with(|| AgentProfileContent {
name: default_profile.name.into(),
tools: default_profile.tools,
enable_all_context_servers: Some(
default_profile.enable_all_context_servers,
),
context_servers: default_profile
.context_servers
.into_iter()
.map(|(server_id, preset)| {
(
server_id,
ContextServerPresetContent {
tools: preset.tools,
},
)
})
.collect(),
});
match tool.source {
ToolSource::Native => {
*profile.tools.entry(tool.name).or_default() = is_enabled;
match tool.source {
ToolSource::Native => {
*profile.tools.entry(tool.name).or_default() = is_enabled;
}
ToolSource::ContextServer { id } => {
let preset = profile
.context_servers
.entry(id.clone().into())
.or_default();
*preset.tools.entry(tool.name.clone()).or_default() = is_enabled;
}
}
ToolSource::ContextServer { id } => {
let preset = profile
.context_servers
.entry(id.clone().into())
.or_default();
*preset.tools.entry(tool.name.clone()).or_default() = is_enabled;
}
}
}
_ => {}
Ok(())
})
.ok();
}
});
}
@@ -268,7 +272,7 @@ impl PickerDelegate for ToolPickerDelegate {
.get(id.as_ref())
.and_then(|preset| preset.tools.get(&tool.name))
.copied()
.unwrap_or(false),
.unwrap_or(self.profile.enable_all_context_servers),
};
Some(

View File

@@ -1,7 +1,7 @@
use assistant_settings::AssistantSettings;
use fs::Fs;
use gpui::{Entity, FocusHandle, SharedString};
use language_model::LanguageModelRegistry;
use language_model_selector::{
LanguageModelSelector, LanguageModelSelectorPopoverMenu, ToggleModelSelector,
};
@@ -9,6 +9,8 @@ use settings::update_settings_file;
use std::sync::Arc;
use ui::{ButtonLike, PopoverMenuHandle, Tooltip, prelude::*};
pub use language_model_selector::ModelType;
pub struct AssistantModelSelector {
selector: Entity<LanguageModelSelector>,
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
@@ -20,6 +22,7 @@ impl AssistantModelSelector {
fs: Arc<dyn Fs>,
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
focus_handle: FocusHandle,
model_type: ModelType,
window: &mut Window,
cx: &mut App,
) -> Self {
@@ -28,12 +31,34 @@ impl AssistantModelSelector {
let fs = fs.clone();
LanguageModelSelector::new(
move |model, cx| {
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _cx| settings.set_model(model.clone()),
);
let provider = model.provider_id().0.to_string();
let model_id = model.id().0.to_string();
match model_type {
ModelType::Default => {
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _cx| {
settings.set_model(model.clone());
},
);
}
ModelType::InlineAssistant => {
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _cx| {
settings.set_inline_assistant_model(
provider.clone(),
model_id.clone(),
);
},
);
}
}
},
model_type,
window,
cx,
)
@@ -50,11 +75,12 @@ impl AssistantModelSelector {
impl Render for AssistantModelSelector {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let active_model = LanguageModelRegistry::read_global(cx).active_model();
let focus_handle = self.focus_handle.clone();
let model_name = match active_model {
Some(model) => model.name().0,
_ => SharedString::from("No model selected"),
let model = self.selector.read(cx).active_model(cx);
let (model_name, model_icon) = match model {
Some(model) => (model.model.name().0, Some(model.provider.icon())),
_ => (SharedString::from("No model selected"), None),
};
LanguageModelSelectorPopoverMenu::new(
@@ -64,10 +90,16 @@ impl Render for AssistantModelSelector {
.child(
h_flex()
.gap_0p5()
.children(
model_icon.map(|icon| {
Icon::new(icon).color(Color::Muted).size(IconSize::Small)
}),
)
.child(
Label::new(model_name)
.size(LabelSize::Small)
.color(Color::Muted),
.color(Color::Muted)
.ml_1(),
)
.child(
Icon::new(IconName::ChevronDown)

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,14 @@
use crate::context::attach_context_to_message;
use crate::context_store::ContextStore;
use crate::context::ContextLoadResult;
use crate::inline_prompt_editor::CodegenStatus;
use anyhow::{Context as _, Result};
use crate::{context::load_context, context_store::ContextStore};
use anyhow::Result;
use client::telemetry::Telemetry;
use collections::HashSet;
use editor::{Anchor, AnchorRangeExt, MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint};
use futures::{SinkExt, Stream, StreamExt, channel::mpsc, future::LocalBoxFuture, join};
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Subscription, Task};
use futures::{
SinkExt, Stream, StreamExt, TryStreamExt as _, channel::mpsc, future::LocalBoxFuture, join,
};
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Subscription, Task, WeakEntity};
use language::{Buffer, IndentKind, Point, TransactionId, line_diff};
use language_model::{
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
@@ -14,7 +16,9 @@ use language_model::{
};
use multi_buffer::MultiBufferRow;
use parking_lot::Mutex;
use project::Project;
use prompt_store::PromptBuilder;
use prompt_store::PromptStore;
use rope::Rope;
use smol::future::FutureExt;
use std::{
@@ -28,7 +32,7 @@ use std::{
time::Instant,
};
use streaming_diff::{CharOperation, LineDiff, LineOperation, StreamingDiff};
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
use telemetry_events::{AssistantEventData, AssistantKind, AssistantPhase};
pub struct BufferCodegen {
alternatives: Vec<Entity<CodegenAlternative>>,
@@ -39,6 +43,8 @@ pub struct BufferCodegen {
range: Range<Anchor>,
initial_transaction_id: Option<TransactionId>,
context_store: Entity<ContextStore>,
project: WeakEntity<Project>,
prompt_store: Option<Entity<PromptStore>>,
telemetry: Arc<Telemetry>,
builder: Arc<PromptBuilder>,
pub is_insertion: bool,
@@ -50,6 +56,8 @@ impl BufferCodegen {
range: Range<Anchor>,
initial_transaction_id: Option<TransactionId>,
context_store: Entity<ContextStore>,
project: WeakEntity<Project>,
prompt_store: Option<Entity<PromptStore>>,
telemetry: Arc<Telemetry>,
builder: Arc<PromptBuilder>,
cx: &mut Context<Self>,
@@ -60,6 +68,8 @@ impl BufferCodegen {
range.clone(),
false,
Some(context_store.clone()),
project.clone(),
prompt_store.clone(),
Some(telemetry.clone()),
builder.clone(),
cx,
@@ -75,6 +85,8 @@ impl BufferCodegen {
range,
initial_transaction_id,
context_store,
project,
prompt_store,
telemetry,
builder,
};
@@ -131,7 +143,12 @@ impl BufferCodegen {
cx.notify();
}
pub fn start(&mut self, user_prompt: String, cx: &mut Context<Self>) -> Result<()> {
pub fn start(
&mut self,
primary_model: Arc<dyn LanguageModel>,
user_prompt: String,
cx: &mut Context<Self>,
) -> Result<()> {
let alternative_models = LanguageModelRegistry::read_global(cx)
.inline_alternative_models()
.to_vec();
@@ -148,6 +165,8 @@ impl BufferCodegen {
self.range.clone(),
false,
Some(self.context_store.clone()),
self.project.clone(),
self.prompt_store.clone(),
Some(self.telemetry.clone()),
self.builder.clone(),
cx,
@@ -155,10 +174,6 @@ impl BufferCodegen {
}));
}
let primary_model = LanguageModelRegistry::read_global(cx)
.active_model()
.context("no active model")?;
for (model, alternative) in iter::once(primary_model)
.chain(alternative_models)
.zip(&self.alternatives)
@@ -228,13 +243,14 @@ pub struct CodegenAlternative {
generation: Task<()>,
diff: Diff,
context_store: Option<Entity<ContextStore>>,
project: WeakEntity<Project>,
prompt_store: Option<Entity<PromptStore>>,
telemetry: Option<Arc<Telemetry>>,
_subscription: gpui::Subscription,
builder: Arc<PromptBuilder>,
active: bool,
edits: Vec<(Range<Anchor>, String)>,
line_operations: Vec<LineOperation>,
request: Option<LanguageModelRequest>,
elapsed_time: Option<f64>,
completion: Option<String>,
pub message_id: Option<String>,
@@ -248,6 +264,8 @@ impl CodegenAlternative {
range: Range<Anchor>,
active: bool,
context_store: Option<Entity<ContextStore>>,
project: WeakEntity<Project>,
prompt_store: Option<Entity<PromptStore>>,
telemetry: Option<Arc<Telemetry>>,
builder: Arc<PromptBuilder>,
cx: &mut Context<Self>,
@@ -289,6 +307,8 @@ impl CodegenAlternative {
generation: Task::ready(()),
diff: Diff::default(),
context_store,
project,
prompt_store,
telemetry,
_subscription: cx.subscribe(&buffer, Self::handle_buffer_event),
builder,
@@ -296,7 +316,6 @@ impl CodegenAlternative {
edits: Vec::new(),
line_operations: Vec::new(),
range,
request: None,
elapsed_time: None,
completion: None,
}
@@ -365,16 +384,18 @@ impl CodegenAlternative {
async { Ok(LanguageModelTextStream::default()) }.boxed_local()
} else {
let request = self.build_request(user_prompt, cx)?;
self.request = Some(request.clone());
cx.spawn(async move |_, cx| model.stream_completion_text(request, &cx).await)
cx.spawn(async move |_, cx| model.stream_completion_text(request.await, &cx).await)
.boxed_local()
};
self.handle_stream(telemetry_id, provider_id.to_string(), api_key, stream, cx);
Ok(())
}
fn build_request(&self, user_prompt: String, cx: &mut App) -> Result<LanguageModelRequest> {
fn build_request(
&self,
user_prompt: String,
cx: &mut App,
) -> Result<Task<LanguageModelRequest>> {
let buffer = self.buffer.read(cx).snapshot(cx);
let language = buffer.language_at(self.range.start);
let language_name = if let Some(language) = language.as_ref() {
@@ -407,28 +428,44 @@ impl CodegenAlternative {
.generate_inline_transformation_prompt(user_prompt, language_name, buffer, range)
.map_err(|e| anyhow::anyhow!("Failed to generate content prompt: {}", e))?;
let mut request_message = LanguageModelRequestMessage {
role: Role::User,
content: Vec::new(),
cache: false,
};
let context_task = self.context_store.as_ref().map(|context_store| {
if let Some(project) = self.project.upgrade() {
let context = context_store
.read(cx)
.context()
.cloned()
.collect::<Vec<_>>();
load_context(context, &project, &self.prompt_store, cx)
} else {
Task::ready(ContextLoadResult::default())
}
});
if let Some(context_store) = &self.context_store {
attach_context_to_message(
&mut request_message,
context_store.read(cx).context().iter(),
cx,
);
}
Ok(cx.spawn(async move |_cx| {
let mut request_message = LanguageModelRequestMessage {
role: Role::User,
content: Vec::new(),
cache: false,
};
request_message.content.push(prompt.into());
if let Some(context_task) = context_task {
context_task
.await
.loaded_context
.add_to_request_message(&mut request_message);
}
Ok(LanguageModelRequest {
tools: Vec::new(),
stop: Vec::new(),
temperature: None,
messages: vec![request_message],
})
request_message.content.push(prompt.into());
LanguageModelRequest {
thread_id: None,
prompt_id: None,
tools: Vec::new(),
stop: Vec::new(),
temperature: None,
messages: vec![request_message],
}
}))
}
pub fn handle_stream(
@@ -466,7 +503,7 @@ impl CodegenAlternative {
}
}
let http_client = cx.http_client().clone();
let http_client = cx.http_client();
let telemetry = self.telemetry.clone();
let language_name = {
let multibuffer = self.buffer.read(cx);
@@ -505,7 +542,9 @@ impl CodegenAlternative {
let mut response_latency = None;
let request_start = Instant::now();
let diff = async {
let chunks = StripInvalidSpans::new(stream?.stream);
let chunks = StripInvalidSpans::new(
stream?.stream.map_err(|error| error.into()),
);
futures::pin_mut!(chunks);
let mut diff = StreamingDiff::new(selected_text.to_string());
let mut line_diff = LineDiff::default();
@@ -600,7 +639,7 @@ impl CodegenAlternative {
let error_message = result.as_ref().err().map(|error| error.to_string());
report_assistant_event(
AssistantEvent {
AssistantEventData {
conversation_id: None,
message_id,
kind: AssistantKind::Inline,
@@ -1031,6 +1070,7 @@ impl Diff {
#[cfg(test)]
mod tests {
use super::*;
use fs::FakeFs;
use futures::{
Stream,
stream::{self},
@@ -1073,12 +1113,16 @@ mod tests {
snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(4, 5))
});
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
let fs = FakeFs::new(cx.executor());
let project = Project::test(fs, vec![], cx).await;
let codegen = cx.new(|cx| {
CodegenAlternative::new(
buffer.clone(),
range.clone(),
true,
None,
project.downgrade(),
None,
None,
prompt_builder,
cx,
@@ -1137,12 +1181,16 @@ mod tests {
snapshot.anchor_before(Point::new(1, 6))..snapshot.anchor_after(Point::new(1, 6))
});
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
let fs = FakeFs::new(cx.executor());
let project = Project::test(fs, vec![], cx).await;
let codegen = cx.new(|cx| {
CodegenAlternative::new(
buffer.clone(),
range.clone(),
true,
None,
project.downgrade(),
None,
None,
prompt_builder,
cx,
@@ -1204,12 +1252,16 @@ mod tests {
snapshot.anchor_before(Point::new(1, 2))..snapshot.anchor_after(Point::new(1, 2))
});
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
let fs = FakeFs::new(cx.executor());
let project = Project::test(fs, vec![], cx).await;
let codegen = cx.new(|cx| {
CodegenAlternative::new(
buffer.clone(),
range.clone(),
true,
None,
project.downgrade(),
None,
None,
prompt_builder,
cx,
@@ -1271,12 +1323,16 @@ mod tests {
snapshot.anchor_before(Point::new(0, 0))..snapshot.anchor_after(Point::new(4, 2))
});
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
let fs = FakeFs::new(cx.executor());
let project = Project::test(fs, vec![], cx).await;
let codegen = cx.new(|cx| {
CodegenAlternative::new(
buffer.clone(),
range.clone(),
true,
None,
project.downgrade(),
None,
None,
prompt_builder,
cx,
@@ -1326,12 +1382,16 @@ mod tests {
snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 14))
});
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
let fs = FakeFs::new(cx.executor());
let project = Project::test(fs, vec![], cx).await;
let codegen = cx.new(|cx| {
CodegenAlternative::new(
buffer.clone(),
range.clone(),
false,
None,
project.downgrade(),
None,
None,
prompt_builder,
cx,

805
crates/agent/src/context.rs Normal file
View File

@@ -0,0 +1,805 @@
use std::hash::{Hash, Hasher};
use std::{ops::Range, path::Path, sync::Arc};
use collections::HashSet;
use futures::future;
use futures::{FutureExt, future::Shared};
use gpui::{App, AppContext as _, Entity, SharedString, Task};
use language::Buffer;
use language_model::{LanguageModelImage, LanguageModelRequestMessage, MessageContent};
use project::{Project, ProjectEntryId, ProjectPath, Worktree};
use prompt_store::{PromptStore, UserPromptId};
use ref_cast::RefCast;
use rope::{Point, Rope};
use text::{Anchor, OffsetRangeExt as _};
use ui::{ElementId, IconName};
use util::{ResultExt as _, post_inc};
use crate::thread::Thread;
pub const RULES_ICON: IconName = IconName::Context;
pub enum ContextKind {
File,
Directory,
Symbol,
Selection,
FetchedUrl,
Thread,
Rules,
Image,
}
impl ContextKind {
pub fn icon(&self) -> IconName {
match self {
ContextKind::File => IconName::File,
ContextKind::Directory => IconName::Folder,
ContextKind::Symbol => IconName::Code,
ContextKind::Selection => IconName::Context,
ContextKind::FetchedUrl => IconName::Globe,
ContextKind::Thread => IconName::MessageBubbles,
ContextKind::Rules => RULES_ICON,
ContextKind::Image => IconName::Image,
}
}
}
/// Handle for context that can be added to a user message.
///
/// This uses IDs that are stable enough for tracking renames and identifying when context has
/// already been added to the thread. To use this in a set, wrap it in `AgentContextKey` to opt in
/// to `PartialEq` and `Hash` impls that use the subset of the fields used for this stable identity.
#[derive(Debug, Clone)]
pub enum AgentContext {
File(FileContext),
Directory(DirectoryContext),
Symbol(SymbolContext),
Selection(SelectionContext),
FetchedUrl(FetchedUrlContext),
Thread(ThreadContext),
Rules(RulesContext),
Image(ImageContext),
}
impl AgentContext {
fn id(&self) -> ContextId {
match self {
Self::File(context) => context.context_id,
Self::Directory(context) => context.context_id,
Self::Symbol(context) => context.context_id,
Self::Selection(context) => context.context_id,
Self::FetchedUrl(context) => context.context_id,
Self::Thread(context) => context.context_id,
Self::Rules(context) => context.context_id,
Self::Image(context) => context.context_id,
}
}
pub fn element_id(&self, name: SharedString) -> ElementId {
ElementId::NamedInteger(name, self.id().0)
}
}
/// ID created at time of context add, for use in ElementId. This is not the stable identity of a
/// context, instead that's handled by the `PartialEq` and `Hash` impls of `AgentContextKey`.
#[derive(Debug, Copy, Clone)]
pub struct ContextId(u64);
impl ContextId {
pub fn zero() -> Self {
ContextId(0)
}
fn for_lookup() -> Self {
ContextId(u64::MAX)
}
pub fn post_inc(&mut self) -> Self {
Self(post_inc(&mut self.0))
}
}
/// File context provides the entire contents of a file.
///
/// This holds an `Entity<Buffer>` so that file path renames affect its display and so that it can
/// be opened even if the file has been deleted. An alternative might be to use `ProjectEntryId`,
/// but then when deleted there is no path info or ability to open.
#[derive(Debug, Clone)]
pub struct FileContext {
pub buffer: Entity<Buffer>,
pub context_id: ContextId,
}
impl FileContext {
pub fn eq_for_key(&self, other: &Self) -> bool {
self.buffer == other.buffer
}
pub fn hash_for_key<H: Hasher>(&self, state: &mut H) {
self.buffer.hash(state)
}
pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
let file = self.buffer.read(cx).file()?;
Some(ProjectPath {
worktree_id: file.worktree_id(cx),
path: file.path().clone(),
})
}
fn load(&self, cx: &App) -> Option<Task<(String, Entity<Buffer>)>> {
let buffer_ref = self.buffer.read(cx);
let Some(file) = buffer_ref.file() else {
log::error!("file context missing path");
return None;
};
let full_path = file.full_path(cx);
let rope = buffer_ref.as_rope().clone();
let buffer = self.buffer.clone();
Some(
cx.background_spawn(
async move { (to_fenced_codeblock(&full_path, rope, None), buffer) },
),
)
}
}
/// Directory contents provides the entire contents of text files in a directory.
///
/// This has a `ProjectEntryId` so that it follows renames.
#[derive(Debug, Clone)]
pub struct DirectoryContext {
pub entry_id: ProjectEntryId,
pub context_id: ContextId,
}
impl DirectoryContext {
pub fn eq_for_key(&self, other: &Self) -> bool {
self.entry_id == other.entry_id
}
pub fn hash_for_key<H: Hasher>(&self, state: &mut H) {
self.entry_id.hash(state)
}
fn load(
&self,
project: Entity<Project>,
cx: &mut App,
) -> Option<Task<Vec<(String, Entity<Buffer>)>>> {
let worktree = project.read(cx).worktree_for_entry(self.entry_id, cx)?;
let worktree_ref = worktree.read(cx);
let entry = worktree_ref.entry_for_id(self.entry_id)?;
if entry.is_file() {
log::error!("DirectoryContext unexpectedly refers to a file.");
return None;
}
let file_paths = collect_files_in_path(worktree_ref, entry.path.as_ref());
let texts_future = future::join_all(file_paths.into_iter().map(|path| {
load_file_path_text_as_fenced_codeblock(project.clone(), worktree.clone(), path, cx)
}));
Some(cx.background_spawn(async move {
texts_future.await.into_iter().flatten().collect::<Vec<_>>()
}))
}
}
#[derive(Debug, Clone)]
pub struct SymbolContext {
pub buffer: Entity<Buffer>,
pub symbol: SharedString,
pub range: Range<Anchor>,
/// The range that fully contain the symbol. e.g. for function symbol, this will include not
/// only the signature, but also the body. Not used by `PartialEq` or `Hash` for `AgentContextKey`.
pub enclosing_range: Range<Anchor>,
pub context_id: ContextId,
}
impl SymbolContext {
pub fn eq_for_key(&self, other: &Self) -> bool {
self.buffer == other.buffer && self.symbol == other.symbol && self.range == other.range
}
pub fn hash_for_key<H: Hasher>(&self, state: &mut H) {
self.buffer.hash(state);
self.symbol.hash(state);
self.range.hash(state);
}
fn load(&self, cx: &App) -> Option<Task<(String, Entity<Buffer>)>> {
let buffer_ref = self.buffer.read(cx);
let Some(file) = buffer_ref.file() else {
log::error!("symbol context's file has no path");
return None;
};
let full_path = file.full_path(cx);
let rope = buffer_ref
.text_for_range(self.enclosing_range.clone())
.collect::<Rope>();
let line_range = self.enclosing_range.to_point(&buffer_ref.snapshot());
let buffer = self.buffer.clone();
Some(cx.background_spawn(async move {
(
to_fenced_codeblock(&full_path, rope, Some(line_range)),
buffer,
)
}))
}
}
#[derive(Debug, Clone)]
pub struct SelectionContext {
pub buffer: Entity<Buffer>,
pub range: Range<Anchor>,
pub context_id: ContextId,
}
impl SelectionContext {
pub fn eq_for_key(&self, other: &Self) -> bool {
self.buffer == other.buffer && self.range == other.range
}
pub fn hash_for_key<H: Hasher>(&self, state: &mut H) {
self.buffer.hash(state);
self.range.hash(state);
}
fn load(&self, cx: &App) -> Option<Task<(String, Entity<Buffer>)>> {
let buffer_ref = self.buffer.read(cx);
let Some(file) = buffer_ref.file() else {
log::error!("selection context's file has no path");
return None;
};
let full_path = file.full_path(cx);
let rope = buffer_ref
.text_for_range(self.range.clone())
.collect::<Rope>();
let line_range = self.range.to_point(&buffer_ref.snapshot());
let buffer = self.buffer.clone();
Some(cx.background_spawn(async move {
(
to_fenced_codeblock(&full_path, rope, Some(line_range)),
buffer,
)
}))
}
}
#[derive(Debug, Clone)]
pub struct FetchedUrlContext {
pub url: SharedString,
/// Text contents of the fetched url. Unlike other context types, the contents of this gets
/// populated when added rather than when sending the message. Not used by `PartialEq` or `Hash`
/// for `AgentContextKey`.
pub text: SharedString,
pub context_id: ContextId,
}
impl FetchedUrlContext {
pub fn eq_for_key(&self, other: &Self) -> bool {
self.url == other.url
}
pub fn hash_for_key<H: Hasher>(&self, state: &mut H) {
self.url.hash(state);
}
pub fn lookup_key(url: SharedString) -> AgentContextKey {
AgentContextKey(AgentContext::FetchedUrl(FetchedUrlContext {
url,
text: "".into(),
context_id: ContextId::for_lookup(),
}))
}
}
#[derive(Debug, Clone)]
pub struct ThreadContext {
pub thread: Entity<Thread>,
pub context_id: ContextId,
}
impl ThreadContext {
pub fn eq_for_key(&self, other: &Self) -> bool {
self.thread == other.thread
}
pub fn hash_for_key<H: Hasher>(&self, state: &mut H) {
self.thread.hash(state)
}
pub fn name(&self, cx: &App) -> SharedString {
self.thread
.read(cx)
.summary()
.unwrap_or_else(|| "New thread".into())
}
pub fn load(&self, cx: &App) -> String {
let name = self.name(cx);
let contents = self.thread.read(cx).latest_detailed_summary_or_text();
let mut text = String::new();
text.push_str(&name);
text.push('\n');
text.push_str(&contents.trim());
text.push('\n');
text
}
}
#[derive(Debug, Clone)]
pub struct RulesContext {
pub prompt_id: UserPromptId,
pub context_id: ContextId,
}
impl RulesContext {
pub fn eq_for_key(&self, other: &Self) -> bool {
self.prompt_id == other.prompt_id
}
pub fn hash_for_key<H: Hasher>(&self, state: &mut H) {
self.prompt_id.hash(state)
}
pub fn lookup_key(prompt_id: UserPromptId) -> AgentContextKey {
AgentContextKey(AgentContext::Rules(RulesContext {
prompt_id,
context_id: ContextId::for_lookup(),
}))
}
pub fn load(
&self,
prompt_store: &Option<Entity<PromptStore>>,
cx: &App,
) -> Task<Option<String>> {
let Some(prompt_store) = prompt_store.as_ref() else {
return Task::ready(None);
};
let prompt_store = prompt_store.read(cx);
let prompt_id = self.prompt_id.into();
let Some(metadata) = prompt_store.metadata(prompt_id) else {
return Task::ready(None);
};
let contents_task = prompt_store.load(prompt_id, cx);
cx.background_spawn(async move {
let contents = contents_task.await.ok()?;
let mut text = String::new();
if let Some(title) = metadata.title {
text.push_str("Rules title: ");
text.push_str(&title);
text.push('\n');
}
text.push_str("``````\n");
text.push_str(contents.trim());
text.push_str("\n``````\n");
Some(text)
})
}
}
#[derive(Debug, Clone)]
pub struct ImageContext {
pub original_image: Arc<gpui::Image>,
// TODO: handle this elsewhere and remove `ignore-interior-mutability` opt-out in clippy.toml
// needed due to a false positive of `clippy::mutable_key_type`.
pub image_task: Shared<Task<Option<LanguageModelImage>>>,
pub context_id: ContextId,
}
pub enum ImageStatus {
Loading,
Error,
Ready,
}
impl ImageContext {
pub fn eq_for_key(&self, other: &Self) -> bool {
self.original_image.id == other.original_image.id
}
pub fn hash_for_key<H: Hasher>(&self, state: &mut H) {
self.original_image.id.hash(state);
}
pub fn image(&self) -> Option<LanguageModelImage> {
self.image_task.clone().now_or_never().flatten()
}
pub fn status(&self) -> ImageStatus {
match self.image_task.clone().now_or_never() {
None => ImageStatus::Loading,
Some(None) => ImageStatus::Error,
Some(Some(_)) => ImageStatus::Ready,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ContextLoadResult {
pub loaded_context: LoadedContext,
pub referenced_buffers: HashSet<Entity<Buffer>>,
}
#[derive(Debug, Clone, Default)]
pub struct LoadedContext {
pub contexts: Vec<AgentContext>,
pub text: String,
pub images: Vec<LanguageModelImage>,
}
impl LoadedContext {
pub fn is_empty(&self) -> bool {
self.text.is_empty() && self.images.is_empty()
}
pub fn add_to_request_message(&self, request_message: &mut LanguageModelRequestMessage) {
if !self.text.is_empty() {
request_message
.content
.push(MessageContent::Text(self.text.to_string()));
}
if !self.images.is_empty() {
// Some providers only support image parts after an initial text part
if request_message.content.is_empty() {
request_message
.content
.push(MessageContent::Text("Images attached by user:".to_string()));
}
for image in &self.images {
request_message
.content
.push(MessageContent::Image(image.clone()))
}
}
}
}
/// Loads and formats a collection of contexts.
pub fn load_context(
contexts: Vec<AgentContext>,
project: &Entity<Project>,
prompt_store: &Option<Entity<PromptStore>>,
cx: &mut App,
) -> Task<ContextLoadResult> {
let mut file_tasks = Vec::new();
let mut directory_tasks = Vec::new();
let mut symbol_tasks = Vec::new();
let mut selection_tasks = Vec::new();
let mut fetch_context = Vec::new();
let mut thread_context = Vec::new();
let mut rules_tasks = Vec::new();
let mut image_tasks = Vec::new();
for context in contexts.iter().cloned() {
match context {
AgentContext::File(context) => file_tasks.extend(context.load(cx)),
AgentContext::Directory(context) => {
directory_tasks.extend(context.load(project.clone(), cx))
}
AgentContext::Symbol(context) => symbol_tasks.extend(context.load(cx)),
AgentContext::Selection(context) => selection_tasks.extend(context.load(cx)),
AgentContext::FetchedUrl(context) => fetch_context.push(context),
AgentContext::Thread(context) => thread_context.push(context.load(cx)),
AgentContext::Rules(context) => rules_tasks.push(context.load(prompt_store, cx)),
AgentContext::Image(context) => image_tasks.push(context.image_task.clone()),
}
}
cx.background_spawn(async move {
let (
file_context,
directory_context,
symbol_context,
selection_context,
rules_context,
images,
) = futures::join!(
future::join_all(file_tasks),
future::join_all(directory_tasks),
future::join_all(symbol_tasks),
future::join_all(selection_tasks),
future::join_all(rules_tasks),
future::join_all(image_tasks)
);
let directory_context = directory_context.into_iter().flatten().collect::<Vec<_>>();
let rules_context = rules_context.into_iter().flatten().collect::<Vec<_>>();
let images = images.into_iter().flatten().collect::<Vec<_>>();
let mut referenced_buffers = HashSet::default();
let mut text = String::new();
if file_context.is_empty()
&& directory_context.is_empty()
&& symbol_context.is_empty()
&& selection_context.is_empty()
&& fetch_context.is_empty()
&& thread_context.is_empty()
&& rules_context.is_empty()
{
return ContextLoadResult {
loaded_context: LoadedContext {
contexts,
text,
images,
},
referenced_buffers,
};
}
text.push_str(
"\n<context>\n\
The following items were attached by the user. \
You don't need to use other tools to read them.\n\n",
);
if !file_context.is_empty() {
text.push_str("<files>");
for (file_text, buffer) in file_context {
text.push('\n');
text.push_str(&file_text);
referenced_buffers.insert(buffer);
}
text.push_str("</files>\n");
}
if !directory_context.is_empty() {
text.push_str("<directories>");
for (file_text, buffer) in directory_context {
text.push('\n');
text.push_str(&file_text);
referenced_buffers.insert(buffer);
}
text.push_str("</directories>\n");
}
if !symbol_context.is_empty() {
text.push_str("<symbols>");
for (symbol_text, buffer) in symbol_context {
text.push('\n');
text.push_str(&symbol_text);
referenced_buffers.insert(buffer);
}
text.push_str("</symbols>\n");
}
if !selection_context.is_empty() {
text.push_str("<selections>");
for (selection_text, buffer) in selection_context {
text.push('\n');
text.push_str(&selection_text);
referenced_buffers.insert(buffer);
}
text.push_str("</selections>\n");
}
if !fetch_context.is_empty() {
text.push_str("<fetched_urls>");
for context in fetch_context {
text.push('\n');
text.push_str(&context.url);
text.push('\n');
text.push_str(&context.text);
}
text.push_str("</fetched_urls>\n");
}
if !thread_context.is_empty() {
text.push_str("<conversation_threads>");
for thread_text in thread_context {
text.push('\n');
text.push_str(&thread_text);
}
text.push_str("</conversation_threads>\n");
}
if !rules_context.is_empty() {
text.push_str(
"<user_rules>\n\
The user has specified the following rules that should be applied:\n",
);
for rules_text in rules_context {
text.push('\n');
text.push_str(&rules_text);
}
text.push_str("</user_rules>\n");
}
text.push_str("</context>\n");
ContextLoadResult {
loaded_context: LoadedContext {
contexts,
text,
images,
},
referenced_buffers,
}
})
}
fn collect_files_in_path(worktree: &Worktree, path: &Path) -> Vec<Arc<Path>> {
let mut files = Vec::new();
for entry in worktree.child_entries(path) {
if entry.is_dir() {
files.extend(collect_files_in_path(worktree, &entry.path));
} else if entry.is_file() {
files.push(entry.path.clone());
}
}
files
}
fn load_file_path_text_as_fenced_codeblock(
project: Entity<Project>,
worktree: Entity<Worktree>,
path: Arc<Path>,
cx: &mut App,
) -> Task<Option<(String, Entity<Buffer>)>> {
let worktree_ref = worktree.read(cx);
let worktree_id = worktree_ref.id();
let full_path = worktree_ref.full_path(&path);
let open_task = project.update(cx, |project, cx| {
project.buffer_store().update(cx, |buffer_store, cx| {
let project_path = ProjectPath { worktree_id, path };
buffer_store.open_buffer(project_path, cx)
})
});
let rope_task = cx.spawn(async move |cx| {
let buffer = open_task.await.log_err()?;
let rope = buffer
.read_with(cx, |buffer, _cx| buffer.as_rope().clone())
.log_err()?;
Some((rope, buffer))
});
cx.background_spawn(async move {
let (rope, buffer) = rope_task.await?;
Some((to_fenced_codeblock(&full_path, rope, None), buffer))
})
}
fn to_fenced_codeblock(
full_path: &Path,
content: Rope,
line_range: Option<Range<Point>>,
) -> String {
let line_range_text = line_range.map(|range| {
if range.start.row == range.end.row {
format!(":{}", range.start.row + 1)
} else {
format!(":{}-{}", range.start.row + 1, range.end.row + 1)
}
});
let path_extension = full_path.extension().and_then(|ext| ext.to_str());
let path_string = full_path.to_string_lossy();
let capacity = 3
+ path_extension.map_or(0, |extension| extension.len() + 1)
+ path_string.len()
+ line_range_text.as_ref().map_or(0, |text| text.len())
+ 1
+ content.len()
+ 5;
let mut buffer = String::with_capacity(capacity);
buffer.push_str("```");
if let Some(extension) = path_extension {
buffer.push_str(extension);
buffer.push(' ');
}
buffer.push_str(&path_string);
if let Some(line_range_text) = line_range_text {
buffer.push_str(&line_range_text);
}
buffer.push('\n');
for chunk in content.chunks() {
buffer.push_str(chunk);
}
if !buffer.ends_with('\n') {
buffer.push('\n');
}
buffer.push_str("```\n");
debug_assert!(
buffer.len() == capacity - 1 || buffer.len() == capacity,
"to_fenced_codeblock calculated capacity of {}, but length was {}",
capacity,
buffer.len(),
);
buffer
}
/// Wraps `AgentContext` to opt-in to `PartialEq` and `Hash` impls which use a subset of fields
/// needed for stable context identity.
#[derive(Debug, Clone, RefCast)]
#[repr(transparent)]
pub struct AgentContextKey(pub AgentContext);
impl AsRef<AgentContext> for AgentContextKey {
fn as_ref(&self) -> &AgentContext {
&self.0
}
}
impl Eq for AgentContextKey {}
impl PartialEq for AgentContextKey {
fn eq(&self, other: &Self) -> bool {
match &self.0 {
AgentContext::File(context) => {
if let AgentContext::File(other_context) = &other.0 {
return context.eq_for_key(other_context);
}
}
AgentContext::Directory(context) => {
if let AgentContext::Directory(other_context) = &other.0 {
return context.eq_for_key(other_context);
}
}
AgentContext::Symbol(context) => {
if let AgentContext::Symbol(other_context) = &other.0 {
return context.eq_for_key(other_context);
}
}
AgentContext::Selection(context) => {
if let AgentContext::Selection(other_context) = &other.0 {
return context.eq_for_key(other_context);
}
}
AgentContext::FetchedUrl(context) => {
if let AgentContext::FetchedUrl(other_context) = &other.0 {
return context.eq_for_key(other_context);
}
}
AgentContext::Thread(context) => {
if let AgentContext::Thread(other_context) = &other.0 {
return context.eq_for_key(other_context);
}
}
AgentContext::Rules(context) => {
if let AgentContext::Rules(other_context) = &other.0 {
return context.eq_for_key(other_context);
}
}
AgentContext::Image(context) => {
if let AgentContext::Image(other_context) = &other.0 {
return context.eq_for_key(other_context);
}
}
}
false
}
}
impl Hash for AgentContextKey {
fn hash<H: Hasher>(&self, state: &mut H) {
match &self.0 {
AgentContext::File(context) => context.hash_for_key(state),
AgentContext::Directory(context) => context.hash_for_key(state),
AgentContext::Symbol(context) => context.hash_for_key(state),
AgentContext::Selection(context) => context.hash_for_key(state),
AgentContext::FetchedUrl(context) => context.hash_for_key(state),
AgentContext::Thread(context) => context.hash_for_key(state),
AgentContext::Rules(context) => context.hash_for_key(state),
AgentContext::Image(context) => context.hash_for_key(state),
}
}
}

View File

@@ -0,0 +1,947 @@
mod completion_provider;
mod fetch_context_picker;
mod file_context_picker;
mod rules_context_picker;
mod symbol_context_picker;
mod thread_context_picker;
use std::ops::Range;
use std::path::PathBuf;
use std::sync::Arc;
use anyhow::{Result, anyhow};
pub use completion_provider::ContextPickerCompletionProvider;
use editor::display_map::{Crease, FoldId};
use editor::{Anchor, AnchorRangeExt as _, Editor, ExcerptId, FoldPlaceholder, ToOffset};
use fetch_context_picker::FetchContextPicker;
use file_context_picker::FileContextPicker;
use file_context_picker::render_file_context_entry;
use gpui::{
App, DismissEvent, Empty, Entity, EventEmitter, FocusHandle, Focusable, Subscription, Task,
WeakEntity,
};
use language::Buffer;
use multi_buffer::MultiBufferRow;
use project::{Entry, ProjectPath};
use prompt_store::{PromptStore, UserPromptId};
use rules_context_picker::{RulesContextEntry, RulesContextPicker};
use symbol_context_picker::SymbolContextPicker;
use thread_context_picker::{ThreadContextEntry, ThreadContextPicker, render_thread_context_entry};
use ui::{
ButtonLike, ContextMenu, ContextMenuEntry, ContextMenuItem, Disclosure, TintColor, prelude::*,
};
use uuid::Uuid;
use workspace::{Workspace, notifications::NotifyResultExt};
use crate::AssistantPanel;
use crate::context::RULES_ICON;
use crate::context_store::ContextStore;
use crate::thread::ThreadId;
use crate::thread_store::ThreadStore;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ContextPickerEntry {
Mode(ContextPickerMode),
Action(ContextPickerAction),
}
impl ContextPickerEntry {
pub fn keyword(&self) -> &'static str {
match self {
Self::Mode(mode) => mode.keyword(),
Self::Action(action) => action.keyword(),
}
}
pub fn label(&self) -> &'static str {
match self {
Self::Mode(mode) => mode.label(),
Self::Action(action) => action.label(),
}
}
pub fn icon(&self) -> IconName {
match self {
Self::Mode(mode) => mode.icon(),
Self::Action(action) => action.icon(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ContextPickerMode {
File,
Symbol,
Fetch,
Thread,
Rules,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ContextPickerAction {
AddSelections,
}
impl ContextPickerAction {
pub fn keyword(&self) -> &'static str {
match self {
Self::AddSelections => "selection",
}
}
pub fn label(&self) -> &'static str {
match self {
Self::AddSelections => "Selection",
}
}
pub fn icon(&self) -> IconName {
match self {
Self::AddSelections => IconName::Context,
}
}
}
impl TryFrom<&str> for ContextPickerMode {
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"file" => Ok(Self::File),
"symbol" => Ok(Self::Symbol),
"fetch" => Ok(Self::Fetch),
"thread" => Ok(Self::Thread),
"rules" => Ok(Self::Rules),
_ => Err(format!("Invalid context picker mode: {}", value)),
}
}
}
impl ContextPickerMode {
pub fn keyword(&self) -> &'static str {
match self {
Self::File => "file",
Self::Symbol => "symbol",
Self::Fetch => "fetch",
Self::Thread => "thread",
Self::Rules => "rules",
}
}
pub fn label(&self) -> &'static str {
match self {
Self::File => "Files & Directories",
Self::Symbol => "Symbols",
Self::Fetch => "Fetch",
Self::Thread => "Threads",
Self::Rules => "Rules",
}
}
pub fn icon(&self) -> IconName {
match self {
Self::File => IconName::File,
Self::Symbol => IconName::Code,
Self::Fetch => IconName::Globe,
Self::Thread => IconName::MessageBubbles,
Self::Rules => RULES_ICON,
}
}
}
#[derive(Debug, Clone)]
enum ContextPickerState {
Default(Entity<ContextMenu>),
File(Entity<FileContextPicker>),
Symbol(Entity<SymbolContextPicker>),
Fetch(Entity<FetchContextPicker>),
Thread(Entity<ThreadContextPicker>),
Rules(Entity<RulesContextPicker>),
}
pub(super) struct ContextPicker {
mode: ContextPickerState,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
thread_store: Option<WeakEntity<ThreadStore>>,
prompt_store: Option<Entity<PromptStore>>,
_subscriptions: Vec<Subscription>,
}
impl ContextPicker {
pub fn new(
workspace: WeakEntity<Workspace>,
thread_store: Option<WeakEntity<ThreadStore>>,
context_store: WeakEntity<ContextStore>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let subscriptions = context_store
.upgrade()
.map(|context_store| {
cx.observe(&context_store, |this, _, cx| this.notify_current_picker(cx))
})
.into_iter()
.chain(
thread_store
.as_ref()
.and_then(|thread_store| thread_store.upgrade())
.map(|thread_store| {
cx.observe(&thread_store, |this, _, cx| this.notify_current_picker(cx))
}),
)
.collect::<Vec<Subscription>>();
let prompt_store = thread_store.as_ref().and_then(|thread_store| {
thread_store
.read_with(cx, |thread_store, _cx| thread_store.prompt_store().clone())
.ok()
.flatten()
});
ContextPicker {
mode: ContextPickerState::Default(ContextMenu::build(
window,
cx,
|menu, _window, _cx| menu,
)),
workspace,
context_store,
thread_store,
prompt_store,
_subscriptions: subscriptions,
}
}
pub fn init(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.mode = ContextPickerState::Default(self.build_menu(window, cx));
cx.notify();
}
fn build_menu(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Entity<ContextMenu> {
let context_picker = cx.entity().clone();
let menu = ContextMenu::build(window, cx, move |menu, _window, cx| {
let recent = self.recent_entries(cx);
let has_recent = !recent.is_empty();
let recent_entries = recent
.into_iter()
.enumerate()
.map(|(ix, entry)| self.recent_menu_item(context_picker.clone(), ix, entry));
let entries = self
.workspace
.upgrade()
.map(|workspace| {
available_context_picker_entries(
&self.prompt_store,
&self.thread_store,
&workspace,
cx,
)
})
.unwrap_or_default();
menu.when(has_recent, |menu| {
menu.custom_row(|_, _| {
div()
.mb_1()
.child(
Label::new("Recent")
.color(Color::Muted)
.size(LabelSize::Small),
)
.into_any_element()
})
})
.extend(recent_entries)
.when(has_recent, |menu| menu.separator())
.extend(entries.into_iter().map(|entry| {
let context_picker = context_picker.clone();
ContextMenuEntry::new(entry.label())
.icon(entry.icon())
.icon_size(IconSize::XSmall)
.icon_color(Color::Muted)
.handler(move |window, cx| {
context_picker.update(cx, |this, cx| this.select_entry(entry, window, cx))
})
}))
.keep_open_on_confirm()
});
cx.subscribe(&menu, move |_, _, _: &DismissEvent, cx| {
cx.emit(DismissEvent);
})
.detach();
menu
}
/// Whether threads are allowed as context.
pub fn allow_threads(&self) -> bool {
self.thread_store.is_some()
}
fn select_entry(
&mut self,
entry: ContextPickerEntry,
window: &mut Window,
cx: &mut Context<Self>,
) {
let context_picker = cx.entity().downgrade();
match entry {
ContextPickerEntry::Mode(mode) => match mode {
ContextPickerMode::File => {
self.mode = ContextPickerState::File(cx.new(|cx| {
FileContextPicker::new(
context_picker.clone(),
self.workspace.clone(),
self.context_store.clone(),
window,
cx,
)
}));
}
ContextPickerMode::Symbol => {
self.mode = ContextPickerState::Symbol(cx.new(|cx| {
SymbolContextPicker::new(
context_picker.clone(),
self.workspace.clone(),
self.context_store.clone(),
window,
cx,
)
}));
}
ContextPickerMode::Rules => {
if let Some(prompt_store) = self.prompt_store.as_ref() {
self.mode = ContextPickerState::Rules(cx.new(|cx| {
RulesContextPicker::new(
prompt_store.clone(),
context_picker.clone(),
self.context_store.clone(),
window,
cx,
)
}));
}
}
ContextPickerMode::Fetch => {
self.mode = ContextPickerState::Fetch(cx.new(|cx| {
FetchContextPicker::new(
context_picker.clone(),
self.workspace.clone(),
self.context_store.clone(),
window,
cx,
)
}));
}
ContextPickerMode::Thread => {
if let Some(thread_store) = self.thread_store.as_ref() {
self.mode = ContextPickerState::Thread(cx.new(|cx| {
ThreadContextPicker::new(
thread_store.clone(),
context_picker.clone(),
self.context_store.clone(),
window,
cx,
)
}));
}
}
},
ContextPickerEntry::Action(action) => match action {
ContextPickerAction::AddSelections => {
if let Some((context_store, workspace)) =
self.context_store.upgrade().zip(self.workspace.upgrade())
{
add_selections_as_context(&context_store, &workspace, cx);
}
cx.emit(DismissEvent);
}
},
}
cx.notify();
cx.focus_self(window);
}
fn recent_menu_item(
&self,
context_picker: Entity<ContextPicker>,
ix: usize,
entry: RecentEntry,
) -> ContextMenuItem {
match entry {
RecentEntry::File {
project_path,
path_prefix,
} => {
let context_store = self.context_store.clone();
let worktree_id = project_path.worktree_id;
let path = project_path.path.clone();
ContextMenuItem::custom_entry(
move |_window, cx| {
render_file_context_entry(
ElementId::named_usize("ctx-recent", ix),
worktree_id,
&path,
&path_prefix,
false,
context_store.clone(),
cx,
)
.into_any()
},
move |window, cx| {
context_picker.update(cx, |this, cx| {
this.add_recent_file(project_path.clone(), window, cx);
})
},
)
}
RecentEntry::Thread(thread) => {
let context_store = self.context_store.clone();
let view_thread = thread.clone();
ContextMenuItem::custom_entry(
move |_window, cx| {
render_thread_context_entry(&view_thread, context_store.clone(), cx)
.into_any()
},
move |_window, cx| {
context_picker.update(cx, |this, cx| {
this.add_recent_thread(thread.clone(), cx)
.detach_and_log_err(cx);
})
},
)
}
}
}
fn add_recent_file(
&self,
project_path: ProjectPath,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(context_store) = self.context_store.upgrade() else {
return;
};
let task = context_store.update(cx, |context_store, cx| {
context_store.add_file_from_path(project_path.clone(), true, cx)
});
cx.spawn_in(window, async move |_, cx| task.await.notify_async_err(cx))
.detach();
cx.notify();
}
fn add_recent_thread(
&self,
thread: ThreadContextEntry,
cx: &mut Context<Self>,
) -> Task<Result<()>> {
let Some(context_store) = self.context_store.upgrade() else {
return Task::ready(Err(anyhow!("context store not available")));
};
let Some(thread_store) = self
.thread_store
.as_ref()
.and_then(|thread_store| thread_store.upgrade())
else {
return Task::ready(Err(anyhow!("thread store not available")));
};
let open_thread_task = thread_store.update(cx, |this, cx| this.open_thread(&thread.id, cx));
cx.spawn(async move |this, cx| {
let thread = open_thread_task.await?;
context_store.update(cx, |context_store, cx| {
context_store.add_thread(thread, true, cx);
})?;
this.update(cx, |_this, cx| cx.notify())
})
}
fn recent_entries(&self, cx: &mut App) -> Vec<RecentEntry> {
let Some(workspace) = self.workspace.upgrade() else {
return vec![];
};
let Some(context_store) = self.context_store.upgrade() else {
return vec![];
};
recent_context_picker_entries(context_store, self.thread_store.clone(), workspace, cx)
}
fn notify_current_picker(&mut self, cx: &mut Context<Self>) {
match &self.mode {
ContextPickerState::Default(entity) => entity.update(cx, |_, cx| cx.notify()),
ContextPickerState::File(entity) => entity.update(cx, |_, cx| cx.notify()),
ContextPickerState::Symbol(entity) => entity.update(cx, |_, cx| cx.notify()),
ContextPickerState::Fetch(entity) => entity.update(cx, |_, cx| cx.notify()),
ContextPickerState::Thread(entity) => entity.update(cx, |_, cx| cx.notify()),
ContextPickerState::Rules(entity) => entity.update(cx, |_, cx| cx.notify()),
}
}
}
impl EventEmitter<DismissEvent> for ContextPicker {}
impl Focusable for ContextPicker {
fn focus_handle(&self, cx: &App) -> FocusHandle {
match &self.mode {
ContextPickerState::Default(menu) => menu.focus_handle(cx),
ContextPickerState::File(file_picker) => file_picker.focus_handle(cx),
ContextPickerState::Symbol(symbol_picker) => symbol_picker.focus_handle(cx),
ContextPickerState::Fetch(fetch_picker) => fetch_picker.focus_handle(cx),
ContextPickerState::Thread(thread_picker) => thread_picker.focus_handle(cx),
ContextPickerState::Rules(user_rules_picker) => user_rules_picker.focus_handle(cx),
}
}
}
impl Render for ContextPicker {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.w(px(400.))
.min_w(px(400.))
.map(|parent| match &self.mode {
ContextPickerState::Default(menu) => parent.child(menu.clone()),
ContextPickerState::File(file_picker) => parent.child(file_picker.clone()),
ContextPickerState::Symbol(symbol_picker) => parent.child(symbol_picker.clone()),
ContextPickerState::Fetch(fetch_picker) => parent.child(fetch_picker.clone()),
ContextPickerState::Thread(thread_picker) => parent.child(thread_picker.clone()),
ContextPickerState::Rules(user_rules_picker) => {
parent.child(user_rules_picker.clone())
}
})
}
}
enum RecentEntry {
File {
project_path: ProjectPath,
path_prefix: Arc<str>,
},
Thread(ThreadContextEntry),
}
fn available_context_picker_entries(
prompt_store: &Option<Entity<PromptStore>>,
thread_store: &Option<WeakEntity<ThreadStore>>,
workspace: &Entity<Workspace>,
cx: &mut App,
) -> Vec<ContextPickerEntry> {
let mut entries = vec![
ContextPickerEntry::Mode(ContextPickerMode::File),
ContextPickerEntry::Mode(ContextPickerMode::Symbol),
];
let has_selection = workspace
.read(cx)
.active_item(cx)
.and_then(|item| item.downcast::<Editor>())
.map_or(false, |editor| {
editor.update(cx, |editor, cx| editor.has_non_empty_selection(cx))
});
if has_selection {
entries.push(ContextPickerEntry::Action(
ContextPickerAction::AddSelections,
));
}
if thread_store.is_some() {
entries.push(ContextPickerEntry::Mode(ContextPickerMode::Thread));
}
if prompt_store.is_some() {
entries.push(ContextPickerEntry::Mode(ContextPickerMode::Rules));
}
entries.push(ContextPickerEntry::Mode(ContextPickerMode::Fetch));
entries
}
fn recent_context_picker_entries(
context_store: Entity<ContextStore>,
thread_store: Option<WeakEntity<ThreadStore>>,
workspace: Entity<Workspace>,
cx: &App,
) -> Vec<RecentEntry> {
let mut recent = Vec::with_capacity(6);
let current_files = context_store.read(cx).file_paths(cx);
let workspace = workspace.read(cx);
let project = workspace.project().read(cx);
recent.extend(
workspace
.recent_navigation_history_iter(cx)
.filter(|(path, _)| !current_files.contains(path))
.take(4)
.filter_map(|(project_path, _)| {
project
.worktree_for_id(project_path.worktree_id, cx)
.map(|worktree| RecentEntry::File {
project_path,
path_prefix: worktree.read(cx).root_name().into(),
})
}),
);
let current_threads = context_store.read(cx).thread_ids();
let active_thread_id = workspace
.panel::<AssistantPanel>(cx)
.map(|panel| panel.read(cx).active_thread(cx).read(cx).id());
if let Some(thread_store) = thread_store.and_then(|thread_store| thread_store.upgrade()) {
recent.extend(
thread_store
.read(cx)
.reverse_chronological_threads()
.into_iter()
.filter(|thread| {
Some(&thread.id) != active_thread_id && !current_threads.contains(&thread.id)
})
.take(2)
.map(|thread| {
RecentEntry::Thread(ThreadContextEntry {
id: thread.id,
summary: thread.summary,
})
}),
);
}
recent
}
fn add_selections_as_context(
context_store: &Entity<ContextStore>,
workspace: &Entity<Workspace>,
cx: &mut App,
) {
let selection_ranges = selection_ranges(workspace, cx);
context_store.update(cx, |context_store, cx| {
for (buffer, range) in selection_ranges {
context_store.add_selection(buffer, range, cx);
}
})
}
fn selection_ranges(
workspace: &Entity<Workspace>,
cx: &mut App,
) -> Vec<(Entity<Buffer>, Range<text::Anchor>)> {
let Some(editor) = workspace
.read(cx)
.active_item(cx)
.and_then(|item| item.act_as::<Editor>(cx))
else {
return Vec::new();
};
editor.update(cx, |editor, cx| {
let selections = editor.selections.all_adjusted(cx);
let buffer = editor.buffer().clone().read(cx);
let snapshot = buffer.snapshot(cx);
selections
.into_iter()
.map(|s| snapshot.anchor_after(s.start)..snapshot.anchor_before(s.end))
.flat_map(|range| {
let (start_buffer, start) = buffer.text_anchor_for_position(range.start, cx)?;
let (end_buffer, end) = buffer.text_anchor_for_position(range.end, cx)?;
if start_buffer != end_buffer {
return None;
}
Some((start_buffer, start..end))
})
.collect::<Vec<_>>()
})
}
pub(crate) fn insert_fold_for_mention(
excerpt_id: ExcerptId,
crease_start: text::Anchor,
content_len: usize,
crease_label: SharedString,
crease_icon_path: SharedString,
editor_entity: Entity<Editor>,
cx: &mut App,
) {
editor_entity.update(cx, |editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
let Some(start) = snapshot.anchor_in_excerpt(excerpt_id, crease_start) else {
return;
};
let start = start.bias_right(&snapshot);
let end = snapshot.anchor_before(start.to_offset(&snapshot) + content_len);
let crease = crease_for_mention(
crease_label,
crease_icon_path,
start..end,
editor_entity.downgrade(),
);
editor.display_map.update(cx, |display_map, cx| {
display_map.fold(vec![crease], cx);
});
});
}
pub fn crease_for_mention(
label: SharedString,
icon_path: SharedString,
range: Range<Anchor>,
editor_entity: WeakEntity<Editor>,
) -> Crease<Anchor> {
let placeholder = FoldPlaceholder {
render: render_fold_icon_button(icon_path, label, editor_entity),
merge_adjacent: false,
..Default::default()
};
let render_trailer = move |_row, _unfold, _window: &mut Window, _cx: &mut App| Empty.into_any();
let crease = Crease::inline(
range,
placeholder.clone(),
fold_toggle("mention"),
render_trailer,
);
crease
}
fn render_fold_icon_button(
icon_path: SharedString,
label: SharedString,
editor: WeakEntity<Editor>,
) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut App) -> AnyElement> {
Arc::new({
move |fold_id, fold_range, cx| {
let is_in_text_selection = editor.upgrade().is_some_and(|editor| {
editor.update(cx, |editor, cx| {
let snapshot = editor
.buffer()
.update(cx, |multi_buffer, cx| multi_buffer.snapshot(cx));
let is_in_pending_selection = || {
editor
.selections
.pending
.as_ref()
.is_some_and(|pending_selection| {
pending_selection
.selection
.range()
.includes(&fold_range, &snapshot)
})
};
let mut is_in_complete_selection = || {
editor
.selections
.disjoint_in_range::<usize>(fold_range.clone(), cx)
.into_iter()
.any(|selection| {
// This is needed to cover a corner case, if we just check for an existing
// selection in the fold range, having a cursor at the start of the fold
// marks it as selected. Non-empty selections don't cause this.
let length = selection.end - selection.start;
length > 0
})
};
is_in_pending_selection() || is_in_complete_selection()
})
});
ButtonLike::new(fold_id)
.style(ButtonStyle::Filled)
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
.toggle_state(is_in_text_selection)
.child(
h_flex()
.gap_1()
.child(
Icon::from_path(icon_path.clone())
.size(IconSize::XSmall)
.color(Color::Muted),
)
.child(
Label::new(label.clone())
.size(LabelSize::Small)
.buffer_font(cx)
.single_line(),
),
)
.into_any_element()
}
})
}
fn fold_toggle(
name: &'static str,
) -> impl Fn(
MultiBufferRow,
bool,
Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
&mut Window,
&mut App,
) -> AnyElement {
move |row, is_folded, fold, _window, _cx| {
Disclosure::new((name, row.0 as u64), !is_folded)
.toggle_state(is_folded)
.on_click(move |_e, window, cx| fold(!is_folded, window, cx))
.into_any_element()
}
}
pub enum MentionLink {
File(ProjectPath, Entry),
Symbol(ProjectPath, String),
Selection(ProjectPath, Range<usize>),
Fetch(String),
Thread(ThreadId),
Rules(UserPromptId),
}
impl MentionLink {
const FILE: &str = "@file";
const SYMBOL: &str = "@symbol";
const SELECTION: &str = "@selection";
const THREAD: &str = "@thread";
const FETCH: &str = "@fetch";
const RULES: &str = "@rules";
const SEPARATOR: &str = ":";
pub fn is_valid(url: &str) -> bool {
url.starts_with(Self::FILE)
|| url.starts_with(Self::SYMBOL)
|| url.starts_with(Self::FETCH)
|| url.starts_with(Self::SELECTION)
|| url.starts_with(Self::THREAD)
|| url.starts_with(Self::RULES)
}
pub fn for_file(file_name: &str, full_path: &str) -> String {
format!("[@{}]({}:{})", file_name, Self::FILE, full_path)
}
pub fn for_symbol(symbol_name: &str, full_path: &str) -> String {
format!(
"[@{}]({}:{}:{})",
symbol_name,
Self::SYMBOL,
full_path,
symbol_name
)
}
pub fn for_selection(file_name: &str, full_path: &str, line_range: Range<usize>) -> String {
format!(
"[@{} ({}-{})]({}:{}:{}-{})",
file_name,
line_range.start,
line_range.end,
Self::SELECTION,
full_path,
line_range.start,
line_range.end
)
}
pub fn for_thread(thread: &ThreadContextEntry) -> String {
format!("[@{}]({}:{})", thread.summary, Self::THREAD, thread.id)
}
pub fn for_fetch(url: &str) -> String {
format!("[@{}]({}:{})", url, Self::FETCH, url)
}
pub fn for_rules(rules: &RulesContextEntry) -> String {
format!("[@{}]({}:{})", rules.title, Self::RULES, rules.prompt_id.0)
}
pub fn try_parse(link: &str, workspace: &Entity<Workspace>, cx: &App) -> Option<Self> {
fn extract_project_path_from_link(
path: &str,
workspace: &Entity<Workspace>,
cx: &App,
) -> Option<ProjectPath> {
let path = PathBuf::from(path);
let worktree_name = path.iter().next()?;
let path: PathBuf = path.iter().skip(1).collect();
let worktree_id = workspace
.read(cx)
.visible_worktrees(cx)
.find(|worktree| worktree.read(cx).root_name() == worktree_name)
.map(|worktree| worktree.read(cx).id())?;
Some(ProjectPath {
worktree_id,
path: path.into(),
})
}
let (prefix, argument) = link.split_once(Self::SEPARATOR)?;
match prefix {
Self::FILE => {
let project_path = extract_project_path_from_link(argument, workspace, cx)?;
let entry = workspace
.read(cx)
.project()
.read(cx)
.entry_for_path(&project_path, cx)?;
Some(MentionLink::File(project_path, entry))
}
Self::SYMBOL => {
let (path, symbol) = argument.split_once(Self::SEPARATOR)?;
let project_path = extract_project_path_from_link(path, workspace, cx)?;
Some(MentionLink::Symbol(project_path, symbol.to_string()))
}
Self::SELECTION => {
let (path, line_args) = argument.split_once(Self::SEPARATOR)?;
let project_path = extract_project_path_from_link(path, workspace, cx)?;
let line_range = {
let (start, end) = line_args
.trim_start_matches('(')
.trim_end_matches(')')
.split_once('-')?;
start.parse::<usize>().ok()?..end.parse::<usize>().ok()?
};
Some(MentionLink::Selection(project_path, line_range))
}
Self::THREAD => {
let thread_id = ThreadId::from(argument);
Some(MentionLink::Thread(thread_id))
}
Self::FETCH => Some(MentionLink::Fetch(argument.to_string())),
Self::RULES => {
let prompt_id = UserPromptId(Uuid::try_parse(argument).ok()?);
Some(MentionLink::Rules(prompt_id))
}
_ => None,
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,7 @@ use picker::{Picker, PickerDelegate};
use ui::{Context, ListItem, Window, prelude::*};
use workspace::Workspace;
use crate::context_picker::{ConfirmBehavior, ContextPicker};
use crate::context_picker::ContextPicker;
use crate::context_store::ContextStore;
pub struct FetchContextPicker {
@@ -23,16 +23,10 @@ impl FetchContextPicker {
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let delegate = FetchContextPickerDelegate::new(
context_picker,
workspace,
context_store,
confirm_behavior,
);
let delegate = FetchContextPickerDelegate::new(context_picker, workspace, context_store);
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
Self { picker }
@@ -62,7 +56,6 @@ pub struct FetchContextPickerDelegate {
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
url: String,
}
@@ -71,13 +64,11 @@ impl FetchContextPickerDelegate {
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
) -> Self {
FetchContextPickerDelegate {
context_picker,
workspace,
context_store,
confirm_behavior,
url: String::new(),
}
}
@@ -202,27 +193,17 @@ impl PickerDelegate for FetchContextPickerDelegate {
return;
};
let http_client = workspace.read(cx).client().http_client().clone();
let http_client = workspace.read(cx).client().http_client();
let url = self.url.clone();
let confirm_behavior = self.confirm_behavior;
cx.spawn_in(window, async move |this, cx| {
let text = cx
.background_spawn(fetch_url_content(http_client, url.clone()))
.await?;
this.update_in(cx, |this, window, cx| {
this.delegate
.context_store
.update(cx, |context_store, _cx| {
context_store.add_fetched_url(url, text);
})?;
match confirm_behavior {
ConfirmBehavior::KeepOpen => {}
ConfirmBehavior::Close => this.delegate.dismissed(window, cx),
}
anyhow::Ok(())
this.update(cx, |this, cx| {
this.delegate.context_store.update(cx, |context_store, cx| {
context_store.add_fetched_url(url, text, cx)
})
})??;
anyhow::Ok(())
@@ -246,7 +227,7 @@ impl PickerDelegate for FetchContextPickerDelegate {
cx: &mut Context<Picker<Self>>,
) -> Option<Self::ListItem> {
let added = self.context_store.upgrade().map_or(false, |context_store| {
context_store.read(cx).includes_url(&self.url).is_some()
context_store.read(cx).includes_url(&self.url)
});
Some(

View File

@@ -11,9 +11,9 @@ use picker::{Picker, PickerDelegate};
use project::{PathMatchCandidateSet, ProjectPath, WorktreeId};
use ui::{ListItem, Tooltip, prelude::*};
use util::ResultExt as _;
use workspace::{Workspace, notifications::NotifyResultExt};
use workspace::Workspace;
use crate::context_picker::{ConfirmBehavior, ContextPicker};
use crate::context_picker::ContextPicker;
use crate::context_store::{ContextStore, FileInclusion};
pub struct FileContextPicker {
@@ -25,16 +25,10 @@ impl FileContextPicker {
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let delegate = FileContextPickerDelegate::new(
context_picker,
workspace,
context_store,
confirm_behavior,
);
let delegate = FileContextPickerDelegate::new(context_picker, workspace, context_store);
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
Self { picker }
@@ -57,8 +51,7 @@ pub struct FileContextPickerDelegate {
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
matches: Vec<PathMatch>,
matches: Vec<FileMatch>,
selected_index: usize,
}
@@ -67,13 +60,11 @@ impl FileContextPickerDelegate {
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
) -> Self {
Self {
context_picker,
workspace,
context_store,
confirm_behavior,
matches: Vec::new(),
selected_index: 0,
}
@@ -114,7 +105,7 @@ impl PickerDelegate for FileContextPickerDelegate {
return Task::ready(());
};
let search_task = search_paths(query, Arc::<AtomicBool>::default(), &workspace, cx);
let search_task = search_files(query, Arc::<AtomicBool>::default(), &workspace, cx);
cx.spawn_in(window, async move |this, cx| {
// TODO: This should be probably be run in the background.
@@ -127,8 +118,8 @@ impl PickerDelegate for FileContextPickerDelegate {
})
}
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
let Some(mat) = self.matches.get(self.selected_index) else {
fn confirm(&mut self, _secondary: bool, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
let Some(FileMatch { mat, .. }) = self.matches.get(self.selected_index) else {
return;
};
@@ -143,9 +134,9 @@ impl PickerDelegate for FileContextPickerDelegate {
.context_store
.update(cx, |context_store, cx| {
if is_directory {
context_store.add_directory(project_path, true, cx)
Task::ready(context_store.add_directory(&project_path, true, cx))
} else {
context_store.add_file_from_path(project_path, true, cx)
context_store.add_file_from_path(project_path.clone(), true, cx)
}
})
.ok()
@@ -153,17 +144,7 @@ impl PickerDelegate for FileContextPickerDelegate {
return;
};
let confirm_behavior = self.confirm_behavior;
cx.spawn_in(window, async move |this, cx| {
match task.await.notify_async_err(cx) {
None => anyhow::Ok(()),
Some(()) => this.update_in(cx, |this, window, cx| match confirm_behavior {
ConfirmBehavior::KeepOpen => {}
ConfirmBehavior::Close => this.delegate.dismissed(window, cx),
}),
}
})
.detach_and_log_err(cx);
task.detach_and_log_err(cx);
}
fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
@@ -181,17 +162,18 @@ impl PickerDelegate for FileContextPickerDelegate {
_window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Option<Self::ListItem> {
let path_match = &self.matches[ix];
let FileMatch { mat, .. } = &self.matches[ix];
Some(
ListItem::new(ix)
.inset(true)
.toggle_state(selected)
.child(render_file_context_entry(
ElementId::NamedInteger("file-ctx-picker".into(), ix),
&path_match.path,
&path_match.path_prefix,
path_match.is_dir,
ElementId::named_usize("file-ctx-picker", ix),
WorktreeId::from_usize(mat.worktree_id),
&mat.path,
&mat.path_prefix,
mat.is_dir,
self.context_store.clone(),
cx,
)),
@@ -199,12 +181,17 @@ impl PickerDelegate for FileContextPickerDelegate {
}
}
pub(crate) fn search_paths(
pub struct FileMatch {
pub mat: PathMatch,
pub is_recent: bool,
}
pub(crate) fn search_files(
query: String,
cancellation_flag: Arc<AtomicBool>,
workspace: &Entity<Workspace>,
cx: &App,
) -> Task<Vec<PathMatch>> {
) -> Task<Vec<FileMatch>> {
if query.is_empty() {
let workspace = workspace.read(cx);
let project = workspace.project().read(cx);
@@ -213,28 +200,34 @@ pub(crate) fn search_paths(
.into_iter()
.filter_map(|(project_path, _)| {
let worktree = project.worktree_for_id(project_path.worktree_id, cx)?;
Some(PathMatch {
score: 0.,
positions: Vec::new(),
worktree_id: project_path.worktree_id.to_usize(),
path: project_path.path,
path_prefix: worktree.read(cx).root_name().into(),
distance_to_relative_ancestor: 0,
is_dir: false,
Some(FileMatch {
mat: PathMatch {
score: 0.,
positions: Vec::new(),
worktree_id: project_path.worktree_id.to_usize(),
path: project_path.path,
path_prefix: worktree.read(cx).root_name().into(),
distance_to_relative_ancestor: 0,
is_dir: false,
},
is_recent: true,
})
});
let file_matches = project.worktrees(cx).flat_map(|worktree| {
let worktree = worktree.read(cx);
let path_prefix: Arc<str> = worktree.root_name().into();
worktree.entries(false, 0).map(move |entry| PathMatch {
score: 0.,
positions: Vec::new(),
worktree_id: worktree.id().to_usize(),
path: entry.path.clone(),
path_prefix: path_prefix.clone(),
distance_to_relative_ancestor: 0,
is_dir: entry.is_dir(),
worktree.entries(false, 0).map(move |entry| FileMatch {
mat: PathMatch {
score: 0.,
positions: Vec::new(),
worktree_id: worktree.id().to_usize(),
path: entry.path.clone(),
path_prefix: path_prefix.clone(),
distance_to_relative_ancestor: 0,
is_dir: entry.is_dir(),
},
is_recent: false,
})
});
@@ -269,6 +262,12 @@ pub(crate) fn search_paths(
executor,
)
.await
.into_iter()
.map(|mat| FileMatch {
mat,
is_recent: false,
})
.collect::<Vec<_>>()
})
}
}
@@ -311,19 +310,26 @@ pub fn extract_file_name_and_directory(
pub fn render_file_context_entry(
id: ElementId,
path: &Path,
worktree_id: WorktreeId,
path: &Arc<Path>,
path_prefix: &Arc<str>,
is_directory: bool,
context_store: WeakEntity<ContextStore>,
cx: &App,
) -> Stateful<Div> {
let (file_name, directory) = extract_file_name_and_directory(path, path_prefix);
let (file_name, directory) = extract_file_name_and_directory(&path, path_prefix);
let added = context_store.upgrade().and_then(|context_store| {
let project_path = ProjectPath {
worktree_id,
path: path.clone(),
};
if is_directory {
context_store.read(cx).includes_directory(path)
context_store
.read(cx)
.path_included_in_directory(&project_path, cx)
} else {
context_store.read(cx).will_include_file_path(path, cx)
context_store.read(cx).file_path_included(&project_path, cx)
}
});
@@ -351,7 +357,7 @@ pub fn render_file_context_entry(
})),
)
.when_some(added, |el, added| match added {
FileInclusion::Direct(_) => el.child(
FileInclusion::Direct => el.child(
h_flex()
.w_full()
.justify_end()
@@ -363,8 +369,8 @@ pub fn render_file_context_entry(
)
.child(Label::new("Added").size(LabelSize::Small)),
),
FileInclusion::InDirectory(dir_name) => {
let dir_name = dir_name.to_string_lossy().into_owned();
FileInclusion::InDirectory { full_path } => {
let directory_full_path = full_path.to_string_lossy().into_owned();
el.child(
h_flex()
@@ -378,7 +384,7 @@ pub fn render_file_context_entry(
)
.child(Label::new("Included").size(LabelSize::Small)),
)
.tooltip(Tooltip::text(format!("in {dir_name}")))
.tooltip(Tooltip::text(format!("in {directory_full_path}")))
}
})
}

View File

@@ -0,0 +1,224 @@
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use gpui::{App, DismissEvent, Entity, FocusHandle, Focusable, Task, WeakEntity};
use picker::{Picker, PickerDelegate};
use prompt_store::{PromptId, PromptStore, UserPromptId};
use ui::{ListItem, prelude::*};
use util::ResultExt as _;
use crate::context::RULES_ICON;
use crate::context_picker::ContextPicker;
use crate::context_store::{self, ContextStore};
pub struct RulesContextPicker {
picker: Entity<Picker<RulesContextPickerDelegate>>,
}
impl RulesContextPicker {
pub fn new(
prompt_store: Entity<PromptStore>,
context_picker: WeakEntity<ContextPicker>,
context_store: WeakEntity<context_store::ContextStore>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let delegate = RulesContextPickerDelegate::new(prompt_store, context_picker, context_store);
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
RulesContextPicker { picker }
}
}
impl Focusable for RulesContextPicker {
fn focus_handle(&self, cx: &App) -> FocusHandle {
self.picker.focus_handle(cx)
}
}
impl Render for RulesContextPicker {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
self.picker.clone()
}
}
#[derive(Debug, Clone)]
pub struct RulesContextEntry {
pub prompt_id: UserPromptId,
pub title: SharedString,
}
pub struct RulesContextPickerDelegate {
prompt_store: Entity<PromptStore>,
context_picker: WeakEntity<ContextPicker>,
context_store: WeakEntity<context_store::ContextStore>,
matches: Vec<RulesContextEntry>,
selected_index: usize,
}
impl RulesContextPickerDelegate {
pub fn new(
prompt_store: Entity<PromptStore>,
context_picker: WeakEntity<ContextPicker>,
context_store: WeakEntity<context_store::ContextStore>,
) -> Self {
RulesContextPickerDelegate {
prompt_store,
context_picker,
context_store,
matches: Vec::new(),
selected_index: 0,
}
}
}
impl PickerDelegate for RulesContextPickerDelegate {
type ListItem = ListItem;
fn match_count(&self) -> usize {
self.matches.len()
}
fn selected_index(&self) -> usize {
self.selected_index
}
fn set_selected_index(
&mut self,
ix: usize,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) {
self.selected_index = ix;
}
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
"Search available rules…".into()
}
fn update_matches(
&mut self,
query: String,
window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Task<()> {
let search_task = search_rules(
query,
Arc::new(AtomicBool::default()),
&self.prompt_store,
cx,
);
cx.spawn_in(window, async move |this, cx| {
let matches = search_task.await;
this.update(cx, |this, cx| {
this.delegate.matches = matches;
this.delegate.selected_index = 0;
cx.notify();
})
.ok();
})
}
fn confirm(&mut self, _secondary: bool, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
let Some(entry) = self.matches.get(self.selected_index) else {
return;
};
self.context_store
.update(cx, |context_store, cx| {
context_store.add_rules(entry.prompt_id, true, cx)
})
.log_err();
}
fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
self.context_picker
.update(cx, |_, cx| {
cx.emit(DismissEvent);
})
.ok();
}
fn render_match(
&self,
ix: usize,
selected: bool,
_window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Option<Self::ListItem> {
let thread = &self.matches[ix];
Some(ListItem::new(ix).inset(true).toggle_state(selected).child(
render_thread_context_entry(thread, self.context_store.clone(), cx),
))
}
}
pub fn render_thread_context_entry(
user_rules: &RulesContextEntry,
context_store: WeakEntity<ContextStore>,
cx: &mut App,
) -> Div {
let added = context_store.upgrade().map_or(false, |context_store| {
context_store
.read(cx)
.includes_user_rules(user_rules.prompt_id)
});
h_flex()
.gap_1p5()
.w_full()
.justify_between()
.child(
h_flex()
.gap_1p5()
.max_w_72()
.child(
Icon::new(RULES_ICON)
.size(IconSize::XSmall)
.color(Color::Muted),
)
.child(Label::new(user_rules.title.clone()).truncate()),
)
.when(added, |el| {
el.child(
h_flex()
.gap_1()
.child(
Icon::new(IconName::Check)
.size(IconSize::Small)
.color(Color::Success),
)
.child(Label::new("Added").size(LabelSize::Small)),
)
})
}
pub(crate) fn search_rules(
query: String,
cancellation_flag: Arc<AtomicBool>,
prompt_store: &Entity<PromptStore>,
cx: &mut App,
) -> Task<Vec<RulesContextEntry>> {
let search_task = prompt_store.read(cx).search(query, cancellation_flag, cx);
cx.background_spawn(async move {
search_task
.await
.into_iter()
.flat_map(|metadata| {
// Default prompts are filtered out as they are automatically included.
if metadata.default {
None
} else {
match metadata.id {
PromptId::EditWorkflow => None,
PromptId::User { uuid } => Some(RulesContextEntry {
prompt_id: uuid,
title: metadata.title?,
}),
}
}
})
.collect::<Vec<_>>()
})
}

View File

@@ -2,7 +2,7 @@ use std::cmp::Reverse;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use anyhow::{Context as _, Result};
use anyhow::Result;
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
App, AppContext, DismissEvent, Entity, FocusHandle, Focusable, Stateful, Task, WeakEntity,
@@ -10,12 +10,11 @@ use gpui::{
use ordered_float::OrderedFloat;
use picker::{Picker, PickerDelegate};
use project::{DocumentSymbol, Symbol};
use text::OffsetRangeExt;
use ui::{ListItem, prelude::*};
use util::ResultExt as _;
use workspace::Workspace;
use crate::context_picker::{ConfirmBehavior, ContextPicker};
use crate::context_picker::ContextPicker;
use crate::context_store::ContextStore;
pub struct SymbolContextPicker {
@@ -27,16 +26,10 @@ impl SymbolContextPicker {
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let delegate = SymbolContextPickerDelegate::new(
context_picker,
workspace,
context_store,
confirm_behavior,
);
let delegate = SymbolContextPickerDelegate::new(context_picker, workspace, context_store);
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
Self { picker }
@@ -59,7 +52,6 @@ pub struct SymbolContextPickerDelegate {
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
matches: Vec<SymbolEntry>,
selected_index: usize,
}
@@ -69,13 +61,11 @@ impl SymbolContextPickerDelegate {
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
) -> Self {
Self {
context_picker,
workspace,
context_store,
confirm_behavior,
matches: Vec::new(),
selected_index: 0,
}
@@ -119,11 +109,7 @@ impl PickerDelegate for SymbolContextPickerDelegate {
let search_task = search_symbols(query, Arc::<AtomicBool>::default(), &workspace, cx);
let context_store = self.context_store.clone();
cx.spawn_in(window, async move |this, cx| {
let symbols = search_task
.await
.context("Failed to load symbols")
.log_err()
.unwrap_or_default();
let symbols = search_task.await;
let symbol_entries = context_store
.read_with(cx, |context_store, cx| {
@@ -139,7 +125,7 @@ impl PickerDelegate for SymbolContextPickerDelegate {
})
}
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
fn confirm(&mut self, _secondary: bool, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
let Some(mat) = self.matches.get(self.selected_index) else {
return;
};
@@ -147,7 +133,6 @@ impl PickerDelegate for SymbolContextPickerDelegate {
return;
};
let confirm_behavior = self.confirm_behavior;
let add_symbol_task = add_symbol(
mat.symbol.clone(),
true,
@@ -157,16 +142,12 @@ impl PickerDelegate for SymbolContextPickerDelegate {
);
let selected_index = self.selected_index;
cx.spawn_in(window, async move |this, cx| {
cx.spawn(async move |this, cx| {
let included = add_symbol_task.await?;
this.update_in(cx, |this, window, cx| {
this.update(cx, |this, _| {
if let Some(mat) = this.delegate.matches.get_mut(selected_index) {
mat.is_included = included;
}
match confirm_behavior {
ConfirmBehavior::KeepOpen => {}
ConfirmBehavior::Close => this.delegate.dismissed(window, cx),
}
})
})
.detach_and_log_err(cx);
@@ -190,10 +171,7 @@ impl PickerDelegate for SymbolContextPickerDelegate {
let mat = &self.matches[ix];
Some(ListItem::new(ix).inset(true).toggle_state(selected).child(
render_symbol_context_entry(
ElementId::NamedInteger("symbol-ctx-picker".into(), ix),
mat,
),
render_symbol_context_entry(ElementId::named_usize("symbol-ctx-picker", ix), mat),
))
}
}
@@ -246,18 +224,16 @@ pub(crate) fn add_symbol(
)
})?;
context_store
.update(cx, move |context_store, cx| {
context_store.add_symbol(
buffer,
name.into(),
range,
enclosing_range,
remove_if_exists,
cx,
)
})?
.await
context_store.update(cx, move |context_store, cx| {
context_store.add_symbol(
buffer,
name.into(),
range,
enclosing_range,
remove_if_exists,
cx,
)
})
})
}
@@ -285,12 +261,16 @@ fn find_matching_symbol(symbol: &Symbol, candidates: &[DocumentSymbol]) -> Optio
}
}
pub struct SymbolMatch {
pub symbol: Symbol,
}
pub(crate) fn search_symbols(
query: String,
cancellation_flag: Arc<AtomicBool>,
workspace: &Entity<Workspace>,
cx: &mut App,
) -> Task<Result<Vec<(StringMatch, Symbol)>>> {
) -> Task<Vec<SymbolMatch>> {
let symbols_task = workspace.update(cx, |workspace, cx| {
workspace
.project()
@@ -298,19 +278,28 @@ pub(crate) fn search_symbols(
});
let project = workspace.read(cx).project().clone();
cx.spawn(async move |cx| {
let symbols = symbols_task.await?;
let (visible_match_candidates, external_match_candidates): (Vec<_>, Vec<_>) = project
.update(cx, |project, cx| {
symbols
.iter()
.enumerate()
.map(|(id, symbol)| StringMatchCandidate::new(id, &symbol.label.filter_text()))
.partition(|candidate| {
project
.entry_for_path(&symbols[candidate.id].path, cx)
.map_or(false, |e| !e.is_ignored)
})
})?;
let Some(symbols) = symbols_task.await.log_err() else {
return Vec::new();
};
let Some((visible_match_candidates, external_match_candidates)): Option<(Vec<_>, Vec<_>)> =
project
.update(cx, |project, cx| {
symbols
.iter()
.enumerate()
.map(|(id, symbol)| {
StringMatchCandidate::new(id, &symbol.label.filter_text())
})
.partition(|candidate| {
project
.entry_for_path(&symbols[candidate.id].path, cx)
.map_or(false, |e| !e.is_ignored)
})
})
.log_err()
else {
return Vec::new();
};
const MAX_MATCHES: usize = 100;
let mut visible_matches = cx.background_executor().block(fuzzy::match_strings(
@@ -339,7 +328,7 @@ pub(crate) fn search_symbols(
let mut matches = visible_matches;
matches.append(&mut external_matches);
Ok(matches
matches
.into_iter()
.map(|mut mat| {
let symbol = symbols[mat.candidate_id].clone();
@@ -347,49 +336,24 @@ pub(crate) fn search_symbols(
for position in &mut mat.positions {
*position += filter_start;
}
(mat, symbol)
SymbolMatch { symbol }
})
.collect())
.collect()
})
}
fn compute_symbol_entries(
symbols: Vec<(StringMatch, Symbol)>,
symbols: Vec<SymbolMatch>,
context_store: &ContextStore,
cx: &App,
) -> Vec<SymbolEntry> {
let mut symbol_entries = Vec::with_capacity(symbols.len());
for (_, symbol) in symbols {
let symbols_for_path = context_store.included_symbols_by_path().get(&symbol.path);
let is_included = if let Some(symbols_for_path) = symbols_for_path {
let mut is_included = false;
for included_symbol_id in symbols_for_path {
if included_symbol_id.name.as_ref() == symbol.name.as_str() {
if let Some(buffer) = context_store.buffer_for_symbol(included_symbol_id) {
let snapshot = buffer.read(cx).snapshot();
let included_symbol_range =
included_symbol_id.range.to_point_utf16(&snapshot);
if included_symbol_range.start == symbol.range.start.0
&& included_symbol_range.end == symbol.range.end.0
{
is_included = true;
break;
}
}
}
}
is_included
} else {
false
};
symbol_entries.push(SymbolEntry {
symbols
.into_iter()
.map(|SymbolMatch { symbol, .. }| SymbolEntry {
is_included: context_store.includes_symbol(&symbol, cx),
symbol,
is_included,
})
}
symbol_entries
.collect::<Vec<_>>()
}
pub fn render_symbol_context_entry(id: ElementId, entry: &SymbolEntry) -> Stateful<Div> {

View File

@@ -1,11 +1,12 @@
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use fuzzy::StringMatchCandidate;
use gpui::{App, DismissEvent, Entity, FocusHandle, Focusable, Task, WeakEntity};
use picker::{Picker, PickerDelegate};
use ui::{ListItem, prelude::*};
use crate::context_picker::{ConfirmBehavior, ContextPicker};
use crate::context_picker::ContextPicker;
use crate::context_store::{self, ContextStore};
use crate::thread::ThreadId;
use crate::thread_store::ThreadStore;
@@ -19,16 +20,11 @@ impl ThreadContextPicker {
thread_store: WeakEntity<ThreadStore>,
context_picker: WeakEntity<ContextPicker>,
context_store: WeakEntity<context_store::ContextStore>,
confirm_behavior: ConfirmBehavior,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let delegate = ThreadContextPickerDelegate::new(
thread_store,
context_picker,
context_store,
confirm_behavior,
);
let delegate =
ThreadContextPickerDelegate::new(thread_store, context_picker, context_store);
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
ThreadContextPicker { picker }
@@ -57,7 +53,6 @@ pub struct ThreadContextPickerDelegate {
thread_store: WeakEntity<ThreadStore>,
context_picker: WeakEntity<ContextPicker>,
context_store: WeakEntity<context_store::ContextStore>,
confirm_behavior: ConfirmBehavior,
matches: Vec<ThreadContextEntry>,
selected_index: usize,
}
@@ -67,13 +62,11 @@ impl ThreadContextPickerDelegate {
thread_store: WeakEntity<ThreadStore>,
context_picker: WeakEntity<ContextPicker>,
context_store: WeakEntity<context_store::ContextStore>,
confirm_behavior: ConfirmBehavior,
) -> Self {
ThreadContextPickerDelegate {
thread_store,
context_picker,
context_store,
confirm_behavior,
matches: Vec::new(),
selected_index: 0,
}
@@ -110,15 +103,15 @@ impl PickerDelegate for ThreadContextPickerDelegate {
window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Task<()> {
let Some(threads) = self.thread_store.upgrade() else {
let Some(thread_store) = self.thread_store.upgrade() else {
return Task::ready(());
};
let search_task = search_threads(query, threads, cx);
let search_task = search_threads(query, Arc::new(AtomicBool::default()), thread_store, cx);
cx.spawn_in(window, async move |this, cx| {
let matches = search_task.await;
this.update(cx, |this, cx| {
this.delegate.matches = matches;
this.delegate.matches = matches.into_iter().map(|mat| mat.thread).collect();
this.delegate.selected_index = 0;
cx.notify();
})
@@ -126,7 +119,7 @@ impl PickerDelegate for ThreadContextPickerDelegate {
})
}
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
fn confirm(&mut self, _secondary: bool, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
let Some(entry) = self.matches.get(self.selected_index) else {
return;
};
@@ -137,20 +130,15 @@ impl PickerDelegate for ThreadContextPickerDelegate {
let open_thread_task = thread_store.update(cx, |this, cx| this.open_thread(&entry.id, cx));
cx.spawn_in(window, async move |this, cx| {
cx.spawn(async move |this, cx| {
let thread = open_thread_task.await?;
this.update_in(cx, |this, window, cx| {
this.update(cx, |this, cx| {
this.delegate
.context_store
.update(cx, |context_store, cx| {
context_store.add_thread(thread, true, cx)
})
.ok();
match this.delegate.confirm_behavior {
ConfirmBehavior::KeepOpen => {}
ConfirmBehavior::Close => this.delegate.dismissed(window, cx),
}
})
})
.detach_and_log_err(cx);
@@ -185,7 +173,7 @@ pub fn render_thread_context_entry(
cx: &mut App,
) -> Div {
let added = context_store.upgrade().map_or(false, |ctx_store| {
ctx_store.read(cx).includes_thread(&thread.id).is_some()
ctx_store.read(cx).includes_thread(&thread.id)
});
h_flex()
@@ -217,25 +205,38 @@ pub fn render_thread_context_entry(
})
}
#[derive(Clone)]
pub struct ThreadMatch {
pub thread: ThreadContextEntry,
pub is_recent: bool,
}
pub(crate) fn search_threads(
query: String,
cancellation_flag: Arc<AtomicBool>,
thread_store: Entity<ThreadStore>,
cx: &mut App,
) -> Task<Vec<ThreadContextEntry>> {
let threads = thread_store.update(cx, |this, _cx| {
this.threads()
.into_iter()
.map(|thread| ThreadContextEntry {
id: thread.id,
summary: thread.summary,
})
.collect::<Vec<_>>()
});
) -> Task<Vec<ThreadMatch>> {
let threads = thread_store
.read(cx)
.reverse_chronological_threads()
.into_iter()
.map(|thread| ThreadContextEntry {
id: thread.id,
summary: thread.summary,
})
.collect::<Vec<_>>();
let executor = cx.background_executor().clone();
cx.background_spawn(async move {
if query.is_empty() {
threads
.into_iter()
.map(|thread| ThreadMatch {
thread,
is_recent: false,
})
.collect()
} else {
let candidates = threads
.iter()
@@ -247,14 +248,17 @@ pub(crate) fn search_threads(
&query,
false,
100,
&Default::default(),
&cancellation_flag,
executor,
)
.await;
matches
.into_iter()
.map(|mat| threads[mat.candidate_id].clone())
.map(|mat| ThreadMatch {
thread: threads[mat.candidate_id].clone(),
is_recent: false,
})
.collect()
}
})

View File

@@ -0,0 +1,485 @@
use std::ops::Range;
use std::path::PathBuf;
use std::sync::Arc;
use anyhow::{Result, anyhow};
use collections::{HashSet, IndexSet};
use futures::future::join_all;
use futures::{self, FutureExt};
use gpui::{App, Context, Entity, Image, SharedString, Task, WeakEntity};
use language::Buffer;
use language_model::LanguageModelImage;
use project::{Project, ProjectItem, ProjectPath, Symbol};
use prompt_store::UserPromptId;
use ref_cast::RefCast as _;
use text::{Anchor, OffsetRangeExt};
use util::ResultExt as _;
use crate::ThreadStore;
use crate::context::{
AgentContext, AgentContextKey, ContextId, DirectoryContext, FetchedUrlContext, FileContext,
ImageContext, RulesContext, SelectionContext, SymbolContext, ThreadContext,
};
use crate::context_strip::SuggestedContext;
use crate::thread::{Thread, ThreadId};
pub struct ContextStore {
project: WeakEntity<Project>,
thread_store: Option<WeakEntity<ThreadStore>>,
thread_summary_tasks: Vec<Task<()>>,
next_context_id: ContextId,
context_set: IndexSet<AgentContextKey>,
context_thread_ids: HashSet<ThreadId>,
}
impl ContextStore {
pub fn new(
project: WeakEntity<Project>,
thread_store: Option<WeakEntity<ThreadStore>>,
) -> Self {
Self {
project,
thread_store,
thread_summary_tasks: Vec::new(),
next_context_id: ContextId::zero(),
context_set: IndexSet::default(),
context_thread_ids: HashSet::default(),
}
}
pub fn context(&self) -> impl Iterator<Item = &AgentContext> {
self.context_set.iter().map(|entry| entry.as_ref())
}
pub fn clear(&mut self) {
self.context_set.clear();
self.context_thread_ids.clear();
}
pub fn new_context_for_thread(&self, thread: &Thread) -> Vec<AgentContext> {
let existing_context = thread
.messages()
.flat_map(|message| &message.loaded_context.contexts)
.map(AgentContextKey::ref_cast)
.collect::<HashSet<_>>();
self.context_set
.iter()
.filter(|context| !existing_context.contains(context))
.map(|entry| entry.0.clone())
.collect::<Vec<_>>()
}
pub fn add_file_from_path(
&mut self,
project_path: ProjectPath,
remove_if_exists: bool,
cx: &mut Context<Self>,
) -> Task<Result<()>> {
let Some(project) = self.project.upgrade() else {
return Task::ready(Err(anyhow!("failed to read project")));
};
cx.spawn(async move |this, cx| {
let open_buffer_task = project.update(cx, |project, cx| {
project.open_buffer(project_path.clone(), cx)
})?;
let buffer = open_buffer_task.await?;
this.update(cx, |this, cx| {
this.add_file_from_buffer(&project_path, buffer, remove_if_exists, cx)
})
})
}
pub fn add_file_from_buffer(
&mut self,
project_path: &ProjectPath,
buffer: Entity<Buffer>,
remove_if_exists: bool,
cx: &mut Context<Self>,
) {
let context_id = self.next_context_id.post_inc();
let context = AgentContext::File(FileContext { buffer, context_id });
let already_included = if self.has_context(&context) {
if remove_if_exists {
self.remove_context(&context, cx);
}
true
} else {
self.path_included_in_directory(project_path, cx).is_some()
};
if !already_included {
self.insert_context(context, cx);
}
}
pub fn add_directory(
&mut self,
project_path: &ProjectPath,
remove_if_exists: bool,
cx: &mut Context<Self>,
) -> Result<()> {
let Some(project) = self.project.upgrade() else {
return Err(anyhow!("failed to read project"));
};
let Some(entry_id) = project
.read(cx)
.entry_for_path(project_path, cx)
.map(|entry| entry.id)
else {
return Err(anyhow!("no entry found for directory context"));
};
let context_id = self.next_context_id.post_inc();
let context = AgentContext::Directory(DirectoryContext {
entry_id,
context_id,
});
if self.has_context(&context) {
if remove_if_exists {
self.remove_context(&context, cx);
}
} else if self.path_included_in_directory(project_path, cx).is_none() {
self.insert_context(context, cx);
}
anyhow::Ok(())
}
pub fn add_symbol(
&mut self,
buffer: Entity<Buffer>,
symbol: SharedString,
range: Range<Anchor>,
enclosing_range: Range<Anchor>,
remove_if_exists: bool,
cx: &mut Context<Self>,
) -> bool {
let context_id = self.next_context_id.post_inc();
let context = AgentContext::Symbol(SymbolContext {
buffer,
symbol,
range,
enclosing_range,
context_id,
});
if self.has_context(&context) {
if remove_if_exists {
self.remove_context(&context, cx);
}
return false;
}
self.insert_context(context, cx)
}
pub fn add_thread(
&mut self,
thread: Entity<Thread>,
remove_if_exists: bool,
cx: &mut Context<Self>,
) {
let context_id = self.next_context_id.post_inc();
let context = AgentContext::Thread(ThreadContext { thread, context_id });
if self.has_context(&context) {
if remove_if_exists {
self.remove_context(&context, cx);
}
} else {
self.insert_context(context, cx);
}
}
fn start_summarizing_thread_if_needed(
&mut self,
thread: &Entity<Thread>,
cx: &mut Context<Self>,
) {
if let Some(summary_task) =
thread.update(cx, |thread, cx| thread.generate_detailed_summary(cx))
{
let thread = thread.clone();
let thread_store = self.thread_store.clone();
self.thread_summary_tasks.push(cx.spawn(async move |_, cx| {
summary_task.await;
if let Some(thread_store) = thread_store {
// Save thread so its summary can be reused later
let save_task = thread_store
.update(cx, |thread_store, cx| thread_store.save_thread(&thread, cx));
if let Some(save_task) = save_task.ok() {
save_task.await.log_err();
}
}
}));
}
}
pub fn wait_for_summaries(&mut self, cx: &App) -> Task<()> {
let tasks = std::mem::take(&mut self.thread_summary_tasks);
cx.spawn(async move |_cx| {
join_all(tasks).await;
})
}
pub fn add_rules(
&mut self,
prompt_id: UserPromptId,
remove_if_exists: bool,
cx: &mut Context<ContextStore>,
) {
let context_id = self.next_context_id.post_inc();
let context = AgentContext::Rules(RulesContext {
prompt_id,
context_id,
});
if self.has_context(&context) {
if remove_if_exists {
self.remove_context(&context, cx);
}
} else {
self.insert_context(context, cx);
}
}
pub fn add_fetched_url(
&mut self,
url: String,
text: impl Into<SharedString>,
cx: &mut Context<ContextStore>,
) {
let context = AgentContext::FetchedUrl(FetchedUrlContext {
url: url.into(),
text: text.into(),
context_id: self.next_context_id.post_inc(),
});
self.insert_context(context, cx);
}
pub fn add_image(&mut self, image: Arc<Image>, cx: &mut Context<ContextStore>) {
let image_task = LanguageModelImage::from_image(image.clone(), cx).shared();
let context = AgentContext::Image(ImageContext {
original_image: image,
image_task,
context_id: self.next_context_id.post_inc(),
});
self.insert_context(context, cx);
}
pub fn add_selection(
&mut self,
buffer: Entity<Buffer>,
range: Range<Anchor>,
cx: &mut Context<ContextStore>,
) {
let context_id = self.next_context_id.post_inc();
let context = AgentContext::Selection(SelectionContext {
buffer,
range,
context_id,
});
self.insert_context(context, cx);
}
pub fn add_suggested_context(
&mut self,
suggested: &SuggestedContext,
cx: &mut Context<ContextStore>,
) {
match suggested {
SuggestedContext::File {
buffer,
icon_path: _,
name: _,
} => {
if let Some(buffer) = buffer.upgrade() {
let context_id = self.next_context_id.post_inc();
self.insert_context(AgentContext::File(FileContext { buffer, context_id }), cx);
};
}
SuggestedContext::Thread { thread, name: _ } => {
if let Some(thread) = thread.upgrade() {
let context_id = self.next_context_id.post_inc();
self.insert_context(
AgentContext::Thread(ThreadContext { thread, context_id }),
cx,
);
}
}
}
}
fn insert_context(&mut self, context: AgentContext, cx: &mut Context<Self>) -> bool {
match &context {
AgentContext::Thread(thread_context) => {
self.context_thread_ids
.insert(thread_context.thread.read(cx).id().clone());
self.start_summarizing_thread_if_needed(&thread_context.thread, cx);
}
_ => {}
}
let inserted = self.context_set.insert(AgentContextKey(context));
if inserted {
cx.notify();
}
inserted
}
pub fn remove_context(&mut self, context: &AgentContext, cx: &mut Context<Self>) {
if self
.context_set
.shift_remove(AgentContextKey::ref_cast(context))
{
match context {
AgentContext::Thread(thread_context) => {
self.context_thread_ids
.remove(thread_context.thread.read(cx).id());
}
_ => {}
}
cx.notify();
}
}
pub fn has_context(&mut self, context: &AgentContext) -> bool {
self.context_set
.contains(AgentContextKey::ref_cast(context))
}
/// Returns whether this file path is already included directly in the context, or if it will be
/// included in the context via a directory.
pub fn file_path_included(&self, path: &ProjectPath, cx: &App) -> Option<FileInclusion> {
let project = self.project.upgrade()?.read(cx);
self.context().find_map(|context| match context {
AgentContext::File(file_context) => FileInclusion::check_file(file_context, path, cx),
AgentContext::Directory(directory_context) => {
FileInclusion::check_directory(directory_context, path, project, cx)
}
_ => None,
})
}
pub fn path_included_in_directory(
&self,
path: &ProjectPath,
cx: &App,
) -> Option<FileInclusion> {
let project = self.project.upgrade()?.read(cx);
self.context().find_map(|context| match context {
AgentContext::Directory(directory_context) => {
FileInclusion::check_directory(directory_context, path, project, cx)
}
_ => None,
})
}
pub fn includes_symbol(&self, symbol: &Symbol, cx: &App) -> bool {
self.context().any(|context| match context {
AgentContext::Symbol(context) => {
if context.symbol != symbol.name {
return false;
}
let buffer = context.buffer.read(cx);
let Some(context_path) = buffer.project_path(cx) else {
return false;
};
if context_path != symbol.path {
return false;
}
let context_range = context.range.to_point_utf16(&buffer.snapshot());
context_range.start == symbol.range.start.0
&& context_range.end == symbol.range.end.0
}
_ => false,
})
}
pub fn includes_thread(&self, thread_id: &ThreadId) -> bool {
self.context_thread_ids.contains(thread_id)
}
pub fn includes_user_rules(&self, prompt_id: UserPromptId) -> bool {
self.context_set
.contains(&RulesContext::lookup_key(prompt_id))
}
pub fn includes_url(&self, url: impl Into<SharedString>) -> bool {
self.context_set
.contains(&FetchedUrlContext::lookup_key(url.into()))
}
pub fn file_paths(&self, cx: &App) -> HashSet<ProjectPath> {
self.context()
.filter_map(|context| match context {
AgentContext::File(file) => {
let buffer = file.buffer.read(cx);
buffer.project_path(cx)
}
AgentContext::Directory(_)
| AgentContext::Symbol(_)
| AgentContext::Selection(_)
| AgentContext::FetchedUrl(_)
| AgentContext::Thread(_)
| AgentContext::Rules(_)
| AgentContext::Image(_) => None,
})
.collect()
}
pub fn thread_ids(&self) -> &HashSet<ThreadId> {
&self.context_thread_ids
}
}
pub enum FileInclusion {
Direct,
InDirectory { full_path: PathBuf },
}
impl FileInclusion {
fn check_file(file_context: &FileContext, path: &ProjectPath, cx: &App) -> Option<Self> {
let file_path = file_context.buffer.read(cx).project_path(cx)?;
if path == &file_path {
Some(FileInclusion::Direct)
} else {
None
}
}
fn check_directory(
directory_context: &DirectoryContext,
path: &ProjectPath,
project: &Project,
cx: &App,
) -> Option<Self> {
let worktree = project
.worktree_for_entry(directory_context.entry_id, cx)?
.read(cx);
let entry = worktree.entry_for_id(directory_context.entry_id)?;
let directory_path = ProjectPath {
worktree_id: worktree.id(),
path: entry.path.clone(),
};
if path.starts_with(&directory_path) {
if path == &directory_path {
Some(FileInclusion::Direct)
} else {
Some(FileInclusion::InDirectory {
full_path: worktree.full_path(&entry.path),
})
}
} else {
None
}
}
}

View File

@@ -1,3 +1,4 @@
use std::path::Path;
use std::rc::Rc;
use collections::HashSet;
@@ -9,11 +10,12 @@ use gpui::{
};
use itertools::Itertools;
use language::Buffer;
use project::ProjectItem;
use ui::{KeyBinding, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*};
use workspace::{Workspace, notifications::NotifyResultExt};
use workspace::Workspace;
use crate::context::{ContextId, ContextKind};
use crate::context_picker::{ConfirmBehavior, ContextPicker};
use crate::context::{AgentContext, ContextKind};
use crate::context_picker::ContextPicker;
use crate::context_store::ContextStore;
use crate::thread::Thread;
use crate::thread_store::ThreadStore;
@@ -30,6 +32,7 @@ pub struct ContextStrip {
focus_handle: FocusHandle,
suggest_context_kind: SuggestContextKind,
workspace: WeakEntity<Workspace>,
thread_store: Option<WeakEntity<ThreadStore>>,
_subscriptions: Vec<Subscription>,
focused_index: Option<usize>,
children_bounds: Option<Vec<Bounds<Pixels>>>,
@@ -50,7 +53,6 @@ impl ContextStrip {
workspace.clone(),
thread_store.clone(),
context_store.downgrade(),
ConfirmBehavior::KeepOpen,
window,
cx,
)
@@ -59,6 +61,7 @@ impl ContextStrip {
let focus_handle = cx.focus_handle();
let subscriptions = vec![
cx.observe(&context_store, |_, _, cx| cx.notify()),
cx.subscribe_in(&context_picker, window, Self::handle_context_picker_event),
cx.on_focus(&focus_handle, window, Self::handle_focus),
cx.on_blur(&focus_handle, window, Self::handle_blur),
@@ -71,12 +74,31 @@ impl ContextStrip {
focus_handle,
suggest_context_kind,
workspace,
thread_store,
_subscriptions: subscriptions,
focused_index: None,
children_bounds: None,
}
}
fn added_contexts(&self, cx: &App) -> Vec<AddedContext> {
if let Some(workspace) = self.workspace.upgrade() {
let project = workspace.read(cx).project().read(cx);
let prompt_store = self
.thread_store
.as_ref()
.and_then(|thread_store| thread_store.upgrade())
.and_then(|thread_store| thread_store.read(cx).prompt_store().as_ref());
self.context_store
.read(cx)
.context()
.flat_map(|context| AddedContext::new(context.clone(), prompt_store, project, cx))
.collect::<Vec<_>>()
} else {
Vec::new()
}
}
fn suggested_context(&self, cx: &Context<Self>) -> Option<SuggestedContext> {
match self.suggest_context_kind {
SuggestContextKind::File => self.suggested_file(cx),
@@ -91,27 +113,21 @@ impl ContextStrip {
let editor = active_item.to_any().downcast::<Editor>().ok()?.read(cx);
let active_buffer_entity = editor.buffer().read(cx).as_singleton()?;
let active_buffer = active_buffer_entity.read(cx);
let path = active_buffer.file()?.full_path(cx);
let project_path = active_buffer.project_path(cx)?;
if self
.context_store
.read(cx)
.will_include_buffer(active_buffer.remote_id(), &path)
.file_path_included(&project_path, cx)
.is_some()
{
return None;
}
let name = match path.file_name() {
Some(name) => name.to_string_lossy().into_owned().into(),
None => path.to_string_lossy().into_owned().into(),
};
let icon_path = FileIcons::get_icon(&path, cx);
let file_name = active_buffer.file()?.file_name(cx);
let icon_path = FileIcons::get_icon(&Path::new(&file_name), cx);
Some(SuggestedContext::File {
name,
name: file_name.to_string_lossy().into_owned().into(),
buffer: active_buffer_entity.downgrade(),
icon_path,
})
@@ -136,7 +152,6 @@ impl ContextStrip {
.context_store
.read(cx)
.includes_thread(active_thread.id())
.is_some()
{
return None;
}
@@ -273,12 +288,12 @@ impl ContextStrip {
best.map(|(index, _, _)| index)
}
fn open_context(&mut self, id: ContextId, window: &mut Window, cx: &mut App) {
fn open_context(&mut self, context: &AgentContext, window: &mut Window, cx: &mut App) {
let Some(workspace) = self.workspace.upgrade() else {
return;
};
crate::active_thread::open_context(id, self.context_store.clone(), workspace, window, cx);
crate::active_thread::open_context(context, workspace, window, cx);
}
fn remove_focused_context(
@@ -288,17 +303,17 @@ impl ContextStrip {
cx: &mut Context<Self>,
) {
if let Some(index) = self.focused_index {
let mut is_empty = false;
let added_contexts = self.added_contexts(cx);
let Some(context) = added_contexts.get(index) else {
return;
};
self.context_store.update(cx, |this, _cx| {
if let Some(item) = this.context().get(index) {
this.remove_context(item.id());
}
is_empty = this.context().is_empty();
self.context_store.update(cx, |this, cx| {
this.remove_context(&context.context, cx);
});
if is_empty {
let is_now_empty = added_contexts.len() == 1;
if is_now_empty {
cx.emit(ContextStripEvent::BlurredEmpty);
} else {
self.focused_index = Some(index.saturating_sub(1));
@@ -307,49 +322,28 @@ impl ContextStrip {
}
}
fn is_suggested_focused<T>(&self, context: &Vec<T>) -> bool {
fn is_suggested_focused(&self, added_contexts: &Vec<AddedContext>) -> bool {
// We only suggest one item after the actual context
self.focused_index == Some(context.len())
self.focused_index == Some(added_contexts.len())
}
fn accept_suggested_context(
&mut self,
_: &AcceptSuggestedContext,
window: &mut Window,
_window: &mut Window,
cx: &mut Context<Self>,
) {
if let Some(suggested) = self.suggested_context(cx) {
let context_store = self.context_store.read(cx);
if self.is_suggested_focused(context_store.context()) {
self.add_suggested_context(&suggested, window, cx);
if self.is_suggested_focused(&self.added_contexts(cx)) {
self.add_suggested_context(&suggested, cx);
}
}
}
fn add_suggested_context(
&mut self,
suggested: &SuggestedContext,
window: &mut Window,
cx: &mut Context<Self>,
) {
let task = self.context_store.update(cx, |context_store, cx| {
context_store.accept_suggested_context(&suggested, cx)
fn add_suggested_context(&mut self, suggested: &SuggestedContext, cx: &mut Context<Self>) {
self.context_store.update(cx, |context_store, cx| {
context_store.add_suggested_context(&suggested, cx)
});
cx.spawn_in(window, async move |this, cx| {
match task.await.notify_async_err(cx) {
None => {}
Some(()) => {
if let Some(this) = this.upgrade() {
this.update(cx, |_, cx| cx.notify())?;
}
}
}
anyhow::Ok(())
})
.detach_and_log_err(cx);
cx.notify();
}
}
@@ -362,17 +356,10 @@ impl Focusable for ContextStrip {
impl Render for ContextStrip {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let context_store = self.context_store.read(cx);
let context = context_store.context();
let context_picker = self.context_picker.clone();
let focus_handle = self.focus_handle.clone();
let suggested_context = self.suggested_context(cx);
let added_contexts = context
.iter()
.map(|c| AddedContext::new(c, cx))
.collect::<Vec<_>>();
let added_contexts = self.added_contexts(cx);
let dupe_names = added_contexts
.iter()
.map(|c| c.name.clone())
@@ -381,6 +368,14 @@ impl Render for ContextStrip {
.filter(|(a, b)| a == b)
.map(|(a, _)| a)
.collect::<HashSet<SharedString>>();
let no_added_context = added_contexts.is_empty();
let suggested_context = self.suggested_context(cx).map(|suggested_context| {
(
suggested_context,
self.is_suggested_focused(&added_contexts),
)
});
h_flex()
.flex_wrap()
@@ -437,7 +432,7 @@ impl Render for ContextStrip {
})
.with_handle(self.context_picker_menu_handle.clone()),
)
.when(context.is_empty() && suggested_context.is_none(), {
.when(no_added_context && suggested_context.is_none(), {
|parent| {
parent.child(
h_flex()
@@ -467,16 +462,17 @@ impl Render for ContextStrip {
.enumerate()
.map(|(i, added_context)| {
let name = added_context.name.clone();
let id = added_context.id;
let context = added_context.context.clone();
ContextPill::added(
added_context,
dupe_names.contains(&name),
self.focused_index == Some(i),
Some({
let context = context.clone();
let context_store = self.context_store.clone();
Rc::new(cx.listener(move |_this, _event, _window, cx| {
context_store.update(cx, |this, _cx| {
this.remove_context(id);
context_store.update(cx, |this, cx| {
this.remove_context(&context, cx);
});
cx.notify();
}))
@@ -485,7 +481,7 @@ impl Render for ContextStrip {
.on_click({
Rc::new(cx.listener(move |this, event: &ClickEvent, window, cx| {
if event.down.click_count > 1 {
this.open_context(id, window, cx);
this.open_context(&context, window, cx);
} else {
this.focused_index = Some(i);
}
@@ -494,22 +490,22 @@ impl Render for ContextStrip {
})
}),
)
.when_some(suggested_context, |el, suggested| {
.when_some(suggested_context, |el, (suggested, focused)| {
el.child(
ContextPill::suggested(
suggested.name().clone(),
suggested.icon_path(),
suggested.kind(),
self.is_suggested_focused(&context),
focused,
)
.on_click(Rc::new(cx.listener(
move |this, _event, window, cx| {
this.add_suggested_context(&suggested, window, cx);
move |this, _event, _window, cx| {
this.add_suggested_context(&suggested, cx);
},
))),
)
})
.when(!context.is_empty(), {
.when(!no_added_context, {
move |parent| {
parent.child(
IconButton::new("remove-all-context", IconName::Eraser)
@@ -535,6 +531,7 @@ impl Render for ContextStrip {
)
}
})
.into_any()
}
}

View File

@@ -4,6 +4,7 @@ use gpui::{Entity, prelude::*};
use crate::thread_store::{SerializedThreadMetadata, ThreadStore};
#[derive(Debug)]
pub enum HistoryEntry {
Thread(SerializedThreadMetadata),
Context(SavedContextMetadata),
@@ -21,25 +22,27 @@ impl HistoryEntry {
pub struct HistoryStore {
thread_store: Entity<ThreadStore>,
context_store: Entity<assistant_context_editor::ContextStore>,
_subscriptions: Vec<gpui::Subscription>,
}
impl HistoryStore {
pub fn new(
thread_store: Entity<ThreadStore>,
context_store: Entity<assistant_context_editor::ContextStore>,
_cx: &mut Context<Self>,
cx: &mut Context<Self>,
) -> Self {
let subscriptions = vec![
cx.observe(&thread_store, |_, _, cx| cx.notify()),
cx.observe(&context_store, |_, _, cx| cx.notify()),
];
Self {
thread_store,
context_store,
_subscriptions: subscriptions,
}
}
/// Returns the number of history entries.
pub fn entry_count(&self, cx: &mut Context<Self>) -> usize {
self.entries(cx).len()
}
pub fn entries(&self, cx: &mut Context<Self>) -> Vec<HistoryEntry> {
let mut history_entries = Vec::new();
@@ -48,7 +51,10 @@ impl HistoryStore {
return history_entries;
}
for thread in self.thread_store.update(cx, |this, _cx| this.threads()) {
for thread in self
.thread_store
.update(cx, |this, _cx| this.reverse_chronological_threads())
{
history_entries.push(HistoryEntry::Thread(thread));
}

View File

@@ -24,21 +24,24 @@ use gpui::{
WeakEntity, Window, point,
};
use language::{Buffer, Point, Selection, TransactionId};
use language_model::ConfiguredModel;
use language_model::{LanguageModelRegistry, report_assistant_event};
use multi_buffer::MultiBufferRow;
use parking_lot::Mutex;
use project::LspAction;
use project::Project;
use project::{CodeAction, ProjectTransaction};
use prompt_store::PromptBuilder;
use prompt_store::PromptStore;
use settings::{Settings, SettingsStore};
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
use telemetry_events::{AssistantEventData, AssistantKind, AssistantPhase};
use terminal_view::{TerminalView, terminal_panel::TerminalPanel};
use text::{OffsetRangeExt, ToPoint as _};
use ui::prelude::*;
use util::RangeExt;
use util::ResultExt;
use workspace::{ItemHandle, Toast, Workspace, notifications::NotificationId};
use workspace::{ShowConfiguration, dock::Panel};
use workspace::{ItemHandle, Toast, Workspace, dock::Panel, notifications::NotificationId};
use zed_actions::agent::OpenConfiguration;
use crate::AssistantPanel;
use crate::buffer_codegen::{BufferCodegen, CodegenAlternative, CodegenEvent};
@@ -239,13 +242,17 @@ impl InlineAssistant {
let is_authenticated = || {
LanguageModelRegistry::read_global(cx)
.active_provider()
.map_or(false, |provider| provider.is_authenticated(cx))
.inline_assistant_model()
.map_or(false, |model| model.provider.is_authenticated(cx))
};
let thread_store = workspace
let assistant_panel = workspace
.panel::<AssistantPanel>(cx)
.map(|assistant_panel| assistant_panel.read(cx).thread_store().downgrade());
.map(|assistant_panel| assistant_panel.read(cx));
let prompt_store = assistant_panel
.and_then(|assistant_panel| assistant_panel.prompt_store().as_ref().cloned());
let thread_store =
assistant_panel.map(|assistant_panel| assistant_panel.thread_store().downgrade());
let handle_assist =
|window: &mut Window, cx: &mut Context<Workspace>| match inline_assist_target {
@@ -254,6 +261,8 @@ impl InlineAssistant {
assistant.assist(
&active_editor,
cx.entity().downgrade(),
workspace.project().downgrade(),
prompt_store,
thread_store,
window,
cx,
@@ -265,6 +274,8 @@ impl InlineAssistant {
assistant.assist(
&active_terminal,
cx.entity().downgrade(),
workspace.project().downgrade(),
prompt_store,
thread_store,
window,
cx,
@@ -279,8 +290,8 @@ impl InlineAssistant {
cx.spawn_in(window, async move |_workspace, cx| {
let Some(task) = cx.update(|_, cx| {
LanguageModelRegistry::read_global(cx)
.active_provider()
.map_or(None, |provider| Some(provider.authenticate(cx)))
.inline_assistant_model()
.map_or(None, |model| Some(model.provider.authenticate(cx)))
})?
else {
let answer = cx
@@ -295,7 +306,7 @@ impl InlineAssistant {
if let Some(answer) = answer {
if answer == 0 {
cx.update(|window, cx| {
window.dispatch_action(Box::new(ShowConfiguration), cx)
window.dispatch_action(Box::new(OpenConfiguration), cx)
})
.ok();
}
@@ -318,6 +329,8 @@ impl InlineAssistant {
&mut self,
editor: &Entity<Editor>,
workspace: WeakEntity<Workspace>,
project: WeakEntity<Project>,
prompt_store: Option<Entity<PromptStore>>,
thread_store: Option<WeakEntity<ThreadStore>>,
window: &mut Window,
cx: &mut App,
@@ -401,14 +414,14 @@ impl InlineAssistant {
codegen_ranges.push(anchor_range);
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
self.telemetry.report_assistant_event(AssistantEvent {
if let Some(model) = LanguageModelRegistry::read_global(cx).inline_assistant_model() {
self.telemetry.report_assistant_event(AssistantEventData {
conversation_id: None,
kind: AssistantKind::Inline,
phase: AssistantPhase::Invoked,
message_id: None,
model: model.telemetry_id(),
model_provider: model.provider_id().to_string(),
model: model.model.telemetry_id(),
model_provider: model.provider.id().to_string(),
response_latency: None,
error_message: None,
language_name: buffer.language().map(|language| language.name().to_proto()),
@@ -424,13 +437,16 @@ impl InlineAssistant {
let mut assist_to_focus = None;
for range in codegen_ranges {
let assist_id = self.next_assist_id.post_inc();
let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
let context_store =
cx.new(|_cx| ContextStore::new(project.clone(), thread_store.clone()));
let codegen = cx.new(|cx| {
BufferCodegen::new(
editor.read(cx).buffer().clone(),
range.clone(),
None,
context_store.clone(),
project.clone(),
prompt_store.clone(),
self.telemetry.clone(),
self.prompt_builder.clone(),
cx,
@@ -518,7 +534,8 @@ impl InlineAssistant {
initial_prompt: String,
initial_transaction_id: Option<TransactionId>,
focus: bool,
workspace: WeakEntity<Workspace>,
workspace: Entity<Workspace>,
prompt_store: Option<Entity<PromptStore>>,
thread_store: Option<WeakEntity<ThreadStore>>,
window: &mut Window,
cx: &mut App,
@@ -536,7 +553,8 @@ impl InlineAssistant {
range.end = range.end.bias_right(&snapshot);
}
let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
let project = workspace.read(cx).project().downgrade();
let context_store = cx.new(|_cx| ContextStore::new(project.clone(), thread_store.clone()));
let codegen = cx.new(|cx| {
BufferCodegen::new(
@@ -544,6 +562,8 @@ impl InlineAssistant {
range.clone(),
initial_transaction_id,
context_store.clone(),
project,
prompt_store,
self.telemetry.clone(),
self.prompt_builder.clone(),
cx,
@@ -560,7 +580,7 @@ impl InlineAssistant {
codegen.clone(),
self.fs.clone(),
context_store,
workspace.clone(),
workspace.downgrade(),
thread_store,
window,
cx,
@@ -587,7 +607,7 @@ impl InlineAssistant {
end_block_id,
range,
codegen.clone(),
workspace.clone(),
workspace.downgrade(),
window,
cx,
),
@@ -619,14 +639,14 @@ impl InlineAssistant {
BlockProperties {
style: BlockStyle::Sticky,
placement: BlockPlacement::Above(range.start),
height: prompt_editor_height,
height: Some(prompt_editor_height),
render: build_assist_editor_renderer(prompt_editor),
priority: 0,
},
BlockProperties {
style: BlockStyle::Sticky,
placement: BlockPlacement::Below(range.end),
height: 0,
height: None,
render: Arc::new(|cx| {
v_flex()
.h_full()
@@ -974,7 +994,7 @@ impl InlineAssistant {
let active_alternative = assist.codegen.read(cx).active_alternative().clone();
let message_id = active_alternative.read(cx).message_id.clone();
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
if let Some(model) = LanguageModelRegistry::read_global(cx).inline_assistant_model() {
let language_name = assist.editor.upgrade().and_then(|editor| {
let multibuffer = editor.read(cx).buffer().read(cx);
let snapshot = multibuffer.snapshot(cx);
@@ -985,7 +1005,7 @@ impl InlineAssistant {
.map(|language| language.name())
});
report_assistant_event(
AssistantEvent {
AssistantEventData {
conversation_id: None,
kind: AssistantKind::Inline,
message_id,
@@ -994,15 +1014,15 @@ impl InlineAssistant {
} else {
AssistantPhase::Accepted
},
model: model.telemetry_id(),
model_provider: model.provider_id().to_string(),
model: model.model.telemetry_id(),
model_provider: model.model.provider_id().to_string(),
response_latency: None,
error_message: None,
language_name: language_name.map(|name| name.to_proto()),
},
Some(self.telemetry.clone()),
cx.http_client(),
model.api_key(cx),
model.model.api_key(cx),
cx.background_executor(),
);
}
@@ -1215,9 +1235,15 @@ impl InlineAssistant {
self.prompt_history.pop_front();
}
let Some(ConfiguredModel { model, .. }) =
LanguageModelRegistry::read_global(cx).inline_assistant_model()
else {
return;
};
assist
.codegen
.update(cx, |codegen, cx| codegen.start(user_prompt, cx))
.update(cx, |codegen, cx| codegen.start(model, user_prompt, cx))
.log_err();
}
@@ -1315,7 +1341,7 @@ impl InlineAssistant {
editor.highlight_rows::<InlineAssist>(
row_range,
cx.theme().status().info_background,
false,
Default::default(),
cx,
);
}
@@ -1363,10 +1389,7 @@ impl InlineAssistant {
multi_buffer.update(cx, |multi_buffer, cx| {
multi_buffer.push_excerpts(
old_buffer.clone(),
Some(ExcerptRange {
context: buffer_start..buffer_end,
primary: None,
}),
Some(ExcerptRange::new(buffer_start..buffer_end)),
cx,
);
});
@@ -1383,7 +1406,7 @@ impl InlineAssistant {
editor.highlight_rows::<DeletedLines>(
Anchor::min()..Anchor::max(),
cx.theme().status().deleted_background,
false,
Default::default(),
cx,
);
editor
@@ -1393,7 +1416,7 @@ impl InlineAssistant {
deleted_lines_editor.update(cx, |editor, cx| editor.max_point(cx).row().0 + 1);
new_blocks.push(BlockProperties {
placement: BlockPlacement::Above(new_row),
height,
height: Some(height),
style: BlockStyle::Flex,
render: Arc::new(move |cx| {
div()
@@ -1779,7 +1802,9 @@ impl CodeActionProvider for AssistantCodeActionProvider {
let editor = self.editor.clone();
let workspace = self.workspace.clone();
let thread_store = self.thread_store.clone();
let prompt_store = PromptStore::global(cx);
window.spawn(cx, async move |cx| {
let workspace = workspace.upgrade().context("workspace was released")?;
let editor = editor.upgrade().context("editor was released")?;
let range = editor
.update(cx, |editor, cx| {
@@ -1818,6 +1843,7 @@ impl CodeActionProvider for AssistantCodeActionProvider {
})?
.context("invalid range")?;
let prompt_store = prompt_store.await.ok();
cx.update_global(|assistant: &mut InlineAssistant, window, cx| {
let assist_id = assistant.suggest_assist(
&editor,
@@ -1826,6 +1852,7 @@ impl CodeActionProvider for AssistantCodeActionProvider {
None,
true,
workspace,
prompt_store,
thread_store,
window,
cx,

View File

@@ -13,14 +13,14 @@ use editor::{
Editor, EditorElement, EditorEvent, EditorMode, EditorStyle, GutterDimensions, MultiBuffer,
actions::{MoveDown, MoveUp},
};
use feature_flags::{FeatureFlagAppExt as _, ZedPro};
use feature_flags::{FeatureFlagAppExt as _, ZedProFeatureFlag};
use fs::Fs;
use gpui::{
AnyElement, App, ClickEvent, Context, CursorStyle, Entity, EventEmitter, FocusHandle,
Focusable, FontWeight, Subscription, TextStyle, WeakEntity, Window, anchored, deferred, point,
};
use language_model::{LanguageModel, LanguageModelRegistry};
use language_model_selector::ToggleModelSelector;
use language_model_selector::{ModelType, ToggleModelSelector};
use parking_lot::Mutex;
use settings::Settings;
use std::cmp;
@@ -132,7 +132,7 @@ impl<T: 'static> Render for PromptEditor<T> {
let error_message = SharedString::from(error.to_string());
if error.error_code() == proto::ErrorCode::RateLimitExceeded
&& cx.has_flag::<ZedPro>()
&& cx.has_flag::<ZedProFeatureFlag>()
{
el.child(
v_flex()
@@ -582,7 +582,7 @@ impl<T: 'static> PromptEditor<T> {
let disabled = matches!(codegen.status(cx), CodegenStatus::Idle);
let model_registry = LanguageModelRegistry::read_global(cx);
let default_model = model_registry.active_model();
let default_model = model_registry.default_model().map(|default| default.model);
let alternative_models = model_registry.inline_alternative_models();
let get_model_name = |index: usize| -> String {
@@ -890,6 +890,7 @@ impl PromptEditor<BufferCodegen> {
fs,
model_selector_menu_handle,
prompt_editor.focus_handle(cx),
ModelType::InlineAssistant,
window,
cx,
)
@@ -930,7 +931,7 @@ impl PromptEditor<BufferCodegen> {
.update(cx, |editor, _| editor.set_read_only(false));
}
CodegenStatus::Error(error) => {
if cx.has_flag::<ZedPro>()
if cx.has_flag::<ZedProFeatureFlag>()
&& error.error_code() == proto::ErrorCode::RateLimitExceeded
&& !dismissed_rate_limit_notice()
{
@@ -1042,6 +1043,7 @@ impl PromptEditor<TerminalCodegen> {
fs,
model_selector_menu_handle.clone(),
prompt_editor.focus_handle(cx),
ModelType::InlineAssistant,
window,
cx,
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,13 @@
use std::sync::Arc;
use assistant_settings::{AgentProfile, AssistantSettings};
use assistant_settings::{AgentProfile, AgentProfileId, AssistantSettings};
use fs::Fs;
use gpui::{Action, Entity, FocusHandle, Subscription, WeakEntity, prelude::*};
use indexmap::IndexMap;
use language_model::LanguageModelRegistry;
use settings::{Settings as _, SettingsStore, update_settings_file};
use ui::{
ButtonLike, ContextMenu, ContextMenuEntry, KeyBinding, PopoverMenu, PopoverMenuHandle,
ButtonLike, ContextMenu, ContextMenuEntry, KeyBinding, PopoverMenu, PopoverMenuHandle, Tooltip,
prelude::*,
};
use util::ResultExt as _;
@@ -14,7 +15,7 @@ use util::ResultExt as _;
use crate::{ManageProfiles, ThreadStore, ToggleProfileSelector};
pub struct ProfileSelector {
profiles: IndexMap<Arc<str>, AgentProfile>,
profiles: IndexMap<AgentProfileId, AgentProfile>,
fs: Arc<dyn Fs>,
thread_store: WeakEntity<ThreadStore>,
focus_handle: FocusHandle,
@@ -85,7 +86,7 @@ impl ProfileSelector {
thread_store
.update(cx, |this, cx| {
this.load_profile_by_id(&profile_id, cx);
this.load_profile_by_id(profile_id.clone(), cx);
})
.log_err();
}
@@ -127,7 +128,12 @@ impl Render for ProfileSelector {
.map(|profile| profile.name.clone())
.unwrap_or_else(|| "Unknown".into());
let icon = match profile_id.as_ref() {
let model_registry = LanguageModelRegistry::read_global(cx);
let supports_tools = model_registry
.default_model()
.map_or(false, |default| default.model.supports_tools());
let icon = match profile_id.as_str() {
"write" => IconName::Pencil,
"ask" => IconName::MessageBubbles,
_ => IconName::UserRoundPen,
@@ -139,7 +145,7 @@ impl Render for ProfileSelector {
.menu(move |window, cx| {
Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
})
.trigger(
.trigger(if supports_tools {
ButtonLike::new("profile-selector-button").child(
h_flex()
.gap_1()
@@ -164,8 +170,19 @@ impl Render for ProfileSelector {
)
.map(|kb| kb.size(rems_from_px(10.)))
})),
),
)
)
} else {
ButtonLike::new("tools-not-supported-button")
.disabled(true)
.child(
h_flex().gap_1().child(
Label::new("No Tools")
.size(LabelSize::Small)
.color(Color::Muted),
),
)
.tooltip(Tooltip::text("The current model does not support tools."))
})
.anchor(gpui::Corner::BottomLeft)
.with_handle(self.menu_handle.clone())
}

View File

@@ -2,9 +2,11 @@ use crate::inline_prompt_editor::CodegenStatus;
use client::telemetry::Telemetry;
use futures::{SinkExt, StreamExt, channel::mpsc};
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Task};
use language_model::{LanguageModelRegistry, LanguageModelRequest, report_assistant_event};
use language_model::{
ConfiguredModel, LanguageModelRegistry, LanguageModelRequest, report_assistant_event,
};
use std::{sync::Arc, time::Instant};
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
use telemetry_events::{AssistantEventData, AssistantKind, AssistantPhase};
use terminal::Terminal;
pub struct TerminalCodegen {
@@ -30,8 +32,10 @@ impl TerminalCodegen {
}
}
pub fn start(&mut self, prompt: LanguageModelRequest, cx: &mut Context<Self>) {
let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
pub fn start(&mut self, prompt_task: Task<LanguageModelRequest>, cx: &mut Context<Self>) {
let Some(ConfiguredModel { model, .. }) =
LanguageModelRegistry::read_global(cx).inline_assistant_model()
else {
return;
};
@@ -41,6 +45,7 @@ impl TerminalCodegen {
self.status = CodegenStatus::Pending;
self.transaction = Some(TerminalTransaction::start(self.terminal.clone()));
self.generation = cx.spawn(async move |this, cx| {
let prompt = prompt_task.await;
let model_telemetry_id = model.telemetry_id();
let model_provider_id = model.provider_id();
let response = model.stream_completion_text(prompt, &cx).await;
@@ -75,7 +80,7 @@ impl TerminalCodegen {
let error_message = result.as_ref().err().map(|error| error.to_string());
report_assistant_event(
AssistantEvent {
AssistantEventData {
conversation_id: None,
kind: AssistantKind::InlineTerminal,
message_id,

View File

@@ -1,4 +1,4 @@
use crate::context::attach_context_to_message;
use crate::context::load_context;
use crate::context_store::ContextStore;
use crate::inline_prompt_editor::{
CodegenStatus, PromptEditor, PromptEditorEvent, TerminalInlineAssistId,
@@ -10,15 +10,16 @@ use client::telemetry::Telemetry;
use collections::{HashMap, VecDeque};
use editor::{MultiBuffer, actions::SelectAll};
use fs::Fs;
use gpui::{App, Entity, Focusable, Global, Subscription, UpdateGlobal, WeakEntity};
use gpui::{App, Entity, Focusable, Global, Subscription, Task, UpdateGlobal, WeakEntity};
use language::Buffer;
use language_model::{
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
report_assistant_event,
ConfiguredModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
Role, report_assistant_event,
};
use prompt_store::PromptBuilder;
use project::Project;
use prompt_store::{PromptBuilder, PromptStore};
use std::sync::Arc;
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
use telemetry_events::{AssistantEventData, AssistantKind, AssistantPhase};
use terminal_view::TerminalView;
use ui::prelude::*;
use util::ResultExt;
@@ -67,6 +68,8 @@ impl TerminalInlineAssistant {
&mut self,
terminal_view: &Entity<TerminalView>,
workspace: WeakEntity<Workspace>,
project: WeakEntity<Project>,
prompt_store: Option<Entity<PromptStore>>,
thread_store: Option<WeakEntity<ThreadStore>>,
window: &mut Window,
cx: &mut App,
@@ -75,7 +78,7 @@ impl TerminalInlineAssistant {
let assist_id = self.next_assist_id.post_inc();
let prompt_buffer =
cx.new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(String::new(), cx)), cx));
let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
let context_store = cx.new(|_cx| ContextStore::new(project, thread_store.clone()));
let codegen = cx.new(|_| TerminalCodegen::new(terminal, self.telemetry.clone()));
let prompt_editor = cx.new(|cx| {
@@ -107,6 +110,7 @@ impl TerminalInlineAssistant {
prompt_editor,
workspace.clone(),
context_store,
prompt_store,
window,
cx,
);
@@ -194,11 +198,11 @@ impl TerminalInlineAssistant {
.log_err();
let codegen = assist.codegen.clone();
let Some(request) = self.request_for_inline_assist(assist_id, cx).log_err() else {
let Some(request_task) = self.request_for_inline_assist(assist_id, cx).log_err() else {
return;
};
codegen.update(cx, |codegen, cx| codegen.start(request, cx));
codegen.update(cx, |codegen, cx| codegen.start(request_task, cx));
}
fn stop_assist(&mut self, assist_id: TerminalInlineAssistId, cx: &mut App) {
@@ -215,7 +219,7 @@ impl TerminalInlineAssistant {
&self,
assist_id: TerminalInlineAssistId,
cx: &mut App,
) -> Result<LanguageModelRequest> {
) -> Result<Task<LanguageModelRequest>> {
let assist = self.assists.get(&assist_id).context("invalid assist")?;
let shell = std::env::var("SHELL").ok();
@@ -244,26 +248,40 @@ impl TerminalInlineAssistant {
&latest_output,
)?;
let mut request_message = LanguageModelRequestMessage {
role: Role::User,
content: vec![],
cache: false,
};
let contexts = assist
.context_store
.read(cx)
.context()
.cloned()
.collect::<Vec<_>>();
let context_load_task = assist.workspace.update(cx, |workspace, cx| {
let project = workspace.project();
load_context(contexts, project, &assist.prompt_store, cx)
})?;
attach_context_to_message(
&mut request_message,
assist.context_store.read(cx).context().iter(),
cx,
);
Ok(cx.background_spawn(async move {
let mut request_message = LanguageModelRequestMessage {
role: Role::User,
content: vec![],
cache: false,
};
request_message.content.push(prompt.into());
context_load_task
.await
.loaded_context
.add_to_request_message(&mut request_message);
Ok(LanguageModelRequest {
messages: vec![request_message],
tools: Vec::new(),
stop: Vec::new(),
temperature: None,
})
request_message.content.push(prompt.into());
LanguageModelRequest {
thread_id: None,
prompt_id: None,
messages: vec![request_message],
tools: Vec::new(),
stop: Vec::new(),
temperature: None,
}
}))
}
fn finish_assist(
@@ -285,11 +303,13 @@ impl TerminalInlineAssistant {
})
.log_err();
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
if let Some(ConfiguredModel { model, .. }) =
LanguageModelRegistry::read_global(cx).inline_assistant_model()
{
let codegen = assist.codegen.read(cx);
let executor = cx.background_executor().clone();
report_assistant_event(
AssistantEvent {
AssistantEventData {
conversation_id: None,
kind: AssistantKind::InlineTerminal,
message_id: codegen.message_id.clone(),
@@ -374,6 +394,7 @@ struct TerminalInlineAssist {
codegen: Entity<TerminalCodegen>,
workspace: WeakEntity<Workspace>,
context_store: Entity<ContextStore>,
prompt_store: Option<Entity<PromptStore>>,
_subscriptions: Vec<Subscription>,
}
@@ -384,6 +405,7 @@ impl TerminalInlineAssist {
prompt_editor: Entity<PromptEditor<TerminalCodegen>>,
workspace: WeakEntity<Workspace>,
context_store: Entity<ContextStore>,
prompt_store: Option<Entity<PromptStore>>,
window: &mut Window,
cx: &mut App,
) -> Self {
@@ -394,6 +416,7 @@ impl TerminalInlineAssist {
codegen: codegen.clone(),
workspace: workspace.clone(),
context_store,
prompt_store,
_subscriptions: vec![
window.subscribe(&prompt_editor, cx, |prompt_editor, event, window, cx| {
TerminalInlineAssistant::update_global(cx, |this, cx| {

2717
crates/agent/src/thread.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,628 @@
use std::sync::Arc;
use assistant_context_editor::SavedContextMetadata;
use editor::{Editor, EditorEvent};
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
App, Entity, FocusHandle, Focusable, ScrollStrategy, Stateful, Task, UniformListScrollHandle,
WeakEntity, Window, uniform_list,
};
use time::{OffsetDateTime, UtcOffset};
use ui::{
HighlightedLabel, IconButtonShape, ListItem, ListItemSpacing, Scrollbar, ScrollbarState,
Tooltip, prelude::*,
};
use util::ResultExt;
use crate::history_store::{HistoryEntry, HistoryStore};
use crate::thread_store::SerializedThreadMetadata;
use crate::{AssistantPanel, RemoveSelectedThread};
pub struct ThreadHistory {
assistant_panel: WeakEntity<AssistantPanel>,
history_store: Entity<HistoryStore>,
scroll_handle: UniformListScrollHandle,
selected_index: usize,
search_query: SharedString,
search_editor: Entity<Editor>,
all_entries: Arc<Vec<HistoryEntry>>,
matches: Vec<StringMatch>,
_subscriptions: Vec<gpui::Subscription>,
_search_task: Option<Task<()>>,
scrollbar_visibility: bool,
scrollbar_state: ScrollbarState,
}
impl ThreadHistory {
pub(crate) fn new(
assistant_panel: WeakEntity<AssistantPanel>,
history_store: Entity<HistoryStore>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let search_editor = cx.new(|cx| {
let mut editor = Editor::single_line(window, cx);
editor.set_placeholder_text("Search threads...", cx);
editor
});
let search_editor_subscription =
cx.subscribe(&search_editor, |this, search_editor, event, cx| {
if let EditorEvent::BufferEdited = event {
let query = search_editor.read(cx).text(cx);
this.search_query = query.into();
this.update_search(cx);
}
});
let entries: Arc<Vec<_>> = history_store
.update(cx, |store, cx| store.entries(cx))
.into();
let history_store_subscription = cx.observe(&history_store, |this, _, cx| {
this.update_all_entries(cx);
});
let scroll_handle = UniformListScrollHandle::default();
let scrollbar_state = ScrollbarState::new(scroll_handle.clone());
Self {
assistant_panel,
history_store,
scroll_handle,
selected_index: 0,
search_query: SharedString::new_static(""),
all_entries: entries,
matches: Vec::new(),
search_editor,
_subscriptions: vec![search_editor_subscription, history_store_subscription],
_search_task: None,
scrollbar_visibility: true,
scrollbar_state,
}
}
fn update_all_entries(&mut self, cx: &mut Context<Self>) {
self.all_entries = self
.history_store
.update(cx, |store, cx| store.entries(cx))
.into();
self.matches.clear();
self.update_search(cx);
}
fn update_search(&mut self, cx: &mut Context<Self>) {
self._search_task.take();
if self.has_search_query() {
self.perform_search(cx);
} else {
self.matches.clear();
self.set_selected_index(0, cx);
cx.notify();
}
}
fn perform_search(&mut self, cx: &mut Context<Self>) {
let query = self.search_query.clone();
let all_entries = self.all_entries.clone();
let task = cx.spawn(async move |this, cx| {
let executor = cx.background_executor().clone();
let matches = cx
.background_spawn(async move {
let mut candidates = Vec::with_capacity(all_entries.len());
for (idx, entry) in all_entries.iter().enumerate() {
match entry {
HistoryEntry::Thread(thread) => {
candidates.push(StringMatchCandidate::new(idx, &thread.summary));
}
HistoryEntry::Context(context) => {
candidates.push(StringMatchCandidate::new(idx, &context.title));
}
}
}
const MAX_MATCHES: usize = 100;
fuzzy::match_strings(
&candidates,
&query,
false,
MAX_MATCHES,
&Default::default(),
executor,
)
.await
})
.await;
this.update(cx, |this, cx| {
this.matches = matches;
this.set_selected_index(0, cx);
cx.notify();
})
.log_err();
});
self._search_task = Some(task);
}
fn has_search_query(&self) -> bool {
!self.search_query.is_empty()
}
fn matched_count(&self) -> usize {
if self.has_search_query() {
self.matches.len()
} else {
self.all_entries.len()
}
}
fn get_match(&self, ix: usize) -> Option<&HistoryEntry> {
if self.has_search_query() {
self.matches
.get(ix)
.and_then(|m| self.all_entries.get(m.candidate_id))
} else {
self.all_entries.get(ix)
}
}
pub fn select_previous(
&mut self,
_: &menu::SelectPrevious,
_window: &mut Window,
cx: &mut Context<Self>,
) {
let count = self.matched_count();
if count > 0 {
if self.selected_index == 0 {
self.set_selected_index(count - 1, cx);
} else {
self.set_selected_index(self.selected_index - 1, cx);
}
}
}
pub fn select_next(
&mut self,
_: &menu::SelectNext,
_window: &mut Window,
cx: &mut Context<Self>,
) {
let count = self.matched_count();
if count > 0 {
if self.selected_index == count - 1 {
self.set_selected_index(0, cx);
} else {
self.set_selected_index(self.selected_index + 1, cx);
}
}
}
fn select_first(
&mut self,
_: &menu::SelectFirst,
_window: &mut Window,
cx: &mut Context<Self>,
) {
let count = self.matched_count();
if count > 0 {
self.set_selected_index(0, cx);
}
}
fn select_last(&mut self, _: &menu::SelectLast, _window: &mut Window, cx: &mut Context<Self>) {
let count = self.matched_count();
if count > 0 {
self.set_selected_index(count - 1, cx);
}
}
fn set_selected_index(&mut self, index: usize, cx: &mut Context<Self>) {
self.selected_index = index;
self.scroll_handle
.scroll_to_item(index, ScrollStrategy::Top);
cx.notify();
}
fn render_scrollbar(&self, cx: &mut Context<Self>) -> Option<Stateful<Div>> {
if !(self.scrollbar_visibility || self.scrollbar_state.is_dragging()) {
return None;
}
Some(
div()
.occlude()
.id("thread-history-scroll")
.h_full()
.bg(cx.theme().colors().panel_background.opacity(0.8))
.border_l_1()
.border_color(cx.theme().colors().border_variant)
.absolute()
.right_1()
.top_0()
.bottom_0()
.w_4()
.pl_1()
.cursor_default()
.on_mouse_move(cx.listener(|_, _, _window, cx| {
cx.notify();
cx.stop_propagation()
}))
.on_hover(|_, _window, cx| {
cx.stop_propagation();
})
.on_any_mouse_down(|_, _window, cx| {
cx.stop_propagation();
})
.on_scroll_wheel(cx.listener(|_, _, _window, cx| {
cx.notify();
}))
.children(Scrollbar::vertical(self.scrollbar_state.clone())),
)
}
fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
if let Some(entry) = self.get_match(self.selected_index) {
let task_result = match entry {
HistoryEntry::Thread(thread) => self
.assistant_panel
.update(cx, move |this, cx| this.open_thread(&thread.id, window, cx)),
HistoryEntry::Context(context) => {
self.assistant_panel.update(cx, move |this, cx| {
this.open_saved_prompt_editor(context.path.clone(), window, cx)
})
}
};
if let Some(task) = task_result.log_err() {
task.detach_and_log_err(cx);
};
cx.notify();
}
}
fn remove_selected_thread(
&mut self,
_: &RemoveSelectedThread,
_window: &mut Window,
cx: &mut Context<Self>,
) {
if let Some(entry) = self.get_match(self.selected_index) {
let task_result = match entry {
HistoryEntry::Thread(thread) => self
.assistant_panel
.update(cx, |this, cx| this.delete_thread(&thread.id, cx)),
HistoryEntry::Context(context) => self
.assistant_panel
.update(cx, |this, cx| this.delete_context(context.path.clone(), cx)),
};
if let Some(task) = task_result.log_err() {
task.detach_and_log_err(cx);
};
cx.notify();
}
}
}
impl Focusable for ThreadHistory {
fn focus_handle(&self, cx: &App) -> FocusHandle {
self.search_editor.focus_handle(cx)
}
}
impl Render for ThreadHistory {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let selected_index = self.selected_index;
v_flex()
.key_context("ThreadHistory")
.size_full()
.on_action(cx.listener(Self::select_previous))
.on_action(cx.listener(Self::select_next))
.on_action(cx.listener(Self::select_first))
.on_action(cx.listener(Self::select_last))
.on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::remove_selected_thread))
.when(!self.all_entries.is_empty(), |parent| {
parent.child(
h_flex()
.h(px(41.)) // Match the toolbar perfectly
.w_full()
.py_1()
.px_2()
.gap_2()
.justify_between()
.border_b_1()
.border_color(cx.theme().colors().border)
.child(
Icon::new(IconName::MagnifyingGlass)
.color(Color::Muted)
.size(IconSize::Small),
)
.child(self.search_editor.clone()),
)
})
.child({
let view = v_flex()
.id("list-container")
.relative()
.overflow_hidden()
.flex_grow();
if self.all_entries.is_empty() {
view.justify_center()
.child(
h_flex().w_full().justify_center().child(
Label::new("You don't have any past threads yet.")
.size(LabelSize::Small),
),
)
} else if self.has_search_query() && self.matches.is_empty() {
view.justify_center().child(
h_flex().w_full().justify_center().child(
Label::new("No threads match your search.").size(LabelSize::Small),
),
)
} else {
view.pr_5()
.child(
uniform_list(
cx.entity().clone(),
"thread-history",
self.matched_count(),
move |history, range, _window, _cx| {
let range_start = range.start;
let assistant_panel = history.assistant_panel.clone();
let render_item = |index: usize,
entry: &HistoryEntry,
highlight_positions: Vec<usize>|
-> Div {
h_flex().w_full().pb_1().child(match entry {
HistoryEntry::Thread(thread) => PastThread::new(
thread.clone(),
assistant_panel.clone(),
selected_index == index + range_start,
highlight_positions,
)
.into_any_element(),
HistoryEntry::Context(context) => PastContext::new(
context.clone(),
assistant_panel.clone(),
selected_index == index + range_start,
highlight_positions,
)
.into_any_element(),
})
};
if history.has_search_query() {
history.matches[range]
.iter()
.enumerate()
.filter_map(|(index, m)| {
history.all_entries.get(m.candidate_id).map(
|entry| {
render_item(
index,
entry,
m.positions.clone(),
)
},
)
})
.collect()
} else {
history.all_entries[range]
.iter()
.enumerate()
.map(|(index, entry)| render_item(index, entry, vec![]))
.collect()
}
},
)
.p_1()
.track_scroll(self.scroll_handle.clone())
.flex_grow(),
)
.when_some(self.render_scrollbar(cx), |div, scrollbar| {
div.child(scrollbar)
})
}
})
}
}
#[derive(IntoElement)]
pub struct PastThread {
thread: SerializedThreadMetadata,
assistant_panel: WeakEntity<AssistantPanel>,
selected: bool,
highlight_positions: Vec<usize>,
}
impl PastThread {
pub fn new(
thread: SerializedThreadMetadata,
assistant_panel: WeakEntity<AssistantPanel>,
selected: bool,
highlight_positions: Vec<usize>,
) -> Self {
Self {
thread,
assistant_panel,
selected,
highlight_positions,
}
}
}
impl RenderOnce for PastThread {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let summary = self.thread.summary;
let thread_timestamp = time_format::format_localized_timestamp(
OffsetDateTime::from_unix_timestamp(self.thread.updated_at.timestamp()).unwrap(),
OffsetDateTime::now_utc(),
self.assistant_panel
.update(cx, |this, _cx| this.local_timezone())
.unwrap_or(UtcOffset::UTC),
time_format::TimestampFormat::EnhancedAbsolute,
);
ListItem::new(SharedString::from(self.thread.id.to_string()))
.rounded()
.toggle_state(self.selected)
.spacing(ListItemSpacing::Sparse)
.start_slot(
div().max_w_4_5().child(
HighlightedLabel::new(summary, self.highlight_positions)
.size(LabelSize::Small)
.truncate(),
),
)
.end_slot(
h_flex()
.gap_1p5()
.child(
Label::new(thread_timestamp)
.color(Color::Muted)
.size(LabelSize::XSmall),
)
.child(
IconButton::new("delete", IconName::TrashAlt)
.shape(IconButtonShape::Square)
.icon_size(IconSize::XSmall)
.icon_color(Color::Muted)
.tooltip(move |window, cx| {
Tooltip::for_action("Delete", &RemoveSelectedThread, window, cx)
})
.on_click({
let assistant_panel = self.assistant_panel.clone();
let id = self.thread.id.clone();
move |_event, _window, cx| {
assistant_panel
.update(cx, |this, cx| {
this.delete_thread(&id, cx).detach_and_log_err(cx);
})
.ok();
}
}),
),
)
.on_click({
let assistant_panel = self.assistant_panel.clone();
let id = self.thread.id.clone();
move |_event, window, cx| {
assistant_panel
.update(cx, |this, cx| {
this.open_thread(&id, window, cx).detach_and_log_err(cx);
})
.ok();
}
})
}
}
#[derive(IntoElement)]
pub struct PastContext {
context: SavedContextMetadata,
assistant_panel: WeakEntity<AssistantPanel>,
selected: bool,
highlight_positions: Vec<usize>,
}
impl PastContext {
pub fn new(
context: SavedContextMetadata,
assistant_panel: WeakEntity<AssistantPanel>,
selected: bool,
highlight_positions: Vec<usize>,
) -> Self {
Self {
context,
assistant_panel,
selected,
highlight_positions,
}
}
}
impl RenderOnce for PastContext {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let summary = self.context.title;
let context_timestamp = time_format::format_localized_timestamp(
OffsetDateTime::from_unix_timestamp(self.context.mtime.timestamp()).unwrap(),
OffsetDateTime::now_utc(),
self.assistant_panel
.update(cx, |this, _cx| this.local_timezone())
.unwrap_or(UtcOffset::UTC),
time_format::TimestampFormat::EnhancedAbsolute,
);
ListItem::new(SharedString::from(
self.context.path.to_string_lossy().to_string(),
))
.rounded()
.toggle_state(self.selected)
.spacing(ListItemSpacing::Sparse)
.start_slot(
div().max_w_4_5().child(
HighlightedLabel::new(summary, self.highlight_positions)
.size(LabelSize::Small)
.truncate(),
),
)
.end_slot(
h_flex()
.gap_1p5()
.child(
Label::new(context_timestamp)
.color(Color::Muted)
.size(LabelSize::XSmall),
)
.child(
IconButton::new("delete", IconName::TrashAlt)
.shape(IconButtonShape::Square)
.icon_size(IconSize::XSmall)
.icon_color(Color::Muted)
.tooltip(move |window, cx| {
Tooltip::for_action("Delete", &RemoveSelectedThread, window, cx)
})
.on_click({
let assistant_panel = self.assistant_panel.clone();
let path = self.context.path.clone();
move |_event, _window, cx| {
assistant_panel
.update(cx, |this, cx| {
this.delete_context(path.clone(), cx)
.detach_and_log_err(cx);
})
.ok();
}
}),
),
)
.on_click({
let assistant_panel = self.assistant_panel.clone();
let path = self.context.path.clone();
move |_event, window, cx| {
assistant_panel
.update(cx, |this, cx| {
this.open_saved_prompt_editor(path.clone(), window, cx)
.detach_and_log_err(cx);
})
.ok();
}
})
}
}

View File

@@ -0,0 +1,933 @@
use std::borrow::Cow;
use std::cell::{Ref, RefCell};
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::Arc;
use anyhow::{Context as _, Result, anyhow};
use assistant_settings::{AgentProfile, AgentProfileId, AssistantSettings};
use assistant_tool::{ToolId, ToolSource, ToolWorkingSet};
use chrono::{DateTime, Utc};
use collections::HashMap;
use context_server::manager::ContextServerManager;
use context_server::{ContextServerFactoryRegistry, ContextServerTool};
use futures::channel::{mpsc, oneshot};
use futures::future::{self, BoxFuture, Shared};
use futures::{FutureExt as _, StreamExt as _};
use gpui::{
App, BackgroundExecutor, Context, Entity, EventEmitter, Global, ReadGlobal, SharedString,
Subscription, Task, prelude::*,
};
use heed::Database;
use heed::types::SerdeBincode;
use language_model::{LanguageModelToolUseId, Role, TokenUsage};
use project::{Project, ProjectItem, ProjectPath, Worktree};
use prompt_store::{
ProjectContext, PromptBuilder, PromptId, PromptStore, PromptsUpdatedEvent, RulesFileContext,
UserRulesContext, WorktreeContext,
};
use serde::{Deserialize, Serialize};
use settings::{Settings as _, SettingsStore};
use util::ResultExt as _;
use crate::thread::{
DetailedSummaryState, ExceededWindowError, MessageId, ProjectSnapshot, Thread, ThreadId,
};
const RULES_FILE_NAMES: [&'static str; 6] = [
".rules",
".cursorrules",
".windsurfrules",
".clinerules",
".github/copilot-instructions.md",
"CLAUDE.md",
];
pub fn init(cx: &mut App) {
ThreadsDatabase::init(cx);
}
/// A system prompt shared by all threads created by this ThreadStore
#[derive(Clone, Default)]
pub struct SharedProjectContext(Rc<RefCell<Option<ProjectContext>>>);
impl SharedProjectContext {
pub fn borrow(&self) -> Ref<Option<ProjectContext>> {
self.0.borrow()
}
}
pub struct ThreadStore {
project: Entity<Project>,
tools: Entity<ToolWorkingSet>,
prompt_builder: Arc<PromptBuilder>,
prompt_store: Option<Entity<PromptStore>>,
context_server_manager: Entity<ContextServerManager>,
context_server_tool_ids: HashMap<Arc<str>, Vec<ToolId>>,
threads: Vec<SerializedThreadMetadata>,
project_context: SharedProjectContext,
reload_system_prompt_tx: mpsc::Sender<()>,
_reload_system_prompt_task: Task<()>,
_subscriptions: Vec<Subscription>,
}
pub struct RulesLoadingError {
pub message: SharedString,
}
impl EventEmitter<RulesLoadingError> for ThreadStore {}
impl ThreadStore {
pub fn load(
project: Entity<Project>,
tools: Entity<ToolWorkingSet>,
prompt_store: Option<Entity<PromptStore>>,
prompt_builder: Arc<PromptBuilder>,
cx: &mut App,
) -> Task<Result<Entity<Self>>> {
cx.spawn(async move |cx| {
let (thread_store, ready_rx) = cx.update(|cx| {
let mut option_ready_rx = None;
let thread_store = cx.new(|cx| {
let (thread_store, ready_rx) =
Self::new(project, tools, prompt_builder, prompt_store, cx);
option_ready_rx = Some(ready_rx);
thread_store
});
(thread_store, option_ready_rx.take().unwrap())
})?;
ready_rx.await?;
Ok(thread_store)
})
}
fn new(
project: Entity<Project>,
tools: Entity<ToolWorkingSet>,
prompt_builder: Arc<PromptBuilder>,
prompt_store: Option<Entity<PromptStore>>,
cx: &mut Context<Self>,
) -> (Self, oneshot::Receiver<()>) {
let context_server_factory_registry = ContextServerFactoryRegistry::default_global(cx);
let context_server_manager = cx.new(|cx| {
ContextServerManager::new(context_server_factory_registry, project.clone(), cx)
});
let mut subscriptions = vec![
cx.observe_global::<SettingsStore>(move |this: &mut Self, cx| {
this.load_default_profile(cx);
}),
cx.subscribe(&project, Self::handle_project_event),
];
if let Some(prompt_store) = prompt_store.as_ref() {
subscriptions.push(cx.subscribe(
prompt_store,
|this, _prompt_store, PromptsUpdatedEvent, _cx| {
this.enqueue_system_prompt_reload();
},
))
}
// This channel and task prevent concurrent and redundant loading of the system prompt.
let (reload_system_prompt_tx, mut reload_system_prompt_rx) = mpsc::channel(1);
let (ready_tx, ready_rx) = oneshot::channel();
let mut ready_tx = Some(ready_tx);
let reload_system_prompt_task = cx.spawn({
let prompt_store = prompt_store.clone();
async move |thread_store, cx| {
loop {
let Some(reload_task) = thread_store
.update(cx, |thread_store, cx| {
thread_store.reload_system_prompt(prompt_store.clone(), cx)
})
.ok()
else {
return;
};
reload_task.await;
if let Some(ready_tx) = ready_tx.take() {
ready_tx.send(()).ok();
}
reload_system_prompt_rx.next().await;
}
}
});
let this = Self {
project,
tools,
prompt_builder,
prompt_store,
context_server_manager,
context_server_tool_ids: HashMap::default(),
threads: Vec::new(),
project_context: SharedProjectContext::default(),
reload_system_prompt_tx,
_reload_system_prompt_task: reload_system_prompt_task,
_subscriptions: subscriptions,
};
this.load_default_profile(cx);
this.register_context_server_handlers(cx);
this.reload(cx).detach_and_log_err(cx);
(this, ready_rx)
}
fn handle_project_event(
&mut self,
_project: Entity<Project>,
event: &project::Event,
_cx: &mut Context<Self>,
) {
match event {
project::Event::WorktreeAdded(_) | project::Event::WorktreeRemoved(_) => {
self.enqueue_system_prompt_reload();
}
project::Event::WorktreeUpdatedEntries(_, items) => {
if items.iter().any(|(path, _, _)| {
RULES_FILE_NAMES
.iter()
.any(|name| path.as_ref() == Path::new(name))
}) {
self.enqueue_system_prompt_reload();
}
}
_ => {}
}
}
fn enqueue_system_prompt_reload(&mut self) {
self.reload_system_prompt_tx.try_send(()).ok();
}
// Note that this should only be called from `reload_system_prompt_task`.
fn reload_system_prompt(
&self,
prompt_store: Option<Entity<PromptStore>>,
cx: &mut Context<Self>,
) -> Task<()> {
let worktrees = self
.project
.read(cx)
.visible_worktrees(cx)
.collect::<Vec<_>>();
let worktree_tasks = worktrees
.into_iter()
.map(|worktree| {
Self::load_worktree_info_for_system_prompt(worktree, self.project.clone(), cx)
})
.collect::<Vec<_>>();
let default_user_rules_task = match prompt_store {
None => Task::ready(vec![]),
Some(prompt_store) => prompt_store.read_with(cx, |prompt_store, cx| {
let prompts = prompt_store.default_prompt_metadata();
let load_tasks = prompts.into_iter().map(|prompt_metadata| {
let contents = prompt_store.load(prompt_metadata.id, cx);
async move { (contents.await, prompt_metadata) }
});
cx.background_spawn(future::join_all(load_tasks))
}),
};
cx.spawn(async move |this, cx| {
let (worktrees, default_user_rules) =
future::join(future::join_all(worktree_tasks), default_user_rules_task).await;
let worktrees = worktrees
.into_iter()
.map(|(worktree, rules_error)| {
if let Some(rules_error) = rules_error {
this.update(cx, |_, cx| cx.emit(rules_error)).ok();
}
worktree
})
.collect::<Vec<_>>();
let default_user_rules = default_user_rules
.into_iter()
.flat_map(|(contents, prompt_metadata)| match contents {
Ok(contents) => Some(UserRulesContext {
uuid: match prompt_metadata.id {
PromptId::User { uuid } => uuid,
PromptId::EditWorkflow => return None,
},
title: prompt_metadata.title.map(|title| title.to_string()),
contents,
}),
Err(err) => {
this.update(cx, |_, cx| {
cx.emit(RulesLoadingError {
message: format!("{err:?}").into(),
});
})
.ok();
None
}
})
.collect::<Vec<_>>();
this.update(cx, |this, _cx| {
*this.project_context.0.borrow_mut() =
Some(ProjectContext::new(worktrees, default_user_rules));
})
.ok();
})
}
fn load_worktree_info_for_system_prompt(
worktree: Entity<Worktree>,
project: Entity<Project>,
cx: &mut App,
) -> Task<(WorktreeContext, Option<RulesLoadingError>)> {
let root_name = worktree.read(cx).root_name().into();
let rules_task = Self::load_worktree_rules_file(worktree, project, cx);
let Some(rules_task) = rules_task else {
return Task::ready((
WorktreeContext {
root_name,
rules_file: None,
},
None,
));
};
cx.spawn(async move |_| {
let (rules_file, rules_file_error) = match rules_task.await {
Ok(rules_file) => (Some(rules_file), None),
Err(err) => (
None,
Some(RulesLoadingError {
message: format!("{err}").into(),
}),
),
};
let worktree_info = WorktreeContext {
root_name,
rules_file,
};
(worktree_info, rules_file_error)
})
}
fn load_worktree_rules_file(
worktree: Entity<Worktree>,
project: Entity<Project>,
cx: &mut App,
) -> Option<Task<Result<RulesFileContext>>> {
let worktree_ref = worktree.read(cx);
let worktree_id = worktree_ref.id();
let selected_rules_file = RULES_FILE_NAMES
.into_iter()
.filter_map(|name| {
worktree_ref
.entry_for_path(name)
.filter(|entry| entry.is_file())
.map(|entry| entry.path.clone())
})
.next();
// Note that Cline supports `.clinerules` being a directory, but that is not currently
// supported. This doesn't seem to occur often in GitHub repositories.
selected_rules_file.map(|path_in_worktree| {
let project_path = ProjectPath {
worktree_id,
path: path_in_worktree.clone(),
};
let buffer_task =
project.update(cx, |project, cx| project.open_buffer(project_path, cx));
let rope_task = cx.spawn(async move |cx| {
buffer_task.await?.read_with(cx, |buffer, cx| {
let project_entry_id = buffer.entry_id(cx).context("buffer has no file")?;
anyhow::Ok((project_entry_id, buffer.as_rope().clone()))
})?
});
// Build a string from the rope on a background thread.
cx.background_spawn(async move {
let (project_entry_id, rope) = rope_task.await?;
anyhow::Ok(RulesFileContext {
path_in_worktree,
text: rope.to_string().trim().to_string(),
project_entry_id: project_entry_id.to_usize(),
})
})
})
}
pub fn context_server_manager(&self) -> Entity<ContextServerManager> {
self.context_server_manager.clone()
}
pub fn prompt_store(&self) -> &Option<Entity<PromptStore>> {
&self.prompt_store
}
pub fn tools(&self) -> Entity<ToolWorkingSet> {
self.tools.clone()
}
/// Returns the number of threads.
pub fn thread_count(&self) -> usize {
self.threads.len()
}
pub fn reverse_chronological_threads(&self) -> Vec<SerializedThreadMetadata> {
let mut threads = self.threads.iter().cloned().collect::<Vec<_>>();
threads.sort_unstable_by_key(|thread| std::cmp::Reverse(thread.updated_at));
threads
}
pub fn create_thread(&mut self, cx: &mut Context<Self>) -> Entity<Thread> {
cx.new(|cx| {
Thread::new(
self.project.clone(),
self.tools.clone(),
self.prompt_builder.clone(),
self.project_context.clone(),
cx,
)
})
}
pub fn open_thread(
&self,
id: &ThreadId,
cx: &mut Context<Self>,
) -> Task<Result<Entity<Thread>>> {
let id = id.clone();
let database_future = ThreadsDatabase::global_future(cx);
cx.spawn(async move |this, cx| {
let database = database_future.await.map_err(|err| anyhow!(err))?;
let thread = database
.try_find_thread(id.clone())
.await?
.ok_or_else(|| anyhow!("no thread found with ID: {id:?}"))?;
let thread = this.update(cx, |this, cx| {
cx.new(|cx| {
Thread::deserialize(
id.clone(),
thread,
this.project.clone(),
this.tools.clone(),
this.prompt_builder.clone(),
this.project_context.clone(),
cx,
)
})
})?;
Ok(thread)
})
}
pub fn save_thread(&self, thread: &Entity<Thread>, cx: &mut Context<Self>) -> Task<Result<()>> {
let (metadata, serialized_thread) =
thread.update(cx, |thread, cx| (thread.id().clone(), thread.serialize(cx)));
let database_future = ThreadsDatabase::global_future(cx);
cx.spawn(async move |this, cx| {
let serialized_thread = serialized_thread.await?;
let database = database_future.await.map_err(|err| anyhow!(err))?;
database.save_thread(metadata, serialized_thread).await?;
this.update(cx, |this, cx| this.reload(cx))?.await
})
}
pub fn delete_thread(&mut self, id: &ThreadId, cx: &mut Context<Self>) -> Task<Result<()>> {
let id = id.clone();
let database_future = ThreadsDatabase::global_future(cx);
cx.spawn(async move |this, cx| {
let database = database_future.await.map_err(|err| anyhow!(err))?;
database.delete_thread(id.clone()).await?;
this.update(cx, |this, cx| {
this.threads.retain(|thread| thread.id != id);
cx.notify();
})
})
}
pub fn reload(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
let database_future = ThreadsDatabase::global_future(cx);
cx.spawn(async move |this, cx| {
let threads = database_future
.await
.map_err(|err| anyhow!(err))?
.list_threads()
.await?;
this.update(cx, |this, cx| {
this.threads = threads;
cx.notify();
})
})
}
fn load_default_profile(&self, cx: &mut Context<Self>) {
let assistant_settings = AssistantSettings::get_global(cx);
self.load_profile_by_id(assistant_settings.default_profile.clone(), cx);
}
pub fn load_profile_by_id(&self, profile_id: AgentProfileId, cx: &mut Context<Self>) {
let assistant_settings = AssistantSettings::get_global(cx);
if let Some(profile) = assistant_settings.profiles.get(&profile_id) {
self.load_profile(profile.clone(), cx);
}
}
pub fn load_profile(&self, profile: AgentProfile, cx: &mut Context<Self>) {
self.tools.update(cx, |tools, cx| {
tools.disable_all_tools(cx);
tools.enable(
ToolSource::Native,
&profile
.tools
.iter()
.filter_map(|(tool, enabled)| enabled.then(|| tool.clone()))
.collect::<Vec<_>>(),
cx,
);
});
if profile.enable_all_context_servers {
for context_server in self.context_server_manager.read(cx).all_servers() {
self.tools.update(cx, |tools, cx| {
tools.enable_source(
ToolSource::ContextServer {
id: context_server.id().into(),
},
cx,
);
});
}
// Enable all the tools from all context servers, but disable the ones that are explicitly disabled
for (context_server_id, preset) in &profile.context_servers {
self.tools.update(cx, |tools, cx| {
tools.disable(
ToolSource::ContextServer {
id: context_server_id.clone().into(),
},
&preset
.tools
.iter()
.filter_map(|(tool, enabled)| (!enabled).then(|| tool.clone()))
.collect::<Vec<_>>(),
cx,
)
})
}
} else {
for (context_server_id, preset) in &profile.context_servers {
self.tools.update(cx, |tools, cx| {
tools.enable(
ToolSource::ContextServer {
id: context_server_id.clone().into(),
},
&preset
.tools
.iter()
.filter_map(|(tool, enabled)| enabled.then(|| tool.clone()))
.collect::<Vec<_>>(),
cx,
)
})
}
}
}
fn register_context_server_handlers(&self, cx: &mut Context<Self>) {
cx.subscribe(
&self.context_server_manager.clone(),
Self::handle_context_server_event,
)
.detach();
}
fn handle_context_server_event(
&mut self,
context_server_manager: Entity<ContextServerManager>,
event: &context_server::manager::Event,
cx: &mut Context<Self>,
) {
let tool_working_set = self.tools.clone();
match event {
context_server::manager::Event::ServerStarted { server_id } => {
if let Some(server) = context_server_manager.read(cx).get_server(server_id) {
let context_server_manager = context_server_manager.clone();
cx.spawn({
let server = server.clone();
let server_id = server_id.clone();
async move |this, cx| {
let Some(protocol) = server.client() else {
return;
};
if protocol.capable(context_server::protocol::ServerCapability::Tools) {
if let Some(tools) = protocol.list_tools().await.log_err() {
let tool_ids = tool_working_set
.update(cx, |tool_working_set, _| {
tools
.tools
.into_iter()
.map(|tool| {
log::info!(
"registering context server tool: {:?}",
tool.name
);
tool_working_set.insert(Arc::new(
ContextServerTool::new(
context_server_manager.clone(),
server.id(),
tool,
),
))
})
.collect::<Vec<_>>()
})
.log_err();
if let Some(tool_ids) = tool_ids {
this.update(cx, |this, cx| {
this.context_server_tool_ids
.insert(server_id, tool_ids);
this.load_default_profile(cx);
})
.log_err();
}
}
}
}
})
.detach();
}
}
context_server::manager::Event::ServerStopped { server_id } => {
if let Some(tool_ids) = self.context_server_tool_ids.remove(server_id) {
tool_working_set.update(cx, |tool_working_set, _| {
tool_working_set.remove(&tool_ids);
});
self.load_default_profile(cx);
}
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SerializedThreadMetadata {
pub id: ThreadId,
pub summary: SharedString,
pub updated_at: DateTime<Utc>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SerializedThread {
pub version: String,
pub summary: SharedString,
pub updated_at: DateTime<Utc>,
pub messages: Vec<SerializedMessage>,
#[serde(default)]
pub initial_project_snapshot: Option<Arc<ProjectSnapshot>>,
#[serde(default)]
pub cumulative_token_usage: TokenUsage,
#[serde(default)]
pub request_token_usage: Vec<TokenUsage>,
#[serde(default)]
pub detailed_summary_state: DetailedSummaryState,
#[serde(default)]
pub exceeded_window_error: Option<ExceededWindowError>,
}
impl SerializedThread {
pub const VERSION: &'static str = "0.2.0";
pub fn from_json(json: &[u8]) -> Result<Self> {
let saved_thread_json = serde_json::from_slice::<serde_json::Value>(json)?;
match saved_thread_json.get("version") {
Some(serde_json::Value::String(version)) => match version.as_str() {
SerializedThreadV0_1_0::VERSION => {
let saved_thread =
serde_json::from_value::<SerializedThreadV0_1_0>(saved_thread_json)?;
Ok(saved_thread.upgrade())
}
SerializedThread::VERSION => Ok(serde_json::from_value::<SerializedThread>(
saved_thread_json,
)?),
_ => Err(anyhow!(
"unrecognized serialized thread version: {}",
version
)),
},
None => {
let saved_thread =
serde_json::from_value::<LegacySerializedThread>(saved_thread_json)?;
Ok(saved_thread.upgrade())
}
version => Err(anyhow!(
"unrecognized serialized thread version: {:?}",
version
)),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SerializedThreadV0_1_0(
// The structure did not change, so we are reusing the latest SerializedThread.
// When making the next version, make sure this points to SerializedThreadV0_2_0
SerializedThread,
);
impl SerializedThreadV0_1_0 {
pub const VERSION: &'static str = "0.1.0";
pub fn upgrade(self) -> SerializedThread {
debug_assert_eq!(SerializedThread::VERSION, "0.2.0");
let mut messages: Vec<SerializedMessage> = Vec::with_capacity(self.0.messages.len());
for message in self.0.messages {
if message.role == Role::User && !message.tool_results.is_empty() {
if let Some(last_message) = messages.last_mut() {
debug_assert!(last_message.role == Role::Assistant);
last_message.tool_results = message.tool_results;
continue;
}
}
messages.push(message);
}
SerializedThread { messages, ..self.0 }
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SerializedMessage {
pub id: MessageId,
pub role: Role,
#[serde(default)]
pub segments: Vec<SerializedMessageSegment>,
#[serde(default)]
pub tool_uses: Vec<SerializedToolUse>,
#[serde(default)]
pub tool_results: Vec<SerializedToolResult>,
#[serde(default)]
pub context: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum SerializedMessageSegment {
#[serde(rename = "text")]
Text {
text: String,
},
#[serde(rename = "thinking")]
Thinking {
text: String,
#[serde(skip_serializing_if = "Option::is_none")]
signature: Option<String>,
},
RedactedThinking {
data: Vec<u8>,
},
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SerializedToolUse {
pub id: LanguageModelToolUseId,
pub name: SharedString,
pub input: serde_json::Value,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SerializedToolResult {
pub tool_use_id: LanguageModelToolUseId,
pub is_error: bool,
pub content: Arc<str>,
}
#[derive(Serialize, Deserialize)]
struct LegacySerializedThread {
pub summary: SharedString,
pub updated_at: DateTime<Utc>,
pub messages: Vec<LegacySerializedMessage>,
#[serde(default)]
pub initial_project_snapshot: Option<Arc<ProjectSnapshot>>,
}
impl LegacySerializedThread {
pub fn upgrade(self) -> SerializedThread {
SerializedThread {
version: SerializedThread::VERSION.to_string(),
summary: self.summary,
updated_at: self.updated_at,
messages: self.messages.into_iter().map(|msg| msg.upgrade()).collect(),
initial_project_snapshot: self.initial_project_snapshot,
cumulative_token_usage: TokenUsage::default(),
request_token_usage: Vec::new(),
detailed_summary_state: DetailedSummaryState::default(),
exceeded_window_error: None,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
struct LegacySerializedMessage {
pub id: MessageId,
pub role: Role,
pub text: String,
#[serde(default)]
pub tool_uses: Vec<SerializedToolUse>,
#[serde(default)]
pub tool_results: Vec<SerializedToolResult>,
}
impl LegacySerializedMessage {
fn upgrade(self) -> SerializedMessage {
SerializedMessage {
id: self.id,
role: self.role,
segments: vec![SerializedMessageSegment::Text { text: self.text }],
tool_uses: self.tool_uses,
tool_results: self.tool_results,
context: String::new(),
}
}
}
struct GlobalThreadsDatabase(
Shared<BoxFuture<'static, Result<Arc<ThreadsDatabase>, Arc<anyhow::Error>>>>,
);
impl Global for GlobalThreadsDatabase {}
pub(crate) struct ThreadsDatabase {
executor: BackgroundExecutor,
env: heed::Env,
threads: Database<SerdeBincode<ThreadId>, SerializedThread>,
}
impl heed::BytesEncode<'_> for SerializedThread {
type EItem = SerializedThread;
fn bytes_encode(item: &Self::EItem) -> Result<Cow<[u8]>, heed::BoxedError> {
serde_json::to_vec(item).map(Cow::Owned).map_err(Into::into)
}
}
impl<'a> heed::BytesDecode<'a> for SerializedThread {
type DItem = SerializedThread;
fn bytes_decode(bytes: &'a [u8]) -> Result<Self::DItem, heed::BoxedError> {
// We implement this type manually because we want to call `SerializedThread::from_json`,
// instead of the Deserialize trait implementation for `SerializedThread`.
SerializedThread::from_json(bytes).map_err(Into::into)
}
}
impl ThreadsDatabase {
fn global_future(
cx: &mut App,
) -> Shared<BoxFuture<'static, Result<Arc<ThreadsDatabase>, Arc<anyhow::Error>>>> {
GlobalThreadsDatabase::global(cx).0.clone()
}
fn init(cx: &mut App) {
let executor = cx.background_executor().clone();
let database_future = executor
.spawn({
let executor = executor.clone();
let database_path = paths::data_dir().join("threads/threads-db.1.mdb");
async move { ThreadsDatabase::new(database_path, executor) }
})
.then(|result| future::ready(result.map(Arc::new).map_err(Arc::new)))
.boxed()
.shared();
cx.set_global(GlobalThreadsDatabase(database_future));
}
pub fn new(path: PathBuf, executor: BackgroundExecutor) -> Result<Self> {
std::fs::create_dir_all(&path)?;
const ONE_GB_IN_BYTES: usize = 1024 * 1024 * 1024;
let env = unsafe {
heed::EnvOpenOptions::new()
.map_size(ONE_GB_IN_BYTES)
.max_dbs(1)
.open(path)?
};
let mut txn = env.write_txn()?;
let threads = env.create_database(&mut txn, Some("threads"))?;
txn.commit()?;
Ok(Self {
executor,
env,
threads,
})
}
pub fn list_threads(&self) -> Task<Result<Vec<SerializedThreadMetadata>>> {
let env = self.env.clone();
let threads = self.threads;
self.executor.spawn(async move {
let txn = env.read_txn()?;
let mut iter = threads.iter(&txn)?;
let mut threads = Vec::new();
while let Some((key, value)) = iter.next().transpose()? {
threads.push(SerializedThreadMetadata {
id: key,
summary: value.summary,
updated_at: value.updated_at,
});
}
Ok(threads)
})
}
pub fn try_find_thread(&self, id: ThreadId) -> Task<Result<Option<SerializedThread>>> {
let env = self.env.clone();
let threads = self.threads;
self.executor.spawn(async move {
let txn = env.read_txn()?;
let thread = threads.get(&txn, &id)?;
Ok(thread)
})
}
pub fn save_thread(&self, id: ThreadId, thread: SerializedThread) -> Task<Result<()>> {
let env = self.env.clone();
let threads = self.threads;
self.executor.spawn(async move {
let mut txn = env.write_txn()?;
threads.put(&mut txn, &id, &thread)?;
txn.commit()?;
Ok(())
})
}
pub fn delete_thread(&self, id: ThreadId) -> Task<Result<()>> {
let env = self.env.clone();
let threads = self.threads;
self.executor.spawn(async move {
let mut txn = env.write_txn()?;
threads.delete(&mut txn, &id)?;
txn.commit()?;
Ok(())
})
}
}

View File

@@ -0,0 +1,94 @@
use std::sync::Arc;
use assistant_tool::{Tool, ToolSource, ToolWorkingSet, ToolWorkingSetEvent};
use collections::HashMap;
use gpui::{App, Context, Entity, IntoElement, Render, Subscription, Window};
use language_model::{LanguageModel, LanguageModelToolSchemaFormat};
use ui::prelude::*;
pub struct IncompatibleToolsState {
cache: HashMap<LanguageModelToolSchemaFormat, Vec<Arc<dyn Tool>>>,
tool_working_set: Entity<ToolWorkingSet>,
_tool_working_set_subscription: Subscription,
}
impl IncompatibleToolsState {
pub fn new(tool_working_set: Entity<ToolWorkingSet>, cx: &mut Context<Self>) -> Self {
let _tool_working_set_subscription =
cx.subscribe(&tool_working_set, |this, _, event, _| match event {
ToolWorkingSetEvent::EnabledToolsChanged => {
this.cache.clear();
}
});
Self {
cache: HashMap::default(),
tool_working_set,
_tool_working_set_subscription,
}
}
pub fn incompatible_tools(
&mut self,
model: &Arc<dyn LanguageModel>,
cx: &App,
) -> &[Arc<dyn Tool>] {
self.cache
.entry(model.tool_input_format())
.or_insert_with(|| {
self.tool_working_set
.read(cx)
.enabled_tools(cx)
.iter()
.filter(|tool| tool.input_schema(model.tool_input_format()).is_err())
.cloned()
.collect()
})
}
}
pub struct IncompatibleToolsTooltip {
pub incompatible_tools: Vec<Arc<dyn Tool>>,
}
impl Render for IncompatibleToolsTooltip {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
ui::tooltip_container(window, cx, |container, _, cx| {
container
.w_72()
.child(Label::new("Incompatible Tools").size(LabelSize::Small))
.child(
Label::new(
"This model is incompatible with the following tools from your MCPs:",
)
.size(LabelSize::Small)
.color(Color::Muted),
)
.child(
v_flex()
.my_1p5()
.py_0p5()
.border_b_1()
.border_color(cx.theme().colors().border_variant)
.children(
self.incompatible_tools
.iter()
.map(|tool| h_flex().gap_4().child(Label::new(tool.name()).size(LabelSize::Small)).map(|parent|
match tool.source() {
ToolSource::Native => parent,
ToolSource::ContextServer { id } => parent.child(Label::new(id).size(LabelSize::Small).color(Color::Muted)),
}
)),
),
)
.child(Label::new("What To Do Instead").size(LabelSize::Small))
.child(
Label::new(
"Every other tool continues to work with this model, but to specifically use those, switch to another model.",
)
.size(LabelSize::Small)
.color(Color::Muted),
)
})
}
}

View File

@@ -1,18 +1,19 @@
use std::sync::Arc;
use anyhow::Result;
use assistant_tool::{Tool, ToolWorkingSet};
use assistant_tool::{AnyToolCard, Tool, ToolUseStatus, ToolWorkingSet};
use collections::HashMap;
use futures::FutureExt as _;
use futures::future::Shared;
use gpui::{App, SharedString, Task};
use gpui::{App, Entity, SharedString, Task};
use language_model::{
LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse,
LanguageModelToolUseId, MessageContent, Role,
LanguageModel, LanguageModelRegistry, LanguageModelRequestMessage, LanguageModelToolResult,
LanguageModelToolUse, LanguageModelToolUseId, MessageContent, Role,
};
use ui::IconName;
use util::truncate_lines_to_byte_limit;
use crate::thread::MessageId;
use crate::thread::{MessageId, PromptId, ThreadId};
use crate::thread_store::SerializedMessage;
#[derive(Debug)]
@@ -26,31 +27,24 @@ pub struct ToolUse {
pub needs_confirmation: bool,
}
#[derive(Debug, Clone)]
pub enum ToolUseStatus {
NeedsConfirmation,
Pending,
Running,
Finished(SharedString),
Error(SharedString),
}
pub struct ToolUseState {
tools: Arc<ToolWorkingSet>,
tools: Entity<ToolWorkingSet>,
tool_uses_by_assistant_message: HashMap<MessageId, Vec<LanguageModelToolUse>>,
tool_uses_by_user_message: HashMap<MessageId, Vec<LanguageModelToolUseId>>,
tool_results: HashMap<LanguageModelToolUseId, LanguageModelToolResult>,
pending_tool_uses_by_id: HashMap<LanguageModelToolUseId, PendingToolUse>,
tool_result_cards: HashMap<LanguageModelToolUseId, AnyToolCard>,
tool_use_metadata_by_id: HashMap<LanguageModelToolUseId, ToolUseMetadata>,
}
impl ToolUseState {
pub fn new(tools: Arc<ToolWorkingSet>) -> Self {
pub fn new(tools: Entity<ToolWorkingSet>) -> Self {
Self {
tools,
tool_uses_by_assistant_message: HashMap::default(),
tool_uses_by_user_message: HashMap::default(),
tool_results: HashMap::default(),
pending_tool_uses_by_id: HashMap::default(),
tool_result_cards: HashMap::default(),
tool_use_metadata_by_id: HashMap::default(),
}
}
@@ -58,9 +52,8 @@ impl ToolUseState {
///
/// Accepts a function to filter the tools that should be used to populate the state.
pub fn from_serialized_messages(
tools: Arc<ToolWorkingSet>,
tools: Entity<ToolWorkingSet>,
messages: &[SerializedMessage],
mut filter_by_tool_name: impl FnMut(&str) -> bool,
) -> Self {
let mut this = Self::new(tools);
let mut tool_names_by_id = HashMap::default();
@@ -72,11 +65,12 @@ impl ToolUseState {
let tool_uses = message
.tool_uses
.iter()
.filter(|tool_use| (filter_by_tool_name)(tool_use.name.as_ref()))
.map(|tool_use| LanguageModelToolUse {
id: tool_use.id.clone(),
name: tool_use.name.clone().into(),
raw_input: tool_use.input.to_string(),
input: tool_use.input.clone(),
is_input_complete: true,
})
.collect::<Vec<_>>();
@@ -88,14 +82,6 @@ impl ToolUseState {
this.tool_uses_by_assistant_message
.insert(message.id, tool_uses);
}
}
Role::User => {
if !message.tool_results.is_empty() {
let tool_uses_by_user_message = this
.tool_uses_by_user_message
.entry(message.id)
.or_default();
for tool_result in &message.tool_results {
let tool_use_id = tool_result.tool_use_id.clone();
@@ -104,11 +90,6 @@ impl ToolUseState {
continue;
};
if !(filter_by_tool_name)(tool_use.as_ref()) {
continue;
}
tool_uses_by_user_message.push(tool_use_id.clone());
this.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
@@ -121,7 +102,7 @@ impl ToolUseState {
}
}
}
Role::System => {}
Role::System | Role::User => {}
}
}
@@ -178,23 +159,31 @@ impl ToolUseState {
PendingToolUseStatus::Error(ref err) => {
ToolUseStatus::Error(err.clone().into())
}
PendingToolUseStatus::InputStillStreaming => {
ToolUseStatus::InputStillStreaming
}
}
} else {
ToolUseStatus::Pending
}
})();
let (icon, needs_confirmation) = if let Some(tool) = self.tools.tool(&tool_use.name, cx)
{
(tool.icon(), tool.needs_confirmation())
} else {
(IconName::Cog, false)
};
let (icon, needs_confirmation) =
if let Some(tool) = self.tools.read(cx).tool(&tool_use.name, cx) {
(tool.icon(), tool.needs_confirmation(&tool_use.input, cx))
} else {
(IconName::Cog, false)
};
tool_uses.push(ToolUse {
id: tool_use.id.clone(),
name: tool_use.name.clone().into(),
ui_text: self.tool_ui_label(&tool_use.name, &tool_use.input, cx),
ui_text: self.tool_ui_label(
&tool_use.name,
&tool_use.input,
tool_use.is_input_complete,
cx,
),
input: tool_use.input.clone(),
status,
icon,
@@ -209,29 +198,40 @@ impl ToolUseState {
&self,
tool_name: &str,
input: &serde_json::Value,
is_input_complete: bool,
cx: &App,
) -> SharedString {
if let Some(tool) = self.tools.tool(tool_name, cx) {
tool.ui_text(input).into()
if let Some(tool) = self.tools.read(cx).tool(tool_name, cx) {
if is_input_complete {
tool.ui_text(input).into()
} else {
tool.still_streaming_ui_text(input).into()
}
} else {
format!("Unknown tool {tool_name:?}").into()
}
}
pub fn tool_results_for_message(&self, message_id: MessageId) -> Vec<&LanguageModelToolResult> {
let empty = Vec::new();
pub fn tool_results_for_message(
&self,
assistant_message_id: MessageId,
) -> Vec<&LanguageModelToolResult> {
let Some(tool_uses) = self
.tool_uses_by_assistant_message
.get(&assistant_message_id)
else {
return Vec::new();
};
self.tool_uses_by_user_message
.get(&message_id)
.unwrap_or(&empty)
tool_uses
.iter()
.filter_map(|tool_use_id| self.tool_results.get(&tool_use_id))
.filter_map(|tool_use| self.tool_results.get(&tool_use.id))
.collect()
}
pub fn message_has_tool_results(&self, message_id: MessageId) -> bool {
self.tool_uses_by_user_message
.get(&message_id)
pub fn message_has_tool_results(&self, assistant_message_id: MessageId) -> bool {
self.tool_uses_by_assistant_message
.get(&assistant_message_id)
.map_or(false, |results| !results.is_empty())
}
@@ -242,24 +242,60 @@ impl ToolUseState {
self.tool_results.get(tool_use_id)
}
pub fn tool_result_card(&self, tool_use_id: &LanguageModelToolUseId) -> Option<&AnyToolCard> {
self.tool_result_cards.get(tool_use_id)
}
pub fn insert_tool_result_card(
&mut self,
tool_use_id: LanguageModelToolUseId,
card: AnyToolCard,
) {
self.tool_result_cards.insert(tool_use_id, card);
}
pub fn request_tool_use(
&mut self,
assistant_message_id: MessageId,
tool_use: LanguageModelToolUse,
metadata: ToolUseMetadata,
cx: &App,
) {
self.tool_uses_by_assistant_message
) -> Arc<str> {
let tool_uses = self
.tool_uses_by_assistant_message
.entry(assistant_message_id)
.or_default()
.push(tool_use.clone());
.or_default();
// The tool use is being requested by the Assistant, so we want to
// attach the tool results to the next user message.
let next_user_message_id = MessageId(assistant_message_id.0 + 1);
self.tool_uses_by_user_message
.entry(next_user_message_id)
.or_default()
.push(tool_use.id.clone());
let mut existing_tool_use_found = false;
for existing_tool_use in tool_uses.iter_mut() {
if existing_tool_use.id == tool_use.id {
*existing_tool_use = tool_use.clone();
existing_tool_use_found = true;
}
}
if !existing_tool_use_found {
tool_uses.push(tool_use.clone());
}
let status = if tool_use.is_input_complete {
self.tool_use_metadata_by_id
.insert(tool_use.id.clone(), metadata);
PendingToolUseStatus::Idle
} else {
PendingToolUseStatus::InputStillStreaming
};
let ui_text: Arc<str> = self
.tool_ui_label(
&tool_use.name,
&tool_use.input,
tool_use.is_input_complete,
cx,
)
.into();
self.pending_tool_uses_by_id.insert(
tool_use.id.clone(),
@@ -267,13 +303,13 @@ impl ToolUseState {
assistant_message_id,
id: tool_use.id,
name: tool_use.name.clone(),
ui_text: self
.tool_ui_label(&tool_use.name, &tool_use.input, cx)
.into(),
ui_text: ui_text.clone(),
input: tool_use.input,
status: PendingToolUseStatus::Idle,
status,
},
);
ui_text
}
pub fn run_pending_tool(
@@ -317,9 +353,48 @@ impl ToolUseState {
tool_use_id: LanguageModelToolUseId,
tool_name: Arc<str>,
output: Result<String>,
cx: &App,
) -> Option<PendingToolUse> {
let metadata = self.tool_use_metadata_by_id.remove(&tool_use_id);
telemetry::event!(
"Agent Tool Finished",
model = metadata
.as_ref()
.map(|metadata| metadata.model.telemetry_id()),
model_provider = metadata
.as_ref()
.map(|metadata| metadata.model.provider_id().to_string()),
thread_id = metadata.as_ref().map(|metadata| metadata.thread_id.clone()),
prompt_id = metadata.as_ref().map(|metadata| metadata.prompt_id.clone()),
tool_name,
success = output.is_ok()
);
match output {
Ok(tool_result) => {
let model_registry = LanguageModelRegistry::read_global(cx);
const BYTES_PER_TOKEN_ESTIMATE: usize = 3;
// Protect from clearly large output
let tool_output_limit = model_registry
.default_model()
.map(|model| model.model.max_token_count() * BYTES_PER_TOKEN_ESTIMATE)
.unwrap_or(usize::MAX);
let tool_result = if tool_result.len() <= tool_output_limit {
tool_result
} else {
let truncated = truncate_lines_to_byte_limit(&tool_result, tool_output_limit);
format!(
"Tool result too long. The first {} bytes:\n\n{}",
truncated.len(),
truncated
)
};
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
@@ -373,31 +448,49 @@ impl ToolUseState {
}
}
pub fn attach_tool_results(
pub fn has_tool_results(&self, assistant_message_id: MessageId) -> bool {
self.tool_uses_by_assistant_message
.contains_key(&assistant_message_id)
}
pub fn tool_results_message(
&self,
message_id: MessageId,
request_message: &mut LanguageModelRequestMessage,
) {
if let Some(tool_uses) = self.tool_uses_by_user_message.get(&message_id) {
for tool_use_id in tool_uses {
if let Some(tool_result) = self.tool_results.get(tool_use_id) {
request_message.content.push(MessageContent::ToolResult(
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name: tool_result.tool_name.clone(),
is_error: tool_result.is_error,
content: if tool_result.content.is_empty() {
// Surprisingly, the API fails if we return an empty string here.
// It thinks we are sending a tool use without a tool result.
"<Tool returned an empty string>".into()
} else {
tool_result.content.clone()
},
assistant_message_id: MessageId,
) -> Option<LanguageModelRequestMessage> {
let tool_uses = self
.tool_uses_by_assistant_message
.get(&assistant_message_id)?;
if tool_uses.is_empty() {
return None;
}
let mut request_message = LanguageModelRequestMessage {
role: Role::User,
content: vec![],
cache: false,
};
for tool_use in tool_uses {
if let Some(tool_result) = self.tool_results.get(&tool_use.id) {
request_message
.content
.push(MessageContent::ToolResult(LanguageModelToolResult {
tool_use_id: tool_use.id.clone(),
tool_name: tool_result.tool_name.clone(),
is_error: tool_result.is_error,
content: if tool_result.content.is_empty() {
// Surprisingly, the API fails if we return an empty string here.
// It thinks we are sending a tool use without a tool result.
"<Tool returned an empty string>".into()
} else {
tool_result.content.clone()
},
));
}
}));
}
}
Some(request_message)
}
}
@@ -424,6 +517,7 @@ pub struct Confirmation {
#[derive(Debug, Clone)]
pub enum PendingToolUseStatus {
InputStillStreaming,
Idle,
NeedsConfirmation(Arc<Confirmation>),
Running { _task: Shared<Task<()>> },
@@ -443,3 +537,10 @@ impl PendingToolUseStatus {
matches!(self, PendingToolUseStatus::NeedsConfirmation { .. })
}
}
#[derive(Clone)]
pub struct ToolUseMetadata {
pub model: Arc<dyn LanguageModel>,
pub thread_id: ThreadId,
pub prompt_id: PromptId,
}

View File

@@ -1,5 +1,9 @@
mod agent_notification;
mod animated_label;
mod context_pill;
mod usage_banner;
pub use agent_notification::*;
pub use animated_label::*;
pub use context_pill::*;
pub use usage_banner::*;

View File

@@ -12,6 +12,7 @@ pub struct AgentNotification {
title: SharedString,
caption: SharedString,
icon: IconName,
project_name: Option<SharedString>,
}
impl AgentNotification {
@@ -19,11 +20,13 @@ impl AgentNotification {
title: impl Into<SharedString>,
caption: impl Into<SharedString>,
icon: IconName,
project_name: Option<impl Into<SharedString>>,
) -> Self {
Self {
title: title.into(),
caption: caption.into(),
icon,
project_name: project_name.map(|name| name.into()),
}
}
@@ -118,23 +121,46 @@ impl Render for AgentNotification {
)
.child(
v_flex()
.flex_1()
.max_w(px(300.))
.child(
div()
.relative()
.text_size(px(14.))
.text_color(cx.theme().colors().text)
.max_w(px(300.))
.truncate()
.child(self.title.clone())
.child(gradient_overflow()),
)
.child(
div()
h_flex()
.relative()
.gap_1p5()
.text_size(px(12.))
.text_color(cx.theme().colors().text_muted)
.max_w(px(340.))
.truncate()
.when_some(
self.project_name.clone(),
|description, project_name| {
description.child(
h_flex()
.gap_1p5()
.child(
div()
.max_w_16()
.truncate()
.child(project_name),
)
.child(
div().size(px(3.)).rounded_full().bg(cx
.theme()
.colors()
.text
.opacity(0.5)),
),
)
},
)
.child(self.caption.clone())
.child(gradient_overflow()),
),

View File

@@ -0,0 +1,121 @@
use gpui::{Animation, AnimationExt, FontWeight, pulsating_between};
use std::time::Duration;
use ui::prelude::*;
#[derive(IntoElement)]
pub struct AnimatedLabel {
base: Label,
text: SharedString,
}
impl AnimatedLabel {
pub fn new(text: impl Into<SharedString>) -> Self {
let text = text.into();
AnimatedLabel {
base: Label::new(text.clone()),
text,
}
}
}
impl LabelCommon for AnimatedLabel {
fn size(mut self, size: LabelSize) -> Self {
self.base = self.base.size(size);
self
}
fn weight(mut self, weight: FontWeight) -> Self {
self.base = self.base.weight(weight);
self
}
fn line_height_style(mut self, line_height_style: LineHeightStyle) -> Self {
self.base = self.base.line_height_style(line_height_style);
self
}
fn color(mut self, color: Color) -> Self {
self.base = self.base.color(color);
self
}
fn strikethrough(mut self) -> Self {
self.base = self.base.strikethrough();
self
}
fn italic(mut self) -> Self {
self.base = self.base.italic();
self
}
fn alpha(mut self, alpha: f32) -> Self {
self.base = self.base.alpha(alpha);
self
}
fn underline(mut self) -> Self {
self.base = self.base.underline();
self
}
fn truncate(mut self) -> Self {
self.base = self.base.truncate();
self
}
fn single_line(mut self) -> Self {
self.base = self.base.single_line();
self
}
fn buffer_font(mut self, cx: &App) -> Self {
self.base = self.base.buffer_font(cx);
self
}
fn inline_code(mut self, cx: &App) -> Self {
self.base = self.base.inline_code(cx);
self
}
}
impl RenderOnce for AnimatedLabel {
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
let text = self.text.clone();
self.base
.color(Color::Muted)
.with_animations(
"animated-label",
vec![
Animation::new(Duration::from_secs(1)),
Animation::new(Duration::from_secs(1)).repeat(),
],
move |mut label, animation_ix, delta| {
match animation_ix {
0 => {
let chars_to_show = (delta * text.len() as f32).ceil() as usize;
let text = SharedString::from(text[0..chars_to_show].to_string());
label.set_text(text);
}
1 => match delta {
d if d < 0.25 => label.set_text(text.clone()),
d if d < 0.5 => label.set_text(format!("{}.", text)),
d if d < 0.75 => label.set_text(format!("{}..", text)),
_ => label.set_text(format!("{}...", text)),
},
_ => {}
}
label
},
)
.with_animation(
"pulsating-label",
Animation::new(Duration::from_secs(2))
.repeat()
.with_easing(pulsating_between(0.6, 1.)),
|label, delta| label.map_element(|label| label.alpha(delta)),
)
}
}

Some files were not shown because too many files have changed in this diff Show More