Compare commits

...

180 Commits

Author SHA1 Message Date
Antonio Scandurra
de8f1c3c84 WIP 2025-06-30 21:01:37 +02:00
Antonio Scandurra
9351e959a5 WIP 2025-06-30 19:32:42 +02:00
Antonio Scandurra
dac0838a80 WIP 2025-06-30 14:24:24 +02:00
Antonio Scandurra
38d5f36d38 WIP 2025-06-30 12:15:19 +02:00
Piotr Osiewicz
e5bcd720e1 debugger: Add UI for tweaking breakpoint properties directly from breakpoint list (#33097)
Release Notes:

- debugger: Breakpoint properties (log/hit condition/condition) can now
be set directly from breakpoint list.
2025-06-28 23:41:44 +02:00
Kirill Bulatov
41583fb066 Fix document colors issues with other inlays and multi buffers (#33598)
Closes https://github.com/zed-industries/zed/issues/33575

* Fixes inlay colors spoiled after document color displayed
* Optimizes the query pattern for large multi buffers

Release Notes:

- Fixed document colors issues with other inlays and multi buffers
2025-06-28 21:10:49 +00:00
Cole Miller
521a223681 Add editor::Rewrap binding to Emacs keymaps (#33588)
`M-q` is `fill-paragraph` which is like `editor::Rewrap`.

Release Notes:

- emacs: Bound `alt-q` to `editor::Rewrap` (like `M-q` or `M-x
fill-paragraph`)
2025-06-28 15:35:59 -04:00
Conrad Irwin
c8c6468f9c vim: Non-interactive shell (#33568)
Closes #33144

Release Notes:

- vim: Run r! in a non-interactive shell
2025-06-28 10:23:57 -06:00
Umesh Yadav
3f4098e87b open_ai: Make OpenAI error message generic (#33383)
Context: In this PR: https://github.com/zed-industries/zed/pull/33362,
we started to use underlying open_ai crate for making api calls for
vercel as well. Now whenever we get the error we get something like the
below. Where on part of the error mentions OpenAI but the rest of the
error returns the actual error from provider. This PR tries to make the
error generic for now so that people don't get confused seeing OpenAI in
their v0 integration.

```
Error interacting with language model
Failed to connect to OpenAI API: 403 Forbidden {"success":false,"error":"Premium or Team plan required to access the v0 API: https://v0.dev/chat/settings/billing"}
```

Release Notes:

- N/A
2025-06-28 14:38:27 +02:00
alphaArgon
1d684c8890 Add shadow back for blurred/transparent window on macOS (#27403)
Closes #15383
Closes #10993

`NSVisualEffectView` is an official API for implementing blur effects
and, by traversing the layers, we **can remove the background color**
that comes with the view. This avoids using private APIs and aligns
better with macOS’s native design.

Currently, `GPUIView` serves as the content view of the window. To add
the blurred view, `GPUIView` is downgraded to a subview of the content
view, placed at the same level as the blurred view.

Release Notes:

- Fixed the missing shadow for blurred-background windows on macOS.

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-28 09:50:54 +03:00
Rift
97c5c5a6e7 vim: Respect count for paragraphs (#33489)
Closes #32462 

Release Notes:

- vim: Paragraph objects now support counts (`d2ap`, `v2ap`, etc.)

---------

Co-authored-by: Rift <no@e.mail>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-27 22:05:47 -06:00
5brian
ba4fc1bcfc vim: Add debug panel ex command (#33560)
Added :Debug to open debug panel, also added
[:display](https://neovim.io/doc/user/change.html#%3Adisplay), alias to
:reg

Release Notes:

- N/A
2025-06-27 21:32:40 -06:00
Smit Barmase
bbf16bda75 editor: Improve rewrap to respect indent and prefix boundaries (#33566)
1. Fixes bug where this would not rewrap:

```rs
// This is the first long comment block to be wrapped.
fn my_func(a: u32);
// This is the second long comment block to be wrapped.
```
2. Comment prefix boundaries (Notice now they don't merge between
different comment prefix):

Initial text:
```rs
// A regular long long comment to be wrapped.
// A second regular long comment to be wrapped.
/// A documentation long comment to be wrapped.
```
Upon rewrap:
```rs
// A regular long long comment to be
// wrapped. A second regular long
// comment to be wrapped.
/// A documentation long comment to be
/// wrapped.
```
3. Indent boundaries (Notice now they don't merge between different
indentation):

Initial text:
```rs
fn foo() {
      // This is a long comment at the base indent.
      // This is a long comment at the base indent.
                 // This is a long comment at the next indent.
                 // This is a long comment at the next indent.
      // This is a long comment at the base indent.
}
```
Upon rewrap:
```rs
fn foo() {
      // This is a long comment at the base
      // indent. This is a long comment at the
      // base indent.
                 // This is a long comment at the 
                 // next indent. This is a long 
                 // comment at the next indent.
      // This is a long comment at the base
      // indent.
}
```

Release Notes:

- Fixed an issue where rewrap would not work with selection when two
comment blocks are separated with line of code.
- Improved rewrap to respect changes in indentation or comment prefix
(e.g. `//` vs `///`) as boundaries so that it doesn't merge them into
one mangled text.
2025-06-28 05:38:18 +05:30
ddoemonn
c56b8904cc Prevent branch name overflow in git panel selection (#33529)
Closes #33527

Release Notes:

- Fixed long branch names overflowing to multiple lines in git panel
branch selector

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-06-27 20:50:53 -03:00
Artem Loenko
695118d110 agent: Show provider icon in model selectors (#30595)
I often switch between models, and I believe many people do. Currently,
it is difficult to determine which provider offers the selected model
because the same models are available from different providers. I
propose a simple change to the selector so that users can distinguish
between providers from the model they have chosen.

As a side note, I would actually prefer to have a text label with the
provider’s name next to the model name in the selector. However, I
understand that this is too opinionated and takes up too much space.

| Before | After |
| ------ | ------ |
|
![before_inline_assist](https://github.com/user-attachments/assets/35617ba5-e8d4-4dab-a997-f7286f73f2bf)
|
![after_inline_assist](https://github.com/user-attachments/assets/c37c81b4-73e4-49e2-955d-b8543b2855ad)
|
|
![before_text_thread](https://github.com/user-attachments/assets/af90303b-12d6-402c-90a5-8b36cd97396e)
|
![after_text_thread](https://github.com/user-attachments/assets/bca5b423-f12b-4eaf-a82e-424d09b7f447)
|
|
![before_thread](https://github.com/user-attachments/assets/0946775f-1d52-437b-a217-9708ee2e789a)
|
![after_thread](https://github.com/user-attachments/assets/f5e53968-9020-446f-9d5e-653ae9fdea3e)
|

Release Notes:

- The model selector has been improved with a provider icon, making it
easier to distinguish between providers.

---------

Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-06-27 19:33:58 -03:00
Conrad Irwin
a675ca7a1e Remove into SelectionEffects from .change_selections (#33554)
In #32656 I generalized the argument to change selections to allow
controling both the scroll and the nav history (and the completion
trigger).

To avoid conflicting with ongoing debugger cherry-picks I left the
argument as an `impl Into<>`, but I think it's clearer to make callers
specify what they want here.

I converted a lot of `None` arguments to `SelectionEffects::no_scroll()`
to be exactly compatible; but I think many people used none as an "i
don't care" value in which case Default::default() might be more
appropraite

Closes #ISSUE

Release Notes:

- N/A
2025-06-27 14:31:31 -06:00
Conrad Irwin
6e762d9c05 Revert "Remove into SelectionEffects from .change_selections"
This reverts commit 28380d714d.
2025-06-27 14:06:17 -06:00
Conrad Irwin
28380d714d Remove into SelectionEffects from .change_selections
In #32656 I generalized the argument to change selections to allow
controling both the scroll and the nav history (and the completion
trigger).

To avoid conflicting with ongoing debugger cherry-picks I left the
argument as an `impl Into<>`, but I think it's clearer to make callers
specify what they want here.

I converted a lot of `None` arguments to `SelectionEffects::no_scroll()`
to be exactly compatible; but I think many people used none as an "i
don't care" value in which case Default::default() might be more
appropraite
2025-06-27 14:03:45 -06:00
Alejandro Fernández Gómez
f338c46bf7 Fix line indices when using a selection in the context (#33549)
Closes #33152.

The label for a file selection context had an off-by-one error on line
indices. This PR adjusts that.

Before:

<img width="1072" alt="Screenshot 2025-06-27 at 20 55 28"
src="https://github.com/user-attachments/assets/da0be503-056b-442b-9a79-38cf504d8a44"
/>

After:

<img width="1090" alt="Screenshot 2025-06-27 at 20 49 35"
src="https://github.com/user-attachments/assets/f98df57a-8f07-4ea5-a06b-a4b6fb8c2e4e"
/>


Release Notes:

- Fixed a off-by-one error on the line indices when using a selection as
context for the agent,
2025-06-27 13:41:17 -06:00
Umesh Yadav
5fbb7b0d40 copilot: Only set Copilot-Vision-Request header for vision requests (#33552)
Closes #31951

The fix is copied and translated from copilot chat actual implementation
code:
ad7cbcae9a/src/platform/openai/node/fetch.ts (L493C1-L495C3)

Release Notes:

- Fix copilot failing due to missing `Copilot-Vision-Request` from
request.
2025-06-27 21:37:26 +02:00
Cole Miller
f12b0dddf4 Touch up extension DAP schemas fix (#33548)
Updates #33546 

Release Notes:

- N/A

Co-authored-by: Piotr <piotr@zed.dev>
2025-06-27 15:34:21 -04:00
Conrad Irwin
14bb10d783 Don't panic on vintage files (#33543)
Release Notes:

- remoting: Fix a crash on the remote side when encountering files from
before 1970.
2025-06-27 13:15:50 -06:00
Cole Miller
c9ce4aec91 Fix debug adapters from extensions not being picked up (#33546)
Copy the debug adapter schemas so that they're available to the
extension host, like we do for other extension assets.

Release Notes:

- N/A
2025-06-27 14:31:58 -04:00
Kirill Bulatov
01dfb6fa82 Respect server capabilities on queries (#33538)
Closes https://github.com/zed-industries/zed/issues/33522

Turns out a bunch of Zed requests were not checking their capabilities
correctly, due to odd copy-paste and due to default that assumed that
the capabilities are met.

Adjust the code, which includes the document colors, add the test on the
colors case.

Release Notes:

- Fixed excessive document colors requests for unrelated files
2025-06-27 16:31:40 +00:00
5brian
f9987a1141 vim: Grep in visual line (#33414)
From
https://github.com/zed-industries/zed/pull/10831#issuecomment-2078523272

> I agree with not prefilling the search bar with a multiline query.

Not sure if it's a bug that a one-line visual line selection does not
get pre filled, this PR corrects the query to use the visual line
selection instead of the 'normal' selection

Release Notes:

- N/A
2025-06-27 10:18:26 -06:00
Ben Kunkle
7432e947bc Add element_selection_background highlight to theme (#32388)
Closes #32354

The issue is that we render selections over the text in the agent panel,
but under the text in editor, so themes that have no alpha for the
selection background color (defaults to 0xff) will just occlude the
selected region. Making the selection render under the text in markdown
would be a significant (and complicated) refactor, as selections can
cross element boundaries (i.e. spanning code block and a header after
the code block).

The solution is to add a new highlight to themes
`element_selection_background` that defaults to the local players
selection background with an alpha of 0.25 (roughly equal to 0x3D which
is the alpha we use for selection backgrounds in default themes) if the
alpha of the local players selection is 1.0. The idea here is to give
theme authors more control over how the selections look outside of
editor, as in the agent panel specifically, the background color is
different, so while an alpha of 0.25 looks acceptable, a different color
would likely be better.

CC: @iamnbutler. Would appreciate your thoughts on this. 

> Note: Before and after using Everforest theme

| Before | After | 
|-------| -----|
| <img width="618" alt="Screenshot 2025-06-09 at 5 23 10 PM"
src="https://github.com/user-attachments/assets/41c7aa02-5b3f-45c6-981c-646ab9e2a1f3"
/> | <img width="618" alt="Screenshot 2025-06-09 at 5 25 03 PM"
src="https://github.com/user-attachments/assets/dfb13ffc-1559-4f01-98f1-a7aea68079b7"
/> |

Clearly, the selection in the after doesn't look _that_ great, but it is
better than the before, and this PR makes the color of the selection
configurable by the theme so that this theme author could make it a
lighter color for better contrast.




Release Notes:

- agent panel: Fixed an issue with some themes where selections inside
the agent panel would occlude the selected text completely

Co-authored-by: Antonio <me@as-cii.com>
2025-06-27 15:46:04 +00:00
Conrad Irwin
157199b65b Replace newlines in search bar (#33504)
Release Notes:

- search: Pasted newlines are now rendered as "\n" (with an underline),
instead of line-wrapping. This should make it much clearer what you're
searching for.
 
<img width="675" alt="Screenshot 2025-06-27 at 00 34 52"
src="https://github.com/user-attachments/assets/67275bc6-bec1-463f-b351-6b9ed0a6df81"
/>
2025-06-27 09:39:38 -06:00
Conrad Irwin
d74f3f4ea6 Fix crash in git checkout (#33499)
Closes #33438

Release Notes:

- git: Use git cli to perform checkouts (to avoid a crash seen in
libgit2)
2025-06-27 09:16:15 -06:00
Smit Barmase
9e2023bffc editor: Fix editor tests from changing on format on save (#33532)
Use placeholder to prevent format-on-save from removing whitespace in
editor tests, which leads to unnecessary git diff and failing tests.

cc: https://github.com/zed-industries/zed/pull/32340

Release Notes:

- N/A
2025-06-27 20:14:01 +05:30
Umesh Yadav
3ab4ad6de8 language_models: Use JsonSchemaSubset for Gemini models in OpenRouter (#33477)
Closes #33466

Release Notes:

- N/A
2025-06-27 16:36:16 +02:00
Peter Tripp
e3ce0618a3 collab: Lookup avatars by GitHub ID instead of username (#33523)
Closes: https://github.com/zed-industries/zed/issues/19207

This will correctly show Avatars for recently renamed/deleted users and
for enterprise users where the username avatar url triggers a redirect
to an auth prompt. Also saves a request (302 redirect) per avatar.

Tested locally and avatars loaded as expected.

Release Notes:

- N/A
2025-06-27 09:40:50 -04:00
Kirill Bulatov
865dd4c5fc Rework LSP tool keyboard story (#33525)
https://github.com/user-attachments/assets/81da68fe-bbc5-4b23-8182-923c752a8bd2

* Removes all extra elements: headers, buttons, to simplify the menu
navigation approach and save space.
Implements the keyboard navigation and panel toggling.

* Keeps the status icon and the server name, and their ordering approach
(current buffer/other) in the menu.
The status icon can still be hovered, but that is not yet possible to
trigger from the keyboard: future ideas would be make a similar side
display instead of hover, as Zeta menu does:


![image](https://github.com/user-attachments/assets/c844bc39-00ed-4fe3-96d5-1c9d323a21cc)

* Allows to start (if all are stopped) and stop (if some are not
stopped) all servers at once now with the button at the bottom

Release Notes:

- N/A
2025-06-27 16:25:56 +03:00
Umesh Yadav
2178f66af6 agent_ui: Rename MaxModeTooltip to BurnModeTooltip (#33521)
Closes #ISSUE

Release Notes:

- N/A
2025-06-27 13:56:46 +02:00
ddoemonn
338a7395a7 Fix blend alpha colors with editor background in inline preview (#33513)
Closes #33505

## Before

<img width="434" alt="Screenshot 2025-06-27 at 12 22 57"
src="https://github.com/user-attachments/assets/ac215a39-b3fe-4c9e-bd7d-0d7568d5fd1f"
/>

## After

<img width="441" alt="Screenshot 2025-06-27 at 12 22 47"
src="https://github.com/user-attachments/assets/28218ed6-c1aa-4d3f-a268-def2fa9f0340"
/>

Release Notes:

- Fixed inline color previews not correctly blending alpha/transparency
values with the editor background
2025-06-27 12:37:05 +03:00
Finn Evers
4c2415b338 editor: Use em_advance everywhere for horizontal scroll position computations (#33514)
Closes #33472

This PR fixes some regressions that were introduced in
https://github.com/zed-industries/zed/pull/32558, which updated the
editor scrolling to use `em_advance` instead of `em_width` for the
horizontal scroll position calculation.
However, not all occurrences were updated, which caused issues with wrap
guides and some small stuttering with horizontal autoscroll whilst
typing/navigating with the keyboard.

Release Notes:

- Fixed an issue where horizontal autoscrolling would stutter and indent
guides would drift when scrolling horizontally.
2025-06-27 09:32:50 +00:00
Ron Harel
e6bc1308af Add SVG preview (#32694)
Closes #10454

Implements SVG file preview capability similar to the existing markdown
preview.
- Adds `svg_preview` crate with preview view and live reloading upon
file save.
- Integrates SVG preview button in quick action bar.
- File preview shortcuts (`ctrl/cmd+k v` and `ctrl/cmd+shift+v`) are
extension-aware.

Release Notes:

- Added SVG file preview, accessible via the quick action bar button or
keyboard shortcuts (`ctrl/cmd+k v` and `ctrl/cmd+shift+v`) when editing
SVG files.
2025-06-27 09:08:05 +00:00
Ben Brandt
6c46e1129d Cleanup remaining references to max mode (#33509)
Release Notes:

- N/A
2025-06-27 08:32:13 +00:00
fantacell
fbb5628ec6 Reset selection goal after helix motion (#33184)
Closes #33060

Motions like `NextWordStart` don't reset the selection goal in vim mode
`helix_normal` unlike in `normal` which can lead to the cursor jumping
back to the previous horizontal position after going up or down.

Release Notes:

- N/A
2025-06-26 22:32:35 -06:00
Conrad Irwin
8c9116daa5 Fix panic in ctrl-g (#33474)
Release Notes:

- vim: Fixed a crash with ctrl-g
2025-06-26 21:26:58 -06:00
Conrad Irwin
20a3e613b8 vim: Better jump list support (#33495)
Closes #23527
Closes #30183
Closes some Discord chats

Release Notes:

- vim: Motions now push to the jump list using the same logic as vim
(i.e.
`G`/`g g`/`g d` always do, but `j`/`k` always don't). Most non-vim
actions
(including clicking with the mouse) continue to push to the jump list
only
  when they move the cursor by 10 or more lines.
2025-06-26 21:25:07 -06:00
Ben Kunkle
ba1c05abf2 keymap: Add ability to update user keymaps (#33487)
Closes #ISSUE

The ability to update user keybindings in their keymap is required for
#32436. This PR adds the ability to do so, reusing much of the existing
infrastructure for updating settings JSON files.

However, the existing JSON update functionality was intended to work
only with objects, therefore, this PR simply wraps the object updating
code with non-general keymap-specific array updating logic, that only
works for top-level arrays and can only append or update entries in said
top-level arrays. This limited API is reflected in the limited
operations that the new `update_keymap` method on `KeymapFile` can take
as arguments.

Additionally, this PR pulls out the existing JSON updating code into its
own module (where array updating code has been added) and adds a
significant number of tests (hence the high line count in the diff)

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-06-26 21:52:26 -04:00
Danilo Leal
2823771c06 Add design improvements to the LSP popover (#33485)
Not the ideal design just yet as that will probably require a different
approach altogether, but am pushing here just some reasonably small UI
adjustments that will make this feel slightly nicer!

Release Notes:

- N/A
2025-06-26 22:27:21 +00:00
Cole Miller
343f155ab9 Update docs for Swift debugging (#33483)
Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-06-26 21:25:03 +00:00
Julia Ryan
2dece13d83 nix: Update to access new rust toolchain version (#33476)
Needed due to #33439

Release Notes:

- N/A
2025-06-26 20:31:24 +00:00
Piotr Osiewicz
985dcf7523 chore: Bump Rust version to 1.88 (#33439)
Goodies in this version:
- if-let chains 🎉
- Better compiler perf for Zed
(https://github.com/rust-lang/rust/pull/138522)

For more, see: https://releases.rs/docs/1.88.0/

Release Notes:

- N/A

---------

Co-authored-by: Junkui Zhang <364772080@qq.com>
2025-06-26 20:54:19 +02:00
Conrad Irwin
b079871428 Fix subtraction with overflow (#33471)
Release Notes:

- N/A
2025-06-26 12:38:54 -06:00
fantacell
4983b01c89 helix: Change word motions (#33408)
When starting on the newline character at the end of a line the helix
word motions select that character, unlike in helix itself. This makes
it easy to accidentaly join two lines together.
Also, word motions that go backwards should stop at the start of a line.
I added that.

Release Notes:

- helix: Fix edge-cases with word motions and newlines
2025-06-26 12:25:47 -06:00
Cole Miller
35863c4302 debugger: Fix treatment of node-terminal scenarios (#33432)
- Normalize `node-terminal` to `pwa-node` before sending to DAP
- Split `command` into `program` and `args`
- Run in external console

Release Notes:

- debugger: Fixed debugging JavaScript tasks that used `"type":
"node-terminal"`.
2025-06-26 14:02:09 -04:00
Kirill Bulatov
a0bd25f218 Feature gate the LSP button (#33463)
Follow-up of https://github.com/zed-industries/zed/pull/32490

The tool still looks like designed by professional developers, and still
may change its UX based on the internal feedback.

Release Notes:

- N/A
2025-06-26 15:41:42 +00:00
Agus Zubiaga
8a1e795746 agent: Move ActiveThread and MessageEditor into ActiveView (#33462)
`ActiveThread` and `MessageEditor` only make sense when `active_view` is
`Thread`, so we moved them in there. This will make it easier to work on
new agent threads.

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2025-06-26 15:36:23 +00:00
Peter Tripp
f4818b648e linux: Add agent::ToggleBurnMode shortcut (super-ctrl-b) (#33458)
Follow-up to: 
- https://github.com/zed-industries/zed/pull/33452
- https://github.com/zed-industries/zed/pull/33190
- https://github.com/zed-industries/zed/pull/31630

Release Notes:

- N/A
2025-06-26 15:07:46 +00:00
Peter Tripp
7031ed8b87 ci: Fix duplicated/failed eval jobs (#33453)
I think this should fix the CI issues with Eval jobs:

1. Duplicated workflows because `synchronize` / `opened` were triggering
distinct runs. This caused failed job entries because the duplicated
workflows had a shared concurrency group and so one would pre-empt the
other.

3. Removes the no-op job, introduced as an attempted workaround in
   https://github.com/zed-industries/zed/pull/29420.

These should correctly show as "Skipped" now:

| Before | After |
| - | - |
| <img width="359" alt="Screenshot 2025-06-26 at 9 57 04"
src="https://github.com/user-attachments/assets/6ddd4f46-27c7-4d82-98ba-0f1166fc55e7"
/> | <img width="355" alt="Screenshot 2025-06-26 at 10 09 54"
src="https://github.com/user-attachments/assets/5faade2c-f17c-447a-9af9-6396f9e53016"
/> |

Release Notes:

- N/A
2025-06-26 11:01:16 -04:00
Richard Feldman
6073d2c93c Automatically retry when API is Overloaded or 500s (#33275)
<img width="484" alt="Screenshot 2025-06-25 at 2 26 16 PM"
src="https://github.com/user-attachments/assets/340f15d7-b115-4895-bae8-b12a915bfda1"
/>

<img width="460" alt="Screenshot 2025-06-25 at 2 26 08 PM"
src="https://github.com/user-attachments/assets/6e587a38-d542-405f-809f-402e87520538"
/>

Now we:
* Automatically retry up to 3 times on upstream Overloaded or 500 errors
(currently for Anthropic only; will add others in future PRs)
* Also automatically retry on rate limit errors (using the provided
duration to wait, if we were given one)
* Give you a notification if you don't have Zed open and we stopped the
thread because of an error

Still todo in future PRs:
* Update collab to report Overloaded and 500 errors differently if
collab itself is passing through an upstream error vs not (currently we
report these as "Zed's API is overloaded" when actually it's the
upstream one!)
* Updating providers other than Anthropic to categorize their errors so
that they benefit from this
* Expanding graceful error handling/retry to other things besides
Overloaded and 500 errors (e.g. connection reset)

Release Notes:

- Automatically retry in Agent Panel instead of erroring out when an
upstream AI API is overloaded or 500s
- Show a notification when an Agent thread errors out and Zed is not the
active window
2025-06-26 10:53:33 -04:00
Danilo Leal
00499aadd4 Add back default keybindings to Burn Mode and branch picker toggles (#33452)
Follow up to https://github.com/zed-industries/zed/pull/33190, as they
were removed because of conflict with VS Code's usage of those bindings
to toggle the right dock. `cmd-ctrl-b` seems like a safe alternative.
Note that this PR is macOS only, though. I couldn't find yet any good
options for Linux as they were all mostly conflicting with something
else.

Release Notes:

- N/A
2025-06-26 11:35:02 -03:00
Peter Tripp
d1eb69c6cd ci: Improve check_docs skipping (#33455)
Follow-up to: https://github.com/zed-industries/zed/pull/33398

Logic there was incomplete. Caused [this
failure](https://github.com/zed-industries/zed/actions/runs/15904169712/job/44854558276).

Release Notes:

- N/A
2025-06-26 14:34:20 +00:00
Marshall Bowers
40cbfb7eb2 docs: Add note about extension submodules needing to use HTTPS URLS (#33454)
This PR adds a note to the extension publishing docs about extension
submodules needing to use HTTPS URLs.

Release Notes:

- N/A
2025-06-26 14:18:56 +00:00
Peter Tripp
5d0f02d356 Add cmd-alt-b (workspace::ToggleRightDock) on macOS (#33450)
Release Notes:

- N/A
2025-06-26 13:54:21 +00:00
Renze Post
ca8e213151 Suggest reST extension for .rst files (#33413)
Suggest the reST extension when opening a .rst file for the first time.

Release Notes:

- N/A
2025-06-26 08:59:04 -04:00
Michael Sloan
90c893747c gpui: Prevent the same action name from being registered multiple times (#33359)
Also removes duplicate `editor::RevertFile` and `vim::HelixDelete`
actions

Release Notes:

- N/A
2025-06-26 06:24:14 +00:00
Smit Barmase
d09c7eb317 language: Add context-aware decrease indent for Python (#33370)
Closes #33238, follow-up to
https://github.com/zed-industries/zed/pull/29625.

Changes:

- Removed `significant_indentation`, which was the way to introduce
indentation scoping in languages like Python. However, it turned out to
be unnecessarily complicated to define and maintain.
- Introduced `decrease_indent_patterns`, which takes a `pattern` keyword
to automatically outdent and `valid_after` keywords to treat as valid
code points to snap to. The outdent happens to the most recent
`valid_after` keyword that also has less or equal indentation than the
currently typed keyword.

Fixes:

1. In Python, typing `except`, `finally`, `else`, and so on now
automatically indents intelligently based on the context in which it
appears. For instance:

```py
try:
    if a == 1:
        try:
             b = 2
             ^  # <-- typing "except:" here would indent it to inner try block
```

but,

```py
try:
    if a == 1:
        try:
             b = 2
    ^  # <-- typing "except:" here would indent it to outer try block
```

2. Fixes comments not maintaining indent.

Release Notes:

- Improved auto outdent for Python while typing keywords like `except`,
`else`, `finally`, etc.
- Fixed the issue where comments in Python would not maintain their
indentation.
2025-06-26 11:11:03 +05:30
Smit Barmase
1753432406 Fix tree sitter python try statement to accept missing else/except/finally (#33431)
We have fork now:
218fcbf3fd

Release Notes:

- N/A
2025-06-26 09:48:44 +05:30
Cole Miller
d9218b10ea Bump livekit-rust-sdks for candidate webrtc-sys build fix (#33387)
Incorporates https://github.com/zed-industries/livekit-rust-sdks/pull/5

Don't merge yet, waiting until after new releases are cut in case of
unexpected breakage.

Release Notes:

- N/A
2025-06-25 21:00:33 -04:00
Michael Sloan
dfdeb1bf51 linux: Don't insert characters if modifiers other than shift are held (#33424)
Closes #32219 #29666

Release Notes:

- Linux: Now skips insertion of characters when modifiers are held. Before, characters were inserted if there's no match in the keymap.
2025-06-25 17:11:59 -06:00
Max Brunsfeld
b9f81c7ce7 Restore missing initialization of text thread actions (#33422)
Fixes a regression introduced in
https://github.com/zed-industries/zed/pull/33289

Release Notes:

- Fixed a bug where some text thread actions were accidentally removed.
2025-06-25 22:48:40 +00:00
Michael Sloan
b1450b6d71 Remove git_panel::GenerateCommitMessage in favor of git::GenerateCommitMessage (#33421)
`git_panel::GenerateCommitMessage` has no handler,
`git::GenerateCommitMessage` should be preferred. Could add a
`#[action(deprecated_aliases = ["git_panel::GenerateCommitMessage"])]`,
but decided not to because that action didn't work. So instead uses of
it will show up as keymap errors.

Closes #32667

Release Notes:

- N/A
2025-06-25 22:29:30 +00:00
David Barsky
1af9f98c1d lsp-log: Avoid trimming leading space in language server logs (#33418)
Not sure what the full intention/right fix for this is, but
https://github.com/zed-industries/zed/pull/32659 re-introduced trimming
of leading spaces. rust-analyzer has [a custom tracing
formatter](317542c1e4/crates/rust-analyzer/src/tracing/hprof.rs)
that is _super_ useful for profiling what the heck rust-analyzer is
doing. It makes prodigious use of whitespace to delineate to create a
tree-shaped structure. This change reintroduces the leading whitespace.

I made a previous change similar to this that removed a `stderr:` in
https://github.com/zed-industries/zed/pull/27213/. If this is a
direction y'all are happy to go with, I'd be happy to add a test for
this!

<details>
<summary>A screenshot of the before</summary>

<img width="1624" alt="Screenshot 2025-06-25 at 2 12 45 PM"
src="https://github.com/user-attachments/assets/a714d973-9377-41ca-8087-3b0e82b41620"
/>

</details>

<details>
<summary>A screenshot of the after</summary>

<img width="1136" alt="Screenshot 2025-06-25 at 2 40 07 PM"
src="https://github.com/user-attachments/assets/b798ca13-11fc-4f97-9602-55e782068a5a"
/>

</details>

cc: @mgsloan.

Release Notes:

- Fixed the removal of leading whitespace in a language server's stderr
logs.
2025-06-25 16:24:51 -06:00
Danilo Leal
1330cb7a1f docs: Update instructions to use Vercel's v0 model (#33415)
To make sure this reflects the current reality as of today's
preview/stable version.

Release Notes:

- N/A
2025-06-25 17:52:23 -03:00
Peter Tripp
dae4e84bc5 Explicitly associate files as JSONC (#33410)
Fixes an issue when the zed repo was checked out to folder other than
`zed` (e.g. `zed2`) files were incorrectly identified as JSON instead of
JSONC.

Release Notes:

- N/A
2025-06-25 16:29:44 -04:00
morgankrey
6fb5500ef2 collab: Save Customer name and billing address to Customer on checkout (#33385)
We are collecting billing address and name on checkout now (for tax) but
we're not saving it back to the Customer level. Updating the Checkout
Session code to make`customer_update.address` equal to `auto`, instead
of the default `never`, as well as the same for `customer_update.name`.

Release Notes:

- N/A
2025-06-25 16:06:14 -04:00
vipex
8f9817173d pane: Update pinned tab count when it exceeds actual tab count (#33405)
## Summary

This PR improves the workaround introduced in #33335 that handles cases
where the pinned tab count exceeds the actual tab count during workspace
deserialization.

## Problem

The original workaround in #33335 successfully prevented the panic but
had two issues:
1. **Console spam**: The warning message was logged repeatedly because
`self.pinned_tab_count` wasn't updated to match the actual tab count
2. **Auto-pinning behavior**: New tabs up until you exceed the old safe
tab count were automatically pinned after the workaround was triggered.

## Solution

Updates the defensive code to set `self.pinned_tab_count = tab_count`
when the mismatch is detected, ensuring:
- The warning is only logged once when encountered.
- New tabs behave normally (aren't auto-pinned)
- The workspace remains in a consistent state

This is an immediate fix for the workaround. I'll attempt to open up a
follow-up PR when i get the chance that will address the root cause by
implementing serialization for empty untitled tabs, as discussed in
#33342.

Release Notes:

- N/A
2025-06-25 19:52:15 +00:00
Michael Sloan
aae4778b4e gpui: Add more flushing of x11 requests (#33407)
Flushes should happen after sending messages to X11 when effects should
be applied quickly. This is not needed for requests that return replies
since it automatically flushes in that case.

Release Notes:

- N/A
2025-06-25 19:46:15 +00:00
Matin Aniss
e5c812fbcb gpui: Dynamic element arena (#32079)
Implements a chunking strategy for the element arena that allows it to
grow dynamically based on allocations, it is initialised with a single
chunk of a total size of 1 mebibyte. On allocation of data with a size
greater than the remaining space of the current chunk a new chunk is
created.

This reduces the memory allocation from the static 32 mebibytes, this
especially helps GPUI applications that don't need such a large element
arena and even Zed in most cases. This also prevents the panic when
allocations ever exceed the element arena.

Release Notes:

- N/A

---------

Co-authored-by: Michael Sloan <michael@zed.dev>
2025-06-25 13:29:13 -06:00
Peter Tripp
294147f473 ci: Skip build_docs more often (#33398)
Don't run `build_docs` when the only change is:
`.github/{workflows,ISSUE_TEMPLATE}/**`.
Example [extra
run](https://github.com/zed-industries/zed/actions/runs/15883155767).

Release Notes:

- N/A
2025-06-25 14:24:47 -04:00
Richard Feldman
4516b099e7 Reduce segment cloning when rendering messages (#33340)
While working on retries, I discovered some opportunities to reduce
cloning of message segments. These segments have full `String`s (not
`SharedString`s), so cloning them means copying cloning all the bytes of
all the strings in the message, which would be nice to avoid!

Release Notes:

- N/A
2025-06-25 14:10:48 -04:00
Peter Tripp
8e831ced5b ci: Remove community_delete_comments (#33396)
This was a temporary mitigation against a spam campaign, I don't think
this is required any longer. We can easily revert if it's still active.

See:
- https://github.com/zed-industries/zed/pull/16886

Release Notes:

- N/A
2025-06-25 13:44:43 -04:00
Joseph T. Lyons
3740eec5bf Do not show update "View Release Notes" notification in nightly builds (#33394)
These are useless in nightly, as the link within the notification simply
directs us to a commit view on GitHub. We update frequently on nightly;
dismissing this after every update is annoying.

Release Notes:

- N/A
2025-06-25 17:28:06 +00:00
Peter Tripp
2a5a1814cd text_thread: Improve roles after assistant::Split (shift-enter) (#33215)
Default to `You` when triggering `assistant::Split` at the end of a thread

Release Notes:

- agent_thread: Improved roles when triggering `assistant::Split`
(`shift-enter`)
2025-06-25 13:26:24 -04:00
Umesh Yadav
cc62125244 agent: Add GEMINI.md as a supported rules file name (#33381)
Gemini cli creates GEMINI.md file. This PR adds support for it.

Release Notes:

- agent: Add GEMINI.md as a supported rules file name
2025-06-25 14:14:49 -03:00
Bennet Bo Fenner
224de2ec6c settings: Remove version fields (#33372)
This cleans up our settings to not include any `version` fields, as we
have an actual settings migrator now.

This PR removes `language_models > anthropic > version`,
`language_models > openai > version` and `agent > version`.

We had migration paths in the code for a long time, so in practice
almost everyone should be using the latest version of these settings.


Release Notes:

- Remove `version` fields in settings for `agent`, `language_models >
anthropic`, `language_models > openai`. Your settings will automatically
be migrated. If you're running into issues with this open an issue
[here](https://github.com/zed-industries/zed/issues)
2025-06-25 19:05:29 +02:00
Kirill Bulatov
c0acd8e8b1 Add language server control tool into the status bar (#32490)
Release Notes:

- Added the language server control tool into the status bar

---------

Co-authored-by: Nate Butler <iamnbutler@gmail.com>
2025-06-25 19:57:28 +03:00
Cole Miller
91c9281cea Default to cargo-zigbuild for ZED_BUILD_REMOTE_SERVER (#33391)
Follow-up to #31467. `cargo-zigbuild` will be installed if it's not
there already, but you have to install Zig yourself. Pass
`ZED_BUILD_REMOTE_SERVER=cross` to use the old way.

Release Notes:

- N/A
2025-06-25 16:49:37 +00:00
Peter Tripp
84494ab26b Make ctrl-alt-b / cmd-alt-b toggle right dock (#33190)
Closes: https://github.com/zed-industries/zed/issues/33147

In VSCode ctrl-alt-b / cmd-alt-b toggles the right dock. Zed should
follow this behavior.

See also:
- https://github.com/zed-industries/zed/pull/31630

Release Notes:

- N/A
2025-06-25 12:48:46 -04:00
Peter Tripp
93d670af13 Fix empty code actions menu trapping cursor (#33386)
Closes: https://github.com/zed-industries/zed/issues/33382
Follow-up to: https://github.com/zed-industries/zed/pull/32579

CC: @ConradIrwin @Anthony-Eid 

Release Notes:

- Fixed an issue with empty code actions menu locking the cursor
(Preview Only)
2025-06-25 12:48:15 -04:00
Peter Tripp
7d087ea5d2 docs: Improve visual-customization.md docs for Zed prompts (#33254)
Release Notes:

- N/A
2025-06-25 12:48:03 -04:00
ddoemonn
19c9fb3118 Allow multiple Markdown preview tabs (#32859)
Closes #32791


https://github.com/user-attachments/assets/8cb90e3d-ef7b-407f-b78b-7ba4ff6d8df2

Release Notes:
- Allowed multiple Markdown preview tabs
2025-06-25 16:43:00 +00:00
Oleksiy Syvokon
b0bab0bf9a agent: Prevent use of disabled tools (#33392)
The agent now checks if a tool is enabled in the current profile before
calling it. Previously, the agent could still call disabled tools, which
commonly happened after switching profiles in the middle of a thread.

Release Notes:

- Fixed a bug where the agent could use disabled tools sometimes
2025-06-25 16:30:22 +00:00
CharlesChen0823
630a326a07 file_finder: Fix create wrong file in multiple worktree (#33139)
When open multiple worktree, using `file_finder` to create a new file
shoud respect current focused worktree.
test case:
```
project:
   worktree A
        file1
   worktree B
        file2 <-  focused
```
when focused `file2`, `ctrl-p` toggle `file_finder` to create `file3`
should exists in worktreeB.


I try add test case for `CreateNew` in file_finder, but found not
worked, if you help me, I can try add this test case.

Release Notes:

- Fixed file finder selecting wrong worktree when creating a file
2025-06-25 19:17:41 +03:00
Joseph T. Lyons
6848073c38 Bump Zed to v0.194 (#33390)
Release Notes:

-N/A
2025-06-25 16:10:11 +00:00
Smit Barmase
eb51041154 debugger_ui: Fix variable completion accept in console appends the whole word (#33378)
Closes #32959

Release Notes:

- Fixed the issue where accepting variable completion in the Debugger
would append the entire variable name instead of the remaining part.
2025-06-25 19:54:08 +05:30
Sarmad Gulzar
308debe47f terminal: Fix trailing single quote included when opening link from terminal (#33376)
Closes #33210 

Release Notes:

- Fixed an issue where a trailing single quote was included when opening
a link from the terminal.
2025-06-25 19:51:33 +05:30
Vladimir Kuznichenkov
0905255fd1 bedrock: Add prompt caching support (#33194)
Closes https://github.com/zed-industries/zed/issues/33221

Bedrock has similar to anthropic caching api, if we want to cache
messages up to a certain point, we should add a special block into that
message.

Additionally, we can cache tools definition by adding cache point block
after tools spec.

See: [Bedrock User Guide: Prompt
Caching](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html#prompt-caching-models)

Release Notes:

- bedrock: Added prompt caching support

---------

Co-authored-by: Oleksiy Syvokon <oleksiy@zed.dev>
2025-06-25 17:15:13 +03:00
Bennet Bo Fenner
59aeede50d vercel: Use proper model identifiers and add image support (#33377)
Follow up to previous PRs:
- Return `true` in `supports_images` - v0 supports images already
- Rename model id to match the exact version of the model `v0-1.5-md`
(For now we do not expose `sm`/`lg` variants since they seem not to be
available via the API)
- Provide autocompletion in settings for using `vercel` as a `provider`

Release Notes:

- N/A
2025-06-25 13:26:41 +00:00
Bennet Bo Fenner
18f1221a44 vercel: Reuse existing OpenAI code (#33362)
Follow up to #33292

Since Vercel's API is OpenAI compatible, we can reuse a bunch of code.

Release Notes:

- N/A
2025-06-25 15:04:43 +02:00
Rodrigo Freire
c979452c2d Implement indent conversion editor commands (#32340)
## Description of Feature or Change

Zed currently lacks a built-in way to convert a file’s indentation style
on the fly. While it's possible to change indentation behavior via
global or language-specific settings, these changes are persistent and
broad in scope as they apply to all files or all files of a given
language. We believe this could be improved for quick one-off
adjustments to specific files.

This PR introduces two new editor commands:
`Editor::convert_indentation_to_spaces` and
`Editor::convert_indentation_to_tabs`. These commands allow users to
convert the indentation of either the entire buffer or a selection of
lines, to spaces or tabs. Indentation levels are preserved, and any
mixed whitespace lines are properly normalized.

This feature is inspired by VS Code’s "Convert Indentation to
Tabs/Spaces" commands, but offers faster execution and supports
selection-based conversion, making it more flexible for quick formatting
changes.

## Implementation Details

To enable selection-based indentation conversion, we initially
considered reusing the existing `Editor::manipulate_lines` function,
which handles selections for line-based manipulations. However, this
method was designed specifically for operations like sorting or
reversing lines, and does not allow modifications to the line contents
themselves.

To address this limitation, we refactored the method into a more
flexible version: `Editor::manipulate_generic_lines`. This new method
passes a reference to the selected text directly into a callback, giving
the callback full control over how to process and construct the
resulting lines. The callback returns a `String` containing the modified
text, as well as the number of lines before and after the
transformation. These counts are computed using `.len()` on the line
vectors during manipulation, which is more efficient than calculating
them after the fact.


```rust
fn manipulate_generic_lines<M>(
  &mut self,
  window: &mut Window,
  cx: &mut Context<Self>,
  mut manipulate: M,
) where
   M: FnMut(&str) -> (String, usize, usize),
 {
   // ... Get text from buffer.text_for_range() ...
   let (new_text, lines_before, lines_after) = manipulate(&text);
   // ...
``` 

We now introduce two specialized methods:
`Editor::manipulate_mutable_lines` and
`Editor::manipulate_immutable_lines`. Each editor command selects the
appropriate method based on whether it needs to modify line contents or
simply reorder them. This distinction is important for performance: when
line contents remain unchanged, working with an immutable reference as
`&mut Vec<&str>` is both faster and more memory-efficient than using an
owned `&mut Vec<String>`.

## Demonstration


https://github.com/user-attachments/assets/e50b37ea-a128-4c2a-b252-46c3c4530d97



Release Notes:

- Added `editor::ConvertIndentationToSpaces` and
`editor::ConvertIndentationToTabs` actions to change editor indents

---------

Co-authored-by: Pedro Silveira <pedroruanosilveira@tecnico.ulisboa.pt>
2025-06-25 12:02:42 +00:00
Shardul Vaidya
4396ac9dd6 bedrock: DeepSeek does not support receiving Reasoning Blocks (#33326)
Closes #32341

Release Notes:

- Fixed DeepSeek R1 errors for reasoning blocks being sent back to the model.
2025-06-25 14:51:25 +03:00
Vladimir Kuznichenkov
c6ff58675f bedrock: Fix empty tool input on project diagnostic in bedrock (#33369)
Bedrock [do not accept][1] `null` as a JSON value input for the tool
call when called back.

Instead of passing null, we will pass back an empty object, which is
accepted by API

Closes #33204

Release Notes:

- Fixed project diagnostic tool call for bedrock

[1]:
https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ToolUseBlock.html
2025-06-25 14:28:36 +03:00
Bennet Bo Fenner
1c6b4712a3 agent: Fix issue where unconfigured MCP extensions would not start server (#33365)
Release Notes:

- agent: Fix an issue where MCP servers that were provided by extensions
would sometimes not start up
2025-06-25 11:48:38 +02:00
Umesh Yadav
108162423d language_models: Emit UsageUpdate events for token usage in DeepSeek and OpenAI (#33242)
Closes #ISSUE

Release Notes:

- N/A
2025-06-25 09:42:30 +02:00
Vladimir Kuznichenkov
098896146e bedrock: Fix subsequent bedrock tool calls fail (#33174)
Closes #30714

Bedrock converse api expect to see tool options if at least one tool was
used in conversation in the past messages.

Right now if `LanguageModelToolChoice::None` isn't supported edit agent
[remove][1] tools from request. That point breaks Converse API of
Bedrock. As was proposed in [the issue][2] we won't drop tool choose but
instead will deny any of them if model will respond with a tool choose.

[1]:
fceba6c795/crates/assistant_tools/src/edit_agent.rs (L703)
[2]:
https://github.com/zed-industries/zed/issues/30714#issuecomment-2886422716

Release Notes:

- Fixed bedrock tool calls in edit mode
2025-06-25 10:37:07 +03:00
Michael Sloan
96409965e4 Cleanup handling of surrounding word logic, fixing crash in editor::SelectAllMatches (#33353)
This reduces code complexity and avoids unnecessary roundtripping
through `DisplayPoint`. Hopefully this doesn't cause behavior changes,
but has one known behavior improvement:

`clip_at_line_ends` logic caused `is_inside_word` to return false when
on a word at the end of the line. In vim mode, this caused
`select_all_matches` to not select words at the end of lines, and in
some cases crashes due to not finding any selections.

Closes #29823

Release Notes:

- N/A
2025-06-24 23:18:35 -06:00
marton csutora
014f93008a Make remote mkdir shell-independent for compatibility (#32997)
- Closes: #30962 

Nushell does not support mkdir -p
So invoke sh -c "mkdir -p" instead which will also work under nushell.

Release Notes:

- Fixed ssh remotes running Nushell (and possibly other non
posix-compliant shells)

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-06-25 03:21:59 +00:00
Cole Miller
17774b17fb debugger: Add a tooltip to the session picker with the session ID (#33331)
This helps correlate sessions in the picker with entries in the debug
adapter logs view.

Release Notes:

- N/A
2025-06-25 02:20:31 +00:00
Remco Smits
cf086544e3 debugger: Add support for completion triggers in debug console (#33211)
Release Notes:

- Debugger: Add support for completion triggers in debug console
2025-06-24 22:19:00 -04:00
Michael Sloan
aa330fcf2c Use background task for settings migrations + notify about errors (#30009)
Release Notes:

- N/A
2025-06-24 23:54:03 +00:00
vipex
be95716e94 helix: Prevent cursor move on entering insert mode (#33201)
Closes #33061


https://github.com/user-attachments/assets/3b3e146e-7c12-412e-b4dd-c70411891b9e

Release Notes:

- Fixed cursor unexpectedly moving when entering/exiting insert mode in
Helix mode, making the behavior consistent with the Helix editor.
2025-06-24 15:20:14 -06:00
waffle
f738fbd4f8 gpui: Disable rounding in the layout engine (#31836)
Rounding broke (among other things, probably) pixel-perfect image
rendering with non-power-of-two scaling factor.

An example which reproduces the problem can be found
[here](https://github.com/WaffleLapkin/gpui_taffy_rounding_whyyyyy).

How it looks with `gpui` from `main`:
![2025-05-31
11:34:25+CEST](https://github.com/user-attachments/assets/2cb19312-6ba6-4e80-8072-f89ddedff77b)

How it looks with this patch:
![2025-05-31
11:35:28+CEST](https://github.com/user-attachments/assets/114b52a9-58c0-4600-871c-a20eceb7179e)

Both screenshots are made on kde+wayland with magnification using kde's
built-in magnification (`Meta`+`+`, `Meta`+`-`). Note that screenshot
apps have a high chance of lying 🙃

The image itself is 400 by 300 pixels of red/green checkerboard pattern
made specifically to exaggerate scaling issues.

Release Notes:

- N/A
2025-06-24 20:28:57 +00:00
Joseph T. Lyons
0c78a115de Patch panic around pinned tab count (#33335)
After much investigation, I have not been able to track down what is
causing [this
panic](https://github.com/zed-industries/zed/issues/33342). I'm clamping
the value for now, because a bug is better than a crash. Hopefully
someone finds reproduction steps, and I will implement a proper fix.

Release Notes:

- N/A
2025-06-24 20:24:06 +00:00
Michael Sloan
9427526a41 gpui: Clear the element arena after presenting the frame (#33338)
This is an easy way to shave some microseconds off the critical path for
frame rendering. On my machine this reduces typical frame rendering
latency by ~100 microseconds, probably quite a bit more on slower
machines.

Here is how long it typically takes to drop elements from the arena,
from a fairly brief run:


![image](https://github.com/user-attachments/assets/65cfd911-eccf-4393-887d-8cad2cd27148)

Release Notes:

- N/A
2025-06-24 19:43:33 +00:00
Kirill Bulatov
eec26c9a41 Add initial docs for editor diagnostics (#33325)
Release Notes:

- N/A
2025-06-24 22:21:27 +03:00
Anthony Eid
3c0475d182 debugger: Reorder step icons to be consistent with other editors (#33330)
Closes #33303

Release Notes:

- debugger: Swap step in/out icon positions in debug panel to be
consistent with other editors
2025-06-24 19:15:34 +00:00
Anthony Eid
fc1fc264ec debugger: Generate inline values based on debugger.scm file (#33081)
## Context

To support inline values a language will have to implement their own
provider trait that walks through tree sitter nodes. This is overly
complicated, hard to accurately implement for each language, and lacks
proper extension support.

This PR switches to a singular inline provider that uses a language's
`debugger.scm` query field to capture variables and scopes. The inline
provider is able to use this information to generate inlays that take
scope into account and work with any language that defines a debugger
query file.

### Todos
- [x] Implement a utility test function to easily test inline values
- [x] Generate inline values based on captures
- [x] Reimplement Python, Rust, and Go support
- [x] Take scope into account when iterating through variable captures
- [x] Add tests for Go inline values
- [x] Remove old inline provider code and trait implementations

Release Notes:

- debugger: Generate inline values based on a language debugger.scm file
2025-06-24 18:24:43 +00:00
Peter Tripp
800b925fd7 Improve Atom keymap (#33329)
Closes: https://github.com/zed-industries/zed/issues/33256

Move some Editor keymap entries into `Editor && mode == full`

Release Notes:

- N/A
2025-06-24 18:02:07 +00:00
fantacell
95cf153ad7 Simulate helix line wrapping (#32763)
In helix the `f`, `F`, `t`, `T`, left and right motions wrap lines. I
added that by default.

Release Notes:

- vim: The `use_multiline_find` setting is replaced by binding to the
correct action in the keymap:
    ```
"f": ["vim::PushFindForward", { "before": false, "multiline": true }],
"t": ["vim::PushFindForward", { "before": true, "multiline": true }],
"shift-f": ["vim::PushFindBackward", { "after": false, "multiline": true
}],
"shift-t": ["vim::PushFindBackward", { "after": true, "multiline": true
}],
    ```
- helix: `f`/`t`/`shift-f`/`shift-t`/`h`/`l`/`left`/`right` are now
multiline by default (like helix)
2025-06-24 10:51:41 -06:00
Bennet Bo Fenner
7be57baef0 agent: Fix issue with Anthropic thinking models (#33317)
cc @osyvokon 

We were seeing a bunch of errors in our backend when people were using
Claude models with thinking enabled.

In the logs we would see
> an error occurred while interacting with the Anthropic API:
invalid_request_error: messages.x.content.0.type: Expected `thinking` or
`redacted_thinking`, but found `text`. When `thinking` is enabled, a
final `assistant` message must start with a thinking block (preceeding
the lastmost set of `tool_use` and `tool_result` blocks). We recommend
you include thinking blocks from previous turns. To avoid this
requirement, disable `thinking`. Please consult our documentation at
https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking

However, this issue did not occur frequently and was not easily
reproducible. Turns out it was triggered by us not correctly handling
[Redacted Thinking
Blocks](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#thinking-redaction).

I could constantly reproduce this issue by including this magic string:
`ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB
` in the request, which forces `claude-3-7-sonnet` to emit redacted
thinking blocks (confusingly the magic string does not seem to be
working for `claude-sonnet-4`). As soon as we hit a tool call Anthropic
would return an error.

Thanks to @osyvokon for pointing me in the right direction 😄!


Release Notes:

- agent: Fixed an issue where Anthropic models would sometimes return an
error when thinking was enabled
2025-06-24 16:23:59 +00:00
Kirill Bulatov
39dc4b9040 Fix being unable to input a whitespace character in collab channels filter (#33318)
Before, `space` was always causing a channel join.
Now it's less fluent, one has to press `ESC` to get the focus out of the
filter editor and then `space` starts joining the channel.

Release Notes:

- Fixed being unable to input a whitespace character in collab channels
filter
2025-06-24 16:11:25 +00:00
Ben Kunkle
deb2564b31 gpui: Add keybind metadata API (#33316)
Closes #ISSUE

Adds a very simple API to track metadata about keybindings in GPUI,
namely the source of the binding. The motivation for this is displaying
the source of keybindings in the [keymap
UI](https://github.com/zed-industries/zed/pull/32436).

The API is designed to be as simple and flexible as possible, storing
only a `Option<u32>` on the bindings themselves to keep the struct
small. It is intended to be used as an index or key into a table/map
created and managed by the consumer of the API to map from indices to
arbitrary meta-data. I.e. the consumer is responsible for both
generating these indices and giving them meaning.

The current usage in Zed is stateless, just a mapping between constants
and User, Default, Base, and Vim keymap sources, however, this can be
extended in the future to also track _which_ base keymap is being used.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-06-24 16:07:45 +00:00
morgankrey
13f134448d collab: Require billing address in all Stripe checkouts (#32980)
Summary

I've successfully implemented the required billing address collection
feature for Stripe Checkout sessions. Here's what was done:

### 1. **Added New Data Structures** (`stripe_client.rs`):
- Added `StripeBillingAddressCollection` enum with `Auto` and `Required`
variants
- Added `billing_address_collection` field to
`StripeCreateCheckoutSessionParams`

### 2. **Updated Stripe Client Implementation**
(`real_stripe_client.rs`):
- Added conversion from `StripeBillingAddressCollection` to Stripe's
`CheckoutSessionBillingAddressCollection`
- Updated the `TryFrom` implementation to map the billing address
collection field when creating checkout sessions
- Added the necessary import

### 3. **Updated Billing Service** (`stripe_billing.rs`):
- Set `billing_address_collection` to `Required` in both
`checkout_with_zed_pro()` and `checkout_with_zed_pro_trial()` methods
- Added the necessary import

### 4. **Updated Test Infrastructure** (`fake_stripe_client.rs`):
- Added `billing_address_collection` field to
`StripeCreateCheckoutSessionCall`
- Updated the `create_checkout_session` implementation to capture the
new field
- Added the necessary import

### 5. **Updated Tests** (`stripe_billing_tests.rs`):
- Added assertions to verify that `billing_address_collection` is set to
`Required` in all three test cases:
  - `test_checkout_with_zed_pro`
  - `test_checkout_with_zed_pro_trial` (regular trial)
  - `test_checkout_with_zed_pro_trial` (extended trial)
- Added the necessary import

The implementation follows the pattern established in the codebase and
ensures that whenever a Stripe Checkout session is created for Zed Pro
subscriptions (both regular and trial), the billing address will be
required from customers. This aligns with the Stripe documentation you
provided, which shows that setting `billing_address_collection=required`
will ensure the billing address is always collected during checkout.

Release Notes:

- N/A

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-06-24 14:06:00 +00:00
Danilo Leal
94735aef69 Add support for Vercel as a language model provider (#33292)
Vercel v0 is an OpenAI-compatible model, so this is mostly a dupe of the
OpenAI provider files with some adaptations for v0, including going
ahead and using the custom endpoint for the API URL field.

Release Notes:

- Added support for Vercel as a language model provider.
2025-06-24 11:02:06 -03:00
Danilo Leal
0d70bcb88c agent: Allow to force uninstall extension if it provides more than the MCP server (#33279) 2025-06-24 09:11:03 -03:00
Ben Brandt
360d73c763 Implement save functionality for diff view (#33298)
Add `can_save` and `save` methods to `DiffView`, enabling users to save
changes made within the diff view.

Release Notes:

- Allow saving changes in the `zed --diff` view
2025-06-24 09:17:53 +00:00
Stanislav Alekseev
668d5eef3b Add horizontal scroll to REPL outputs (#33247)
Release Notes:

- Made the horizontal outputs scrollable in REPL
2025-06-24 10:15:20 +03:00
Max Brunsfeld
4cd4d28531 Move UI code from assistant_context_editor -> agent_ui (#33289)
This breaks a transitive dependency of `agent` on UI crates. I've also
found and eliminated some dead code in assistant_context_editor.

Release Notes:

- N/A
2025-06-23 22:22:01 -07:00
Michael Sloan
786e724684 Fetch and wait for channels when opening channel notes via URL (#33291)
Release Notes:

* Collaboration: Now fetches and waits for channels when opening channel
notes via URL.
2025-06-24 05:14:15 +00:00
Michael Sloan
24c94d474e gpui: Simplify Action macros + support doc comments in actions! (#33263)
Instead of a menagerie of macros for implementing `Action`, now there
are just two:

* `actions!(editor, [MoveLeft, MoveRight])`
* `#[derive(..., Action)]` with `#[action(namespace = editor)]`

In both contexts, `///` doc comments can be provided and will be used in
`JsonSchema`.

In both contexts, parameters can provided in `#[action(...)]`:

- `namespace = some_namespace` sets the namespace. In Zed this is
required.

- `name = "ActionName"` overrides the action's name. This must not
contain "::".

- `no_json` causes the `build` method to always error and
`action_json_schema` to return `None`
and allows actions not implement `serde::Serialize` and
`schemars::JsonSchema`.

- `no_register` skips registering the action. This is useful for
implementing the `Action` trait
while not supporting invocation by name or JSON deserialization.

- `deprecated_aliases = ["editor::SomeAction"]` specifies deprecated old
names for the action.
These action names should *not* correspond to any actions that are
registered. These old names
can then still be used to refer to invoke this action. In Zed, the
keymap JSON schema will
accept these old names and provide warnings.

- `deprecated = "Message about why this action is deprecation"`
specifies a deprecation message.
In Zed, the keymap JSON schema will cause this to be displayed as a
warning. This is a new feature.

Also makes the following changes since this seems like a good time to
make breaking changes:

* In `zed.rs` tests adds a test with an explicit list of namespaces. The
rationale for this is that there is otherwise no checking of `namespace
= ...` attributes.

* `Action::debug_name` renamed to `name_for_type`, since its only
difference with `name` was that it

* `Action::name` now returns `&'static str` instead of `&str` to match
the return of `name_for_type`. This makes the action trait more limited,
but the code was already assuming that `name_for_type` is the same as
`name`, and it requires `&'static`. So really this just makes the trait
harder to misuse.

* Various action reflection methods now use `&'static str` instead of
`SharedString`.

Release Notes:

- N/A
2025-06-24 04:34:51 +00:00
Kirill Bulatov
21f985a018 Stop showing diagnostics in the diff-related editors (#33285)
<img width="1728" alt="image"
src="https://github.com/user-attachments/assets/a63925a7-8e13-4d48-bd31-33f434209ea6"
/>

Diagnostics UI elements (underlines, popovers, hovers) are quite noisy
by themselves and get even more so with the git background colors.

Release Notes:

- Stopped showing diagnostics in the diff-related editors
2025-06-24 01:35:08 +00:00
Max Brunsfeld
2283ec5de2 Extract an agent_ui crate from agent (#33284)
This PR moves the UI-dependent logic in the `agent` crate into its own
crate, `agent_ui`. The remaining `agent` crate no longer depends on
`editor`, `picker`, `ui`, `workspace`, etc.

This has compile time benefits, but the main motivation is to isolate
our core agentic logic, so that we can make agents more
pluggable/configurable.

Release Notes:

- N/A
2025-06-23 18:00:28 -07:00
okhai
371b7355d3 Add icon for Cairo files (#33268)
Discussion: https://github.com/zed-industries/zed/discussions/33270

Release Notes:

- Add file icon for the Cairo programming language.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-06-23 20:53:55 -03:00
Carl Sverre
95f10fd187 Add ability to clone item when using workspace::MoveItemToPane (#32895)
This PR adds an optional `clone: bool` argument to
`workspace::MoveItemToPane` and `workspace::MoveItemToPaneInDirection`
which causes the item to be cloned into the destination pane rather than
moved. It provides similar functionality to
`workbench.action.splitEditorToRightGroup` in vscode.

This PR supercedes #25030.

Closes #24889

Release Notes:

- Add optional `clone: bool` (default: `false`) to
`workspace::MoveItemToPane` and `workspace::MoveItemToPaneInDirection`
which causes the item to be cloned into the destination pane rather than
moved.
2025-06-23 23:41:46 +00:00
Danilo Leal
324cbecb74 agent: Improve MCP with no config editor empty state (#33282)
Removed an additional label from the modal empty state as I figured we
don't need it, given we already have a version of the description in
place for when the extension itself doesn't return any. So, ultimately,
having both the label and the description was redundant.

Release Notes:

- N/A
2025-06-23 23:33:19 +00:00
Danilo Leal
32df9256c3 notification: Add built-in dismiss button in the Status Toast component (#33278)
There may be cases where we're needing to pass a button just so it is
dismissible, so I figured this out help! It also helps when you want to
have two buttons, one to perform an action and another to dismiss and
cancel.

Release Notes:

- N/A
2025-06-23 19:55:21 -03:00
Maxim
36eebb7ba8 Fix race condition between auto-indent and on-type-formatting (#32005)
This PR addresses to fix (#31308) a race condition where auto-indent (in
buffer.cs) and on-type-formatting (in lsp_store.rs) concurrently
calculate indentation using the same buffer snapshot.

Previous Solution (Abandoned): 
https://github.com/zed-industries/zed/pull/31340

Final Solution:
Delay applying on-type-formatting until auto-indent is complete.

Issue:

If AutoindentMode finishes first, formatting works correctly. If
"Formatting on typing" starts before AutoindentMode completes, it
results in double indentation.

Closes #31308

Release Notes:

- Fixed a race condition resulting in incorrect buffer contents when combining auto-indent and on-type-formatting
2025-06-23 17:59:06 -04:00
Anthony Eid
d34d4f2ef1 debugger: Kill debug sessions on app quit (#33273)
Before this PR force quitting Zed would leave hanging debug adapter
processes and not allow debug adapters to clean up their sessions
properly.

This PR fixes this problem by sending a disconnect/terminate to all
debug adapters and force shutting down their processes after they
respond.

Co-authored-by: Cole Miller \<cole@zed.dev\>

Release Notes:

- debugger: Shutdown and clean up debug processes when force quitting
Zed

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Remco Smits <djsmits12@gmail.com>
2025-06-23 20:41:53 +00:00
Richard Feldman
c610ebfb03 Thread Anthropic errors into LanguageModelKnownError (#33261)
This PR is in preparation for doing automatic retries for certain
errors, e.g. Overloaded. It doesn't change behavior yet (aside from some
granularity of error messages shown to the user), but rather mostly
changes some error handling to be exhaustive enum matches instead of
`anyhow` downcasts, and leaves some comments for where the behavior
change will be in a future PR.

Release Notes:

- N/A
2025-06-23 18:48:26 +00:00
Cole Miller
aabfea4c10 debugger: Document workaround for debugging Swift (#33269)
Release Notes:

- N/A
2025-06-23 18:29:20 +00:00
Danilo Leal
51059b6f50 agent: Rename the delete menu item in the MCP section (#33265)
From "Delete" to "Uninstall" for clarity.

Release Notes:

- N/A
2025-06-23 18:20:12 +00:00
Cole Miller
6b0325b059 debugger: Document some troubleshooting tools (#33047)
Release Notes:

- N/A
2025-06-23 17:11:39 +00:00
Cole Miller
c9bd409732 debugger: Support passing custom arguments to debug adapters (#33251)
Custom arguments replace any arguments that we normally pass to the DAP.
For interpreted languages, they are passed to the interpreter after the
DAP path or module. They can be combined with a custom binary, or you
can omit `dap.binary` and just customize the arguments to the DAPs we
download.

This doesn't take care of updating the extension API to support custom
arguments.

Release Notes:

- debugger: Implemented support for passing custom arguments to a debug
adapter binary using the `dap.args` setting.
- debugger: Fixed not being able to use the `dap` setting in
`.zed/settings.json`.
2025-06-23 17:06:48 +00:00
Matin Aniss
a067c16c82 windows: Use drop target helper (#33203)
It now utilises the
[`IDropTargetHelper`](https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-idroptargethelper)
in drag and drop events to render the proper item drop cursor icon which
includes the thumbnail when available and action text. Also swaps the
drop effect from `DROPEFFECT_LINK` to `DROPEFFECT_COPY` to match other
Windows application behaviour.

Example of drop icon

![example_drop](https://github.com/user-attachments/assets/4f8ea86c-929a-4813-9f8e-b3553ecf4d6e)

Release Notes:

- N/A

---------

Co-authored-by: 张小白 <364772080@qq.com>
2025-06-23 13:30:21 +00:00
Matin Aniss
1a6c1b2159 windows: Fix window close animation (#33228)
Implements a workaround which removes the `WS_EX_LAYERED` style from the
window right before closing it which seems to fix the window close
animation not playing.

Release Notes:

- N/A
2025-06-23 21:13:53 +08:00
Danilo Leal
8718019b52 file finder: Ensure filter options keybinding is displayed (#33244)
Follow up to https://github.com/zed-industries/zed/pull/31777. I
could've sworn the filter options keybinding was being displayed in the
icon button tooltip, but just realized it actually wasn't. So, this PR
fixes that!

Release Notes:

- N/A
2025-06-23 09:19:14 -03:00
Umesh Yadav
a32505fcc2 agent: Fix token limit callout to show burn mode only for zed provider (#33096)
The token limit reached callout was shown for all the providers and it
included the burn mode toggle and description. I have made that
conditional to only show for zed provider.

Before this changes
<img width="413" alt="image"
src="https://github.com/user-attachments/assets/b7e177fc-d2e6-4942-8934-39ac8c844649"
/>

After this change:
<img width="404" alt="image"
src="https://github.com/user-attachments/assets/0e091b99-c8e3-4960-8b4d-47170883d224"
/>


Release Notes:

- agent: Fix token limit callout to show burn mode only for zed provider

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-06-23 11:36:44 +00:00
Peter Tripp
980917bb7c agent: Preserve chat box text on 'New From Summary' (#33220)
CC: @danilo-leal Do you have thoughts on this? I found myself typing
chat messages after a long thread and then deciding I would be better
served by restarting from a summary -- and then "poof" the contents of
my chat box was lost.

Release Notes:

- agent: "New From Summary" now preserves any unsent content in the chat
box.
2025-06-23 08:35:25 -03:00
Peter Tripp
bd8471bb40 agent: Add soft-wrap to long terminal command JSON (#33188)
Closes: https://github.com/zed-industries/zed/issues/33179

| Before | After | 
| - | - |
| <img width="341" alt="Screenshot 2025-06-21 at 23 06 01"
src="https://github.com/user-attachments/assets/963a3663-b24f-4946-8b48-2c1222cdef2e"
/> | <img width="424" alt="Screenshot 2025-06-21 at 23 08 13"
src="https://github.com/user-attachments/assets/51051221-fda2-48af-ae42-a04989bb4913"
/> |

Release Notes:

- N/A
2025-06-23 08:35:16 -03:00
Danilo Leal
16f1da1b7e agent: Fix long previous user message double scroll (#33056)
Previously, if editing a long previous user message in the thread, you'd
have a double scroll situation because the editor used in that case had
its max number of lines capped. To solve that, I made the `max_lines` in
the editor `AutoHeight` mode optional, allowing me to not pass any
arbitrary number to the previous user message editor, and ultimately,
solving the double scroll problem by not having any scroll at all.

Release Notes:

- agent: Fixed double scroll that happened when editing a long previous
user message.

@ConradIrwin adding you as a reviewer as I'm touching editor code
here... want to be careful. :)
2025-06-23 08:32:05 -03:00
张小白
272fc672af windows: Dialog QoL improvements (#33241)
Just like in the previous PR #33230, we need to properly set up modal
windows to make them work as expected.

Before this PR, when you opened an "Open File" or "Save File" dialog,
clicking the main window would steal focus from the modal, even though
the main window wasn’t actually interactive.

With this PR, clicking the main window while a modal is open does
nothing — as it should — until the modal is closed.

#### Before



https://github.com/user-attachments/assets/9c6bdff0-1c46-49c1-a5ff-751c52c7d613

#### After



https://github.com/user-attachments/assets/8776bd28-85ff-4f32-8390-bcf5b4eec1fe





Release Notes:

- N/A
2025-06-23 11:02:00 +00:00
Bennet Bo Fenner
e68b95c61b agent: Ensure tool names are unique (#33237)
Closes #31903

Release Notes:

- agent: Fix an issue where an error would occur when MCP servers
specified tools with the same name

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-06-23 12:15:08 +02:00
张小白
28f56ad7ae windows: Avoid setting the mouse cursor while the window is disabled (#33230)
Don’t set the mouse cursor when the window is disabled, it causes issues
with modal dialogs.

Release Notes:

- N/A
2025-06-23 05:59:10 +00:00
Michael Sloan
7f44e4b89c Fix logic for updating insert_range on completion resolution (#32523)
I don't have a concrete misbehavior from it, but this update of
`insert_range` doesn't make sense for two reasons:

* If the resolved completion doesn't have a new `text_edit` it would
clear out the `insert_range`.

* It doesn't update the completion if it has already been resolved,
except this update of `insert_range` happened before that.

Guessing it was written this way because this field needed to only be
mutated within the `CompletionSource::Lsp` case and this was a
convenient match below.

Release Notes:

- N/A

Co-authored-by: Smit <smit@zed.dev>
2025-06-23 01:18:47 +00:00
Bennet Bo Fenner
e5ad2c2518 Add crates/assistant_tools/src/edit_agent/evals/fixtures to file_scan_exclusions (#33224)
Follow up to #32211

Release Notes:

- N/A
2025-06-23 01:06:30 +00:00
Artem Zhurikhin
67ac80bd19 linux: Fix KeePassXC integration via org.freedesktop.Secrets (#33026)
Closes #29956

Unlike GNOME Keyring, KeePassXC locks individual secrets in addition to
the entire database when configured to ask for confirmation for access
requests by DBus clients. As such, before the secret is read it should
be unlocked by the client.

Tested against both KeePassXC and GNOME Keyring, and with this patch Zed
successfully logs in and fetches the API keys from the Secret Service.

Release Notes:

- Fixed KeePassXC integration via org.freedesktop.Secrets
2025-06-22 18:57:45 -06:00
Hiroki Tagato
ac30a8b0df Improve FreeBSD support (#33162)
This PR contains a set of changes for improving FreeBSD support (#15309,
#29550) and is a kind of follow up to the PR #20480 which added an
initial support for FreeBSD.

A summary of changes is as follows:
- Add some more freebsd conditionals which seem missing in the previous
PR.
- Implement `anonymous_fd()` and `current_path()` functions for FreeBSD.
- Improve detection of FreeBSD in telemetry and GPU detection.
- Temporarily disable LiveKit/WebRTC support to make build succeed.
- Remove support for flatpak since it is Linux-only packaging format.

Adding `RUSTFLAGS="-C link-dead-code"` does not seem necessary anymore.
It builds fine without the flag.

Known issues:
- Integrated terminal is painfully laggy and virtually unusable in my
environment. This might be specific to my setup.
- I cannot input Japanese using IME. When I type characters, they appear
on the screen. But when I hit return key, they disappears. Seems the
same issue as #15409.

My environment is MATE desktop on X11 on FreeBSD 14.2 on Intel Core
i5-7260U integrated graphics.

P.S. For those who might be interested, a work-in-progress FreeBSD port
and binary packages are available at
https://github.com/tagattie/FreeBSD-Zed

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-22 16:23:17 -04:00
Willem
6b4c607331 bedrock: Support Claude 3.7 in APAC (#33068)
In ap-northeast-1 we have access to 3.7 and 4.0

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-22 20:08:50 +00:00
Peter Tripp
595f61f0d6 bedrock: Use Claude 3.0 Haiku where Haiku 3.5 is not available (#33214)
Closes: https://github.com/zed-industries/zed/issues/33183

@kuzaxak Can you confirm this works for you?

Release Notes:

- bedrock: Use Anthropic Haiku 3.0 in AWS regions where Haiku 3.5 is
unavailable
2025-06-22 15:15:20 -04:00
Vladimir Kuznichenkov
1047d8adec bedrock: Add Sonnet 4 to cross-region model list (eu/apac) (#33192)
Closes #31946

Sonnet 4 is [now
available](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html)
via Bedrock in EU aws regions.

Release Notes:

- bedrock: Add cross-region usage of Sonnet 4 in EU/APAC AWS regions
2025-06-22 15:15:05 -04:00
Peter Tripp
21fd5c24bf emacs: Fix ctrl-p/ctrl-n navigating popover menus (#33218)
Closes https://github.com/zed-industries/zed/issues/33200

Release Notes:

- emacs: Fixed ctrl-p/ctrl-n keyboard navigation of autocomplete/code
actions menus
2025-06-22 15:02:25 -04:00
Danilo Leal
336d2c41fa docs: Update the AI configuration page (#33208)
- Improve the Vercel v0 model section
- Replace "assistant panel" to "agent panel"
- Replace "Zed assistant" to "Zed agent"
- Fix "open configuration" action name
- Break sentences where useful

Release Notes:

- N/A
2025-06-22 16:13:54 +00:00
张小白
5244085bd0 windows: Fix wrong glyph index being reported (#33193)
Closes #32424

This PR fixes two bugs:

* In cases like `fi ~~something~~`, the `fi` gets rendered as a
ligature, meaning the two characters are combined into a single glyph.
The final glyph index didn’t account for this change, which caused
issues.
* On Windows, some emojis are composed of multiple glyphs. These
composite emojis can now be rendered correctly as well.

![屏幕截图 2025-06-22
161900](https://github.com/user-attachments/assets/e125426b-a15e-41d1-a6e6-403a16924ada)

![屏幕截图 2025-06-22
162005](https://github.com/user-attachments/assets/f5f01022-2404-4e73-89e5-1aaddf7419d9)


Release Notes:

- N/A
2025-06-22 11:14:58 +00:00
张小白
af8f26dd34 Add a Copy button for About Zed (#33197)
Closes #33160

Since `TaskDialog` doesn’t allow users to copy its contents directly,
VSCode added a `Copy` button so users can easily copy the message.




https://github.com/user-attachments/assets/04090753-226f-44d9-992c-8cc8cb8d7ecb




Release Notes:

- N/A
2025-06-22 19:12:12 +08:00
yoshi-taka
3b9f504d75 Remove unused dependencies (#33189)
I verified it after running `cargo shear`.

https://crates.io/crates/cargo-shear

Release Notes:

- N/A
2025-06-22 07:43:37 +00:00
Emanuele Stoppa
0579bf73b0 docs: Document language_ids in extension.toml (#33035)
Document `language-servers.*.language_ids` property in extension.toml.

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-06-22 07:12:09 +00:00
Peter Tripp
534475d7aa Add reference to commit_message_model in git docs. (#33186)
Release Notes:

- N/A
2025-06-22 03:04:55 +00:00
Peter Tripp
8d05f6fdd3 Add visual customization section to documentation (#33133)
Release Notes:

- Improved settings documentation for [visual customization](https://zed.dev/docs/visual-customization/)
2025-06-21 22:55:53 -04:00
Jason Lee
3cb4342a76 editor: Disable tab to indent in single line mode (#33031)
Release Notes:

- Disable indent on `tab` in single line editors.

----


https://github.com/user-attachments/assets/64207777-52c9-4425-be66-88acaf81f0b8
2025-06-22 00:55:19 +03:00
Alvaro Parker
6fb1081b61 Disable next/previous hunk menu items when there are no hunks (#32907)
Closes #32887

Release Notes:

- Button menu hunk is disabled if there are no changes
2025-06-22 00:43:00 +03:00
Arseny Kapoulkine
0ed6b4ef1a git_ui: Add collapse_untracked_diff settings to improve usability for untracked files (#32591)
In repositories with untracked files that are not intended to be added,
showing the expanded diffs in the panel is confusing, as it places the
changes side by side with changes in tracked files.

This change adds a setting, collapse_untracked_diff, that can be enabled
to collapse untracked diffs by default when the panel is opened.

See
https://github.com/zed-industries/zed/pull/31855#issuecomment-2957547018
(and previous comment for examples of why this is useful).

Example before this change, or with the setting in its default state:


![image](https://github.com/user-attachments/assets/f974c849-7ebf-424e-9397-442a6cc2513b)

Example after this change with the setting set to `true`:


![image](https://github.com/user-attachments/assets/bd8934f5-bd9a-4f5a-b723-cd4b798d2c2c)

Release Notes:

- Git: Added `collapse_untracked_diff` setting to auto-collapse
untracked diffs
2025-06-22 00:38:24 +03:00
Dallin Huff
d75e210e73 docs: Fix path to keymap.json config file for macOS (#32728)
Update key-bindings doc page to correctly reference
`~/.config/zed/keymap.json` instead of the old `~/.zed/keymap.json`

Closes #ISSUE

Release Notes:

- N/A
2025-06-22 00:31:01 +03:00
Peter Tripp
2b3e453d2f Avoid using tmpdir when writing Zed settings.json on macOS (#32976)
Closes: https://github.com/zed-industries/zed/issues/23907

Release Notes:

- macOS: Fixed an issue with writing Zed settings.json if user's home
directory is on a non-root volume.
2025-06-21 13:27:06 -04:00
Peter Tripp
cb50f07d23 One Light Theme: Change constant color (#33166)
Closes: https://github.com/zed-industries/zed/issues/4617
Closes: https://github.com/zed-industries/zed/issues/25709

Changes the One Light syntax highlight for `constant` to be a distinct
from `comment`:

| Before | After |
| - | - |
| <img width="424" alt="Screenshot 2025-06-21 at 11 59 22"
src="https://github.com/user-attachments/assets/8780918f-7701-4780-809a-f95951970c90"
/> | <img width="430" alt="Screenshot 2025-06-21 at 11 59 17"
src="https://github.com/user-attachments/assets/d7efaa16-f95a-4076-a0b2-b1170e2ffe68"
/> |

I picked a color which was present in the palette from the original One
Light theme from Atom.

@failable: Sorry this took so long, does this look ok to you?

Release Notes:

- N/A
2025-06-21 12:43:02 -04:00
Peter Tripp
a713c66a9d Redact command environment variables from log output (#32985)
Before/After (linebreaks added for readability)
```log 
# before
INFO  [project::context_server_store::extension]
loaded command for context server mcp-server-github:
Command { 
  command: "/Users/peter/Library/Application Support/Zed/extensions/work/mcp-server-github/github-mcp-server-v0.5.0/github-mcp-server", 
  args: ["stdio"], 
  env: [("GITHUB_PERSONAL_ACCESS_TOKEN", "gho_WOOOOOOOOOOOOOOO")] 
}

#after
INFO  [project::context_server_store::extension]
loaded command for context server mcp-server-github:
Command {
  command: "/Users/peter/Library/Application Support/Zed/extensions/work/mcp-server-github/github-mcp-server-v0.5.0/github-mcp-server",
  args: ["stdio"],
  env: [("GITHUB_PERSONAL_ACCESS_TOKEN", "[REDACTED]")]
}
```

Release Notes:

- Redact sensitive environment variables from MCP logs
2025-06-21 11:19:23 -04:00
Smit Barmase
76e3136369 editor: Utilize filter_text from language server for filter_range (#33155)
Closes #33106

Instead of directly using `filter_text` in `StringMatchCandidate`, which
yields better results for fuzzy matching but messes up with highlighting
letters in bold, as `positions` list generated by fuzzy crate are now of
`filter_text` and not `label` shown to the user.

This PR fixes it by keeping use of `filter_range` in
`StringMatchCandidate`, which is range w.r.t to `label` shown to user.
And actually generating this `filter_range` at source by using
`filter_range` if exists.

- [x] Tests

Release Notes:

- Fixed issue where incorrect letters are marked as bold in completions.
2025-06-21 19:47:16 +05:30
Bennet Bo Fenner
834cdc1271 agent: Store if context server should be enabled/disabled in the settings (#32994)
- [x] Show disabled MCP servers in the list so you can enable them again
- [x] If MCP is not present in the settings, add it

Closes #ISSUE

Release Notes:

- agent: Allow to enable/disable context servers permanently in the
agent configuration view

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-06-21 14:46:36 +02:00
Umesh Yadav
dfdd2b9558 language_models: Add thinking support to OpenRouter provider (#32541)
Did some bit cleanup of code for loading models for settings as that is
not required as we are fetching all the models from openrouter so it's
better to maintain one source of truth

Release Notes:

- Add thinking support to OpenRouter provider
2025-06-21 08:03:50 +02:00
Danilo Leal
b9838efaaa agent: Add button to scroll to top of the thread (#33130)
Release Notes:

- agent: Added a button to scroll to top of the thread.
2025-06-20 20:28:07 -03:00
June
db8acfa9ff Update pathfinder_simd for nightly arm simd fixes (#33131)
`pathfinder_simd` doesn't compile on nightly aarch64 right now, but that
was fixed by https://github.com/servo/pathfinder/pull/575 and updated on
crates.io by https://github.com/servo/pathfinder/pull/577. This PR
simply updates the `pathfinder_simd` dependency to the version that
contains these fixes.

I verified that this compiles with nightly on my machine.
2025-06-20 23:13:37 +00:00
Michael Sloan
5c1dc8b2cc Cargo.lock updates (#33129)
Should have been in #33125

Release Notes:

- N/A
2025-06-20 22:42:57 +00:00
Michael Sloan
7e801dccb0 agent: Fix issues with usage display sometimes showing initially fetched usage (#33125)
Having `Thread::last_usage` as an override of the initially fetched
usage could cause the initial usage to be displayed when the current
thread is empty or in text threads. Fix is to just store last usage info
in `UserStore` and not have these overrides

Release Notes:

- Agent: Fixed request usage display to always include the most recently
known usage - there were some cases where it would show the initially
requested usage.
2025-06-20 21:28:48 +00:00
Joseph T. Lyons
e0c0b6f95d Remove duplicate issues in top-ranking issues script (#33127)
Release Notes:

- N/A
2025-06-20 17:13:26 -04:00
Remco Smits
ad76db7244 debugger: Add variable watchers (#32743)
### This PR introduces support for adding watchers to specific
expressions (such as variable names or evaluated expressions).

This feature is useful in scenarios where many variables are in scope,
but only a few are of interest—especially when tracking variables that
change frequently. By allowing users to add watchers, it becomes easier
to monitor the values of selected expressions across stack frames
without having to sift through a large list of variables.


https://github.com/user-attachments/assets/c49b470a-d912-4182-8419-7406ba4c8f1e

------

**TODO**:
- [x] make render variable code reusable for render watch method
- [x] use SharedString for watches because of a lot of cloning
- [x] add tests
  - [x] basic test
  - [x] test step debugging

Release Notes:

- Debugger Beta: Add support for variable watchers

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Anthony <anthony@zed.dev>
2025-06-20 20:45:55 +00:00
Joseph T. Lyons
9f2c541ab0 Fetch 100 results per page (#33124)
Release Notes:

- N/A
2025-06-20 16:44:39 -04:00
Joseph T. Lyons
9597c73f2b Account for issue types in top-ranking issues script (#33118)
Release Notes:

- N/A
2025-06-20 14:19:29 -04:00
427 changed files with 22986 additions and 13368 deletions

View File

@@ -29,6 +29,7 @@ jobs:
outputs:
run_tests: ${{ steps.filter.outputs.run_tests }}
run_license: ${{ steps.filter.outputs.run_license }}
run_docs: ${{ steps.filter.outputs.run_docs }}
runs-on:
- ubuntu-latest
steps:
@@ -58,6 +59,11 @@ jobs:
else
echo "run_tests=false" >> $GITHUB_OUTPUT
fi
if [[ $(git diff --name-only $COMPARE_REV ${{ github.sha }} | grep '^docs/') ]]; then
echo "run_docs=true" >> $GITHUB_OUTPUT
else
echo "run_docs=false" >> $GITHUB_OUTPUT
fi
if [[ $(git diff --name-only $COMPARE_REV ${{ github.sha }} | grep '^Cargo.lock') ]]; then
echo "run_license=true" >> $GITHUB_OUTPUT
else
@@ -198,7 +204,9 @@ jobs:
timeout-minutes: 60
name: Check docs
needs: [job_spec]
if: github.repository_owner == 'zed-industries'
if: |
github.repository_owner == 'zed-industries' &&
(needs.job_spec.outputs.run_tests == 'true' || needs.job_spec.outputs.run_docs == 'true')
runs-on:
- buildjet-8vcpu-ubuntu-2204
steps:
@@ -452,8 +460,10 @@ jobs:
RET_CODE=0
# Always check style
[[ "${{ needs.style.result }}" != 'success' ]] && { RET_CODE=1; echo "style tests failed"; }
[[ "${{ needs.check_docs.result }}" != 'success' ]] && { RET_CODE=1; echo "docs checks failed"; }
if [[ "${{ needs.job_spec.outputs.run_docs }}" == "true" ]]; then
[[ "${{ needs.check_docs.result }}" != 'success' ]] && { RET_CODE=1; echo "docs checks failed"; }
fi
# 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"; }

View File

@@ -1,34 +0,0 @@
name: Delete Mediafire Comments
on:
issue_comment:
types: [created]
permissions:
issues: write
jobs:
delete_comment:
if: github.repository_owner == 'zed-industries'
runs-on: ubuntu-latest
steps:
- name: Check for specific strings in comment
id: check_comment
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
with:
script: |
const comment = context.payload.comment.body;
const triggerStrings = ['www.mediafire.com'];
return triggerStrings.some(triggerString => comment.includes(triggerString));
- name: Delete comment if it contains any of the specific strings
if: steps.check_comment.outputs.result == 'true'
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
with:
script: |
const commentId = context.payload.comment.id;
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId
});

View File

@@ -7,7 +7,7 @@ on:
pull_request:
branches:
- "**"
types: [opened, synchronize, reopened, labeled]
types: [synchronize, reopened, labeled]
workflow_dispatch:
@@ -25,16 +25,6 @@ env:
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
if: github.repository_owner == 'zed-industries'
steps:
- name: No-op
run: echo "Nothing to do"
run_eval:
timeout-minutes: 60
name: Run Agent Eval

1
.gitignore vendored
View File

@@ -12,6 +12,7 @@
.flatpak-builder
.idea
.netrc
*.pyc
.pytest_cache
.swiftpm
.swiftpm/config/registries.json

4
.rules
View File

@@ -100,9 +100,7 @@ Often event handlers will want to update the entity that's in the current `Conte
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.
Actions with no data defined with the `actions!(some_namespace, [SomeAction, AnotherAction])` macro call. Otherwise the `Action` derive macro is used. Doc comments on actions are displayed to the user.
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`.

View File

@@ -2,11 +2,23 @@
{
"label": "Debug Zed (CodeLLDB)",
"adapter": "CodeLLDB",
"build": { "label": "Build Zed", "command": "cargo", "args": ["build"] }
"build": {
"label": "Build Zed",
"command": "cargo",
"args": [
"build"
]
}
},
{
"label": "Debug Zed (GDB)",
"adapter": "GDB",
"build": { "label": "Build Zed", "command": "cargo", "args": ["build"] }
}
"build": {
"label": "Build Zed",
"command": "cargo",
"args": [
"build"
]
}
},
]

View File

@@ -40,6 +40,7 @@
},
"file_types": {
"Dockerfile": ["Dockerfile*[!dockerignore]"],
"JSONC": ["assets/**/*.json", "renovate.json"],
"Git Ignore": ["dockerignore"]
},
"hard_tabs": false,
@@ -47,7 +48,7 @@
"remove_trailing_whitespace_on_save": true,
"ensure_final_newline_on_save": true,
"file_scan_exclusions": [
"crates/assistant_tools/src/evals/fixtures",
"crates/assistant_tools/src/edit_agent/evals/fixtures",
"crates/eval/worktrees/",
"crates/eval/repos/",
"**/.git",

201
Cargo.lock generated
View File

@@ -14,6 +14,7 @@ dependencies = [
"gpui",
"language",
"project",
"proto",
"release_channel",
"smallvec",
"ui",
@@ -55,7 +56,85 @@ version = "0.1.0"
dependencies = [
"agent_settings",
"anyhow",
"assistant_context_editor",
"assistant_context",
"assistant_tool",
"assistant_tools",
"chrono",
"client",
"collections",
"component",
"context_server",
"convert_case 0.8.0",
"feature_flags",
"fs",
"futures 0.3.31",
"git",
"gpui",
"heed",
"http_client",
"icons",
"indoc",
"itertools 0.14.0",
"language",
"language_model",
"log",
"markdown",
"parking_lot",
"paths",
"postage",
"pretty_assertions",
"project",
"prompt_store",
"proto",
"rand 0.8.5",
"ref-cast",
"rope",
"schemars",
"serde",
"serde_json",
"settings",
"smol",
"sqlez",
"telemetry",
"text",
"theme",
"thiserror 2.0.12",
"time",
"util",
"uuid",
"workspace",
"workspace-hack",
"zed_llm_client",
"zstd",
]
[[package]]
name = "agent_settings"
version = "0.1.0"
dependencies = [
"anyhow",
"collections",
"fs",
"gpui",
"language_model",
"paths",
"schemars",
"serde",
"serde_json",
"serde_json_lenient",
"settings",
"workspace-hack",
"zed_llm_client",
]
[[package]]
name = "agent_ui"
version = "0.1.0"
dependencies = [
"agent",
"agent_settings",
"anyhow",
"assistant_context",
"assistant_slash_command",
"assistant_slash_commands",
"assistant_tool",
@@ -67,7 +146,6 @@ dependencies = [
"collections",
"component",
"context_server",
"convert_case 0.8.0",
"db",
"editor",
"extension",
@@ -77,9 +155,7 @@ dependencies = [
"fs",
"futures 0.3.31",
"fuzzy",
"git",
"gpui",
"heed",
"html_to_markdown",
"http_client",
"indexed_docs",
@@ -89,6 +165,7 @@ dependencies = [
"jsonschema",
"language",
"language_model",
"languages",
"log",
"lsp",
"markdown",
@@ -99,13 +176,11 @@ dependencies = [
"parking_lot",
"paths",
"picker",
"postage",
"pretty_assertions",
"project",
"prompt_store",
"proto",
"rand 0.8.5",
"ref-cast",
"release_channel",
"rope",
"rules_library",
@@ -116,7 +191,6 @@ dependencies = [
"serde_json_lenient",
"settings",
"smol",
"sqlez",
"streaming_diff",
"telemetry",
"telemetry_events",
@@ -124,10 +198,11 @@ dependencies = [
"terminal_view",
"text",
"theme",
"thiserror 2.0.12",
"time",
"time_format",
"tree-sitter-md",
"ui",
"unindent",
"urlencoding",
"util",
"uuid",
@@ -136,33 +211,6 @@ dependencies = [
"workspace-hack",
"zed_actions",
"zed_llm_client",
"zstd",
]
[[package]]
name = "agent_settings"
version = "0.1.0"
dependencies = [
"anthropic",
"anyhow",
"collections",
"deepseek",
"fs",
"gpui",
"language_model",
"lmstudio",
"log",
"mistral",
"ollama",
"open_ai",
"paths",
"schemars",
"serde",
"serde_json",
"serde_json_lenient",
"settings",
"workspace-hack",
"zed_llm_client",
]
[[package]]
@@ -508,7 +556,7 @@ dependencies = [
]
[[package]]
name = "assistant_context_editor"
name = "assistant_context"
version = "0.1.0"
dependencies = [
"agent_settings",
@@ -520,31 +568,23 @@ dependencies = [
"clock",
"collections",
"context_server",
"editor",
"feature_flags",
"fs",
"futures 0.3.31",
"fuzzy",
"gpui",
"indexed_docs",
"indoc",
"language",
"language_model",
"languages",
"log",
"multi_buffer",
"open_ai",
"ordered-float 2.10.1",
"parking_lot",
"paths",
"picker",
"pretty_assertions",
"project",
"prompt_store",
"proto",
"rand 0.8.5",
"regex",
"rope",
"rpc",
"serde",
"serde_json",
@@ -553,15 +593,12 @@ dependencies = [
"smol",
"telemetry_events",
"text",
"theme",
"tree-sitter-md",
"ui",
"unindent",
"util",
"uuid",
"workspace",
"workspace-hack",
"zed_actions",
"zed_llm_client",
]
@@ -2648,6 +2685,7 @@ dependencies = [
"http_client",
"language",
"log",
"postage",
"rand 0.8.5",
"release_channel",
"rpc",
@@ -2821,6 +2859,7 @@ dependencies = [
"cocoa 0.26.0",
"collections",
"credentials_provider",
"derive_more",
"feature_flags",
"fs",
"futures 0.3.31",
@@ -2859,6 +2898,7 @@ dependencies = [
"windows 0.61.1",
"workspace-hack",
"worktree",
"zed_llm_client",
]
[[package]]
@@ -2979,7 +3019,7 @@ version = "0.44.0"
dependencies = [
"agent_settings",
"anyhow",
"assistant_context_editor",
"assistant_context",
"assistant_slash_command",
"async-stripe",
"async-trait",
@@ -4117,6 +4157,7 @@ dependencies = [
"paths",
"serde",
"serde_json",
"shlex",
"task",
"util",
"workspace-hack",
@@ -4270,6 +4311,7 @@ version = "0.1.0"
dependencies = [
"alacritty_terminal",
"anyhow",
"bitflags 2.9.0",
"client",
"collections",
"command_palette_hooks",
@@ -4304,6 +4346,7 @@ dependencies = [
"terminal_view",
"theme",
"tree-sitter",
"tree-sitter-go",
"tree-sitter-json",
"ui",
"unindent",
@@ -5060,6 +5103,7 @@ version = "0.1.0"
dependencies = [
"agent",
"agent_settings",
"agent_ui",
"anyhow",
"assistant_tool",
"assistant_tools",
@@ -8158,12 +8202,11 @@ dependencies = [
name = "inline_completion"
version = "0.1.0"
dependencies = [
"anyhow",
"client",
"gpui",
"language",
"project",
"workspace-hack",
"zed_llm_client",
]
[[package]]
@@ -8941,6 +8984,7 @@ dependencies = [
"ui",
"ui_input",
"util",
"vercel",
"workspace-hack",
"zed_llm_client",
]
@@ -8974,11 +9018,13 @@ dependencies = [
"collections",
"copilot",
"editor",
"feature_flags",
"futures 0.3.31",
"gpui",
"itertools 0.14.0",
"language",
"lsp",
"picker",
"project",
"release_channel",
"serde_json",
@@ -9196,7 +9242,7 @@ dependencies = [
[[package]]
name = "libwebrtc"
version = "0.3.10"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=80bb8f4c9112789f7c24cc98d8423010977806a6#80bb8f4c9112789f7c24cc98d8423010977806a6"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4#d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4"
dependencies = [
"cxx",
"jni",
@@ -9276,7 +9322,7 @@ checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
[[package]]
name = "livekit"
version = "0.7.8"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=80bb8f4c9112789f7c24cc98d8423010977806a6#80bb8f4c9112789f7c24cc98d8423010977806a6"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4#d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4"
dependencies = [
"chrono",
"futures-util",
@@ -9299,7 +9345,7 @@ dependencies = [
[[package]]
name = "livekit-api"
version = "0.4.2"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=80bb8f4c9112789f7c24cc98d8423010977806a6#80bb8f4c9112789f7c24cc98d8423010977806a6"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4#d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4"
dependencies = [
"futures-util",
"http 0.2.12",
@@ -9323,7 +9369,7 @@ dependencies = [
[[package]]
name = "livekit-protocol"
version = "0.3.9"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=80bb8f4c9112789f7c24cc98d8423010977806a6#80bb8f4c9112789f7c24cc98d8423010977806a6"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4#d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4"
dependencies = [
"futures-util",
"livekit-runtime",
@@ -9340,7 +9386,7 @@ dependencies = [
[[package]]
name = "livekit-runtime"
version = "0.4.0"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=80bb8f4c9112789f7c24cc98d8423010977806a6#80bb8f4c9112789f7c24cc98d8423010977806a6"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4#d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4"
dependencies = [
"tokio",
"tokio-stream",
@@ -11190,9 +11236,9 @@ dependencies = [
[[package]]
name = "pathfinder_simd"
version = "0.5.4"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cf07ef4804cfa9aea3b04a7bbdd5a40031dbb6b4f2cbaf2b011666c80c5b4f2"
checksum = "bf9027960355bf3afff9841918474a81a5f972ac6d226d518060bba758b5ad57"
dependencies = [
"rustc_version",
]
@@ -13122,6 +13168,7 @@ dependencies = [
"thiserror 2.0.12",
"urlencoding",
"util",
"which 6.0.3",
"workspace-hack",
]
@@ -14509,12 +14556,12 @@ dependencies = [
"serde_json",
"serde_json_lenient",
"smallvec",
"streaming-iterator",
"tree-sitter",
"tree-sitter-json",
"unindent",
"util",
"workspace-hack",
"zlog",
]
[[package]]
@@ -15481,6 +15528,18 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb"
[[package]]
name = "svg_preview"
version = "0.1.0"
dependencies = [
"editor",
"file_icons",
"gpui",
"ui",
"workspace",
"workspace-hack",
]
[[package]]
name = "svgtypes"
version = "0.15.3"
@@ -15992,7 +16051,6 @@ dependencies = [
"indexmap",
"log",
"palette",
"rust-embed",
"serde",
"serde_json",
"serde_json_lenient",
@@ -16823,8 +16881,7 @@ dependencies = [
[[package]]
name = "tree-sitter-python"
version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d065aaa27f3aaceaf60c1f0e0ac09e1cb9eb8ed28e7bcdaa52129cffc7f4b04"
source = "git+https://github.com/zed-industries/tree-sitter-python?rev=218fcbf3fda3d029225f3dec005cb497d111b35e#218fcbf3fda3d029225f3dec005cb497d111b35e"
dependencies = [
"cc",
"tree-sitter-language",
@@ -17380,6 +17437,17 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vercel"
version = "0.1.0"
dependencies = [
"anyhow",
"schemars",
"serde",
"strum 0.27.1",
"workspace-hack",
]
[[package]]
name = "version-compare"
version = "0.2.0"
@@ -18229,7 +18297,7 @@ dependencies = [
[[package]]
name = "webrtc-sys"
version = "0.3.7"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=80bb8f4c9112789f7c24cc98d8423010977806a6#80bb8f4c9112789f7c24cc98d8423010977806a6"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4#d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4"
dependencies = [
"cc",
"cxx",
@@ -18242,7 +18310,7 @@ dependencies = [
[[package]]
name = "webrtc-sys-build"
version = "0.3.6"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=80bb8f4c9112789f7c24cc98d8423010977806a6#80bb8f4c9112789f7c24cc98d8423010977806a6"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4#d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4"
dependencies = [
"fs2",
"regex",
@@ -19862,16 +19930,16 @@ dependencies = [
[[package]]
name = "zed"
version = "0.193.0"
version = "0.194.0"
dependencies = [
"activity_indicator",
"agent",
"agent_settings",
"agent_ui",
"anyhow",
"ashpd",
"askpass",
"assets",
"assistant_context_editor",
"assistant_tool",
"assistant_tools",
"audio",
@@ -19918,6 +19986,7 @@ dependencies = [
"inline_completion_button",
"inspector_ui",
"install_cli",
"itertools 0.14.0",
"jj_ui",
"journal",
"language",
@@ -19942,6 +20011,7 @@ dependencies = [
"parking_lot",
"paths",
"picker",
"pretty_assertions",
"profiling",
"project",
"project_panel",
@@ -19965,6 +20035,7 @@ dependencies = [
"snippet_provider",
"snippets_ui",
"supermaven",
"svg_preview",
"sysinfo",
"tab_switcher",
"task",

View File

@@ -2,12 +2,13 @@
resolver = "2"
members = [
"crates/activity_indicator",
"crates/agent_ui",
"crates/agent",
"crates/agent_settings",
"crates/anthropic",
"crates/askpass",
"crates/assets",
"crates/assistant_context_editor",
"crates/assistant_context",
"crates/assistant_slash_command",
"crates/assistant_slash_commands",
"crates/assistant_tool",
@@ -94,6 +95,7 @@ members = [
"crates/markdown_preview",
"crates/media",
"crates/menu",
"crates/svg_preview",
"crates/migrator",
"crates/mistral",
"crates/multi_buffer",
@@ -164,6 +166,7 @@ members = [
"crates/ui_prompt",
"crates/util",
"crates/util_macros",
"crates/vercel",
"crates/vim",
"crates/vim_mode_setting",
"crates/watch",
@@ -214,12 +217,13 @@ edition = "2024"
activity_indicator = { path = "crates/activity_indicator" }
agent = { path = "crates/agent" }
agent_ui = { path = "crates/agent_ui" }
agent_settings = { path = "crates/agent_settings" }
ai = { path = "crates/ai" }
anthropic = { path = "crates/anthropic" }
askpass = { path = "crates/askpass" }
assets = { path = "crates/assets" }
assistant_context_editor = { path = "crates/assistant_context_editor" }
assistant_context = { path = "crates/assistant_context" }
assistant_slash_command = { path = "crates/assistant_slash_command" }
assistant_slash_commands = { path = "crates/assistant_slash_commands" }
assistant_tool = { path = "crates/assistant_tool" }
@@ -301,6 +305,7 @@ lmstudio = { path = "crates/lmstudio" }
lsp = { path = "crates/lsp" }
markdown = { path = "crates/markdown" }
markdown_preview = { path = "crates/markdown_preview" }
svg_preview = { path = "crates/svg_preview" }
media = { path = "crates/media" }
menu = { path = "crates/menu" }
migrator = { path = "crates/migrator" }
@@ -373,6 +378,7 @@ ui_macros = { path = "crates/ui_macros" }
ui_prompt = { path = "crates/ui_prompt" }
util = { path = "crates/util" }
util_macros = { path = "crates/util_macros" }
vercel = { path = "crates/vercel" }
vim = { path = "crates/vim" }
vim_mode_setting = { path = "crates/vim_mode_setting" }
@@ -455,7 +461,6 @@ futures-batch = "0.6.1"
futures-lite = "1.13"
git2 = { version = "0.20.1", default-features = false }
globset = "0.4"
hashbrown = "0.15.3"
handlebars = "4.3"
heck = "0.5"
heed = { version = "0.21.0", features = ["read-txn-no-tls"] }
@@ -483,7 +488,6 @@ 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"] }
moka = { version = "0.12.10", features = ["sync"] }
naga = { version = "25.0", features = ["wgsl-in"] }
nanoid = "0.4"
@@ -518,7 +522,6 @@ 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 = "951c770a32f1998d6e999cef3e59e0013e6c4415", default-features = false, features = [
"charset",
"http2",
@@ -550,7 +553,6 @@ serde_repr = "0.1"
sha2 = "0.10"
shellexpand = "2.1.0"
shlex = "1.3.0"
signal-hook = "0.3.17"
simplelog = "0.12.2"
smallvec = { version = "1.6", features = ["union"] }
smol = "2.0"
@@ -595,7 +597,7 @@ tree-sitter-html = "0.23"
tree-sitter-jsdoc = "0.23"
tree-sitter-json = "0.24"
tree-sitter-md = { git = "https://github.com/tree-sitter-grammars/tree-sitter-markdown", rev = "9a23c1a96c0513d8fc6520972beedd419a973539" }
tree-sitter-python = "0.23"
tree-sitter-python = { git = "https://github.com/zed-industries/tree-sitter-python", rev = "218fcbf3fda3d029225f3dec005cb497d111b35e" }
tree-sitter-regex = "0.24"
tree-sitter-ruby = "0.23"
tree-sitter-rust = "0.24"

View File

@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.2
FROM rust:1.87-bookworm as builder
FROM rust:1.88-bookworm as builder
WORKDIR app
COPY . .

View File

@@ -0,0 +1,16 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2639_570)">
<g clip-path="url(#clip1_2639_570)">
<path d="M9.85676 4H13.6675C15.2128 4 16.4654 5.25266 16.4654 6.7979V10.4322H14.9002V6.7979C14.9002 6.76067 14.8988 6.7237 14.8959 6.68706L11.0851 10.4316C11.098 10.432 11.1109 10.4322 11.1238 10.4322H14.9002V11.9105H11.1238C9.57856 11.9105 8.29152 10.6456 8.29152 9.10032V5.47569H9.85676V9.10032C9.85676 9.17012 9.86216 9.23908 9.87264 9.30672L13.7673 5.4798C13.7344 5.47708 13.7012 5.47569 13.6675 5.47569H9.85676V4Z" fill="black"/>
<path d="M6.00752 11.6382L0.5 5.47504H2.71573L5.94924 9.09348V5.47504H7.6014V11.0298C7.6014 11.8682 6.56616 12.2634 6.00752 11.6382Z" fill="black"/>
</g>
</g>
<defs>
<clipPath id="clip0_2639_570">
<rect width="16" height="16" fill="white" transform="translate(0.5)"/>
</clipPath>
<clipPath id="clip1_2639_570">
<rect width="16" height="8" fill="white" transform="translate(0.5 4)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1015 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-down10-icon lucide-arrow-down-1-0"><path d="m3 16 4 4 4-4"/><path d="M7 20V4"/><path d="M17 10V4h-2"/><path d="M15 10h4"/><rect x="15" y="14" width="4" height="6" ry="2"/></svg>

After

Width:  |  Height:  |  Size: 386 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="M8 3.5L12.5 8M8 3.5L3.5 8M8 3.5V12.5" stroke="black" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 233 B

View File

@@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.75776 5.50003H8.49988C8.70769 5.50003 8.89518 5.62971 8.95455 5.82346C9.04049 6.01876 8.9858 6.23906 8.82956 6.37656L4.82971 9.87643C4.65315 10.0295 4.39488 10.042 4.20614 9.90455C4.01724 9.76705 3.94849 9.51706 4.04052 9.30301L5.24219 6.49999H3.48601C3.2918 6.49999 3.10524 6.37031 3.03197 6.17657C2.9587 5.98126 3.014 5.76096 3.1708 5.62346L7.17018 2.12375C7.34674 1.97001 7.60454 1.95829 7.7936 2.09547C7.98265 2.23275 8.0514 2.48218 7.95922 2.69695L6.75776 5.50003Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 601 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="M8.51298 2C5.19374 2 2.5 4.6945 2.5 8.01298C2.5 9.41828 2.99491 10.7205 3.80143 11.7485C4.45214 10.8243 5.15479 9.95214 6.03997 9.23651C6.06594 9.21054 6.13086 9.17159 6.20876 9.11965C6.57307 8.84623 6.8335 8.44297 6.89842 7.98702V7.97403C7.11991 6.50306 7.71869 5.99593 9.3974 5.99593C9.54099 5.99593 9.70978 5.99593 9.86558 6.00891C10.7248 6.04786 11.2189 6.29532 11.2709 6.42515C11.3098 6.49007 11.2968 6.56874 11.2839 6.64664L11.2189 6.63366C10.6851 6.56874 9.87856 6.72454 9.76171 7.31034C9.69679 7.63569 9.77469 8 9.80066 8.32536C9.83961 8.6637 9.86558 9.01502 9.86558 9.35336C9.86558 9.37933 9.83961 9.50916 9.86558 9.52215C8.95443 8.64995 6.84572 9.74364 6.18203 10.2248C6.24695 10.1988 6.31263 10.1729 6.39053 10.1469C7.02826 9.92541 8.94145 9.35336 9.68304 9.99109C10.3078 10.7587 9.74796 12.1777 9.27979 12.8803C9.00636 13.2966 8.66802 13.6739 8.29073 14H8.51222C11.8315 14 14.5252 11.3055 14.5252 7.98702C14.5252 4.66853 11.8452 2 8.51298 2ZM9.20265 5.25356C8.55193 5.25356 8.01808 4.7197 8.01808 4.06899C8.01808 3.41828 8.55193 2.88442 9.20265 2.88442C9.85336 2.88442 10.3872 3.41828 10.3872 4.06899C10.3872 4.7197 9.86634 5.25356 9.20265 5.25356Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,12 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 3L7 4" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 4L10 3" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.002 6V5.51658C5.98992 5.32067 6.03266 5.12502 6.12762 4.94143C6.22259 4.75784 6.36781 4.59012 6.55453 4.44839C6.74125 4.30666 6.9656 4.19386 7.21403 4.1168C7.46246 4.03973 7.72983 4 8 4C8.27017 4 8.53754 4.03973 8.78597 4.1168C9.0344 4.19386 9.25875 4.30666 9.44547 4.44839C9.63219 4.59012 9.77741 4.75784 9.87238 4.94143C9.96734 5.12502 10.0101 5.32067 9.998 5.51658V6" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 13C6.35 13 5 11.5462 5 9.76923V8.15385C5 7.58261 5.21071 7.03477 5.58579 6.63085C5.96086 6.22692 6.46957 6 7 6H9C9.53043 6 10.0391 6.22692 10.4142 6.63085C10.7893 7.03477 11 7.58261 11 8.15385V9.76923C11 11.5462 9.65 13 8 13Z" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 6.16663C3.90652 6.06663 3 5.21663 3 4.16663" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 9H3" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 13C3 11.95 3.89474 11.05 5 11" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13 4C13 5.05 12.0857 5.9 11 6" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13 9H11" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 11C12.1053 11.05 13 11.95 13 13" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.84265 10.7778C4.39206 11.6001 5.17295 12.241 6.08658 12.6194C7.00021 12.9978 8.00555 13.0969 8.97545 12.9039C9.94535 12.711 10.8363 12.2348 11.5355 11.5355C12.2348 10.8363 12.711 9.94535 12.9039 8.97545C13.0969 8.00555 12.9978 7.00021 12.6194 6.08658C12.241 5.17295 11.6001 4.39206 10.7778 3.84265C9.9556 3.29324 8.9889 3 8 3C6.60219 3.00526 5.26054 3.55068 4.25556 4.52222L3 5.77778" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 3V6H6" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 685 B

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 13C10.7614 13 13 10.7614 13 8C13 5.23858 10.7614 3 8 3C5.23858 3 3 5.23858 3 8C3 10.7614 5.23858 13 8 13Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 5L11 11" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 409 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-scroll-text-icon lucide-scroll-text"><path d="M15 12h-5"/><path d="M15 8h-5"/><path d="M19 17V5a2 2 0 0 0-2-2H4"/><path d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3"/></svg>

After

Width:  |  Height:  |  Size: 441 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-split-icon lucide-split"><path d="M16 3h5v5"/><path d="M8 3H3v5"/><path d="M12 22v-8.3a4 4 0 0 0-1.172-2.872L3 3"/><path d="m15 9 6-6"/></svg>

After

Width:  |  Height:  |  Size: 345 B

View File

@@ -41,7 +41,8 @@
"shift-f11": "debugger::StepOut",
"f11": "zed::ToggleFullScreen",
"ctrl-alt-z": "edit_prediction::RateCompletions",
"ctrl-shift-i": "edit_prediction::ToggleMenu"
"ctrl-shift-i": "edit_prediction::ToggleMenu",
"ctrl-alt-l": "lsp_tool::ToggleMenu"
}
},
{
@@ -243,8 +244,8 @@
"ctrl-alt-e": "agent::RemoveAllContext",
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-shift-enter": "agent::ContinueThread",
"alt-enter": "agent::ContinueWithBurnMode",
"ctrl-alt-b": "agent::ToggleBurnMode"
"super-ctrl-b": "agent::ToggleBurnMode",
"alt-enter": "agent::ContinueWithBurnMode"
}
},
{
@@ -490,13 +491,27 @@
"ctrl-k r": "editor::RevealInFileManager",
"ctrl-k p": "editor::CopyPath",
"ctrl-\\": "pane::SplitRight",
"ctrl-k v": "markdown::OpenPreviewToTheSide",
"ctrl-shift-v": "markdown::OpenPreview",
"ctrl-alt-shift-c": "editor::DisplayCursorNames",
"alt-.": "editor::GoToHunk",
"alt-,": "editor::GoToPreviousHunk"
}
},
{
"context": "Editor && extension == md",
"use_key_equivalents": true,
"bindings": {
"ctrl-k v": "markdown::OpenPreviewToTheSide",
"ctrl-shift-v": "markdown::OpenPreview"
}
},
{
"context": "Editor && extension == svg",
"use_key_equivalents": true,
"bindings": {
"ctrl-k v": "svg::OpenPreviewToTheSide",
"ctrl-shift-v": "svg::OpenPreview"
}
},
{
"context": "Editor && mode == full",
"bindings": {
@@ -894,14 +909,19 @@
"right": "variable_list::ExpandSelectedEntry",
"enter": "variable_list::EditVariable",
"ctrl-c": "variable_list::CopyVariableValue",
"ctrl-alt-c": "variable_list::CopyVariableName"
"ctrl-alt-c": "variable_list::CopyVariableName",
"delete": "variable_list::RemoveWatch",
"backspace": "variable_list::RemoveWatch",
"alt-enter": "variable_list::AddWatch"
}
},
{
"context": "BreakpointList",
"bindings": {
"space": "debugger::ToggleEnableBreakpoint",
"backspace": "debugger::UnsetBreakpoint"
"backspace": "debugger::UnsetBreakpoint",
"left": "debugger::PreviousBreakpointProperty",
"right": "debugger::NextBreakpointProperty"
}
},
{
@@ -942,7 +962,7 @@
}
},
{
"context": "FileFinder",
"context": "FileFinder || (FileFinder > Picker > Editor)",
"bindings": {
"ctrl-shift-a": "file_finder::ToggleSplitMenu",
"ctrl-shift-i": "file_finder::ToggleFilterMenu"
@@ -1037,7 +1057,8 @@
"context": "DebugConsole > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "menu::Confirm"
"enter": "menu::Confirm",
"alt-enter": "console::WatchExpression"
}
},
{

View File

@@ -47,7 +47,8 @@
"fn-f": "zed::ToggleFullScreen",
"ctrl-cmd-f": "zed::ToggleFullScreen",
"ctrl-cmd-z": "edit_prediction::RateCompletions",
"ctrl-cmd-i": "edit_prediction::ToggleMenu"
"ctrl-cmd-i": "edit_prediction::ToggleMenu",
"ctrl-cmd-l": "lsp_tool::ToggleMenu"
}
},
{
@@ -282,9 +283,9 @@
"cmd->": "assistant::QuoteSelection",
"cmd-alt-e": "agent::RemoveAllContext",
"cmd-shift-e": "project_panel::ToggleFocus",
"cmd-ctrl-b": "agent::ToggleBurnMode",
"cmd-shift-enter": "agent::ContinueThread",
"alt-enter": "agent::ContinueWithBurnMode",
"cmd-alt-b": "agent::ToggleBurnMode"
"alt-enter": "agent::ContinueWithBurnMode"
}
},
{
@@ -544,11 +545,25 @@
"cmd-k r": "editor::RevealInFileManager",
"cmd-k p": "editor::CopyPath",
"cmd-\\": "pane::SplitRight",
"cmd-k v": "markdown::OpenPreviewToTheSide",
"cmd-shift-v": "markdown::OpenPreview",
"ctrl-cmd-c": "editor::DisplayCursorNames"
}
},
{
"context": "Editor && extension == md",
"use_key_equivalents": true,
"bindings": {
"cmd-k v": "markdown::OpenPreviewToTheSide",
"cmd-shift-v": "markdown::OpenPreview"
}
},
{
"context": "Editor && extension == svg",
"use_key_equivalents": true,
"bindings": {
"cmd-k v": "svg::OpenPreviewToTheSide",
"cmd-shift-v": "svg::OpenPreview"
}
},
{
"context": "Editor && mode == full",
"use_key_equivalents": true,
@@ -587,7 +602,7 @@
"alt-cmd-o": ["projects::OpenRecent", { "create_new_window": false }],
"ctrl-cmd-o": ["projects::OpenRemote", { "from_existing_connection": false, "create_new_window": false }],
"ctrl-cmd-shift-o": ["projects::OpenRemote", { "from_existing_connection": true, "create_new_window": false }],
"alt-cmd-b": "branches::OpenRecent",
"cmd-ctrl-b": "branches::OpenRecent",
"ctrl-~": "workspace::NewTerminal",
"cmd-s": "workspace::Save",
"cmd-k s": "workspace::SaveWithoutFormat",
@@ -605,6 +620,7 @@
"cmd-8": ["workspace::ActivatePane", 7],
"cmd-9": ["workspace::ActivatePane", 8],
"cmd-b": "workspace::ToggleLeftDock",
"cmd-alt-b": "workspace::ToggleRightDock",
"cmd-r": "workspace::ToggleRightDock",
"cmd-j": "workspace::ToggleBottomDock",
"alt-cmd-y": "workspace::CloseAllDocks",
@@ -864,7 +880,10 @@
"right": "variable_list::ExpandSelectedEntry",
"enter": "variable_list::EditVariable",
"cmd-c": "variable_list::CopyVariableValue",
"cmd-alt-c": "variable_list::CopyVariableName"
"cmd-alt-c": "variable_list::CopyVariableName",
"delete": "variable_list::RemoveWatch",
"backspace": "variable_list::RemoveWatch",
"alt-enter": "variable_list::AddWatch"
}
},
{
@@ -961,7 +980,9 @@
"context": "BreakpointList",
"bindings": {
"space": "debugger::ToggleEnableBreakpoint",
"backspace": "debugger::UnsetBreakpoint"
"backspace": "debugger::UnsetBreakpoint",
"left": "debugger::PreviousBreakpointProperty",
"right": "debugger::NextBreakpointProperty"
}
},
{
@@ -1008,7 +1029,7 @@
}
},
{
"context": "FileFinder",
"context": "FileFinder || (FileFinder > Picker > Editor)",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-a": "file_finder::ToggleSplitMenu",
@@ -1135,7 +1156,8 @@
"context": "DebugConsole > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "menu::Confirm"
"enter": "menu::Confirm",
"alt-enter": "console::WatchExpression"
}
},
{

View File

@@ -9,6 +9,13 @@
},
{
"context": "Editor",
"bindings": {
"ctrl-k ctrl-u": "editor::ConvertToUpperCase", // editor:upper-case
"ctrl-k ctrl-l": "editor::ConvertToLowerCase" // editor:lower-case
}
},
{
"context": "Editor && mode == full",
"bindings": {
"ctrl-shift-l": "language_selector::Toggle", // grammar-selector:show
"ctrl-|": "pane::RevealInProjectPanel", // tree-view:reveal-active-file
@@ -19,25 +26,20 @@
"shift-f3": ["editor::SelectPrevious", { "replace_newest": true }], //find-and-replace:find-previous
"alt-shift-down": "editor::AddSelectionBelow", // editor:add-selection-below
"alt-shift-up": "editor::AddSelectionAbove", // editor:add-selection-above
"ctrl-k ctrl-u": "editor::ConvertToUpperCase", // editor:upper-case
"ctrl-k ctrl-l": "editor::ConvertToLowerCase", // editor:lower-case
"ctrl-j": "editor::JoinLines", // editor:join-lines
"ctrl-shift-d": "editor::DuplicateLineDown", // editor:duplicate-lines
"ctrl-up": "editor::MoveLineUp", // editor:move-line-up
"ctrl-down": "editor::MoveLineDown", // editor:move-line-down
"ctrl-\\": "workspace::ToggleLeftDock", // tree-view:toggle
"ctrl-shift-m": "markdown::OpenPreviewToTheSide" // markdown-preview:toggle
}
},
{
"context": "Editor && mode == full",
"bindings": {
"ctrl-shift-m": "markdown::OpenPreviewToTheSide", // markdown-preview:toggle
"ctrl-r": "outline::Toggle" // symbols-view:toggle-project-symbols
}
},
{
"context": "BufferSearchBar",
"bindings": {
"f3": ["editor::SelectNext", { "replace_newest": true }], // find-and-replace:find-next
"shift-f3": ["editor::SelectPrevious", { "replace_newest": true }], //find-and-replace:find-previous
"ctrl-f3": "search::SelectNextMatch", // find-and-replace:find-next-selected
"ctrl-shift-f3": "search::SelectPreviousMatch" // find-and-replace:find-previous-selected
}

View File

@@ -8,7 +8,6 @@
"ctrl-shift-i": "agent::ToggleFocus",
"ctrl-l": "agent::ToggleFocus",
"ctrl-shift-l": "agent::ToggleFocus",
"ctrl-alt-b": "agent::ToggleFocus",
"ctrl-shift-j": "agent::OpenConfiguration"
}
},
@@ -42,7 +41,6 @@
"ctrl-shift-i": "workspace::ToggleRightDock",
"ctrl-l": "workspace::ToggleRightDock",
"ctrl-shift-l": "workspace::ToggleRightDock",
"ctrl-alt-b": "workspace::ToggleRightDock",
"ctrl-w": "workspace::ToggleRightDock", // technically should close chat
"ctrl-.": "agent::ToggleProfileSelector",
"ctrl-/": "agent::ToggleModelSelector",

View File

@@ -59,7 +59,8 @@
"alt->": "editor::MoveToEnd", // end-of-buffer
"ctrl-l": "editor::ScrollCursorCenterTopBottom", // recenter-top-bottom
"ctrl-s": "buffer_search::Deploy", // isearch-forward
"alt-^": "editor::JoinLines" // join-line
"alt-^": "editor::JoinLines", // join-line
"alt-q": "editor::Rewrap" // fill-paragraph
}
},
{
@@ -90,6 +91,13 @@
"ctrl-g": "editor::Cancel"
}
},
{
"context": "Editor && (showing_code_actions || showing_completions)",
"bindings": {
"ctrl-p": "editor::ContextMenuPrevious",
"ctrl-n": "editor::ContextMenuNext"
}
},
{
"context": "Workspace",
"bindings": {

View File

@@ -9,6 +9,14 @@
},
{
"context": "Editor",
"bindings": {
"cmd-shift-backspace": "editor::DeleteToBeginningOfLine",
"cmd-k cmd-u": "editor::ConvertToUpperCase",
"cmd-k cmd-l": "editor::ConvertToLowerCase"
}
},
{
"context": "Editor && mode == full",
"bindings": {
"ctrl-shift-l": "language_selector::Toggle",
"cmd-|": "pane::RevealInProjectPanel",
@@ -19,26 +27,20 @@
"cmd-shift-g": ["editor::SelectPrevious", { "replace_newest": true }],
"ctrl-shift-down": "editor::AddSelectionBelow",
"ctrl-shift-up": "editor::AddSelectionAbove",
"cmd-shift-backspace": "editor::DeleteToBeginningOfLine",
"cmd-k cmd-u": "editor::ConvertToUpperCase",
"cmd-k cmd-l": "editor::ConvertToLowerCase",
"alt-enter": "editor::Newline",
"cmd-shift-d": "editor::DuplicateLineDown",
"ctrl-cmd-up": "editor::MoveLineUp",
"ctrl-cmd-down": "editor::MoveLineDown",
"cmd-\\": "workspace::ToggleLeftDock",
"ctrl-shift-m": "markdown::OpenPreviewToTheSide"
}
},
{
"context": "Editor && mode == full",
"bindings": {
"ctrl-shift-m": "markdown::OpenPreviewToTheSide",
"cmd-r": "outline::Toggle"
}
},
{
"context": "BufferSearchBar",
"bindings": {
"cmd-g": ["editor::SelectNext", { "replace_newest": true }],
"cmd-shift-g": ["editor::SelectPrevious", { "replace_newest": true }],
"cmd-f3": "search::SelectNextMatch",
"cmd-shift-f3": "search::SelectPreviousMatch"
}

View File

@@ -8,7 +8,6 @@
"cmd-shift-i": "agent::ToggleFocus",
"cmd-l": "agent::ToggleFocus",
"cmd-shift-l": "agent::ToggleFocus",
"cmd-alt-b": "agent::ToggleFocus",
"cmd-shift-j": "agent::OpenConfiguration"
}
},
@@ -43,7 +42,6 @@
"cmd-shift-i": "workspace::ToggleRightDock",
"cmd-l": "workspace::ToggleRightDock",
"cmd-shift-l": "workspace::ToggleRightDock",
"cmd-alt-b": "workspace::ToggleRightDock",
"cmd-w": "workspace::ToggleRightDock", // technically should close chat
"cmd-.": "agent::ToggleProfileSelector",
"cmd-/": "agent::ToggleModelSelector",

View File

@@ -59,7 +59,8 @@
"alt->": "editor::MoveToEnd", // end-of-buffer
"ctrl-l": "editor::ScrollCursorCenterTopBottom", // recenter-top-bottom
"ctrl-s": "buffer_search::Deploy", // isearch-forward
"alt-^": "editor::JoinLines" // join-line
"alt-^": "editor::JoinLines", // join-line
"alt-q": "editor::Rewrap" // fill-paragraph
}
},
{
@@ -90,6 +91,13 @@
"ctrl-g": "editor::Cancel"
}
},
{
"context": "Editor && (showing_code_actions || showing_completions)",
"bindings": {
"ctrl-p": "editor::ContextMenuPrevious",
"ctrl-n": "editor::ContextMenuNext"
}
},
{
"context": "Workspace",
"bindings": {

View File

@@ -85,10 +85,10 @@
"[ {": ["vim::UnmatchedBackward", { "char": "{" }],
"] )": ["vim::UnmatchedForward", { "char": ")" }],
"[ (": ["vim::UnmatchedBackward", { "char": "(" }],
"f": ["vim::PushFindForward", { "before": false }],
"t": ["vim::PushFindForward", { "before": true }],
"shift-f": ["vim::PushFindBackward", { "after": false }],
"shift-t": ["vim::PushFindBackward", { "after": true }],
"f": ["vim::PushFindForward", { "before": false, "multiline": false }],
"t": ["vim::PushFindForward", { "before": true, "multiline": false }],
"shift-f": ["vim::PushFindBackward", { "after": false, "multiline": false }],
"shift-t": ["vim::PushFindBackward", { "after": true, "multiline": false }],
"m": "vim::PushMark",
"'": ["vim::PushJump", { "line": true }],
"`": ["vim::PushJump", { "line": false }],
@@ -368,6 +368,10 @@
"escape": "editor::Cancel",
"ctrl-[": "editor::Cancel",
":": "command_palette::Toggle",
"left": "vim::WrappingLeft",
"right": "vim::WrappingRight",
"h": "vim::WrappingLeft",
"l": "vim::WrappingRight",
"shift-d": "vim::DeleteToEndOfLine",
"shift-j": "vim::JoinLines",
"y": "editor::Copy",
@@ -385,6 +389,10 @@
"shift-p": ["vim::Paste", { "before": true }],
"u": "vim::Undo",
"ctrl-r": "vim::Redo",
"f": ["vim::PushFindForward", { "before": false, "multiline": true }],
"t": ["vim::PushFindForward", { "before": true, "multiline": true }],
"shift-f": ["vim::PushFindBackward", { "after": false, "multiline": true }],
"shift-t": ["vim::PushFindBackward", { "after": true, "multiline": true }],
"r": "vim::PushReplace",
"s": "vim::Substitute",
"shift-s": "vim::SubstituteLine",

View File

@@ -80,6 +80,7 @@
"inactive_opacity": 1.0
},
// Layout mode of the bottom dock. Defaults to "contained"
// choices: contained, full, left_aligned, right_aligned
"bottom_dock_layout": "contained",
// The direction that you want to split panes horizontally. Defaults to "up"
"pane_split_direction_horizontal": "up",
@@ -94,11 +95,9 @@
// workspace when the centered layout is used.
"right_padding": 0.2
},
// All settings related to the image viewer.
// Image viewer settings
"image_viewer": {
// The unit for image file sizes.
// By default we're setting it to binary.
// The second option is decimal.
// The unit for image file sizes: "binary" (KiB, MiB) or decimal (KB, MB)
"unit": "binary"
},
// Determines the modifier to be used to add multiple cursors with the mouse. The open hover link mouse gestures will adapt such that it do not conflict with the multicursor modifier.
@@ -119,7 +118,14 @@
"hover_popover_delay": 300,
// Whether to confirm before quitting Zed.
"confirm_quit": false,
// Whether to restore last closed project when fresh Zed instance is opened.
// Whether to restore last closed project when fresh Zed instance is opened
// May take 3 values:
// 1. All workspaces open during last session
// "restore_on_startup": "last_session"
// 2. The workspace opened
// "restore_on_startup": "last_workspace",
// 3. Do not restore previous workspaces
// "restore_on_startup": "none",
"restore_on_startup": "last_session",
// Whether to attempt to restore previous file's state when opening it again.
// The state is stored per pane.
@@ -132,7 +138,9 @@
"restore_on_file_reopen": true,
// Whether to automatically close files that have been deleted on disk.
"close_on_file_delete": false,
// Size of the drop target in the editor.
// Relative size of the drop target in the editor that will open dropped file as a split pane (0-0.5)
// E.g. 0.25 == If you drop onto the top/bottom quarter of the pane a new vertical split will be used
// If you drop onto the left/right quarter of the pane a new horizontal split will be used
"drop_target_size": 0.2,
// Whether the window should be closed when using 'close active item' on a window with no tabs.
// May take 3 values:
@@ -485,7 +493,7 @@
},
// Whether the editor will scroll beyond the last line.
"scroll_beyond_last_line": "one_page",
// The number of lines to keep above/below the cursor when scrolling.
// The number of lines to keep above/below the cursor when scrolling with the keyboard
"vertical_scroll_margin": 3,
// Whether to scroll when clicking near the edge of the visible text area.
"autoscroll_on_clicks": false,
@@ -701,23 +709,27 @@
"default_width": 360,
// Style of the git status indicator in the panel.
//
// Choices: label_color, icon
// Default: icon
"status_style": "icon",
// What branch name to use if init.defaultBranch
// is not set
// What branch name to use if `init.defaultBranch` is not set
//
// Default: main
"fallback_branch_name": "main",
// Whether to sort entries in the panel by path
// or by status (the default).
// Whether to sort entries in the panel by path or by status (the default).
//
// Default: false
"sort_by_path": false,
// Whether to collapse untracked files in the diff panel.
//
// Default: false
"collapse_untracked_diff": false,
"scrollbar": {
// When to show the scrollbar in the git panel.
//
// Choices: always, auto, never, system
// Default: inherits editor scrollbar settings
"show": null
// "show": null
}
},
"message_editor": {
@@ -995,8 +1007,7 @@
// Removes any lines containing only whitespace at the end of the file and
// ensures just one newline at the end.
"ensure_final_newline_on_save": true,
// Whether or not to perform a buffer format before saving
//
// Whether or not to perform a buffer format before saving: [on, off, prettier, language_server]
// Keep in mind, if the autosave with delay is enabled, format_on_save will be ignored
"format_on_save": "on",
// How to perform a buffer format. This setting can take 4 values:
@@ -1177,6 +1188,12 @@
// 2. Display predictions inline only when holding a modifier key (alt by default).
// "mode": "subtle"
"mode": "eager",
// Copilot-specific settings
// "copilot": {
// "enterprise_uri": "",
// "proxy": "",
// "proxy_no_verify": false
// },
// Whether edit predictions are enabled when editing text threads.
// This setting has no effect if globally disabled.
"enabled_in_text_threads": true
@@ -1342,6 +1359,8 @@
// the terminal will default to matching the buffer's font fallbacks.
// This will be merged with the platform's default font fallbacks
// "font_fallbacks": ["FiraCode Nerd Fonts"],
// The weight of the editor font in standard CSS units from 100 to 900.
// "font_weight": 400
// Sets the maximum number of lines in the terminal's scrollback buffer.
// Default: 10_000, maximum: 100_000 (all bigger values set will be treated as 100_000), 0 disables the scrolling.
// Existing terminals will not pick up this change until they are recreated.
@@ -1701,6 +1720,11 @@
// }
// }
},
// Common language server settings.
"global_lsp_settings": {
// Whether to show the LSP servers button in the status bar.
"button": true
},
// Jupyter settings
"jupyter": {
"enabled": true
@@ -1715,7 +1739,6 @@
"default_mode": "normal",
"toggle_relative_line_numbers": false,
"use_system_clipboard": "always",
"use_multiline_find": false,
"use_smartcase_find": false,
"highlight_on_yank_duration": 200,
"custom_digraphs": {},
@@ -1798,6 +1821,7 @@
"debugger": {
"stepping_granularity": "line",
"save_breakpoints": true,
"dock": "bottom",
"button": true
}
}

View File

@@ -601,7 +601,7 @@
"font_weight": null
},
"constant": {
"color": "#669f59ff",
"color": "#c18401ff",
"font_style": null,
"font_weight": null
},

View File

@@ -21,6 +21,7 @@ futures.workspace = true
gpui.workspace = true
language.workspace = true
project.workspace = true
proto.workspace = true
smallvec.workspace = true
ui.workspace = true
util.workspace = true

View File

@@ -80,10 +80,13 @@ impl ActivityIndicator {
let this = cx.new(|cx| {
let mut status_events = languages.language_server_binary_statuses();
cx.spawn(async move |this, cx| {
while let Some((name, status)) = status_events.next().await {
while let Some((name, binary_status)) = status_events.next().await {
this.update(cx, |this: &mut ActivityIndicator, cx| {
this.statuses.retain(|s| s.name != name);
this.statuses.push(ServerStatus { name, status });
this.statuses.push(ServerStatus {
name,
status: LanguageServerStatusUpdate::Binary(binary_status),
});
cx.notify();
})?;
}
@@ -112,8 +115,76 @@ impl ActivityIndicator {
cx.subscribe(
&project.read(cx).lsp_store(),
|_, _, event, cx| match event {
LspStoreEvent::LanguageServerUpdate { .. } => cx.notify(),
|activity_indicator, _, event, cx| match event {
LspStoreEvent::LanguageServerUpdate { name, message, .. } => {
if let proto::update_language_server::Variant::StatusUpdate(status_update) =
message
{
let Some(name) = name.clone() else {
return;
};
let status = match &status_update.status {
Some(proto::status_update::Status::Binary(binary_status)) => {
if let Some(binary_status) =
proto::ServerBinaryStatus::from_i32(*binary_status)
{
let binary_status = match binary_status {
proto::ServerBinaryStatus::None => BinaryStatus::None,
proto::ServerBinaryStatus::CheckingForUpdate => {
BinaryStatus::CheckingForUpdate
}
proto::ServerBinaryStatus::Downloading => {
BinaryStatus::Downloading
}
proto::ServerBinaryStatus::Starting => {
BinaryStatus::Starting
}
proto::ServerBinaryStatus::Stopping => {
BinaryStatus::Stopping
}
proto::ServerBinaryStatus::Stopped => {
BinaryStatus::Stopped
}
proto::ServerBinaryStatus::Failed => {
let Some(error) = status_update.message.clone()
else {
return;
};
BinaryStatus::Failed { error }
}
};
LanguageServerStatusUpdate::Binary(binary_status)
} else {
return;
}
}
Some(proto::status_update::Status::Health(health_status)) => {
if let Some(health) =
proto::ServerHealth::from_i32(*health_status)
{
let health = match health {
proto::ServerHealth::Ok => ServerHealth::Ok,
proto::ServerHealth::Warning => ServerHealth::Warning,
proto::ServerHealth::Error => ServerHealth::Error,
};
LanguageServerStatusUpdate::Health(
health,
status_update.message.clone().map(SharedString::from),
)
} else {
return;
}
}
None => return,
};
activity_indicator.statuses.retain(|s| s.name != name);
activity_indicator
.statuses
.push(ServerStatus { name, status });
}
cx.notify()
}
_ => {}
},
)
@@ -228,9 +299,23 @@ impl ActivityIndicator {
_: &mut Window,
cx: &mut Context<Self>,
) {
if let Some(updater) = &self.auto_updater {
updater.update(cx, |updater, cx| updater.dismiss_error(cx));
let error_dismissed = if let Some(updater) = &self.auto_updater {
updater.update(cx, |updater, cx| updater.dismiss_error(cx))
} else {
false
};
if error_dismissed {
return;
}
self.project.update(cx, |project, cx| {
if project.last_formatting_failure(cx).is_some() {
project.reset_last_formatting_failure(cx);
true
} else {
false
}
});
}
fn pending_language_server_work<'a>(
@@ -399,6 +484,12 @@ impl ActivityIndicator {
let mut servers_to_clear_statuses = HashSet::<LanguageServerName>::default();
for status in &self.statuses {
match &status.status {
LanguageServerStatusUpdate::Binary(
BinaryStatus::Starting | BinaryStatus::Stopping,
) => {}
LanguageServerStatusUpdate::Binary(BinaryStatus::Stopped) => {
servers_to_clear_statuses.insert(status.name.clone());
}
LanguageServerStatusUpdate::Binary(BinaryStatus::CheckingForUpdate) => {
checking_for_update.push(status.name.clone());
}

View File

@@ -21,94 +21,60 @@ test-support = [
[dependencies]
agent_settings.workspace = true
anyhow.workspace = true
assistant_context_editor.workspace = true
assistant_slash_command.workspace = true
assistant_slash_commands.workspace = true
assistant_context.workspace = true
assistant_tool.workspace = true
audio.workspace = true
buffer_diff.workspace = true
chrono.workspace = true
client.workspace = true
collections.workspace = true
component.workspace = true
context_server.workspace = true
convert_case.workspace = true
db.workspace = true
editor.workspace = true
extension.workspace = true
extension_host.workspace = true
feature_flags.workspace = true
file_icons.workspace = true
fs.workspace = true
futures.workspace = true
fuzzy.workspace = true
git.workspace = true
gpui.workspace = true
heed.workspace = true
html_to_markdown.workspace = true
icons.workspace = true
indoc.workspace = true
http_client.workspace = true
indexed_docs.workspace = true
inventory.workspace = true
itertools.workspace = true
jsonschema.workspace = true
language.workspace = true
language_model.workspace = true
log.workspace = true
lsp.workspace = true
markdown.workspace = true
menu.workspace = true
multi_buffer.workspace = true
notifications.workspace = true
ordered-float.workspace = true
parking_lot.workspace = true
paths.workspace = true
picker.workspace = true
postage.workspace = true
project.workspace = true
prompt_store.workspace = true
proto.workspace = true
ref-cast.workspace = true
release_channel.workspace = true
rope.workspace = true
rules_library.workspace = true
schemars.workspace = true
search.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_json_lenient.workspace = true
settings.workspace = true
smol.workspace = true
sqlez.workspace = true
streaming_diff.workspace = true
telemetry.workspace = true
telemetry_events.workspace = true
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
urlencoding.workspace = true
util.workspace = true
uuid.workspace = true
watch.workspace = true
workspace-hack.workspace = true
workspace.workspace = true
zed_actions.workspace = true
zed_llm_client.workspace = true
zstd.workspace = true
[dev-dependencies]
assistant_tools.workspace = true
buffer_diff = { workspace = true, features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, "features" = ["test-support"] }
indoc.workspace = true
language = { workspace = true, "features" = ["test-support"] }
language_model = { workspace = true, "features" = ["test-support"] }
parking_lot.workspace = true
pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }
rand.workspace = true

View File

@@ -1,297 +1,23 @@
mod active_thread;
mod agent_configuration;
mod agent_diff;
mod agent_model_selector;
mod agent_panel;
mod agent_profile;
mod buffer_codegen;
mod context;
mod context_picker;
mod context_server_configuration;
mod context_server_tool;
mod context_store;
mod context_strip;
mod debug;
mod history_store;
mod inline_assistant;
mod inline_prompt_editor;
mod message_editor;
mod profile_selector;
mod slash_command_settings;
mod terminal_codegen;
mod terminal_inline_assistant;
mod thread;
mod thread_history;
mod thread_store;
mod tool_compatibility;
mod tool_use;
mod ui;
mod agent2;
pub mod agent_profile;
pub mod context;
pub mod context_server_tool;
pub mod context_store;
pub mod history_store;
pub mod thread;
pub mod thread_store;
mod zed_agent;
use std::sync::Arc;
use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
use assistant_slash_command::SlashCommandRegistry;
use client::Client;
use feature_flags::FeatureFlagAppExt as _;
use fs::Fs;
use gpui::{App, Entity, actions, impl_actions};
use language::LanguageRegistry;
use language_model::{
ConfiguredModel, LanguageModel, LanguageModelId, LanguageModelProviderId, LanguageModelRegistry,
};
use prompt_store::PromptBuilder;
use schemars::JsonSchema;
use serde::Deserialize;
use settings::{Settings as _, SettingsStore};
use thread::ThreadId;
pub use crate::active_thread::ActiveThread;
use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal};
pub use crate::agent_panel::{AgentPanel, ConcreteAssistantPanelDelegate};
pub use crate::context::{ContextLoadResult, LoadedContext};
pub use crate::inline_assistant::InlineAssistant;
use crate::slash_command_settings::SlashCommandSettings;
pub use crate::thread::{Message, MessageSegment, Thread, ThreadEvent};
pub use crate::thread_store::{SerializedThread, TextThreadStore, ThreadStore};
pub use agent_diff::{AgentDiffPane, AgentDiffToolbar};
pub use agent2::*;
pub use context::{AgentContext, ContextId, ContextLoadResult};
pub use context_store::ContextStore;
pub use ui::preview::{all_agent_previews, get_agent_preview};
pub use thread::{
LastRestoreCheckpoint, Message, MessageCrease, MessageSegment, Thread, ThreadError,
ThreadEvent, ThreadFeedback, ThreadTitle, TokenUsageRatio,
};
pub use thread_store::{SerializedThread, TextThreadStore, ThreadStore};
pub use zed_agent::*;
actions!(
agent,
[
NewTextThread,
ToggleContextPicker,
ToggleNavigationMenu,
ToggleOptionsMenu,
DeleteRecentlyOpenThread,
ToggleProfileSelector,
RemoveAllContext,
ExpandMessageEditor,
OpenHistory,
AddContextServer,
RemoveSelectedThread,
Chat,
ChatWithFollow,
CycleNextInlineAssist,
CyclePreviousInlineAssist,
FocusUp,
FocusDown,
FocusLeft,
FocusRight,
RemoveFocusedContext,
AcceptSuggestedContext,
OpenActiveThreadAsMarkdown,
OpenAgentDiff,
Keep,
Reject,
RejectAll,
KeepAll,
Follow,
ResetTrialUpsell,
ResetTrialEndUpsell,
ContinueThread,
ContinueWithBurnMode,
ToggleBurnMode,
]
);
#[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<AgentProfileId>,
}
impl ManageProfiles {
pub fn customize_tools(profile_id: AgentProfileId) -> Self {
Self {
customize_tools: Some(profile_id),
}
}
}
impl_actions!(agent, [NewThread, ManageProfiles]);
#[derive(Clone)]
pub(crate) enum ModelUsageContext {
Thread(Entity<Thread>),
InlineAssistant,
}
impl ModelUsageContext {
pub fn configured_model(&self, cx: &App) -> Option<ConfiguredModel> {
match self {
Self::Thread(thread) => thread.read(cx).configured_model(),
Self::InlineAssistant => {
LanguageModelRegistry::read_global(cx).inline_assistant_model()
}
}
}
pub fn language_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>> {
self.configured_model(cx)
.map(|configured_model| configured_model.model)
}
}
/// Initializes the `agent` crate.
pub fn init(
fs: Arc<dyn Fs>,
client: Arc<Client>,
prompt_builder: Arc<PromptBuilder>,
language_registry: Arc<LanguageRegistry>,
is_eval: bool,
cx: &mut App,
) {
AgentSettings::register(cx);
SlashCommandSettings::register(cx);
assistant_context_editor::init(client.clone(), cx);
rules_library::init(cx);
if !is_eval {
// Initializing the language model from the user settings messes with the eval, so we only initialize them when
// we're not running inside of the eval.
init_language_model_settings(cx);
}
assistant_slash_command::init(cx);
pub fn init(cx: &mut gpui::App) {
thread_store::init(cx);
agent_panel::init(cx);
context_server_configuration::init(language_registry.clone(), fs.clone(), cx);
register_slash_commands(cx);
inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
client.telemetry().clone(),
cx,
);
terminal_inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
client.telemetry().clone(),
cx,
);
indexed_docs::init(cx);
cx.observe_new(move |workspace, window, cx| {
ConfigureContextServerModal::register(workspace, language_registry.clone(), window, cx)
})
.detach();
cx.observe_new(ManageProfilesModal::register).detach();
}
fn init_language_model_settings(cx: &mut App) {
update_active_language_model_from_settings(cx);
cx.observe_global::<SettingsStore>(update_active_language_model_from_settings)
.detach();
cx.subscribe(
&LanguageModelRegistry::global(cx),
|_, event: &language_model::Event, cx| match event {
language_model::Event::ProviderStateChanged
| language_model::Event::AddedProvider(_)
| language_model::Event::RemovedProvider(_) => {
update_active_language_model_from_settings(cx);
}
_ => {}
},
)
.detach();
}
fn update_active_language_model_from_settings(cx: &mut App) {
let settings = AgentSettings::get_global(cx);
fn to_selected_model(selection: &LanguageModelSelection) -> language_model::SelectedModel {
language_model::SelectedModel {
provider: LanguageModelProviderId::from(selection.provider.0.clone()),
model: LanguageModelId::from(selection.model.clone()),
}
}
let default = to_selected_model(&settings.default_model);
let inline_assistant = settings
.inline_assistant_model
.as_ref()
.map(to_selected_model);
let commit_message = settings
.commit_message_model
.as_ref()
.map(to_selected_model);
let thread_summary = settings
.thread_summary_model
.as_ref()
.map(to_selected_model);
let inline_alternatives = settings
.inline_alternatives
.iter()
.map(to_selected_model)
.collect::<Vec<_>>();
LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
registry.select_default_model(Some(&default), cx);
registry.select_inline_assistant_model(inline_assistant.as_ref(), cx);
registry.select_commit_message_model(commit_message.as_ref(), cx);
registry.select_thread_summary_model(thread_summary.as_ref(), cx);
registry.select_inline_alternative_models(inline_alternatives, cx);
});
}
fn register_slash_commands(cx: &mut App) {
let slash_command_registry = SlashCommandRegistry::global(cx);
slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true);
slash_command_registry
.register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true);
slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false);
slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false);
slash_command_registry
.register_command(assistant_slash_commands::DiagnosticsSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true);
cx.observe_flag::<assistant_slash_commands::StreamingExampleSlashCommandFeatureFlag, _>({
let slash_command_registry = slash_command_registry.clone();
move |is_enabled, _cx| {
if is_enabled {
slash_command_registry.register_command(
assistant_slash_commands::StreamingExampleSlashCommand,
false,
);
}
}
})
.detach();
update_slash_commands_from_settings(cx);
cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
.detach();
}
fn update_slash_commands_from_settings(cx: &mut App) {
let slash_command_registry = SlashCommandRegistry::global(cx);
let settings = SlashCommandSettings::get_global(cx);
if settings.docs.enabled {
slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true);
} else {
slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand);
}
if settings.cargo_workspace.enabled {
slash_command_registry
.register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
} else {
slash_command_registry
.unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand);
}
}

117
crates/agent/src/agent2.rs Normal file
View File

@@ -0,0 +1,117 @@
use anyhow::Result;
use assistant_tool::{Tool, ToolResultOutput};
use futures::{channel::oneshot, future::BoxFuture, stream::BoxStream};
use gpui::SharedString;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::{
fmt::{self, Display},
sync::Arc,
};
#[derive(
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
)]
pub struct ThreadId(SharedString);
impl ThreadId {
pub fn as_str(&self) -> &str {
&self.0
}
pub fn to_string(&self) -> String {
self.0.to_string()
}
}
impl From<&str> for ThreadId {
fn from(value: &str) -> Self {
ThreadId(SharedString::from(value.to_string()))
}
}
impl Display for ThreadId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct MessageId(pub usize);
#[derive(Debug, Clone)]
pub struct AgentThreadToolCallId(SharedString);
pub enum AgentThreadResponseEvent {
Text(String),
Thinking(String),
ToolCallChunk {
id: AgentThreadToolCallId,
tool: Arc<dyn Tool>,
input: serde_json::Value,
},
ToolCall {
id: AgentThreadToolCallId,
tool: Arc<dyn Tool>,
input: serde_json::Value,
response_tx: oneshot::Sender<Result<ToolResultOutput>>,
},
}
pub enum AgentThreadMessage {
User {
id: MessageId,
chunks: Vec<AgentThreadUserMessageChunk>,
},
Assistant {
id: MessageId,
chunks: Vec<AgentThreadAssistantMessageChunk>,
},
}
pub enum AgentThreadUserMessageChunk {
Text(String),
// here's where we would put mentions, etc.
}
pub enum AgentThreadAssistantMessageChunk {
Text(String),
Thinking(String),
ToolResult {
id: AgentThreadToolCallId,
tool: Arc<dyn Tool>,
input: serde_json::Value,
output: Result<ToolResultOutput>,
},
}
pub struct AgentThreadResponse {
pub user_message_id: MessageId,
pub assistant_message_id: MessageId,
pub events: BoxStream<'static, Result<AgentThreadResponseEvent>>,
}
pub trait Agent {
fn create_thread() -> BoxFuture<'static, Result<Arc<dyn AgentThread>>>;
fn list_threads();
fn load_thread(&self, thread_id: ThreadId) -> BoxFuture<'static, Result<Arc<dyn AgentThread>>>;
}
pub trait AgentThread {
fn id(&self) -> ThreadId;
fn title(&self) -> BoxFuture<'static, Result<String>>;
fn summary(&self) -> BoxFuture<'static, Result<String>>;
fn messages(&self) -> BoxFuture<'static, Result<Vec<AgentThreadMessage>>>;
fn truncate(&self, message_id: MessageId) -> BoxFuture<'static, Result<()>>;
fn edit(
&self,
message_id: MessageId,
content: Vec<AgentThreadUserMessageChunk>,
max_iterations: usize,
) -> BoxFuture<'static, Result<AgentThreadResponse>>;
fn send(
&self,
content: Vec<AgentThreadUserMessageChunk>,
max_iterations: usize,
) -> BoxFuture<'static, Result<AgentThreadResponse>>;
}

View File

@@ -5,9 +5,8 @@ use assistant_tool::{Tool, ToolSource, ToolWorkingSet};
use collections::IndexMap;
use convert_case::{Case, Casing};
use fs::Fs;
use gpui::{App, Entity};
use gpui::{App, Entity, SharedString};
use settings::{Settings, update_settings_file};
use ui::SharedString;
use util::ResultExt;
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -86,6 +85,14 @@ impl AgentProfile {
.collect()
}
pub fn is_tool_enabled(&self, source: ToolSource, tool_name: String, cx: &App) -> bool {
let Some(settings) = AgentSettings::get_global(cx).profiles.get(&self.id) else {
return false;
};
return Self::is_enabled(settings, source, tool_name);
}
fn is_enabled(settings: &AgentProfileSettings, source: ToolSource, name: String) -> bool {
match source {
ToolSource::Native => *settings.tools.get(name.as_str()).unwrap_or(&false),
@@ -108,11 +115,11 @@ mod tests {
use agent_settings::ContextServerPreset;
use assistant_tool::ToolRegistry;
use collections::IndexMap;
use gpui::SharedString;
use gpui::{AppContext, TestAppContext};
use http_client::FakeHttpClient;
use project::Project;
use settings::{Settings, SettingsStore};
use ui::SharedString;
use super::*;
@@ -302,7 +309,7 @@ mod tests {
unimplemented!()
}
fn icon(&self) -> ui::IconName {
fn icon(&self) -> icons::IconName {
unimplemented!()
}

View File

@@ -1,30 +1,25 @@
use std::fmt::{self, Display, Formatter, Write as _};
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::{ops::Range, path::Path, sync::Arc};
use assistant_context_editor::AssistantContext;
use crate::thread::Thread;
use assistant_context::AssistantContext;
use assistant_tool::outline;
use collections::{HashMap, HashSet};
use editor::display_map::CreaseId;
use editor::{Addon, Editor};
use collections::HashSet;
use futures::future;
use futures::{FutureExt, future::Shared};
use gpui::{App, AppContext as _, Entity, SharedString, Subscription, Task};
use gpui::{App, AppContext as _, ElementId, Entity, SharedString, Task};
use icons::IconName;
use language::{Buffer, ParseStatus};
use language_model::{LanguageModelImage, LanguageModelRequestMessage, MessageContent};
use project::{Project, ProjectEntryId, ProjectPath, Worktree};
use prompt_store::{PromptStore, UserPromptId};
use ref_cast::RefCast;
use rope::Point;
use std::fmt::{self, Display, Formatter, Write as _};
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::{ops::Range, path::Path, sync::Arc};
use text::{Anchor, OffsetRangeExt as _};
use ui::{Context, ElementId, IconName};
use util::markdown::MarkdownCodeBlock;
use util::{ResultExt as _, post_inc};
use crate::context_store::{ContextStore, ContextStoreEvent};
use crate::thread::Thread;
pub const RULES_ICON: IconName = IconName::Context;
pub enum ContextKind {
@@ -586,7 +581,7 @@ impl ThreadContextHandle {
}
pub fn title(&self, cx: &App) -> SharedString {
self.thread.read(cx).summary().or_default()
self.thread.read(cx).title().or_default()
}
fn load(self, cx: &App) -> Task<Option<(AgentContext, Vec<Entity<Buffer>>)>> {
@@ -594,7 +589,7 @@ impl ThreadContextHandle {
let text = Thread::wait_for_detailed_summary_or_text(&self.thread, cx).await?;
let title = self
.thread
.read_with(cx, |thread, _cx| thread.summary().or_default())
.read_with(cx, |thread, _cx| thread.title().or_default())
.ok()?;
let context = AgentContext::Thread(ThreadContext {
title,
@@ -1117,69 +1112,6 @@ impl Hash for AgentContextKey {
}
}
#[derive(Default)]
pub struct ContextCreasesAddon {
creases: HashMap<AgentContextKey, Vec<(CreaseId, SharedString)>>,
_subscription: Option<Subscription>,
}
impl Addon for ContextCreasesAddon {
fn to_any(&self) -> &dyn std::any::Any {
self
}
fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
Some(self)
}
}
impl ContextCreasesAddon {
pub fn new() -> Self {
Self {
creases: HashMap::default(),
_subscription: None,
}
}
pub fn add_creases(
&mut self,
context_store: &Entity<ContextStore>,
key: AgentContextKey,
creases: impl IntoIterator<Item = (CreaseId, SharedString)>,
cx: &mut Context<Editor>,
) {
self.creases.entry(key).or_default().extend(creases);
self._subscription = Some(cx.subscribe(
&context_store,
|editor, _, event, cx| match event {
ContextStoreEvent::ContextRemoved(key) => {
let Some(this) = editor.addon_mut::<Self>() else {
return;
};
let (crease_ids, replacement_texts): (Vec<_>, Vec<_>) = this
.creases
.remove(key)
.unwrap_or_default()
.into_iter()
.unzip();
let ranges = editor
.remove_creases(crease_ids, cx)
.into_iter()
.map(|(_, range)| range)
.collect::<Vec<_>>();
editor.unfold_ranges(&ranges, false, false, cx);
editor.edit(ranges.into_iter().zip(replacement_texts), cx);
cx.notify();
}
},
))
}
pub fn into_inner(self) -> HashMap<AgentContextKey, Vec<(CreaseId, SharedString)>> {
self.creases
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -4,9 +4,9 @@ use anyhow::{Result, anyhow, bail};
use assistant_tool::{ActionLog, Tool, ToolResult, ToolSource};
use context_server::{ContextServerId, types};
use gpui::{AnyWindowHandle, App, Entity, Task};
use icons::IconName;
use language_model::{LanguageModel, LanguageModelRequest, LanguageModelToolSchemaFormat};
use project::{Project, context_server_store::ContextServerStore};
use ui::IconName;
pub struct ContextServerTool {
store: Entity<ContextServerStore>,

View File

@@ -1,28 +1,29 @@
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use crate::{
MessageId, ThreadId,
context::{
AgentContextHandle, AgentContextKey, ContextId, ContextKind, DirectoryContextHandle,
FetchedUrlContext, FileContextHandle, ImageContext, RulesContextHandle,
SelectionContextHandle, SymbolContextHandle, TextThreadContextHandle, ThreadContextHandle,
},
thread::Thread,
thread_store::ThreadStore,
};
use anyhow::{Context as _, Result, anyhow};
use assistant_context_editor::AssistantContext;
use assistant_context::AssistantContext;
use collections::{HashSet, IndexSet};
use futures::{self, FutureExt};
use gpui::{App, Context, Entity, EventEmitter, Image, SharedString, Task, WeakEntity};
use language::{Buffer, File as _};
use language_model::LanguageModelImage;
use project::image_store::is_image_file;
use project::{Project, ProjectItem, ProjectPath, Symbol};
use project::{Project, ProjectItem, ProjectPath, Symbol, image_store::is_image_file};
use prompt_store::UserPromptId;
use ref_cast::RefCast as _;
use text::{Anchor, OffsetRangeExt};
use crate::ThreadStore;
use crate::context::{
AgentContextHandle, AgentContextKey, ContextId, DirectoryContextHandle, FetchedUrlContext,
FileContextHandle, ImageContext, RulesContextHandle, SelectionContextHandle,
SymbolContextHandle, TextThreadContextHandle, ThreadContextHandle,
use std::{
ops::Range,
path::{Path, PathBuf},
sync::Arc,
};
use crate::context_strip::SuggestedContext;
use crate::thread::{MessageId, Thread, ThreadId};
use text::{Anchor, OffsetRangeExt};
pub struct ContextStore {
project: WeakEntity<Project>,
@@ -71,7 +72,8 @@ impl ContextStore {
) -> Vec<AgentContextHandle> {
let existing_context = thread
.messages()
.take_while(|message| exclude_messages_from_id.is_none_or(|id| message.id != id))
.iter()
.take_while(|message| exclude_messages_from_id.is_none_or(|id| message.id() != id))
.flat_map(|message| {
message
.loaded_context
@@ -441,7 +443,7 @@ impl ContextStore {
match context {
AgentContextHandle::Thread(thread_context) => {
self.context_thread_ids
.remove(thread_context.thread.read(cx).id());
.remove(&thread_context.thread.read(cx).id());
}
AgentContextHandle::TextThread(text_thread_context) => {
if let Some(path) = text_thread_context.context.read(cx).path() {
@@ -561,6 +563,49 @@ impl ContextStore {
}
}
#[derive(Clone)]
pub enum SuggestedContext {
File {
name: SharedString,
icon_path: Option<SharedString>,
buffer: WeakEntity<Buffer>,
},
Thread {
name: SharedString,
thread: WeakEntity<Thread>,
},
TextThread {
name: SharedString,
context: WeakEntity<AssistantContext>,
},
}
impl SuggestedContext {
pub fn name(&self) -> &SharedString {
match self {
Self::File { name, .. } => name,
Self::Thread { name, .. } => name,
Self::TextThread { name, .. } => name,
}
}
pub fn icon_path(&self) -> Option<SharedString> {
match self {
Self::File { icon_path, .. } => icon_path.clone(),
Self::Thread { .. } => None,
Self::TextThread { .. } => None,
}
}
pub fn kind(&self) -> ContextKind {
match self {
Self::File { .. } => ContextKind::File,
Self::Thread { .. } => ContextKind::Thread,
Self::TextThread { .. } => ContextKind::TextThread,
}
}
}
pub enum FileInclusion {
Direct,
InDirectory { full_path: PathBuf },

View File

@@ -1,21 +1,17 @@
use std::{collections::VecDeque, path::Path, sync::Arc};
use crate::{
ThreadId,
thread_store::{SerializedThreadMetadata, ThreadStore},
};
use anyhow::{Context as _, Result};
use assistant_context_editor::SavedContextMetadata;
use assistant_context::SavedContextMetadata;
use chrono::{DateTime, Utc};
use gpui::{AsyncApp, Entity, SharedString, Task, prelude::*};
use gpui::{App, AsyncApp, Entity, SharedString, Task, prelude::*};
use itertools::Itertools;
use paths::contexts_dir;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use ui::App;
use std::{collections::VecDeque, path::Path, sync::Arc, time::Duration};
use util::ResultExt as _;
use crate::{
thread::ThreadId,
thread_store::{SerializedThreadMetadata, ThreadStore},
};
const MAX_RECENTLY_OPENED_ENTRIES: usize = 6;
const NAVIGATION_HISTORY_PATH: &str = "agent-navigation-history.json";
const SAVE_RECENTLY_OPENED_ENTRIES_DEBOUNCE: Duration = Duration::from_millis(50);
@@ -66,7 +62,7 @@ enum SerializedRecentOpen {
pub struct HistoryStore {
thread_store: Entity<ThreadStore>,
context_store: Entity<assistant_context_editor::ContextStore>,
context_store: Entity<assistant_context::ContextStore>,
recently_opened_entries: VecDeque<HistoryEntryId>,
_subscriptions: Vec<gpui::Subscription>,
_save_recently_opened_entries_task: Task<()>,
@@ -75,7 +71,7 @@ pub struct HistoryStore {
impl HistoryStore {
pub fn new(
thread_store: Entity<ThreadStore>,
context_store: Entity<assistant_context_editor::ContextStore>,
context_store: Entity<assistant_context::ContextStore>,
initial_recent_entries: impl IntoIterator<Item = HistoryEntryId>,
cx: &mut Context<Self>,
) -> Self {

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,24 @@
use std::cell::{Ref, RefCell};
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use crate::{
MessageId, ThreadId,
context_server_tool::ContextServerTool,
thread::{DetailedSummaryState, ExceededWindowError, ProjectSnapshot, Thread},
};
use agent_settings::{AgentProfileId, CompletionMode};
use anyhow::{Context as _, Result, anyhow};
use assistant_tool::{ToolId, ToolWorkingSet};
use chrono::{DateTime, Utc};
use collections::HashMap;
use context_server::ContextServerId;
use futures::channel::{mpsc, oneshot};
use futures::future::{self, BoxFuture, Shared};
use futures::{FutureExt as _, StreamExt as _};
use futures::{
FutureExt as _, StreamExt as _,
channel::{mpsc, oneshot},
future::{self, BoxFuture, Shared},
};
use gpui::{
App, BackgroundExecutor, Context, Entity, EventEmitter, Global, ReadGlobal, SharedString,
Subscription, Task, prelude::*,
Subscription, Task, Window, prelude::*,
};
use indoc::indoc;
use language_model::{LanguageModelToolResultContent, LanguageModelToolUseId, Role, TokenUsage};
use project::context_server_store::{ContextServerStatus, ContextServerStore};
use project::{Project, ProjectItem, ProjectPath, Worktree};
@@ -25,19 +27,18 @@ use prompt_store::{
UserRulesContext, WorktreeContext,
};
use serde::{Deserialize, Serialize};
use ui::Window;
use util::ResultExt as _;
use crate::context_server_tool::ContextServerTool;
use crate::thread::{
DetailedSummaryState, ExceededWindowError, MessageId, ProjectSnapshot, Thread, ThreadId,
};
use indoc::indoc;
use sqlez::{
bindable::{Bind, Column},
connection::Connection,
statement::Statement,
};
use std::{
cell::{Ref, RefCell},
path::{Path, PathBuf},
rc::Rc,
sync::{Arc, Mutex},
};
use util::ResultExt as _;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DataType {
@@ -69,7 +70,7 @@ impl Column for DataType {
}
}
const RULES_FILE_NAMES: [&'static str; 8] = [
const RULES_FILE_NAMES: [&'static str; 9] = [
".rules",
".cursorrules",
".windsurfrules",
@@ -78,6 +79,7 @@ const RULES_FILE_NAMES: [&'static str; 8] = [
"CLAUDE.md",
"AGENT.md",
"AGENTS.md",
"GEMINI.md",
];
pub fn init(cx: &mut App) {
@@ -94,7 +96,7 @@ impl SharedProjectContext {
}
}
pub type TextThreadStore = assistant_context_editor::ContextStore;
pub type TextThreadStore = assistant_context::ContextStore;
pub struct ThreadStore {
project: Entity<Project>,
@@ -397,35 +399,17 @@ impl ThreadStore {
self.threads.iter()
}
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 create_thread_from_serialized(
&mut self,
serialized: SerializedThread,
cx: &mut Context<Self>,
) -> Entity<Thread> {
cx.new(|cx| {
Thread::deserialize(
ThreadId::new(),
serialized,
self.project.clone(),
self.tools.clone(),
self.prompt_builder.clone(),
self.project_context.clone(),
None,
cx,
)
})
pub fn create_thread(&mut self, cx: &mut Context<Self>) -> Task<Result<Entity<Thread>>> {
todo!()
// cx.new(|cx| {
// Thread::new(
// self.project.clone(),
// self.tools.clone(),
// self.prompt_builder.clone(),
// self.project_context.clone(),
// cx,
// )
// })
}
pub fn open_thread(
@@ -444,51 +428,53 @@ impl ThreadStore {
.await?
.with_context(|| format!("no thread found with ID: {id:?}"))?;
let thread = this.update_in(cx, |this, window, cx| {
cx.new(|cx| {
Thread::deserialize(
id.clone(),
thread,
this.project.clone(),
this.tools.clone(),
this.prompt_builder.clone(),
this.project_context.clone(),
Some(window),
cx,
)
})
})?;
Ok(thread)
todo!();
// let thread = this.update_in(cx, |this, window, cx| {
// cx.new(|cx| {
// Thread::deserialize(
// id.clone(),
// thread,
// this.project.clone(),
// this.tools.clone(),
// this.prompt_builder.clone(),
// this.project_context.clone(),
// Some(window),
// 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)));
todo!()
// 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?;
// 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
})
// 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?;
todo!()
// 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();
})
})
// this.update(cx, |this, cx| {
// this.threads.retain(|thread| thread.id != id);
// cx.notify();
// })
// })
}
pub fn reload(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
@@ -729,7 +715,7 @@ pub enum SerializedMessageSegment {
signature: Option<String>,
},
RedactedThinking {
data: Vec<u8>,
data: String,
},
}
@@ -1064,7 +1050,7 @@ impl ThreadsDatabase {
#[cfg(test)]
mod tests {
use super::*;
use crate::thread::{DetailedSummaryState, MessageId};
use crate::{MessageId, thread::DetailedSummaryState};
use chrono::Utc;
use language_model::{Role, TokenUsage};
use pretty_assertions::assert_eq;

View File

@@ -1,568 +0,0 @@
use std::sync::Arc;
use anyhow::Result;
use assistant_tool::{
AnyToolCard, Tool, ToolResultContent, ToolResultOutput, ToolUseStatus, ToolWorkingSet,
};
use collections::HashMap;
use futures::FutureExt as _;
use futures::future::Shared;
use gpui::{App, Entity, SharedString, Task};
use language_model::{
ConfiguredModel, LanguageModel, LanguageModelRequest, LanguageModelToolResult,
LanguageModelToolResultContent, LanguageModelToolUse, LanguageModelToolUseId, Role,
};
use project::Project;
use ui::{IconName, Window};
use util::truncate_lines_to_byte_limit;
use crate::thread::{MessageId, PromptId, ThreadId};
use crate::thread_store::SerializedMessage;
#[derive(Debug)]
pub struct ToolUse {
pub id: LanguageModelToolUseId,
pub name: SharedString,
pub ui_text: SharedString,
pub status: ToolUseStatus,
pub input: serde_json::Value,
pub icon: ui::IconName,
pub needs_confirmation: bool,
}
pub struct ToolUseState {
tools: Entity<ToolWorkingSet>,
tool_uses_by_assistant_message: HashMap<MessageId, Vec<LanguageModelToolUse>>,
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: Entity<ToolWorkingSet>) -> Self {
Self {
tools,
tool_uses_by_assistant_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(),
}
}
/// Constructs a [`ToolUseState`] from the given list of [`SerializedMessage`]s.
///
/// Accepts a function to filter the tools that should be used to populate the state.
///
/// If `window` is `None` (e.g., when in headless mode or when running evals),
/// tool cards won't be deserialized
pub fn from_serialized_messages(
tools: Entity<ToolWorkingSet>,
messages: &[SerializedMessage],
project: Entity<Project>,
window: Option<&mut Window>, // None in headless mode
cx: &mut App,
) -> Self {
let mut this = Self::new(tools);
let mut tool_names_by_id = HashMap::default();
let mut window = window;
for message in messages {
match message.role {
Role::Assistant => {
if !message.tool_uses.is_empty() {
let tool_uses = message
.tool_uses
.iter()
.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<_>>();
tool_names_by_id.extend(
tool_uses
.iter()
.map(|tool_use| (tool_use.id.clone(), tool_use.name.clone())),
);
this.tool_uses_by_assistant_message
.insert(message.id, tool_uses);
for tool_result in &message.tool_results {
let tool_use_id = tool_result.tool_use_id.clone();
let Some(tool_use) = tool_names_by_id.get(&tool_use_id) else {
log::warn!("no tool name found for tool use: {tool_use_id:?}");
continue;
};
this.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name: tool_use.clone(),
is_error: tool_result.is_error,
content: tool_result.content.clone(),
output: tool_result.output.clone(),
},
);
if let Some(window) = &mut window {
if let Some(tool) = this.tools.read(cx).tool(tool_use, cx) {
if let Some(output) = tool_result.output.clone() {
if let Some(card) = tool.deserialize_card(
output,
project.clone(),
window,
cx,
) {
this.tool_result_cards.insert(tool_use_id, card);
}
}
}
}
}
}
}
Role::System | Role::User => {}
}
}
this
}
pub fn cancel_pending(&mut self) -> Vec<PendingToolUse> {
let mut cancelled_tool_uses = Vec::new();
self.pending_tool_uses_by_id
.retain(|tool_use_id, tool_use| {
if matches!(tool_use.status, PendingToolUseStatus::Error { .. }) {
return true;
}
let content = "Tool canceled by user".into();
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name: tool_use.name.clone(),
content,
output: None,
is_error: true,
},
);
cancelled_tool_uses.push(tool_use.clone());
false
});
cancelled_tool_uses
}
pub fn pending_tool_uses(&self) -> Vec<&PendingToolUse> {
self.pending_tool_uses_by_id.values().collect()
}
pub fn tool_uses_for_message(&self, id: MessageId, cx: &App) -> Vec<ToolUse> {
let Some(tool_uses_for_message) = &self.tool_uses_by_assistant_message.get(&id) else {
return Vec::new();
};
let mut tool_uses = Vec::new();
for tool_use in tool_uses_for_message.iter() {
let tool_result = self.tool_results.get(&tool_use.id);
let status = (|| {
if let Some(tool_result) = tool_result {
let content = tool_result
.content
.to_str()
.map(|str| str.to_owned().into())
.unwrap_or_default();
return if tool_result.is_error {
ToolUseStatus::Error(content)
} else {
ToolUseStatus::Finished(content)
};
}
if let Some(pending_tool_use) = self.pending_tool_uses_by_id.get(&tool_use.id) {
match pending_tool_use.status {
PendingToolUseStatus::Idle => ToolUseStatus::Pending,
PendingToolUseStatus::NeedsConfirmation { .. } => {
ToolUseStatus::NeedsConfirmation
}
PendingToolUseStatus::Running { .. } => ToolUseStatus::Running,
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.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,
tool_use.is_input_complete,
cx,
),
input: tool_use.input.clone(),
status,
icon,
needs_confirmation,
})
}
tool_uses
}
pub fn tool_ui_label(
&self,
tool_name: &str,
input: &serde_json::Value,
is_input_complete: bool,
cx: &App,
) -> SharedString {
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,
assistant_message_id: MessageId,
) -> Vec<&LanguageModelToolResult> {
let Some(tool_uses) = self
.tool_uses_by_assistant_message
.get(&assistant_message_id)
else {
return Vec::new();
};
tool_uses
.iter()
.filter_map(|tool_use| self.tool_results.get(&tool_use.id))
.collect()
}
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())
}
pub fn tool_result(
&self,
tool_use_id: &LanguageModelToolUseId,
) -> Option<&LanguageModelToolResult> {
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,
) -> Arc<str> {
let tool_uses = self
.tool_uses_by_assistant_message
.entry(assistant_message_id)
.or_default();
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();
let may_perform_edits = self
.tools
.read(cx)
.tool(&tool_use.name, cx)
.is_some_and(|tool| tool.may_perform_edits());
self.pending_tool_uses_by_id.insert(
tool_use.id.clone(),
PendingToolUse {
assistant_message_id,
id: tool_use.id,
name: tool_use.name.clone(),
ui_text: ui_text.clone(),
input: tool_use.input,
may_perform_edits,
status,
},
);
ui_text
}
pub fn run_pending_tool(
&mut self,
tool_use_id: LanguageModelToolUseId,
ui_text: SharedString,
task: Task<()>,
) {
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
tool_use.ui_text = ui_text.into();
tool_use.status = PendingToolUseStatus::Running {
_task: task.shared(),
};
}
}
pub fn confirm_tool_use(
&mut self,
tool_use_id: LanguageModelToolUseId,
ui_text: impl Into<Arc<str>>,
input: serde_json::Value,
request: Arc<LanguageModelRequest>,
tool: Arc<dyn Tool>,
) {
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
let ui_text = ui_text.into();
tool_use.ui_text = ui_text.clone();
let confirmation = Confirmation {
tool_use_id,
input,
request,
tool,
ui_text,
};
tool_use.status = PendingToolUseStatus::NeedsConfirmation(Arc::new(confirmation));
}
}
pub fn insert_tool_output(
&mut self,
tool_use_id: LanguageModelToolUseId,
tool_name: Arc<str>,
output: Result<ToolResultOutput>,
configured_model: Option<&ConfiguredModel>,
) -> 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(output) => {
let tool_result = output.content;
const BYTES_PER_TOKEN_ESTIMATE: usize = 3;
let old_use = self.pending_tool_uses_by_id.remove(&tool_use_id);
// Protect from overly large output
let tool_output_limit = configured_model
.map(|model| model.model.max_token_count() as usize * BYTES_PER_TOKEN_ESTIMATE)
.unwrap_or(usize::MAX);
let content = match tool_result {
ToolResultContent::Text(text) => {
let text = if text.len() < tool_output_limit {
text
} else {
let truncated = truncate_lines_to_byte_limit(&text, tool_output_limit);
format!(
"Tool result too long. The first {} bytes:\n\n{}",
truncated.len(),
truncated
)
};
LanguageModelToolResultContent::Text(text.into())
}
ToolResultContent::Image(language_model_image) => {
if language_model_image.estimate_tokens() < tool_output_limit {
LanguageModelToolResultContent::Image(language_model_image)
} else {
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name,
content: "Tool responded with an image that would exceeded the remaining tokens".into(),
is_error: true,
output: None,
},
);
return old_use;
}
}
};
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name,
content,
is_error: false,
output: output.output,
},
);
old_use
}
Err(err) => {
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name,
content: LanguageModelToolResultContent::Text(err.to_string().into()),
is_error: true,
output: None,
},
);
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
tool_use.status = PendingToolUseStatus::Error(err.to_string().into());
}
self.pending_tool_uses_by_id.get(&tool_use_id).cloned()
}
}
}
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(
&self,
assistant_message_id: MessageId,
) -> impl Iterator<Item = (&LanguageModelToolUse, Option<&LanguageModelToolResult>)> {
self.tool_uses_by_assistant_message
.get(&assistant_message_id)
.into_iter()
.flatten()
.map(|tool_use| (tool_use, self.tool_results.get(&tool_use.id)))
}
}
#[derive(Debug, Clone)]
pub struct PendingToolUse {
pub id: LanguageModelToolUseId,
/// The ID of the Assistant message in which the tool use was requested.
#[allow(unused)]
pub assistant_message_id: MessageId,
pub name: Arc<str>,
pub ui_text: Arc<str>,
pub input: serde_json::Value,
pub status: PendingToolUseStatus,
pub may_perform_edits: bool,
}
#[derive(Debug, Clone)]
pub struct Confirmation {
pub tool_use_id: LanguageModelToolUseId,
pub input: serde_json::Value,
pub ui_text: Arc<str>,
pub request: Arc<LanguageModelRequest>,
pub tool: Arc<dyn Tool>,
}
#[derive(Debug, Clone)]
pub enum PendingToolUseStatus {
InputStillStreaming,
Idle,
NeedsConfirmation(Arc<Confirmation>),
Running { _task: Shared<Task<()>> },
Error(#[allow(unused)] Arc<str>),
}
impl PendingToolUseStatus {
pub fn is_idle(&self) -> bool {
matches!(self, PendingToolUseStatus::Idle)
}
pub fn is_error(&self) -> bool {
matches!(self, PendingToolUseStatus::Error(_))
}
pub fn needs_confirmation(&self) -> bool {
matches!(self, PendingToolUseStatus::NeedsConfirmation { .. })
}
}
#[derive(Clone)]
pub struct ToolUseMetadata {
pub model: Arc<dyn LanguageModel>,
pub thread_id: ThreadId,
pub prompt_id: PromptId,
}

View File

@@ -0,0 +1 @@
pub struct ZedAgentThread {}

View File

@@ -12,17 +12,10 @@ workspace = true
path = "src/agent_settings.rs"
[dependencies]
anthropic = { workspace = true, features = ["schemars"] }
anyhow.workspace = true
collections.workspace = true
gpui.workspace = true
language_model.workspace = true
lmstudio = { workspace = true, features = ["schemars"] }
log.workspace = true
ollama = { workspace = true, features = ["schemars"] }
open_ai = { workspace = true, features = ["schemars"] }
deepseek = { workspace = true, features = ["schemars"] }
mistral = { workspace = true, features = ["schemars"] }
schemars.workspace = true
serde.workspace = true
settings.workspace = true

View File

@@ -2,16 +2,10 @@ mod agent_profile;
use std::sync::Arc;
use ::open_ai::Model as OpenAiModel;
use anthropic::Model as AnthropicModel;
use anyhow::{Result, bail};
use collections::IndexMap;
use deepseek::Model as DeepseekModel;
use gpui::{App, Pixels, SharedString};
use language_model::LanguageModel;
use lmstudio::Model as LmStudioModel;
use mistral::Model as MistralModel;
use ollama::Model as OllamaModel;
use schemars::{JsonSchema, schema::Schema};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
@@ -48,45 +42,6 @@ pub enum NotifyWhenAgentWaiting {
Never,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
#[serde(tag = "name", rename_all = "snake_case")]
#[schemars(deny_unknown_fields)]
pub enum AgentProviderContentV1 {
#[serde(rename = "zed.dev")]
ZedDotDev { default_model: Option<String> },
#[serde(rename = "openai")]
OpenAi {
default_model: Option<OpenAiModel>,
api_url: Option<String>,
available_models: Option<Vec<OpenAiModel>>,
},
#[serde(rename = "anthropic")]
Anthropic {
default_model: Option<AnthropicModel>,
api_url: Option<String>,
},
#[serde(rename = "ollama")]
Ollama {
default_model: Option<OllamaModel>,
api_url: Option<String>,
},
#[serde(rename = "lmstudio")]
LmStudio {
default_model: Option<LmStudioModel>,
api_url: Option<String>,
},
#[serde(rename = "deepseek")]
DeepSeek {
default_model: Option<DeepseekModel>,
api_url: Option<String>,
},
#[serde(rename = "mistral")]
Mistral {
default_model: Option<MistralModel>,
api_url: Option<String>,
},
}
#[derive(Default, Clone, Debug)]
pub struct AgentSettings {
pub enabled: bool,
@@ -168,366 +123,56 @@ impl LanguageModelParameters {
}
}
/// Agent panel settings
#[derive(Clone, Serialize, Deserialize, Debug, Default)]
pub struct AgentSettingsContent {
#[serde(flatten)]
pub inner: Option<AgentSettingsContentInner>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum AgentSettingsContentInner {
Versioned(Box<VersionedAgentSettingsContent>),
Legacy(LegacyAgentSettingsContent),
}
impl AgentSettingsContentInner {
fn for_v2(content: AgentSettingsContentV2) -> Self {
AgentSettingsContentInner::Versioned(Box::new(VersionedAgentSettingsContent::V2(content)))
}
}
impl JsonSchema for AgentSettingsContent {
fn schema_name() -> String {
VersionedAgentSettingsContent::schema_name()
}
fn json_schema(r#gen: &mut schemars::r#gen::SchemaGenerator) -> Schema {
VersionedAgentSettingsContent::json_schema(r#gen)
}
fn is_referenceable() -> bool {
VersionedAgentSettingsContent::is_referenceable()
}
}
impl AgentSettingsContent {
pub fn is_version_outdated(&self) -> bool {
match &self.inner {
Some(AgentSettingsContentInner::Versioned(settings)) => match **settings {
VersionedAgentSettingsContent::V1(_) => true,
VersionedAgentSettingsContent::V2(_) => false,
},
Some(AgentSettingsContentInner::Legacy(_)) => true,
None => false,
}
}
fn upgrade(&self) -> AgentSettingsContentV2 {
match &self.inner {
Some(AgentSettingsContentInner::Versioned(settings)) => match **settings {
VersionedAgentSettingsContent::V1(ref settings) => AgentSettingsContentV2 {
enabled: settings.enabled,
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_width,
default_model: settings
.provider
.clone()
.and_then(|provider| match provider {
AgentProviderContentV1::ZedDotDev { default_model } => default_model
.map(|model| LanguageModelSelection {
provider: "zed.dev".into(),
model,
}),
AgentProviderContentV1::OpenAi { default_model, .. } => default_model
.map(|model| LanguageModelSelection {
provider: "openai".into(),
model: model.id().to_string(),
}),
AgentProviderContentV1::Anthropic { default_model, .. } => {
default_model.map(|model| LanguageModelSelection {
provider: "anthropic".into(),
model: model.id().to_string(),
})
}
AgentProviderContentV1::Ollama { default_model, .. } => default_model
.map(|model| LanguageModelSelection {
provider: "ollama".into(),
model: model.id().to_string(),
}),
AgentProviderContentV1::LmStudio { default_model, .. } => default_model
.map(|model| LanguageModelSelection {
provider: "lmstudio".into(),
model: model.id().to_string(),
}),
AgentProviderContentV1::DeepSeek { default_model, .. } => default_model
.map(|model| LanguageModelSelection {
provider: "deepseek".into(),
model: model.id().to_string(),
}),
AgentProviderContentV1::Mistral { default_model, .. } => default_model
.map(|model| LanguageModelSelection {
provider: "mistral".into(),
model: model.id().to_string(),
}),
}),
inline_assistant_model: None,
commit_message_model: None,
thread_summary_model: None,
inline_alternatives: None,
default_profile: None,
default_view: None,
profiles: None,
always_allow_tool_actions: None,
notify_when_agent_waiting: None,
stream_edits: None,
single_file_review: None,
model_parameters: Vec::new(),
preferred_completion_mode: None,
enable_feedback: None,
play_sound_when_agent_done: None,
},
VersionedAgentSettingsContent::V2(ref settings) => settings.clone(),
},
Some(AgentSettingsContentInner::Legacy(settings)) => AgentSettingsContentV2 {
enabled: None,
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_height,
default_model: Some(LanguageModelSelection {
provider: "openai".into(),
model: settings
.default_open_ai_model
.clone()
.unwrap_or_default()
.id()
.to_string(),
}),
inline_assistant_model: None,
commit_message_model: None,
thread_summary_model: None,
inline_alternatives: None,
default_profile: None,
default_view: None,
profiles: None,
always_allow_tool_actions: None,
notify_when_agent_waiting: None,
stream_edits: None,
single_file_review: None,
model_parameters: Vec::new(),
preferred_completion_mode: None,
enable_feedback: None,
play_sound_when_agent_done: None,
},
None => AgentSettingsContentV2::default(),
}
}
pub fn set_dock(&mut self, dock: AgentDockPosition) {
match &mut self.inner {
Some(AgentSettingsContentInner::Versioned(settings)) => match **settings {
VersionedAgentSettingsContent::V1(ref mut settings) => {
settings.dock = Some(dock);
}
VersionedAgentSettingsContent::V2(ref mut settings) => {
settings.dock = Some(dock);
}
},
Some(AgentSettingsContentInner::Legacy(settings)) => {
settings.dock = Some(dock);
}
None => {
self.inner = Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 {
dock: Some(dock),
..Default::default()
}))
}
}
self.dock = Some(dock);
}
pub fn set_model(&mut self, language_model: Arc<dyn LanguageModel>) {
let model = language_model.id().0.to_string();
let provider = language_model.provider_id().0.to_string();
match &mut self.inner {
Some(AgentSettingsContentInner::Versioned(settings)) => match **settings {
VersionedAgentSettingsContent::V1(ref mut settings) => match provider.as_ref() {
"zed.dev" => {
log::warn!("attempted to set zed.dev model on outdated settings");
}
"anthropic" => {
let api_url = match &settings.provider {
Some(AgentProviderContentV1::Anthropic { api_url, .. }) => {
api_url.clone()
}
_ => None,
};
settings.provider = Some(AgentProviderContentV1::Anthropic {
default_model: AnthropicModel::from_id(&model).ok(),
api_url,
});
}
"ollama" => {
let api_url = match &settings.provider {
Some(AgentProviderContentV1::Ollama { api_url, .. }) => api_url.clone(),
_ => None,
};
settings.provider = Some(AgentProviderContentV1::Ollama {
default_model: Some(ollama::Model::new(
&model,
None,
None,
Some(language_model.supports_tools()),
Some(language_model.supports_images()),
None,
)),
api_url,
});
}
"lmstudio" => {
let api_url = match &settings.provider {
Some(AgentProviderContentV1::LmStudio { api_url, .. }) => {
api_url.clone()
}
_ => None,
};
settings.provider = Some(AgentProviderContentV1::LmStudio {
default_model: Some(lmstudio::Model::new(
&model, None, None, false, false,
)),
api_url,
});
}
"openai" => {
let (api_url, available_models) = match &settings.provider {
Some(AgentProviderContentV1::OpenAi {
api_url,
available_models,
..
}) => (api_url.clone(), available_models.clone()),
_ => (None, None),
};
settings.provider = Some(AgentProviderContentV1::OpenAi {
default_model: OpenAiModel::from_id(&model).ok(),
api_url,
available_models,
});
}
"deepseek" => {
let api_url = match &settings.provider {
Some(AgentProviderContentV1::DeepSeek { api_url, .. }) => {
api_url.clone()
}
_ => None,
};
settings.provider = Some(AgentProviderContentV1::DeepSeek {
default_model: DeepseekModel::from_id(&model).ok(),
api_url,
});
}
_ => {}
},
VersionedAgentSettingsContent::V2(ref mut settings) => {
settings.default_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
},
Some(AgentSettingsContentInner::Legacy(settings)) => {
if let Ok(model) = OpenAiModel::from_id(&language_model.id().0) {
settings.default_open_ai_model = Some(model);
}
}
None => {
self.inner = Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 {
default_model: Some(LanguageModelSelection {
provider: provider.into(),
model,
}),
..Default::default()
}));
}
}
self.default_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
self.v2_setting(|setting| {
setting.inline_assistant_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
Ok(())
})
.ok();
self.inline_assistant_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
pub fn set_commit_message_model(&mut self, provider: String, model: String) {
self.v2_setting(|setting| {
setting.commit_message_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
Ok(())
})
.ok();
}
pub fn v2_setting(
&mut self,
f: impl FnOnce(&mut AgentSettingsContentV2) -> anyhow::Result<()>,
) -> anyhow::Result<()> {
match self.inner.get_or_insert_with(|| {
AgentSettingsContentInner::for_v2(AgentSettingsContentV2 {
..Default::default()
})
}) {
AgentSettingsContentInner::Versioned(boxed) => {
if let VersionedAgentSettingsContent::V2(ref mut settings) = **boxed {
f(settings)
} else {
Ok(())
}
}
_ => Ok(()),
}
self.commit_message_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
pub fn set_thread_summary_model(&mut self, provider: String, model: String) {
self.v2_setting(|setting| {
setting.thread_summary_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
Ok(())
})
.ok();
self.thread_summary_model = Some(LanguageModelSelection {
provider: provider.into(),
model,
});
}
pub fn set_always_allow_tool_actions(&mut self, allow: bool) {
self.v2_setting(|setting| {
setting.always_allow_tool_actions = Some(allow);
Ok(())
})
.ok();
self.always_allow_tool_actions = Some(allow);
}
pub fn set_play_sound_when_agent_done(&mut self, allow: bool) {
self.v2_setting(|setting| {
setting.play_sound_when_agent_done = Some(allow);
Ok(())
})
.ok();
self.play_sound_when_agent_done = Some(allow);
}
pub fn set_single_file_review(&mut self, allow: bool) {
self.v2_setting(|setting| {
setting.single_file_review = Some(allow);
Ok(())
})
.ok();
self.single_file_review = Some(allow);
}
pub fn set_profile(&mut self, profile_id: AgentProfileId) {
self.v2_setting(|setting| {
setting.default_profile = Some(profile_id);
Ok(())
})
.ok();
self.default_profile = Some(profile_id);
}
pub fn create_profile(
@@ -535,79 +180,39 @@ impl AgentSettingsContent {
profile_id: AgentProfileId,
profile_settings: AgentProfileSettings,
) -> Result<()> {
self.v2_setting(|settings| {
let profiles = settings.profiles.get_or_insert_default();
if profiles.contains_key(&profile_id) {
bail!("profile with ID '{profile_id}' already exists");
}
let profiles = self.profiles.get_or_insert_default();
if profiles.contains_key(&profile_id) {
bail!("profile with ID '{profile_id}' already exists");
}
profiles.insert(
profile_id,
AgentProfileContent {
name: profile_settings.name.into(),
tools: profile_settings.tools,
enable_all_context_servers: Some(profile_settings.enable_all_context_servers),
context_servers: profile_settings
.context_servers
.into_iter()
.map(|(server_id, preset)| {
(
server_id,
ContextServerPresetContent {
tools: preset.tools,
},
)
})
.collect(),
},
);
profiles.insert(
profile_id,
AgentProfileContent {
name: profile_settings.name.into(),
tools: profile_settings.tools,
enable_all_context_servers: Some(profile_settings.enable_all_context_servers),
context_servers: profile_settings
.context_servers
.into_iter()
.map(|(server_id, preset)| {
(
server_id,
ContextServerPresetContent {
tools: preset.tools,
},
)
})
.collect(),
},
);
Ok(())
})
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
#[serde(tag = "version")]
#[schemars(deny_unknown_fields)]
pub enum VersionedAgentSettingsContent {
#[serde(rename = "1")]
V1(AgentSettingsContentV1),
#[serde(rename = "2")]
V2(AgentSettingsContentV2),
}
impl Default for VersionedAgentSettingsContent {
fn default() -> Self {
Self::V2(AgentSettingsContentV2 {
enabled: None,
button: None,
dock: None,
default_width: None,
default_height: None,
default_model: None,
inline_assistant_model: None,
commit_message_model: None,
thread_summary_model: None,
inline_alternatives: None,
default_profile: None,
default_view: None,
profiles: None,
always_allow_tool_actions: None,
notify_when_agent_waiting: None,
stream_edits: None,
single_file_review: None,
model_parameters: Vec::new(),
preferred_completion_mode: None,
enable_feedback: None,
play_sound_when_agent_done: None,
})
Ok(())
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug, Default)]
#[schemars(deny_unknown_fields)]
pub struct AgentSettingsContentV2 {
pub struct AgentSettingsContent {
/// Whether the Agent is enabled.
///
/// Default: true
@@ -734,6 +339,7 @@ impl JsonSchema for LanguageModelProviderSetting {
"deepseek".into(),
"openrouter".into(),
"mistral".into(),
"vercel".into(),
]),
..Default::default()
}
@@ -778,65 +384,6 @@ pub struct ContextServerPresetContent {
pub tools: IndexMap<Arc<str>, bool>,
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
#[schemars(deny_unknown_fields)]
pub struct AgentSettingsContentV1 {
/// Whether the Agent is enabled.
///
/// Default: true
enabled: Option<bool>,
/// Whether to show the Agent panel button in the status bar.
///
/// Default: true
button: Option<bool>,
/// Where to dock the Agent.
///
/// Default: right
dock: Option<AgentDockPosition>,
/// Default width in pixels when the Agent is docked to the left or right.
///
/// Default: 640
default_width: Option<f32>,
/// Default height in pixels when the Agent is docked to the bottom.
///
/// Default: 320
default_height: Option<f32>,
/// The provider of the Agent service.
///
/// This can be "openai", "anthropic", "ollama", "lmstudio", "deepseek", "zed.dev"
/// each with their respective default models and configurations.
provider: Option<AgentProviderContentV1>,
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
#[schemars(deny_unknown_fields)]
pub struct LegacyAgentSettingsContent {
/// Whether to show the Agent panel button in the status bar.
///
/// Default: true
pub button: Option<bool>,
/// Where to dock the Agent.
///
/// Default: right
pub dock: Option<AgentDockPosition>,
/// Default width in pixels when the Agent is docked to the left or right.
///
/// Default: 640
pub default_width: Option<f32>,
/// Default height in pixels when the Agent is docked to the bottom.
///
/// Default: 320
pub default_height: Option<f32>,
/// The default OpenAI model to use when creating new chats.
///
/// Default: gpt-4-1106-preview
pub default_open_ai_model: Option<OpenAiModel>,
/// OpenAI API base URL to use when creating new chats.
///
/// Default: <https://api.openai.com/v1>
pub openai_api_url: Option<String>,
}
impl Settings for AgentSettings {
const KEY: Option<&'static str> = Some("agent");
@@ -853,11 +400,6 @@ impl Settings for AgentSettings {
let mut settings = AgentSettings::default();
for value in sources.defaults_and_customizations() {
if value.is_version_outdated() {
settings.using_outdated_settings_version = true;
}
let value = value.upgrade();
merge(&mut settings.enabled, value.enabled);
merge(&mut settings.button, value.button);
merge(&mut settings.dock, value.dock);
@@ -869,17 +411,23 @@ impl Settings for AgentSettings {
&mut settings.default_height,
value.default_height.map(Into::into),
);
merge(&mut settings.default_model, value.default_model);
merge(&mut settings.default_model, value.default_model.clone());
settings.inline_assistant_model = value
.inline_assistant_model
.clone()
.or(settings.inline_assistant_model.take());
settings.commit_message_model = value
.clone()
.commit_message_model
.or(settings.commit_message_model.take());
settings.thread_summary_model = value
.clone()
.thread_summary_model
.or(settings.thread_summary_model.take());
merge(&mut settings.inline_alternatives, value.inline_alternatives);
merge(
&mut settings.inline_alternatives,
value.inline_alternatives.clone(),
);
merge(
&mut settings.always_allow_tool_actions,
value.always_allow_tool_actions,
@@ -894,7 +442,7 @@ impl Settings for AgentSettings {
);
merge(&mut settings.stream_edits, value.stream_edits);
merge(&mut settings.single_file_review, value.single_file_review);
merge(&mut settings.default_profile, value.default_profile);
merge(&mut settings.default_profile, value.default_profile.clone());
merge(&mut settings.default_view, value.default_view);
merge(
&mut settings.preferred_completion_mode,
@@ -906,24 +454,24 @@ impl Settings for AgentSettings {
.model_parameters
.extend_from_slice(&value.model_parameters);
if let Some(profiles) = value.profiles {
if let Some(profiles) = value.profiles.as_ref() {
settings
.profiles
.extend(profiles.into_iter().map(|(id, profile)| {
(
id,
id.clone(),
AgentProfileSettings {
name: profile.name.into(),
tools: profile.tools,
name: profile.name.clone().into(),
tools: profile.tools.clone(),
enable_all_context_servers: profile
.enable_all_context_servers
.unwrap_or_default(),
context_servers: profile
.context_servers
.into_iter()
.iter()
.map(|(context_server_id, preset)| {
(
context_server_id,
context_server_id.clone(),
ContextServerPreset {
tools: preset.tools.clone(),
},
@@ -944,28 +492,8 @@ impl Settings for AgentSettings {
.read_value("chat.agent.enabled")
.and_then(|b| b.as_bool())
{
match &mut current.inner {
Some(AgentSettingsContentInner::Versioned(versioned)) => match versioned.as_mut() {
VersionedAgentSettingsContent::V1(setting) => {
setting.enabled = Some(b);
setting.button = Some(b);
}
VersionedAgentSettingsContent::V2(setting) => {
setting.enabled = Some(b);
setting.button = Some(b);
}
},
Some(AgentSettingsContentInner::Legacy(setting)) => setting.button = Some(b),
None => {
current.inner =
Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 {
enabled: Some(b),
button: Some(b),
..Default::default()
}));
}
}
current.enabled = Some(b);
current.button = Some(b);
}
}
}
@@ -975,149 +503,3 @@ fn merge<T>(target: &mut T, value: Option<T>) {
*target = value;
}
}
#[cfg(test)]
mod tests {
use fs::Fs;
use gpui::{ReadGlobal, TestAppContext};
use settings::SettingsStore;
use super::*;
#[gpui::test]
async fn test_deserialize_agent_settings_with_version(cx: &mut TestAppContext) {
let fs = fs::FakeFs::new(cx.executor().clone());
fs.create_dir(paths::settings_file().parent().unwrap())
.await
.unwrap();
cx.update(|cx| {
let test_settings = settings::SettingsStore::test(cx);
cx.set_global(test_settings);
AgentSettings::register(cx);
});
cx.update(|cx| {
assert!(!AgentSettings::get_global(cx).using_outdated_settings_version);
assert_eq!(
AgentSettings::get_global(cx).default_model,
LanguageModelSelection {
provider: "zed.dev".into(),
model: "claude-sonnet-4".into(),
}
);
});
cx.update(|cx| {
settings::SettingsStore::global(cx).update_settings_file::<AgentSettings>(
fs.clone(),
|settings, _| {
*settings = AgentSettingsContent {
inner: Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 {
default_model: Some(LanguageModelSelection {
provider: "test-provider".into(),
model: "gpt-99".into(),
}),
inline_assistant_model: None,
commit_message_model: None,
thread_summary_model: None,
inline_alternatives: None,
enabled: None,
button: None,
dock: None,
default_width: None,
default_height: None,
default_profile: None,
default_view: None,
profiles: None,
always_allow_tool_actions: None,
play_sound_when_agent_done: None,
notify_when_agent_waiting: None,
stream_edits: None,
single_file_review: None,
enable_feedback: None,
model_parameters: Vec::new(),
preferred_completion_mode: None,
})),
}
},
);
});
cx.run_until_parked();
let raw_settings_value = fs.load(paths::settings_file()).await.unwrap();
assert!(raw_settings_value.contains(r#""version": "2""#));
#[derive(Debug, Deserialize)]
struct AgentSettingsTest {
agent: AgentSettingsContent,
}
let agent_settings: AgentSettingsTest =
serde_json_lenient::from_str(&raw_settings_value).unwrap();
assert!(!agent_settings.agent.is_version_outdated());
}
#[gpui::test]
async fn test_load_settings_from_old_key(cx: &mut TestAppContext) {
let fs = fs::FakeFs::new(cx.executor().clone());
fs.create_dir(paths::settings_file().parent().unwrap())
.await
.unwrap();
cx.update(|cx| {
let mut test_settings = settings::SettingsStore::test(cx);
let user_settings_content = r#"{
"assistant": {
"enabled": true,
"version": "2",
"default_model": {
"provider": "zed.dev",
"model": "gpt-99"
},
}}"#;
test_settings
.set_user_settings(user_settings_content, cx)
.unwrap();
cx.set_global(test_settings);
AgentSettings::register(cx);
});
cx.run_until_parked();
let agent_settings = cx.update(|cx| AgentSettings::get_global(cx).clone());
assert!(agent_settings.enabled);
assert!(!agent_settings.using_outdated_settings_version);
assert_eq!(agent_settings.default_model.model, "gpt-99");
cx.update_global::<SettingsStore, _>(|settings_store, cx| {
settings_store.update_user_settings::<AgentSettings>(cx, |settings| {
*settings = AgentSettingsContent {
inner: Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 {
enabled: Some(false),
default_model: Some(LanguageModelSelection {
provider: "xai".to_owned().into(),
model: "grok".to_owned(),
}),
..Default::default()
})),
};
});
});
cx.run_until_parked();
let settings = cx.update(|cx| SettingsStore::global(cx).raw_user_settings().clone());
#[derive(Debug, Deserialize)]
struct AgentSettingsTest {
assistant: AgentSettingsContent,
agent: Option<serde_json_lenient::Value>,
}
let agent_settings: AgentSettingsTest = serde_json::from_value(settings).unwrap();
assert!(agent_settings.agent.is_none());
}
}

110
crates/agent_ui/Cargo.toml Normal file
View File

@@ -0,0 +1,110 @@
[package]
name = "agent_ui"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/agent_ui.rs"
doctest = false
[features]
test-support = [
"gpui/test-support",
"language/test-support",
]
[dependencies]
agent.workspace = true
agent_settings.workspace = true
anyhow.workspace = true
assistant_context.workspace = true
assistant_slash_command.workspace = true
assistant_slash_commands.workspace = true
assistant_tool.workspace = true
audio.workspace = true
buffer_diff.workspace = true
chrono.workspace = true
client.workspace = true
collections.workspace = true
component.workspace = true
context_server.workspace = true
db.workspace = true
editor.workspace = true
extension.workspace = true
extension_host.workspace = true
feature_flags.workspace = true
file_icons.workspace = true
fs.workspace = true
futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
html_to_markdown.workspace = true
indoc.workspace = true
http_client.workspace = true
indexed_docs.workspace = true
inventory.workspace = true
itertools.workspace = true
jsonschema.workspace = true
language.workspace = true
language_model.workspace = true
log.workspace = true
lsp.workspace = true
markdown.workspace = true
menu.workspace = true
multi_buffer.workspace = true
notifications.workspace = true
ordered-float.workspace = true
parking_lot.workspace = true
paths.workspace = true
picker.workspace = true
project.workspace = true
prompt_store.workspace = true
proto.workspace = true
release_channel.workspace = true
rope.workspace = true
rules_library.workspace = true
schemars.workspace = true
search.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_json_lenient.workspace = true
settings.workspace = true
smol.workspace = true
streaming_diff.workspace = true
telemetry.workspace = true
telemetry_events.workspace = true
terminal.workspace = true
terminal_view.workspace = true
text.workspace = true
theme.workspace = true
time.workspace = true
time_format.workspace = true
ui.workspace = true
urlencoding.workspace = true
util.workspace = true
uuid.workspace = true
watch.workspace = true
workspace-hack.workspace = true
workspace.workspace = true
zed_actions.workspace = true
zed_llm_client.workspace = true
[dev-dependencies]
assistant_tools.workspace = true
buffer_diff = { workspace = true, features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, "features" = ["test-support"] }
indoc.workspace = true
language = { workspace = true, "features" = ["test-support"] }
languages = { workspace = true, features = ["test-support"] }
language_model = { workspace = true, "features" = ["test-support"] }
pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
rand.workspace = true
tree-sitter-md.workspace = true
unindent.workspace = true

View File

@@ -20,7 +20,7 @@ use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageMod
use notifications::status_toast::{StatusToast, ToastIcon};
use project::{
context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore},
project_settings::ProjectSettings,
project_settings::{ContextServerSettings, ProjectSettings},
};
use settings::{Settings, update_settings_file};
use ui::{
@@ -593,7 +593,7 @@ impl AgentConfiguration {
}
})
.separator()
.entry("Delete", None, {
.entry("Uninstall", None, {
let fs = fs.clone();
let context_server_id = context_server_id.clone();
let context_server_store = context_server_store.clone();
@@ -741,24 +741,59 @@ impl AgentConfiguration {
let context_server_manager =
self.context_server_store.clone();
let context_server_id = context_server_id.clone();
let fs = self.fs.clone();
move |state, _window, cx| match state {
ToggleState::Unselected
| ToggleState::Indeterminate => {
context_server_manager.update(cx, |this, cx| {
this.stop_server(&context_server_id, cx)
.log_err();
});
}
ToggleState::Selected => {
context_server_manager.update(cx, |this, cx| {
if let Some(server) =
this.get_server(&context_server_id)
{
this.start_server(server, cx);
move |state, _window, cx| {
let is_enabled = match state {
ToggleState::Unselected
| ToggleState::Indeterminate => {
context_server_manager.update(
cx,
|this, cx| {
this.stop_server(
&context_server_id,
cx,
)
.log_err();
},
);
false
}
ToggleState::Selected => {
context_server_manager.update(
cx,
|this, cx| {
if let Some(server) =
this.get_server(&context_server_id)
{
this.start_server(server, cx);
}
},
);
true
}
};
update_settings_file::<ProjectSettings>(
fs.clone(),
cx,
{
let context_server_id =
context_server_id.clone();
move |settings, _| {
settings
.context_servers
.entry(context_server_id.0)
.or_insert_with(|| {
ContextServerSettings::Extension {
enabled: is_enabled,
settings: serde_json::json!({}),
}
})
.set_enabled(is_enabled);
}
})
}
},
);
}
}),
),
@@ -905,15 +940,56 @@ fn show_unable_to_uninstall_extension_with_context_server(
id: ContextServerId,
cx: &mut App,
) {
let workspace_handle = workspace.weak_handle();
let context_server_id = id.clone();
let status_toast = StatusToast::new(
format!(
"Unable to uninstall the {} extension, as it provides more than just the MCP server.",
"The {} extension provides more than just the MCP server. Proceed to uninstall anyway?",
id.0
),
cx,
|this, _cx| {
move |this, _cx| {
let workspace_handle = workspace_handle.clone();
let context_server_id = context_server_id.clone();
this.icon(ToastIcon::new(IconName::Warning).color(Color::Warning))
.action("Dismiss", |_, _| {})
.dismiss_button(true)
.action("Uninstall", move |_, _cx| {
if let Some((extension_id, _)) =
resolve_extension_for_context_server(&context_server_id, _cx)
{
ExtensionStore::global(_cx).update(_cx, |store, cx| {
store
.uninstall_extension(extension_id, cx)
.detach_and_log_err(cx);
});
workspace_handle
.update(_cx, |workspace, cx| {
let fs = workspace.app_state().fs.clone();
cx.spawn({
let context_server_id = context_server_id.clone();
async move |_workspace_handle, cx| {
cx.update(|cx| {
update_settings_file::<ProjectSettings>(
fs,
cx,
move |settings, _| {
settings
.context_servers
.remove(&context_server_id.0);
},
);
})?;
anyhow::Ok(())
}
})
.detach_and_log_err(cx);
})
.log_err();
}
})
},
);

View File

@@ -140,8 +140,15 @@ impl ConfigurationSource {
fn output(&self, cx: &mut App) -> Result<(ContextServerId, ContextServerSettings)> {
match self {
ConfigurationSource::New { editor } | ConfigurationSource::Existing { editor } => {
parse_input(&editor.read(cx).text(cx))
.map(|(id, command)| (id, ContextServerSettings::Custom { command }))
parse_input(&editor.read(cx).text(cx)).map(|(id, command)| {
(
id,
ContextServerSettings::Custom {
enabled: true,
command,
},
)
})
}
ConfigurationSource::Extension {
id,
@@ -160,7 +167,13 @@ impl ConfigurationSource {
return Err(anyhow::anyhow!(error.to_string()));
}
}
Ok((id.clone(), ContextServerSettings::Extension { settings }))
Ok((
id.clone(),
ContextServerSettings::Extension {
enabled: true,
settings,
},
))
}
}
}
@@ -282,9 +295,7 @@ impl ConfigureContextServerModal {
ContextServerDescriptorRegistry::default_global(cx)
.read(cx)
.context_server_descriptor(&server_id.0)
.map(|_| ContextServerSettings::Extension {
settings: serde_json::json!({}),
})
.map(|_| ContextServerSettings::default_extension())
})
else {
return Task::ready(Err(anyhow::anyhow!("Context server not found")));
@@ -292,7 +303,10 @@ impl ConfigureContextServerModal {
window.spawn(cx, async move |cx| {
let target = match settings {
ContextServerSettings::Custom { command } => Some(ConfigurationTarget::Existing {
ContextServerSettings::Custom {
enabled: _,
command,
} => Some(ConfigurationTarget::Existing {
id: server_id,
command,
}),
@@ -486,10 +500,7 @@ impl ConfigureContextServerModal {
ConfigurationSource::Existing { editor } => editor,
ConfigurationSource::Extension { editor, .. } => {
let Some(editor) = editor else {
return Label::new(
"No configuration options available for this context server. Visit the Repository for any further instructions.",
)
.color(Color::Muted).into_any_element();
return div().into_any_element();
};
editor
}
@@ -737,7 +748,7 @@ pub(crate) fn default_markdown_style(window: &Window, cx: &App) -> MarkdownStyle
MarkdownStyle {
base_text_style: text_style.clone(),
selection_background_color: cx.theme().players().local().selection,
selection_background_color: colors.element_selection_background,
link: TextStyleRefinement {
background_color: Some(colors.editor_foreground.opacity(0.025)),
underline: Some(UnderlineStyle {

View File

@@ -15,8 +15,8 @@ use workspace::{ModalView, Workspace};
use crate::agent_configuration::manage_profiles_modal::profile_modal_header::ProfileModalHeader;
use crate::agent_configuration::tool_picker::{ToolPicker, ToolPickerDelegate};
use crate::agent_profile::AgentProfile;
use crate::{AgentPanel, ManageProfiles};
use agent::agent_profile::AgentProfile;
use super::tool_picker::ToolPickerMode;

View File

@@ -272,42 +272,35 @@ impl PickerDelegate for ToolPickerDelegate {
let server_id = server_id.clone();
let tool_name = tool_name.clone();
move |settings: &mut AgentSettingsContent, _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(),
});
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,
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(),
});
if let Some(server_id) = server_id {
let preset = profile.context_servers.entry(server_id).or_default();
*preset.tools.entry(tool_name).or_default() = !is_currently_enabled;
} else {
*profile.tools.entry(tool_name).or_default() = !is_currently_enabled;
}
Ok(())
})
.ok();
if let Some(server_id) = server_id {
let preset = profile.context_servers.entry(server_id).or_default();
*preset.tools.entry(tool_name).or_default() = !is_currently_enabled;
} else {
*profile.tools.entry(tool_name).or_default() = !is_currently_enabled;
}
}
});
}

View File

@@ -1,10 +1,12 @@
use crate::{Keep, KeepAll, OpenAgentDiff, Reject, RejectAll, Thread, ThreadEvent};
use crate::{Keep, KeepAll, OpenAgentDiff, Reject, RejectAll};
use agent::{Thread, ThreadEvent};
use agent_settings::AgentSettings;
use anyhow::Result;
use buffer_diff::DiffHunkStatus;
use collections::{HashMap, HashSet};
use editor::{
Direction, Editor, EditorEvent, EditorSettings, MultiBuffer, MultiBufferSnapshot, ToPoint,
Direction, Editor, EditorEvent, EditorSettings, MultiBuffer, MultiBufferSnapshot,
SelectionEffects, ToPoint,
actions::{GoToHunk, GoToPreviousHunk},
scroll::Autoscroll,
};
@@ -170,15 +172,9 @@ impl AgentDiffPane {
if let Some(first_hunk) = first_hunk {
let first_hunk_start = first_hunk.multi_buffer_range().start;
editor.change_selections(
Some(Autoscroll::fit()),
window,
cx,
|selections| {
selections
.select_anchor_ranges([first_hunk_start..first_hunk_start]);
},
)
editor.change_selections(Default::default(), window, cx, |selections| {
selections.select_anchor_ranges([first_hunk_start..first_hunk_start]);
})
}
}
@@ -215,7 +211,7 @@ impl AgentDiffPane {
}
fn update_title(&mut self, cx: &mut Context<Self>) {
let new_title = self.thread.read(cx).summary().unwrap_or("Agent Changes");
let new_title = self.thread.read(cx).title().unwrap_or("Agent Changes");
if new_title != self.title {
self.title = new_title;
cx.emit(EditorEvent::TitleChanged);
@@ -241,7 +237,7 @@ impl AgentDiffPane {
if let Some(first_hunk) = first_hunk {
let first_hunk_start = first_hunk.multi_buffer_range().start;
editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
editor.change_selections(Default::default(), window, cx, |selections| {
selections.select_anchor_ranges([first_hunk_start..first_hunk_start]);
})
}
@@ -415,7 +411,7 @@ fn update_editor_selection(
};
if let Some(target_hunk) = target_hunk {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
editor.change_selections(Default::default(), window, cx, |selections| {
let next_hunk_start = target_hunk.multi_buffer_range().start;
selections.select_anchor_ranges([next_hunk_start..next_hunk_start]);
})
@@ -465,7 +461,7 @@ impl Item for AgentDiffPane {
}
fn tab_content(&self, params: TabContentParams, _window: &Window, cx: &App) -> AnyElement {
let summary = self.thread.read(cx).summary().unwrap_or("Agent Changes");
let summary = self.thread.read(cx).title().unwrap_or("Agent Changes");
Label::new(format!("Review: {}", summary))
.color(if params.selected {
Color::Default
@@ -1373,12 +1369,11 @@ impl AgentDiff {
| ThreadEvent::MessageDeleted(_)
| ThreadEvent::SummaryGenerated
| ThreadEvent::SummaryChanged
| ThreadEvent::UsePendingTools { .. }
| ThreadEvent::ToolFinished { .. }
| ThreadEvent::CheckpointChanged
| ThreadEvent::ToolConfirmationNeeded
| ThreadEvent::ToolUseLimitReached
| ThreadEvent::CancelEditing
| ThreadEvent::RetriesFailed { .. }
| ThreadEvent::ProfileChanged => {}
}
}
@@ -1542,7 +1537,7 @@ impl AgentDiff {
let first_hunk_start = first_hunk.multi_buffer_range().start;
editor.change_selections(
Some(Autoscroll::center()),
SelectionEffects::scroll(Autoscroll::center()),
window,
cx,
|selections| {
@@ -1748,7 +1743,8 @@ impl editor::Addon for EditorAgentDiffAddon {
#[cfg(test)]
mod tests {
use super::*;
use crate::{Keep, ThreadStore, thread_store};
use crate::Keep;
use agent::thread_store::{self, ThreadStore};
use agent_settings::AgentSettings;
use assistant_tool::ToolWorkingSet;
use editor::EditorSettings;
@@ -1803,7 +1799,10 @@ mod tests {
})
.await
.unwrap();
let thread = thread_store.update(cx, |store, cx| store.create_thread(cx));
let thread = thread_store
.update(cx, |store, cx| store.create_thread(cx))
.await
.unwrap();
let action_log = thread.read_with(cx, |thread, _| thread.action_log().clone());
let (workspace, cx) =
@@ -1865,7 +1864,7 @@ mod tests {
// Rejecting a hunk also moves the cursor to the next hunk, possibly cycling if it's at the end.
editor.update_in(cx, |editor, window, cx| {
editor.change_selections(None, window, cx, |selections| {
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
selections.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
});
});
@@ -1968,7 +1967,10 @@ mod tests {
})
.await
.unwrap();
let thread = thread_store.update(cx, |store, cx| store.create_thread(cx));
let thread = thread_store
.update(cx, |store, cx| store.create_thread(cx))
.await
.unwrap();
let action_log = thread.read_with(cx, |thread, _| thread.action_log().clone());
let (workspace, cx) =
@@ -2121,7 +2123,7 @@ mod tests {
// Rejecting a hunk also moves the cursor to the next hunk, possibly cycling if it's at the end.
editor1.update_in(cx, |editor, window, cx| {
editor.change_selections(None, window, cx, |selections| {
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
selections.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
});
});

View File

@@ -1,16 +1,17 @@
use crate::{
ModelUsageContext,
language_model_selector::{
LanguageModelSelector, ToggleModelSelector, language_model_selector,
},
};
use agent_settings::AgentSettings;
use fs::Fs;
use gpui::{Entity, FocusHandle, SharedString};
use picker::popover_menu::PickerPopoverMenu;
use crate::ModelUsageContext;
use assistant_context_editor::language_model_selector::{
LanguageModelSelector, ToggleModelSelector, language_model_selector,
};
use language_model::{ConfiguredModel, LanguageModelRegistry};
use picker::popover_menu::PickerPopoverMenu;
use settings::update_settings_file;
use std::sync::Arc;
use ui::{PopoverMenuHandle, Tooltip, prelude::*};
use ui::{ButtonLike, PopoverMenuHandle, Tooltip, prelude::*};
pub struct AgentModelSelector {
selector: Entity<LanguageModelSelector>,
@@ -44,7 +45,7 @@ impl AgentModelSelector {
let registry = LanguageModelRegistry::read_global(cx);
if let Some(provider) = registry.provider(&model.provider_id())
{
thread.set_configured_model(
thread.set_model(
Some(ConfiguredModel {
provider,
model: model.clone(),
@@ -93,20 +94,35 @@ impl Render for AgentModelSelector {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let model = self.selector.read(cx).delegate.active_model(cx);
let model_name = model
.as_ref()
.map(|model| model.model.name().0)
.unwrap_or_else(|| SharedString::from("No model selected"));
let provider_icon = model
.as_ref()
.map(|model| model.provider.icon())
.unwrap_or_else(|| IconName::Ai);
let focus_handle = self.focus_handle.clone();
PickerPopoverMenu::new(
self.selector.clone(),
Button::new("active-model", model_name)
.label_size(LabelSize::Small)
.color(Color::Muted)
.icon(IconName::ChevronDown)
.icon_size(IconSize::XSmall)
.icon_position(IconPosition::End)
.icon_color(Color::Muted),
ButtonLike::new("active-model")
.child(
Icon::new(provider_icon)
.color(Color::Muted)
.size(IconSize::XSmall),
)
.child(
Label::new(model_name)
.color(Color::Muted)
.size(LabelSize::Small)
.ml_0p5(),
)
.child(
Icon::new(IconName::ChevronDown)
.color(Color::Muted)
.size(IconSize::XSmall),
),
move |window, cx| {
Tooltip::for_action_in(
"Change Model",

View File

@@ -0,0 +1,292 @@
mod active_thread;
mod agent_configuration;
mod agent_diff;
mod agent_model_selector;
mod agent_panel;
mod buffer_codegen;
mod burn_mode_tooltip;
mod context_picker;
mod context_server_configuration;
mod context_strip;
mod debug;
mod inline_assistant;
mod inline_prompt_editor;
mod language_model_selector;
mod message_editor;
mod profile_selector;
mod slash_command;
mod slash_command_picker;
mod slash_command_settings;
mod terminal_codegen;
mod terminal_inline_assistant;
mod text_thread_editor;
mod thread_history;
mod tool_compatibility;
mod ui;
use std::sync::Arc;
use agent::{Thread, ThreadId};
use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
use assistant_slash_command::SlashCommandRegistry;
use client::Client;
use feature_flags::FeatureFlagAppExt as _;
use fs::Fs;
use gpui::{Action, App, Entity, actions};
use language::LanguageRegistry;
use language_model::{
ConfiguredModel, LanguageModel, LanguageModelId, LanguageModelProviderId, LanguageModelRegistry,
};
use prompt_store::PromptBuilder;
use schemars::JsonSchema;
use serde::Deserialize;
use settings::{Settings as _, SettingsStore};
pub use crate::active_thread::ActiveThread;
use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal};
pub use crate::agent_panel::{AgentPanel, ConcreteAssistantPanelDelegate};
pub use crate::inline_assistant::InlineAssistant;
use crate::slash_command_settings::SlashCommandSettings;
pub use agent_diff::{AgentDiffPane, AgentDiffToolbar};
pub use text_thread_editor::{AgentPanelDelegate, TextThreadEditor};
pub use ui::preview::{all_agent_previews, get_agent_preview};
actions!(
agent,
[
NewTextThread,
ToggleContextPicker,
ToggleNavigationMenu,
ToggleOptionsMenu,
DeleteRecentlyOpenThread,
ToggleProfileSelector,
RemoveAllContext,
ExpandMessageEditor,
OpenHistory,
AddContextServer,
RemoveSelectedThread,
Chat,
ChatWithFollow,
CycleNextInlineAssist,
CyclePreviousInlineAssist,
FocusUp,
FocusDown,
FocusLeft,
FocusRight,
RemoveFocusedContext,
AcceptSuggestedContext,
OpenActiveThreadAsMarkdown,
OpenAgentDiff,
Keep,
Reject,
RejectAll,
KeepAll,
Follow,
ResetTrialUpsell,
ResetTrialEndUpsell,
ContinueThread,
ContinueWithBurnMode,
ToggleBurnMode,
]
);
#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema, Action)]
#[action(namespace = agent)]
pub struct NewThread {
#[serde(default)]
from_thread_id: Option<ThreadId>,
}
#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema, Action)]
#[action(namespace = agent)]
pub struct ManageProfiles {
#[serde(default)]
pub customize_tools: Option<AgentProfileId>,
}
impl ManageProfiles {
pub fn customize_tools(profile_id: AgentProfileId) -> Self {
Self {
customize_tools: Some(profile_id),
}
}
}
#[derive(Clone)]
pub(crate) enum ModelUsageContext {
Thread(Entity<Thread>),
InlineAssistant,
}
impl ModelUsageContext {
pub fn configured_model(&self, cx: &App) -> Option<ConfiguredModel> {
match self {
Self::Thread(thread) => thread.read(cx).model(),
Self::InlineAssistant => {
LanguageModelRegistry::read_global(cx).inline_assistant_model()
}
}
}
pub fn language_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>> {
self.configured_model(cx)
.map(|configured_model| configured_model.model)
}
}
/// Initializes the `agent` crate.
pub fn init(
fs: Arc<dyn Fs>,
client: Arc<Client>,
prompt_builder: Arc<PromptBuilder>,
language_registry: Arc<LanguageRegistry>,
is_eval: bool,
cx: &mut App,
) {
AgentSettings::register(cx);
SlashCommandSettings::register(cx);
assistant_context::init(client.clone(), cx);
rules_library::init(cx);
if !is_eval {
// Initializing the language model from the user settings messes with the eval, so we only initialize them when
// we're not running inside of the eval.
init_language_model_settings(cx);
}
assistant_slash_command::init(cx);
agent::init(cx);
agent_panel::init(cx);
context_server_configuration::init(language_registry.clone(), fs.clone(), cx);
TextThreadEditor::init(cx);
register_slash_commands(cx);
inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
client.telemetry().clone(),
cx,
);
terminal_inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
client.telemetry().clone(),
cx,
);
indexed_docs::init(cx);
cx.observe_new(move |workspace, window, cx| {
ConfigureContextServerModal::register(workspace, language_registry.clone(), window, cx)
})
.detach();
cx.observe_new(ManageProfilesModal::register).detach();
}
fn init_language_model_settings(cx: &mut App) {
update_active_language_model_from_settings(cx);
cx.observe_global::<SettingsStore>(update_active_language_model_from_settings)
.detach();
cx.subscribe(
&LanguageModelRegistry::global(cx),
|_, event: &language_model::Event, cx| match event {
language_model::Event::ProviderStateChanged
| language_model::Event::AddedProvider(_)
| language_model::Event::RemovedProvider(_) => {
update_active_language_model_from_settings(cx);
}
_ => {}
},
)
.detach();
}
fn update_active_language_model_from_settings(cx: &mut App) {
let settings = AgentSettings::get_global(cx);
fn to_selected_model(selection: &LanguageModelSelection) -> language_model::SelectedModel {
language_model::SelectedModel {
provider: LanguageModelProviderId::from(selection.provider.0.clone()),
model: LanguageModelId::from(selection.model.clone()),
}
}
let default = to_selected_model(&settings.default_model);
let inline_assistant = settings
.inline_assistant_model
.as_ref()
.map(to_selected_model);
let commit_message = settings
.commit_message_model
.as_ref()
.map(to_selected_model);
let thread_summary = settings
.thread_summary_model
.as_ref()
.map(to_selected_model);
let inline_alternatives = settings
.inline_alternatives
.iter()
.map(to_selected_model)
.collect::<Vec<_>>();
LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
registry.select_default_model(Some(&default), cx);
registry.select_inline_assistant_model(inline_assistant.as_ref(), cx);
registry.select_commit_message_model(commit_message.as_ref(), cx);
registry.select_thread_summary_model(thread_summary.as_ref(), cx);
registry.select_inline_alternative_models(inline_alternatives, cx);
});
}
fn register_slash_commands(cx: &mut App) {
let slash_command_registry = SlashCommandRegistry::global(cx);
slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true);
slash_command_registry
.register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true);
slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false);
slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false);
slash_command_registry
.register_command(assistant_slash_commands::DiagnosticsSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true);
cx.observe_flag::<assistant_slash_commands::StreamingExampleSlashCommandFeatureFlag, _>({
let slash_command_registry = slash_command_registry.clone();
move |is_enabled, _cx| {
if is_enabled {
slash_command_registry.register_command(
assistant_slash_commands::StreamingExampleSlashCommand,
false,
);
}
}
})
.detach();
update_slash_commands_from_settings(cx);
cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
.detach();
}
fn update_slash_commands_from_settings(cx: &mut App) {
let slash_command_registry = SlashCommandRegistry::global(cx);
let settings = SlashCommandSettings::get_global(cx);
if settings.docs.enabled {
slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true);
} else {
slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand);
}
if settings.cargo_workspace.enabled {
slash_command_registry
.register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
} else {
slash_command_registry
.unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand);
}
}

View File

@@ -1,6 +1,8 @@
use crate::context::ContextLoadResult;
use crate::inline_prompt_editor::CodegenStatus;
use crate::{context::load_context, context_store::ContextStore};
use agent::{
ContextStore,
context::{ContextLoadResult, load_context},
};
use agent_settings::AgentSettings;
use anyhow::{Context as _, Result};
use client::telemetry::Telemetry;
@@ -18,8 +20,7 @@ use language_model::{
use multi_buffer::MultiBufferRow;
use parking_lot::Mutex;
use project::Project;
use prompt_store::PromptBuilder;
use prompt_store::PromptStore;
use prompt_store::{PromptBuilder, PromptStore};
use rope::Rope;
use smol::future::FutureExt;
use std::{
@@ -1093,15 +1094,9 @@ mod tests {
};
use language_model::{LanguageModelRegistry, TokenUsage};
use rand::prelude::*;
use serde::Serialize;
use settings::SettingsStore;
use std::{future, sync::Arc};
#[derive(Serialize)]
pub struct DummyCompletionRequest {
pub name: String,
}
#[gpui::test(iterations = 10)]
async fn test_transform_autoindent(cx: &mut TestAppContext, mut rng: StdRng) {
init_test(cx);

View File

@@ -1,11 +1,11 @@
use gpui::{Context, FontWeight, IntoElement, Render, Window};
use ui::{prelude::*, tooltip_container};
pub struct MaxModeTooltip {
pub struct BurnModeTooltip {
selected: bool,
}
impl MaxModeTooltip {
impl BurnModeTooltip {
pub fn new() -> Self {
Self { selected: false }
}
@@ -16,7 +16,7 @@ impl MaxModeTooltip {
}
}
impl Render for MaxModeTooltip {
impl Render for BurnModeTooltip {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let (icon, color) = if self.selected {
(IconName::ZedBurnModeOn, Color::Error)

View File

@@ -37,10 +37,12 @@ use uuid::Uuid;
use workspace::{Workspace, notifications::NotifyResultExt};
use crate::AgentPanel;
use crate::context::RULES_ICON;
use crate::context_store::ContextStore;
use crate::thread::ThreadId;
use crate::thread_store::{TextThreadStore, ThreadStore};
use agent::{
ThreadId,
context::RULES_ICON,
context_store::ContextStore,
thread_store::{TextThreadStore, ThreadStore},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ContextPickerEntry {
@@ -659,7 +661,7 @@ fn recent_context_picker_entries(
let active_thread_id = workspace
.panel::<AgentPanel>(cx)
.and_then(|panel| Some(panel.read(cx).active_thread()?.read(cx).id()));
.and_then(|panel| Some(panel.read(cx).active_thread(cx)?.read(cx).id()));
if let Some((thread_store, text_thread_store)) = thread_store
.and_then(|store| store.upgrade())
@@ -668,7 +670,7 @@ fn recent_context_picker_entries(
let mut threads = unordered_thread_entries(thread_store, text_thread_store, cx)
.filter(|(_, thread)| match thread {
ThreadContextEntry::Thread { id, .. } => {
Some(id) != active_thread_id && !current_threads.contains(id)
Some(id) != active_thread_id.as_ref() && !current_threads.contains(id)
}
ThreadContextEntry::Context { .. } => true,
})
@@ -928,8 +930,8 @@ impl MentionLink {
format!(
"[@{} ({}-{})]({}:{}:{}-{})",
file_name,
line_range.start,
line_range.end,
line_range.start + 1,
line_range.end + 1,
Self::SELECTION,
full_path,
line_range.start,

View File

@@ -3,6 +3,7 @@ use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use agent::context_store::ContextStore;
use anyhow::Result;
use editor::{CompletionProvider, Editor, ExcerptId, ToOffset as _};
use file_icons::FileIcons;
@@ -20,10 +21,11 @@ use ui::prelude::*;
use util::ResultExt as _;
use workspace::Workspace;
use crate::Thread;
use crate::context::{AgentContextHandle, AgentContextKey, ContextCreasesAddon, RULES_ICON};
use crate::context_store::ContextStore;
use crate::thread_store::{TextThreadStore, ThreadStore};
use agent::{
Thread,
context::{AgentContextHandle, AgentContextKey, RULES_ICON},
thread_store::{TextThreadStore, ThreadStore},
};
use super::fetch_context_picker::fetch_url_content;
use super::file_context_picker::{FileMatch, search_files};
@@ -35,6 +37,7 @@ use super::{
ContextPickerAction, ContextPickerEntry, ContextPickerMode, MentionLink, RecentEntry,
available_context_picker_entries, recent_context_picker_entries, selection_ranges,
};
use crate::message_editor::ContextCreasesAddon;
pub(crate) enum Match {
File(FileMatch),
@@ -70,7 +73,7 @@ fn search(
recent_entries: Vec<RecentEntry>,
prompt_store: Option<Entity<PromptStore>>,
thread_store: Option<WeakEntity<ThreadStore>>,
text_thread_context_store: Option<WeakEntity<assistant_context_editor::ContextStore>>,
text_thread_context_store: Option<WeakEntity<assistant_context::ContextStore>>,
workspace: Entity<Workspace>,
cx: &mut App,
) -> Task<Vec<Match>> {

View File

@@ -2,6 +2,7 @@ use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
use agent::context_store::ContextStore;
use anyhow::{Context as _, Result, bail};
use futures::AsyncReadExt as _;
use gpui::{App, DismissEvent, Entity, FocusHandle, Focusable, Task, WeakEntity};
@@ -12,7 +13,6 @@ use ui::{Context, ListItem, Window, prelude::*};
use workspace::Workspace;
use crate::context_picker::ContextPicker;
use crate::context_store::ContextStore;
pub struct FetchContextPicker {
picker: Entity<Picker<FetchContextPickerDelegate>>,

View File

@@ -14,7 +14,7 @@ use util::ResultExt as _;
use workspace::Workspace;
use crate::context_picker::ContextPicker;
use crate::context_store::{ContextStore, FileInclusion};
use agent::context_store::{ContextStore, FileInclusion};
pub struct FileContextPicker {
picker: Entity<Picker<FileContextPickerDelegate>>,

View File

@@ -7,9 +7,9 @@ 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};
use agent::context::RULES_ICON;
use agent::context_store::{self, ContextStore};
pub struct RulesContextPicker {
picker: Entity<Picker<RulesContextPickerDelegate>>,

View File

@@ -14,9 +14,9 @@ use ui::{ListItem, prelude::*};
use util::ResultExt as _;
use workspace::Workspace;
use crate::context::AgentContextHandle;
use crate::context_picker::ContextPicker;
use crate::context_store::ContextStore;
use agent::context::AgentContextHandle;
use agent::context_store::ContextStore;
pub struct SymbolContextPicker {
picker: Entity<Picker<SymbolContextPickerDelegate>>,

View File

@@ -9,9 +9,11 @@ use picker::{Picker, PickerDelegate};
use ui::{ListItem, prelude::*};
use crate::context_picker::ContextPicker;
use crate::context_store::{self, ContextStore};
use crate::thread::ThreadId;
use crate::thread_store::{TextThreadStore, ThreadStore};
use agent::{
ThreadId,
context_store::{self, ContextStore},
thread_store::{TextThreadStore, ThreadStore},
};
pub struct ThreadContextPicker {
picker: Entity<Picker<ThreadContextPickerDelegate>>,

View File

@@ -1,7 +1,15 @@
use std::path::Path;
use std::rc::Rc;
use assistant_context_editor::AssistantContext;
use crate::{
AcceptSuggestedContext, AgentPanel, FocusDown, FocusLeft, FocusRight, FocusUp,
ModelUsageContext, RemoveAllContext, RemoveFocusedContext, ToggleContextPicker,
context_picker::ContextPicker,
ui::{AddedContext, ContextPill},
};
use agent::context_store::SuggestedContext;
use agent::{
context::AgentContextHandle,
context_store::ContextStore,
thread_store::{TextThreadStore, ThreadStore},
};
use collections::HashSet;
use editor::Editor;
use file_icons::FileIcons;
@@ -10,22 +18,11 @@ use gpui::{
Subscription, WeakEntity,
};
use itertools::Itertools;
use language::Buffer;
use project::ProjectItem;
use std::{path::Path, rc::Rc};
use ui::{PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*};
use workspace::Workspace;
use crate::context::{AgentContextHandle, ContextKind};
use crate::context_picker::ContextPicker;
use crate::context_store::ContextStore;
use crate::thread::Thread;
use crate::thread_store::{TextThreadStore, ThreadStore};
use crate::ui::{AddedContext, ContextPill};
use crate::{
AcceptSuggestedContext, AgentPanel, FocusDown, FocusLeft, FocusRight, FocusUp,
ModelUsageContext, RemoveAllContext, RemoveFocusedContext, ToggleContextPicker,
};
pub struct ContextStrip {
context_store: Entity<ContextStore>,
context_picker: Entity<ContextPicker>,
@@ -164,7 +161,7 @@ impl ContextStrip {
let workspace = self.workspace.upgrade()?;
let panel = workspace.read(cx).panel::<AgentPanel>(cx)?.read(cx);
if let Some(active_thread) = panel.active_thread() {
if let Some(active_thread) = panel.active_thread(cx) {
let weak_active_thread = active_thread.downgrade();
let active_thread = active_thread.read(cx);
@@ -172,13 +169,13 @@ impl ContextStrip {
if self
.context_store
.read(cx)
.includes_thread(active_thread.id())
.includes_thread(&active_thread.id())
{
return None;
}
Some(SuggestedContext::Thread {
name: active_thread.summary().or_default(),
name: active_thread.title().or_default(),
thread: weak_active_thread,
})
} else if let Some(active_context_editor) = panel.active_context_editor() {
@@ -575,46 +572,3 @@ pub enum SuggestContextKind {
File,
Thread,
}
#[derive(Clone)]
pub enum SuggestedContext {
File {
name: SharedString,
icon_path: Option<SharedString>,
buffer: WeakEntity<Buffer>,
},
Thread {
name: SharedString,
thread: WeakEntity<Thread>,
},
TextThread {
name: SharedString,
context: WeakEntity<AssistantContext>,
},
}
impl SuggestedContext {
pub fn name(&self) -> &SharedString {
match self {
Self::File { name, .. } => name,
Self::Thread { name, .. } => name,
Self::TextThread { name, .. } => name,
}
}
pub fn icon_path(&self) -> Option<SharedString> {
match self {
Self::File { icon_path, .. } => icon_path.clone(),
Self::Thread { .. } => None,
Self::TextThread { .. } => None,
}
}
pub fn kind(&self) -> ContextKind {
match self {
Self::File { .. } => ContextKind::File,
Self::Thread { .. } => ContextKind::Thread,
Self::TextThread { .. } => ContextKind::TextThread,
}
}
}

View File

@@ -1,7 +1,7 @@
#![allow(unused, dead_code)]
use client::{ModelRequestUsage, RequestUsage};
use gpui::Global;
use language_model::RequestUsage;
use std::ops::{Deref, DerefMut};
use ui::prelude::*;
use zed_llm_client::{Plan, UsageLimit};
@@ -17,7 +17,7 @@ pub struct DebugAccountState {
pub enabled: bool,
pub trial_expired: bool,
pub plan: Plan,
pub custom_prompt_usage: RequestUsage,
pub custom_prompt_usage: ModelRequestUsage,
pub usage_based_billing_enabled: bool,
pub monthly_spending_cap: i32,
pub custom_edit_prediction_usage: UsageLimit,
@@ -43,7 +43,7 @@ impl DebugAccountState {
self
}
pub fn set_custom_prompt_usage(&mut self, custom_prompt_usage: RequestUsage) -> &mut Self {
pub fn set_custom_prompt_usage(&mut self, custom_prompt_usage: ModelRequestUsage) -> &mut Self {
self.custom_prompt_usage = custom_prompt_usage;
self
}
@@ -76,10 +76,10 @@ impl Default for DebugAccountState {
enabled: false,
trial_expired: false,
plan: Plan::ZedFree,
custom_prompt_usage: RequestUsage {
custom_prompt_usage: ModelRequestUsage(RequestUsage {
limit: UsageLimit::Unlimited,
amount: 0,
},
}),
usage_based_billing_enabled: false,
// $50.00
monthly_spending_cap: 5000,

View File

@@ -4,18 +4,28 @@ use std::ops::Range;
use std::rc::Rc;
use std::sync::Arc;
use crate::{
AgentPanel,
buffer_codegen::{BufferCodegen, CodegenAlternative, CodegenEvent},
inline_prompt_editor::{CodegenStatus, InlineAssistId, PromptEditor, PromptEditorEvent},
terminal_inline_assistant::TerminalInlineAssistant,
};
use agent::{
context_store::ContextStore,
thread_store::{TextThreadStore, ThreadStore},
};
use agent_settings::AgentSettings;
use anyhow::{Context as _, Result};
use client::telemetry::Telemetry;
use collections::{HashMap, HashSet, VecDeque, hash_map};
use editor::display_map::EditorMargins;
use editor::SelectionEffects;
use editor::{
Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorEvent, ExcerptId, ExcerptRange,
MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint,
actions::SelectAll,
display_map::{
BlockContext, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
ToDisplayPoint,
BlockContext, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, EditorMargins,
RenderBlock, ToDisplayPoint,
},
};
use fs::Fs;
@@ -24,16 +34,13 @@ use gpui::{
WeakEntity, Window, point,
};
use language::{Buffer, Point, Selection, TransactionId};
use language_model::ConfigurationError;
use language_model::ConfiguredModel;
use language_model::{LanguageModelRegistry, report_assistant_event};
use language_model::{
ConfigurationError, ConfiguredModel, 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 project::{CodeAction, LspAction, Project, ProjectTransaction};
use prompt_store::{PromptBuilder, PromptStore};
use settings::{Settings, SettingsStore};
use telemetry_events::{AssistantEventData, AssistantKind, AssistantPhase};
use terminal_view::{TerminalView, terminal_panel::TerminalPanel};
@@ -43,14 +50,6 @@ use util::{RangeExt, ResultExt, maybe};
use workspace::{ItemHandle, Toast, Workspace, dock::Panel, notifications::NotificationId};
use zed_actions::agent::OpenConfiguration;
use crate::AgentPanel;
use crate::buffer_codegen::{BufferCodegen, CodegenAlternative, CodegenEvent};
use crate::context_store::ContextStore;
use crate::inline_prompt_editor::{CodegenStatus, InlineAssistId, PromptEditor, PromptEditorEvent};
use crate::terminal_inline_assistant::TerminalInlineAssistant;
use crate::thread_store::TextThreadStore;
use crate::thread_store::ThreadStore;
pub fn init(
fs: Arc<dyn Fs>,
prompt_builder: Arc<PromptBuilder>,
@@ -1161,7 +1160,7 @@ impl InlineAssistant {
let position = assist.range.start;
editor.update(cx, |editor, cx| {
editor.change_selections(None, window, cx, |selections| {
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
selections.select_anchor_ranges([position..position])
});

View File

@@ -1,15 +1,16 @@
use crate::agent_model_selector::AgentModelSelector;
use crate::buffer_codegen::BufferCodegen;
use crate::context::ContextCreasesAddon;
use crate::context_picker::{ContextPicker, ContextPickerCompletionProvider};
use crate::context_store::ContextStore;
use crate::context_strip::{ContextStrip, ContextStripEvent, SuggestContextKind};
use crate::message_editor::{extract_message_creases, insert_message_creases};
use crate::language_model_selector::ToggleModelSelector;
use crate::message_editor::{ContextCreasesAddon, extract_message_creases, insert_message_creases};
use crate::terminal_codegen::TerminalCodegen;
use crate::thread_store::{TextThreadStore, ThreadStore};
use crate::{CycleNextInlineAssist, CyclePreviousInlineAssist, ModelUsageContext};
use crate::{RemoveAllContext, ToggleContextPicker};
use assistant_context_editor::language_model_selector::ToggleModelSelector;
use agent::{
context_store::ContextStore,
thread_store::{TextThreadStore, ThreadStore},
};
use client::ErrorExt;
use collections::VecDeque;
use db::kvp::Dismissable;
@@ -870,7 +871,7 @@ impl PromptEditor<BufferCodegen> {
let mut editor = Editor::new(
EditorMode::AutoHeight {
min_lines: 1,
max_lines: Self::MAX_LINES as usize,
max_lines: Some(Self::MAX_LINES as usize),
},
prompt_buffer,
None,
@@ -1049,7 +1050,7 @@ impl PromptEditor<TerminalCodegen> {
let mut editor = Editor::new(
EditorMode::AutoHeight {
min_lines: 1,
max_lines: Self::MAX_LINES as usize,
max_lines: Some(Self::MAX_LINES as usize),
},
prompt_buffer,
None,

View File

@@ -4,8 +4,7 @@ use collections::{HashSet, IndexMap};
use feature_flags::ZedProFeatureFlag;
use fuzzy::{StringMatch, StringMatchCandidate, match_strings};
use gpui::{
Action, AnyElement, App, BackgroundExecutor, DismissEvent, Subscription, Task,
action_with_deprecated_aliases,
Action, AnyElement, App, BackgroundExecutor, DismissEvent, Subscription, Task, actions,
};
use language_model::{
AuthenticateError, ConfiguredModel, LanguageModel, LanguageModelProviderId,
@@ -16,12 +15,11 @@ use picker::{Picker, PickerDelegate};
use proto::Plan;
use ui::{ListItem, ListItemSpacing, prelude::*};
action_with_deprecated_aliases!(
actions!(
agent,
ToggleModelSelector,
[
"assistant::ToggleModelSelector",
"assistant2::ToggleModelSelector"
#[action(deprecated_aliases = ["assistant::ToggleModelSelector", "assistant2::ToggleModelSelector"])]
ToggleModelSelector
]
);

View File

@@ -3,21 +3,25 @@ use std::rc::Rc;
use std::sync::Arc;
use crate::agent_model_selector::AgentModelSelector;
use crate::context::{AgentContextKey, ContextCreasesAddon, ContextLoadResult, load_context};
use crate::language_model_selector::ToggleModelSelector;
use crate::tool_compatibility::{IncompatibleToolsState, IncompatibleToolsTooltip};
use crate::ui::{
MaxModeTooltip,
preview::{AgentPreview, UsageCallout},
};
use agent::{
context::{AgentContextKey, ContextLoadResult, load_context},
context_store::ContextStoreEvent,
};
use agent_settings::{AgentSettings, CompletionMode};
use assistant_context_editor::language_model_selector::ToggleModelSelector;
use buffer_diff::BufferDiff;
use client::UserStore;
use collections::{HashMap, HashSet};
use editor::actions::{MoveUp, Paste};
use editor::display_map::CreaseId;
use editor::{
AnchorRangeExt, ContextMenuOptions, ContextMenuPlacement, Editor, EditorElement, EditorEvent,
EditorMode, EditorStyle, MultiBuffer,
Addon, AnchorRangeExt, ContextMenuOptions, ContextMenuPlacement, Editor, EditorElement,
EditorEvent, EditorMode, EditorStyle, MultiBuffer,
};
use file_icons::FileIcons;
use fs::Fs;
@@ -29,8 +33,7 @@ use gpui::{
};
use language::{Buffer, Language, Point};
use language_model::{
ConfiguredModel, LanguageModelRequestMessage, MessageContent, RequestUsage,
ZED_CLOUD_PROVIDER_ID,
ConfiguredModel, LanguageModelRequestMessage, MessageContent, ZED_CLOUD_PROVIDER_ID,
};
use multi_buffer;
use project::Project;
@@ -42,21 +45,23 @@ use theme::ThemeSettings;
use ui::{
Callout, Disclosure, Divider, DividerColor, KeyBinding, PopoverMenuHandle, Tooltip, prelude::*,
};
use util::{ResultExt as _, maybe};
use util::ResultExt as _;
use workspace::{CollaboratorId, Workspace};
use zed_llm_client::CompletionIntent;
use crate::context_picker::{ContextPicker, ContextPickerCompletionProvider, crease_for_mention};
use crate::context_store::ContextStore;
use crate::context_strip::{ContextStrip, ContextStripEvent, SuggestContextKind};
use crate::profile_selector::ProfileSelector;
use crate::thread::{MessageCrease, Thread, TokenUsageRatio};
use crate::thread_store::{TextThreadStore, ThreadStore};
use crate::{
ActiveThread, AgentDiffPane, Chat, ChatWithFollow, ExpandMessageEditor, Follow, KeepAll,
ModelUsageContext, NewThread, OpenAgentDiff, RejectAll, RemoveAllContext, ToggleBurnMode,
ToggleContextPicker, ToggleProfileSelector, register_agent_preview,
};
use agent::{
MessageCrease, Thread, TokenUsageRatio,
context_store::ContextStore,
thread_store::{TextThreadStore, ThreadStore},
};
#[derive(RegisterComponent)]
pub struct MessageEditor {
@@ -90,7 +95,7 @@ pub(crate) fn create_editor(
thread_store: WeakEntity<ThreadStore>,
text_thread_store: WeakEntity<TextThreadStore>,
min_lines: usize,
max_lines: usize,
max_lines: Option<usize>,
window: &mut Window,
cx: &mut App,
) -> Entity<Editor> {
@@ -108,7 +113,7 @@ pub(crate) fn create_editor(
let mut editor = Editor::new(
editor::EditorMode::AutoHeight {
min_lines,
max_lines,
max_lines: max_lines,
},
buffer,
None,
@@ -164,7 +169,7 @@ impl MessageEditor {
thread_store.clone(),
text_thread_store.clone(),
MIN_EDITOR_LINES,
MAX_EDITOR_LINES,
Some(MAX_EDITOR_LINES),
window,
cx,
);
@@ -241,6 +246,21 @@ impl MessageEditor {
&self.context_store
}
pub fn get_text(&self, cx: &App) -> String {
self.editor.read(cx).text(cx)
}
pub fn set_text(
&mut self,
text: impl Into<Arc<str>>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.editor.update(cx, |editor, cx| {
editor.set_text(text, window, cx);
});
}
pub fn expand_message_editor(
&mut self,
_: &ExpandMessageEditor,
@@ -262,7 +282,7 @@ impl MessageEditor {
} else {
editor.set_mode(EditorMode::AutoHeight {
min_lines: MIN_EDITOR_LINES,
max_lines: MAX_EDITOR_LINES,
max_lines: Some(MAX_EDITOR_LINES),
})
}
});
@@ -367,25 +387,14 @@ impl MessageEditor {
thread
.update(cx, |thread, cx| {
thread.insert_user_message(
user_message,
loaded_context,
checkpoint.ok(),
user_message_creases,
cx,
);
})
.log_err();
thread
.update(cx, |thread, cx| {
thread.advance_prompt_id();
thread.send_to_model(
model,
CompletionIntent::UserPrompt,
Some(window_handle),
cx,
);
todo!();
// thread.send(
// user_message,
// loaded_context,
// checkpoint.ok(),
// user_message_creases,
// cx,
// );
})
.log_err();
})
@@ -555,7 +564,7 @@ impl MessageEditor {
fn render_burn_mode_toggle(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
let thread = self.thread.read(cx);
let model = thread.configured_model();
if !model?.model.supports_max_mode() {
if !model?.model.supports_burn_mode() {
return None;
}
@@ -1226,16 +1235,17 @@ impl MessageEditor {
})
}
fn render_usage_callout(&self, line_height: Pixels, cx: &mut Context<Self>) -> Option<Div> {
let is_using_zed_provider = self
.thread
fn is_using_zed_provider(&self, cx: &App) -> bool {
self.thread
.read(cx)
.configured_model()
.map_or(false, |model| {
model.provider.id().0 == ZED_CLOUD_PROVIDER_ID
});
})
}
if !is_using_zed_provider {
fn render_usage_callout(&self, line_height: Pixels, cx: &mut Context<Self>) -> Option<Div> {
if !self.is_using_zed_provider(cx) {
return None;
}
@@ -1257,24 +1267,8 @@ impl MessageEditor {
Plan::ZedProTrial => zed_llm_client::Plan::ZedProTrial,
})
.unwrap_or(zed_llm_client::Plan::ZedFree);
let usage = self.thread.read(cx).last_usage().or_else(|| {
maybe!({
let amount = user_store.model_request_usage_amount()?;
let limit = user_store.model_request_usage_limit()?.variant?;
Some(RequestUsage {
amount: amount as i32,
limit: match limit {
proto::usage_limit::Variant::Limited(limited) => {
zed_llm_client::UsageLimit::Limited(limited.limit as i32)
}
proto::usage_limit::Variant::Unlimited(_) => {
zed_llm_client::UsageLimit::Unlimited
}
},
})
})
})?;
let usage = user_store.model_request_usage()?;
Some(
div()
@@ -1305,37 +1299,41 @@ impl MessageEditor {
"Thread reaching the token limit soon"
};
let description = if self.is_using_zed_provider(cx) {
"To continue, start a new thread from a summary or turn burn mode on."
} else {
"To continue, start a new thread from a summary."
};
let mut callout = Callout::new()
.line_height(line_height)
.icon(icon)
.title(title)
.description(description)
.primary_action(
Button::new("start-new-thread", "Start New Thread")
.label_size(LabelSize::Small)
.on_click(cx.listener(|this, _, window, cx| {
let from_thread_id = Some(this.thread.read(cx).id().clone());
window.dispatch_action(Box::new(NewThread { from_thread_id }), cx);
})),
);
if self.is_using_zed_provider(cx) {
callout = callout.secondary_action(
IconButton::new("burn-mode-callout", IconName::ZedBurnMode)
.icon_size(IconSize::XSmall)
.on_click(cx.listener(|this, _event, window, cx| {
this.toggle_burn_mode(&ToggleBurnMode, window, cx);
})),
);
}
Some(
div()
.border_t_1()
.border_color(cx.theme().colors().border)
.child(
Callout::new()
.line_height(line_height)
.icon(icon)
.title(title)
.description(
"To continue, start a new thread from a summary or turn burn mode on.",
)
.primary_action(
Button::new("start-new-thread", "Start New Thread")
.label_size(LabelSize::Small)
.on_click(cx.listener(|this, _, window, cx| {
let from_thread_id = Some(this.thread.read(cx).id().clone());
window.dispatch_action(
Box::new(NewThread { from_thread_id }),
cx,
);
})),
)
.secondary_action(
IconButton::new("burn-mode-callout", IconName::ZedBurnMode)
.icon_size(IconSize::XSmall)
.on_click(cx.listener(|this, _event, window, cx| {
this.toggle_burn_mode(&ToggleBurnMode, window, cx);
})),
),
),
.child(callout),
)
}
@@ -1463,6 +1461,69 @@ impl MessageEditor {
}
}
#[derive(Default)]
pub struct ContextCreasesAddon {
creases: HashMap<AgentContextKey, Vec<(CreaseId, SharedString)>>,
_subscription: Option<Subscription>,
}
impl Addon for ContextCreasesAddon {
fn to_any(&self) -> &dyn std::any::Any {
self
}
fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
Some(self)
}
}
impl ContextCreasesAddon {
pub fn new() -> Self {
Self {
creases: HashMap::default(),
_subscription: None,
}
}
pub fn add_creases(
&mut self,
context_store: &Entity<ContextStore>,
key: AgentContextKey,
creases: impl IntoIterator<Item = (CreaseId, SharedString)>,
cx: &mut Context<Editor>,
) {
self.creases.entry(key).or_default().extend(creases);
self._subscription = Some(cx.subscribe(
&context_store,
|editor, _, event, cx| match event {
ContextStoreEvent::ContextRemoved(key) => {
let Some(this) = editor.addon_mut::<Self>() else {
return;
};
let (crease_ids, replacement_texts): (Vec<_>, Vec<_>) = this
.creases
.remove(key)
.unwrap_or_default()
.into_iter()
.unzip();
let ranges = editor
.remove_creases(crease_ids, cx)
.into_iter()
.map(|(_, range)| range)
.collect::<Vec<_>>();
editor.unfold_ranges(&ranges, false, false, cx);
editor.edit(ranges.into_iter().zip(replacement_texts), cx);
cx.notify();
}
},
))
}
pub fn into_inner(self) -> HashMap<AgentContextKey, Vec<(CreaseId, SharedString)>> {
self.creases
}
}
pub fn extract_message_creases(
editor: &mut Editor,
cx: &mut Context<'_, Editor>,
@@ -1501,8 +1562,9 @@ pub fn extract_message_creases(
let context = contexts_by_crease_id.remove(&id);
MessageCrease {
range,
metadata,
context,
label: metadata.label,
icon_path: metadata.icon_path,
}
})
.collect()
@@ -1574,8 +1636,8 @@ pub fn insert_message_creases(
let start = buffer_snapshot.anchor_after(crease.range.start);
let end = buffer_snapshot.anchor_before(crease.range.end);
crease_for_mention(
crease.metadata.label.clone(),
crease.metadata.icon_path.clone(),
crease.label.clone(),
crease.icon_path.clone(),
start..end,
cx.weak_entity(),
)
@@ -1587,12 +1649,7 @@ pub fn insert_message_creases(
for (crease, id) in message_creases.iter().zip(ids) {
if let Some(context) = crease.context.as_ref() {
let key = AgentContextKey(context.clone());
addon.add_creases(
context_store,
key,
vec![(id, crease.metadata.label.clone())],
cx,
);
addon.add_creases(context_store, key, vec![(id, crease.label.clone())], cx);
}
}
}

View File

@@ -1,20 +1,19 @@
use std::sync::Arc;
use crate::{ManageProfiles, ToggleProfileSelector};
use agent::{
Thread,
agent_profile::{AgentProfile, AvailableProfiles},
};
use agent_settings::{AgentDockPosition, AgentProfileId, AgentSettings, builtin_profiles};
use fs::Fs;
use gpui::{Action, Empty, Entity, FocusHandle, Subscription, prelude::*};
use language_model::LanguageModelRegistry;
use settings::{Settings as _, SettingsStore, update_settings_file};
use std::sync::Arc;
use ui::{
ContextMenu, ContextMenuEntry, DocumentationSide, PopoverMenu, PopoverMenuHandle, Tooltip,
prelude::*,
};
use crate::{
ManageProfiles, Thread, ToggleProfileSelector,
agent_profile::{AgentProfile, AvailableProfiles},
};
pub struct ProfileSelector {
profiles: AvailableProfiles,
fs: Arc<dyn Fs>,
@@ -157,7 +156,7 @@ impl Render for ProfileSelector {
.map(|profile| profile.name.clone())
.unwrap_or_else(|| "Unknown".into());
let configured_model = self.thread.read(cx).configured_model().or_else(|| {
let configured_model = self.thread.read(cx).model().or_else(|| {
let model_registry = LanguageModelRegistry::read_global(cx);
model_registry.default_model()
});

View File

@@ -1,4 +1,4 @@
use crate::context_editor::ContextEditor;
use crate::text_thread_editor::TextThreadEditor;
use anyhow::Result;
pub use assistant_slash_command::SlashCommand;
use assistant_slash_command::{AfterCompletion, SlashCommandLine, SlashCommandWorkingSet};
@@ -21,14 +21,14 @@ use workspace::Workspace;
pub struct SlashCommandCompletionProvider {
cancel_flag: Mutex<Arc<AtomicBool>>,
slash_commands: Arc<SlashCommandWorkingSet>,
editor: Option<WeakEntity<ContextEditor>>,
editor: Option<WeakEntity<TextThreadEditor>>,
workspace: Option<WeakEntity<Workspace>>,
}
impl SlashCommandCompletionProvider {
pub fn new(
slash_commands: Arc<SlashCommandWorkingSet>,
editor: Option<WeakEntity<ContextEditor>>,
editor: Option<WeakEntity<TextThreadEditor>>,
workspace: Option<WeakEntity<Workspace>>,
) -> Self {
Self {

View File

@@ -1,12 +1,10 @@
use std::sync::Arc;
use crate::text_thread_editor::TextThreadEditor;
use assistant_slash_command::SlashCommandWorkingSet;
use gpui::{AnyElement, AnyView, DismissEvent, SharedString, Task, WeakEntity};
use picker::{Picker, PickerDelegate, PickerEditorPosition};
use std::sync::Arc;
use ui::{ListItem, ListItemSpacing, PopoverMenu, PopoverTrigger, Tooltip, prelude::*};
use crate::context_editor::ContextEditor;
#[derive(IntoElement)]
pub(super) struct SlashCommandSelector<T, TT>
where
@@ -14,7 +12,7 @@ where
TT: Fn(&mut Window, &mut App) -> AnyView + 'static,
{
working_set: Arc<SlashCommandWorkingSet>,
active_context_editor: WeakEntity<ContextEditor>,
active_context_editor: WeakEntity<TextThreadEditor>,
trigger: T,
tooltip: TT,
}
@@ -49,7 +47,7 @@ impl AsRef<str> for SlashCommandEntry {
pub(crate) struct SlashCommandDelegate {
all_commands: Vec<SlashCommandEntry>,
filtered_commands: Vec<SlashCommandEntry>,
active_context_editor: WeakEntity<ContextEditor>,
active_context_editor: WeakEntity<TextThreadEditor>,
selected_index: usize,
}
@@ -60,7 +58,7 @@ where
{
pub(crate) fn new(
working_set: Arc<SlashCommandWorkingSet>,
active_context_editor: WeakEntity<ContextEditor>,
active_context_editor: WeakEntity<TextThreadEditor>,
trigger: T,
tooltip: TT,
) -> Self {

View File

@@ -1,10 +1,12 @@
use crate::context::load_context;
use crate::context_store::ContextStore;
use crate::inline_prompt_editor::{
CodegenStatus, PromptEditor, PromptEditorEvent, TerminalInlineAssistId,
};
use crate::terminal_codegen::{CLEAR_INPUT, CodegenEvent, TerminalCodegen};
use crate::thread_store::{TextThreadStore, ThreadStore};
use agent::{
context::load_context,
context_store::ContextStore,
thread_store::{TextThreadStore, ThreadStore},
};
use agent_settings::AgentSettings;
use anyhow::{Context as _, Result};
use client::telemetry::Telemetry;

View File

@@ -1,8 +1,8 @@
use crate::{
burn_mode_tooltip::BurnModeTooltip,
language_model_selector::{
LanguageModelSelector, ToggleModelSelector, language_model_selector,
},
max_mode_tooltip::MaxModeTooltip,
};
use agent_settings::{AgentSettings, CompletionMode};
use anyhow::Result;
@@ -21,17 +21,16 @@ use editor::{
BlockPlacement, BlockProperties, BlockStyle, Crease, CreaseMetadata, CustomBlockId, FoldId,
RenderBlock, ToDisplayPoint,
},
scroll::Autoscroll,
};
use editor::{FoldPlaceholder, display_map::CreaseId};
use fs::Fs;
use futures::FutureExt;
use gpui::{
Animation, AnimationExt, AnyElement, AnyView, App, ClipboardEntry, ClipboardItem, Empty,
Entity, EventEmitter, FocusHandle, Focusable, FontWeight, Global, InteractiveElement,
Action, Animation, AnimationExt, AnyElement, AnyView, App, ClipboardEntry, ClipboardItem,
Empty, Entity, EventEmitter, FocusHandle, Focusable, FontWeight, Global, InteractiveElement,
IntoElement, ParentElement, Pixels, Render, RenderImage, SharedString, Size,
StatefulInteractiveElement, Styled, Subscription, Task, Transformation, WeakEntity, actions,
div, img, impl_internal_actions, percentage, point, prelude::*, pulsating_between, size,
div, img, percentage, point, prelude::*, pulsating_between, size,
};
use indexed_docs::IndexedDocsStore;
use language::{
@@ -69,21 +68,18 @@ use workspace::{
searchable::{Direction, SearchableItemHandle},
};
use workspace::{
Save, Toast, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
Save, Toast, Workspace,
item::{self, FollowableItem, Item, ItemHandle},
notifications::NotificationId,
pane,
searchable::{SearchEvent, SearchableItem},
};
use crate::{
use crate::{slash_command::SlashCommandCompletionProvider, slash_command_picker};
use assistant_context::{
AssistantContext, CacheStatus, Content, ContextEvent, ContextId, InvokedSlashCommandId,
InvokedSlashCommandStatus, Message, MessageId, MessageMetadata, MessageStatus,
ParsedSlashCommand, PendingSlashCommandStatus,
};
use crate::{
ThoughtProcessOutputSection, slash_command::SlashCommandCompletionProvider,
slash_command_picker,
ParsedSlashCommand, PendingSlashCommandStatus, ThoughtProcessOutputSection,
};
actions!(
@@ -99,14 +95,13 @@ actions!(
]
);
#[derive(PartialEq, Clone)]
#[derive(PartialEq, Clone, Action)]
#[action(namespace = assistant, no_json, no_register)]
pub enum InsertDraggedFiles {
ProjectPaths(Vec<ProjectPath>),
ExternalFiles(Vec<PathBuf>),
}
impl_internal_actions!(assistant, [InsertDraggedFiles]);
#[derive(Copy, Clone, Debug, PartialEq)]
struct ScrollPosition {
offset_before_cursor: gpui::Point<f32>,
@@ -132,7 +127,7 @@ pub trait AgentPanelDelegate {
workspace: &mut Workspace,
window: &mut Window,
cx: &mut Context<Workspace>,
) -> Option<Entity<ContextEditor>>;
) -> Option<Entity<TextThreadEditor>>;
fn open_saved_context(
&self,
@@ -148,7 +143,7 @@ pub trait AgentPanelDelegate {
context_id: ContextId,
window: &mut Window,
cx: &mut Context<Workspace>,
) -> Task<Result<Entity<ContextEditor>>>;
) -> Task<Result<Entity<TextThreadEditor>>>;
fn quote_selection(
&self,
@@ -177,7 +172,7 @@ struct GlobalAssistantPanelDelegate(Arc<dyn AgentPanelDelegate>);
impl Global for GlobalAssistantPanelDelegate {}
pub struct ContextEditor {
pub struct TextThreadEditor {
context: Entity<AssistantContext>,
fs: Arc<dyn Fs>,
slash_commands: Arc<SlashCommandWorkingSet>,
@@ -207,10 +202,24 @@ pub struct ContextEditor {
language_model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
}
pub const DEFAULT_TAB_TITLE: &str = "New Chat";
const MAX_TAB_TITLE_LEN: usize = 16;
impl ContextEditor {
impl TextThreadEditor {
pub fn init(cx: &mut App) {
workspace::FollowableViewRegistry::register::<TextThreadEditor>(cx);
cx.observe_new(
|workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| {
workspace
.register_action(TextThreadEditor::quote_selection)
.register_action(TextThreadEditor::insert_selection)
.register_action(TextThreadEditor::copy_code)
.register_action(TextThreadEditor::handle_insert_dragged_files);
},
)
.detach();
}
pub fn for_context(
context: Entity<AssistantContext>,
fs: Arc<dyn Fs>,
@@ -379,7 +388,7 @@ impl ContextEditor {
cursor..cursor
};
self.editor.update(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
editor.change_selections(Default::default(), window, cx, |selections| {
selections.select_ranges([new_selection])
});
});
@@ -439,8 +448,7 @@ impl ContextEditor {
if let Some(command) = self.slash_commands.command(name, cx) {
self.editor.update(cx, |editor, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor
.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel());
editor.change_selections(Default::default(), window, cx, |s| s.try_cancel());
let snapshot = editor.buffer().read(cx).snapshot(cx);
let newest_cursor = editor.selections.newest::<Point>(cx).head();
if newest_cursor.column > 0
@@ -1280,7 +1288,7 @@ impl ContextEditor {
/// Returns either the selected text, or the content of the Markdown code
/// block surrounding the cursor.
fn get_selection_or_code_block(
context_editor_view: &Entity<ContextEditor>,
context_editor_view: &Entity<TextThreadEditor>,
cx: &mut Context<Workspace>,
) -> Option<(String, bool)> {
const CODE_FENCE_DELIMITER: &'static str = "```";
@@ -1573,7 +1581,7 @@ impl ContextEditor {
self.editor.update(cx, |editor, cx| {
editor.transact(window, cx, |this, window, cx| {
this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
this.change_selections(Default::default(), window, cx, |s| {
s.select(selections);
});
this.insert("", window, cx);
@@ -2030,7 +2038,7 @@ impl ContextEditor {
/// Whether or not we should allow messages to be sent.
/// Will return false if the selected provided has a configuration error or
/// if the user has not accepted the terms of service for this provider.
fn sending_disabled(&self, cx: &mut Context<'_, ContextEditor>) -> bool {
fn sending_disabled(&self, cx: &mut Context<'_, TextThreadEditor>) -> bool {
let model_registry = LanguageModelRegistry::read_global(cx);
let Some(configuration_error) =
model_registry.configuration_error(model_registry.default_model(), cx)
@@ -2065,12 +2073,12 @@ impl ContextEditor {
)
}
fn render_max_mode_toggle(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
fn render_burn_mode_toggle(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
let context = self.context().read(cx);
let active_model = LanguageModelRegistry::read_global(cx)
.default_model()
.map(|default| default.model)?;
if !active_model.supports_max_mode() {
if !active_model.supports_burn_mode() {
return None;
}
@@ -2097,7 +2105,7 @@ impl ContextEditor {
});
}))
.tooltip(move |_window, cx| {
cx.new(|_| MaxModeTooltip::new().selected(burn_mode_enabled))
cx.new(|_| BurnModeTooltip::new().selected(burn_mode_enabled))
.into()
})
.into_any_element(),
@@ -2112,12 +2120,21 @@ impl ContextEditor {
let active_model = LanguageModelRegistry::read_global(cx)
.default_model()
.map(|default| default.model);
let focus_handle = self.editor().focus_handle(cx).clone();
let model_name = match active_model {
Some(model) => model.name().0,
None => SharedString::from("No model selected"),
};
let active_provider = LanguageModelRegistry::read_global(cx)
.default_model()
.map(|default| default.provider);
let provider_icon = match active_provider {
Some(provider) => provider.icon(),
None => IconName::Ai,
};
let focus_handle = self.editor().focus_handle(cx).clone();
PickerPopoverMenu::new(
self.language_model_selector.clone(),
ButtonLike::new("active-model")
@@ -2125,10 +2142,16 @@ impl ContextEditor {
.child(
h_flex()
.gap_0p5()
.child(
Icon::new(provider_icon)
.color(Color::Muted)
.size(IconSize::XSmall),
)
.child(
Label::new(model_name)
.color(Color::Muted)
.size(LabelSize::Small)
.color(Color::Muted),
.ml_0p5(),
)
.child(
Icon::new(IconName::ChevronDown)
@@ -2547,10 +2570,10 @@ struct SelectedCreaseMetadata {
crease: CreaseMetadata,
}
impl EventEmitter<EditorEvent> for ContextEditor {}
impl EventEmitter<SearchEvent> for ContextEditor {}
impl EventEmitter<EditorEvent> for TextThreadEditor {}
impl EventEmitter<SearchEvent> for TextThreadEditor {}
impl Render for ContextEditor {
impl Render for TextThreadEditor {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let provider = LanguageModelRegistry::read_global(cx)
.default_model()
@@ -2565,19 +2588,19 @@ impl Render for ContextEditor {
};
let language_model_selector = self.language_model_selector_menu_handle.clone();
let max_mode_toggle = self.render_max_mode_toggle(cx);
let burn_mode_toggle = self.render_burn_mode_toggle(cx);
v_flex()
.key_context("ContextEditor")
.capture_action(cx.listener(ContextEditor::cancel))
.capture_action(cx.listener(ContextEditor::save))
.capture_action(cx.listener(ContextEditor::copy))
.capture_action(cx.listener(ContextEditor::cut))
.capture_action(cx.listener(ContextEditor::paste))
.capture_action(cx.listener(ContextEditor::cycle_message_role))
.capture_action(cx.listener(ContextEditor::confirm_command))
.on_action(cx.listener(ContextEditor::assist))
.on_action(cx.listener(ContextEditor::split))
.capture_action(cx.listener(TextThreadEditor::cancel))
.capture_action(cx.listener(TextThreadEditor::save))
.capture_action(cx.listener(TextThreadEditor::copy))
.capture_action(cx.listener(TextThreadEditor::cut))
.capture_action(cx.listener(TextThreadEditor::paste))
.capture_action(cx.listener(TextThreadEditor::cycle_message_role))
.capture_action(cx.listener(TextThreadEditor::confirm_command))
.on_action(cx.listener(TextThreadEditor::assist))
.on_action(cx.listener(TextThreadEditor::split))
.on_action(move |_: &ToggleModelSelector, window, cx| {
language_model_selector.toggle(window, cx);
})
@@ -2620,7 +2643,7 @@ impl Render for ContextEditor {
h_flex()
.gap_0p5()
.child(self.render_inject_context_menu(cx))
.when_some(max_mode_toggle, |this, element| this.child(element)),
.when_some(burn_mode_toggle, |this, element| this.child(element)),
)
.child(
h_flex()
@@ -2632,13 +2655,13 @@ impl Render for ContextEditor {
}
}
impl Focusable for ContextEditor {
impl Focusable for TextThreadEditor {
fn focus_handle(&self, cx: &App) -> FocusHandle {
self.editor.focus_handle(cx)
}
}
impl Item for ContextEditor {
impl Item for TextThreadEditor {
type Event = editor::EditorEvent;
fn tab_content_text(&self, _detail: usize, cx: &App) -> SharedString {
@@ -2711,7 +2734,7 @@ impl Item for ContextEditor {
}
}
impl SearchableItem for ContextEditor {
impl SearchableItem for TextThreadEditor {
type Match = <Editor as SearchableItem>::Match;
fn clear_matches(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -2792,7 +2815,7 @@ impl SearchableItem for ContextEditor {
}
}
impl FollowableItem for ContextEditor {
impl FollowableItem for TextThreadEditor {
fn remote_id(&self) -> Option<workspace::ViewId> {
self.remote_id
}
@@ -2914,22 +2937,8 @@ impl FollowableItem for ContextEditor {
}
}
pub struct ContextEditorToolbarItem {
active_context_editor: Option<WeakEntity<ContextEditor>>,
model_summary_editor: Entity<Editor>,
}
impl ContextEditorToolbarItem {
pub fn new(model_summary_editor: Entity<Editor>) -> Self {
Self {
active_context_editor: None,
model_summary_editor,
}
}
}
pub fn render_remaining_tokens(
context_editor: &Entity<ContextEditor>,
context_editor: &Entity<TextThreadEditor>,
cx: &App,
) -> Option<impl IntoElement + use<>> {
let context = &context_editor.read(cx).context;
@@ -2980,98 +2989,6 @@ pub fn render_remaining_tokens(
)
}
impl Render for ContextEditorToolbarItem {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let left_side = h_flex()
.group("chat-title-group")
.gap_1()
.items_center()
.flex_grow()
.child(
div()
.w_full()
.when(self.active_context_editor.is_some(), |left_side| {
left_side.child(self.model_summary_editor.clone())
}),
)
.child(
div().visible_on_hover("chat-title-group").child(
IconButton::new("regenerate-context", IconName::RefreshTitle)
.shape(ui::IconButtonShape::Square)
.tooltip(Tooltip::text("Regenerate Title"))
.on_click(cx.listener(move |_, _, _window, cx| {
cx.emit(ContextEditorToolbarItemEvent::RegenerateSummary)
})),
),
);
let right_side = h_flex()
.gap_2()
// TODO display this in a nicer way, once we have a design for it.
// .children({
// let project = self
// .workspace
// .upgrade()
// .map(|workspace| workspace.read(cx).project().downgrade());
//
// let scan_items_remaining = cx.update_global(|db: &mut SemanticDb, cx| {
// project.and_then(|project| db.remaining_summaries(&project, cx))
// });
// scan_items_remaining
// .map(|remaining_items| format!("Files to scan: {}", remaining_items))
// })
.children(
self.active_context_editor
.as_ref()
.and_then(|editor| editor.upgrade())
.and_then(|editor| render_remaining_tokens(&editor, cx)),
);
h_flex()
.px_0p5()
.size_full()
.gap_2()
.justify_between()
.child(left_side)
.child(right_side)
}
}
impl ToolbarItemView for ContextEditorToolbarItem {
fn set_active_pane_item(
&mut self,
active_pane_item: Option<&dyn ItemHandle>,
_window: &mut Window,
cx: &mut Context<Self>,
) -> ToolbarItemLocation {
self.active_context_editor = active_pane_item
.and_then(|item| item.act_as::<ContextEditor>(cx))
.map(|editor| editor.downgrade());
cx.notify();
if self.active_context_editor.is_none() {
ToolbarItemLocation::Hidden
} else {
ToolbarItemLocation::PrimaryRight
}
}
fn pane_focus_update(
&mut self,
_pane_focused: bool,
_window: &mut Window,
cx: &mut Context<Self>,
) {
cx.notify();
}
}
impl EventEmitter<ToolbarItemEvent> for ContextEditorToolbarItem {}
pub enum ContextEditorToolbarItemEvent {
RegenerateSummary,
}
impl EventEmitter<ContextEditorToolbarItemEvent> for ContextEditorToolbarItem {}
enum PendingSlashCommand {}
fn invoked_slash_command_fold_placeholder(
@@ -3237,6 +3154,7 @@ pub fn make_lsp_adapter_delegate(
#[cfg(test)]
mod tests {
use super::*;
use editor::SelectionEffects;
use fs::FakeFs;
use gpui::{App, TestAppContext, VisualTestContext};
use indoc::indoc;
@@ -3406,7 +3324,7 @@ mod tests {
cx: &mut TestAppContext,
) -> (
Entity<AssistantContext>,
Entity<ContextEditor>,
Entity<TextThreadEditor>,
VisualTestContext,
) {
cx.update(init_test);
@@ -3422,7 +3340,7 @@ mod tests {
let context_editor = window
.update(&mut cx, |_, window, cx| {
cx.new(|cx| {
let editor = ContextEditor::for_context(
let editor = TextThreadEditor::for_context(
context.clone(),
fs,
workspace.downgrade(),
@@ -3455,14 +3373,16 @@ mod tests {
}
fn assert_copy_paste_context_editor<T: editor::ToOffset>(
context_editor: &Entity<ContextEditor>,
context_editor: &Entity<TextThreadEditor>,
range: Range<T>,
expected_text: &str,
cx: &mut VisualTestContext,
) {
context_editor.update_in(cx, |context_editor, window, cx| {
context_editor.editor.update(cx, |editor, cx| {
editor.change_selections(None, window, cx, |s| s.select_ranges([range]));
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
s.select_ranges([range])
});
});
context_editor.copy(&Default::default(), window, cx);

View File

@@ -1,7 +1,5 @@
use std::fmt::Display;
use std::ops::Range;
use std::sync::Arc;
use crate::{AgentPanel, RemoveSelectedThread};
use agent::history_store::{HistoryEntry, HistoryStore};
use chrono::{Datelike as _, Local, NaiveDate, TimeDelta};
use editor::{Editor, EditorEvent};
use fuzzy::{StringMatch, StringMatchCandidate};
@@ -9,6 +7,7 @@ use gpui::{
App, ClickEvent, Empty, Entity, FocusHandle, Focusable, ScrollStrategy, Stateful, Task,
UniformListScrollHandle, WeakEntity, Window, uniform_list,
};
use std::{fmt::Display, ops::Range, sync::Arc};
use time::{OffsetDateTime, UtcOffset};
use ui::{
HighlightedLabel, IconButtonShape, ListItem, ListItemSpacing, Scrollbar, ScrollbarState,
@@ -16,9 +15,6 @@ use ui::{
};
use util::ResultExt;
use crate::history_store::{HistoryEntry, HistoryStore};
use crate::{AgentPanel, RemoveSelectedThread};
pub struct ThreadHistory {
agent_panel: WeakEntity<AgentPanel>,
history_store: Entity<HistoryStore>,

View File

@@ -1,13 +1,11 @@
use std::sync::Arc;
use agent::{Thread, ThreadEvent};
use assistant_tool::{Tool, ToolSource};
use collections::HashMap;
use gpui::{App, Context, Entity, IntoElement, Render, Subscription, Window};
use language_model::{LanguageModel, LanguageModelToolSchemaFormat};
use std::sync::Arc;
use ui::prelude::*;
use crate::{Thread, ThreadEvent};
pub struct IncompatibleToolsState {
cache: HashMap<LanguageModelToolSchemaFormat, Vec<Arc<dyn Tool>>>,
thread: Entity<Thread>,

View File

@@ -1,13 +1,13 @@
mod agent_notification;
mod animated_label;
mod burn_mode_tooltip;
mod context_pill;
mod max_mode_tooltip;
mod onboarding_modal;
pub mod preview;
mod upsell;
pub use agent_notification::*;
pub use animated_label::*;
pub use burn_mode_tooltip::*;
pub use context_pill::*;
pub use max_mode_tooltip::*;
pub use onboarding_modal::*;

View File

@@ -12,7 +12,7 @@ use prompt_store::PromptStore;
use rope::Point;
use ui::{IconButtonShape, Tooltip, prelude::*, tooltip_container};
use crate::context::{
use agent::context::{
AgentContext, AgentContextHandle, ContextId, ContextKind, DirectoryContext,
DirectoryContextHandle, FetchedUrlContext, FileContext, FileContextHandle, ImageContext,
ImageStatus, RulesContext, RulesContextHandle, SelectionContext, SelectionContextHandle,
@@ -498,7 +498,7 @@ impl AddedContext {
render_hover: {
let thread = handle.thread.clone();
Some(Rc::new(move |_, cx| {
let text = thread.read(cx).latest_detailed_summary_or_text();
let text = thread.read(cx).latest_detailed_summary_or_text(cx);
ContextPillHover::new_text(text.clone(), cx).into()
}))
},

View File

@@ -1,18 +1,17 @@
use client::zed_urls;
use client::{ModelRequestUsage, RequestUsage, zed_urls};
use component::{empty_example, example_group_with_title, single_example};
use gpui::{AnyElement, App, IntoElement, RenderOnce, Window};
use language_model::RequestUsage;
use ui::{Callout, prelude::*};
use zed_llm_client::{Plan, UsageLimit};
#[derive(IntoElement, RegisterComponent)]
pub struct UsageCallout {
plan: Plan,
usage: RequestUsage,
usage: ModelRequestUsage,
}
impl UsageCallout {
pub fn new(plan: Plan, usage: RequestUsage) -> Self {
pub fn new(plan: Plan, usage: ModelRequestUsage) -> Self {
Self { plan, usage }
}
}
@@ -128,10 +127,10 @@ impl Component for UsageCallout {
"Approaching limit (90%)",
UsageCallout::new(
Plan::ZedFree,
RequestUsage {
ModelRequestUsage(RequestUsage {
limit: UsageLimit::Limited(50),
amount: 45, // 90% of limit
},
}),
)
.into_any_element(),
),
@@ -139,10 +138,10 @@ impl Component for UsageCallout {
"Limit reached (100%)",
UsageCallout::new(
Plan::ZedFree,
RequestUsage {
ModelRequestUsage(RequestUsage {
limit: UsageLimit::Limited(50),
amount: 50, // 100% of limit
},
}),
)
.into_any_element(),
),
@@ -156,10 +155,10 @@ impl Component for UsageCallout {
"Approaching limit (90%)",
UsageCallout::new(
Plan::ZedProTrial,
RequestUsage {
ModelRequestUsage(RequestUsage {
limit: UsageLimit::Limited(150),
amount: 135, // 90% of limit
},
}),
)
.into_any_element(),
),
@@ -167,10 +166,10 @@ impl Component for UsageCallout {
"Limit reached (100%)",
UsageCallout::new(
Plan::ZedProTrial,
RequestUsage {
ModelRequestUsage(RequestUsage {
limit: UsageLimit::Limited(150),
amount: 150, // 100% of limit
},
}),
)
.into_any_element(),
),
@@ -184,10 +183,10 @@ impl Component for UsageCallout {
"Limit reached (100%)",
UsageCallout::new(
Plan::ZedPro,
RequestUsage {
ModelRequestUsage(RequestUsage {
limit: UsageLimit::Limited(500),
amount: 500, // 100% of limit
},
}),
)
.into_any_element(),
),

View File

@@ -1,10 +1,11 @@
use std::io;
use std::str::FromStr;
use std::time::Duration;
use anyhow::{Context as _, Result, anyhow};
use chrono::{DateTime, Utc};
use futures::{AsyncBufReadExt, AsyncReadExt, StreamExt, io::BufReader, stream::BoxStream};
use http_client::http::{HeaderMap, HeaderValue};
use http_client::http::{self, HeaderMap, HeaderValue};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
use serde::{Deserialize, Serialize};
use strum::{EnumIter, EnumString};
@@ -336,7 +337,7 @@ pub async fn complete(
let uri = format!("{api_url}/v1/messages");
let beta_headers = Model::from_id(&request.model)
.map(|model| model.beta_headers())
.unwrap_or_else(|_err| Model::DEFAULT_BETA_HEADERS.join(","));
.unwrap_or_else(|_| Model::DEFAULT_BETA_HEADERS.join(","));
let request_builder = HttpRequest::builder()
.method(Method::POST)
.uri(uri)
@@ -346,39 +347,30 @@ pub async fn complete(
.header("Content-Type", "application/json");
let serialized_request =
serde_json::to_string(&request).context("failed to serialize request")?;
serde_json::to_string(&request).map_err(AnthropicError::SerializeRequest)?;
let request = request_builder
.body(AsyncBody::from(serialized_request))
.context("failed to construct request body")?;
.map_err(AnthropicError::BuildRequestBody)?;
let mut response = client
.send(request)
.await
.context("failed to send request to Anthropic")?;
if response.status().is_success() {
let mut body = Vec::new();
response
.body_mut()
.read_to_end(&mut body)
.await
.context("failed to read response body")?;
let response_message: Response =
serde_json::from_slice(&body).context("failed to deserialize response body")?;
Ok(response_message)
.map_err(AnthropicError::HttpSend)?;
let status = response.status();
let mut body = String::new();
response
.body_mut()
.read_to_string(&mut body)
.await
.map_err(AnthropicError::ReadResponse)?;
if status.is_success() {
Ok(serde_json::from_str(&body).map_err(AnthropicError::DeserializeResponse)?)
} else {
let mut body = Vec::new();
response
.body_mut()
.read_to_end(&mut body)
.await
.context("failed to read response body")?;
let body_str =
std::str::from_utf8(&body).context("failed to parse response body as UTF-8")?;
Err(AnthropicError::Other(anyhow!(
"Failed to connect to API: {} {}",
response.status(),
body_str
)))
Err(AnthropicError::HttpResponseError {
status: status.as_u16(),
body,
})
}
}
@@ -491,7 +483,7 @@ pub async fn stream_completion_with_rate_limit_info(
let uri = format!("{api_url}/v1/messages");
let beta_headers = Model::from_id(&request.base.model)
.map(|model| model.beta_headers())
.unwrap_or_else(|_err| Model::DEFAULT_BETA_HEADERS.join(","));
.unwrap_or_else(|_| Model::DEFAULT_BETA_HEADERS.join(","));
let request_builder = HttpRequest::builder()
.method(Method::POST)
.uri(uri)
@@ -500,15 +492,15 @@ pub async fn stream_completion_with_rate_limit_info(
.header("X-Api-Key", api_key)
.header("Content-Type", "application/json");
let serialized_request =
serde_json::to_string(&request).context("failed to serialize request")?;
serde_json::to_string(&request).map_err(AnthropicError::SerializeRequest)?;
let request = request_builder
.body(AsyncBody::from(serialized_request))
.context("failed to construct request body")?;
.map_err(AnthropicError::BuildRequestBody)?;
let mut response = client
.send(request)
.await
.context("failed to send request to Anthropic")?;
.map_err(AnthropicError::HttpSend)?;
let rate_limits = RateLimitInfo::from_headers(response.headers());
if response.status().is_success() {
let reader = BufReader::new(response.into_body());
@@ -520,37 +512,31 @@ pub async fn stream_completion_with_rate_limit_info(
let line = line.strip_prefix("data: ")?;
match serde_json::from_str(line) {
Ok(response) => Some(Ok(response)),
Err(error) => Some(Err(AnthropicError::Other(anyhow!(error)))),
Err(error) => Some(Err(AnthropicError::DeserializeResponse(error))),
}
}
Err(error) => Some(Err(AnthropicError::Other(anyhow!(error)))),
Err(error) => Some(Err(AnthropicError::ReadResponse(error))),
}
})
.boxed();
Ok((stream, Some(rate_limits)))
} else if let Some(retry_after) = rate_limits.retry_after {
Err(AnthropicError::RateLimit(retry_after))
Err(AnthropicError::RateLimit { retry_after })
} else {
let mut body = Vec::new();
let mut body = String::new();
response
.body_mut()
.read_to_end(&mut body)
.read_to_string(&mut body)
.await
.context("failed to read response body")?;
.map_err(AnthropicError::ReadResponse)?;
let body_str =
std::str::from_utf8(&body).context("failed to parse response body as UTF-8")?;
match serde_json::from_str::<Event>(body_str) {
match serde_json::from_str::<Event>(&body) {
Ok(Event::Error { error }) => Err(AnthropicError::ApiError(error)),
Ok(_) => Err(AnthropicError::Other(anyhow!(
"Unexpected success response while expecting an error: '{body_str}'",
))),
Err(_) => Err(AnthropicError::Other(anyhow!(
"Failed to connect to API: {} {}",
response.status(),
body_str,
))),
Ok(_) => Err(AnthropicError::UnexpectedResponseFormat(body)),
Err(_) => Err(AnthropicError::HttpResponseError {
status: response.status().as_u16(),
body: body,
}),
}
}
}
@@ -797,17 +783,38 @@ pub struct MessageDelta {
pub stop_sequence: Option<String>,
}
#[derive(Error, Debug)]
#[derive(Debug)]
pub enum AnthropicError {
#[error("rate limit exceeded, retry after {0:?}")]
RateLimit(Duration),
#[error("an error occurred while interacting with the Anthropic API: {error_type}: {message}", error_type = .0.error_type, message = .0.message)]
/// Failed to serialize the HTTP request body to JSON
SerializeRequest(serde_json::Error),
/// Failed to construct the HTTP request body
BuildRequestBody(http::Error),
/// Failed to send the HTTP request
HttpSend(anyhow::Error),
/// Failed to deserialize the response from JSON
DeserializeResponse(serde_json::Error),
/// Failed to read from response stream
ReadResponse(io::Error),
/// HTTP error response from the API
HttpResponseError { status: u16, body: String },
/// Rate limit exceeded
RateLimit { retry_after: Duration },
/// API returned an error response
ApiError(ApiError),
#[error("{0}")]
Other(#[from] anyhow::Error),
/// Unexpected response format
UnexpectedResponseFormat(String),
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, Error)]
#[error("Anthropic API Error: {error_type}: {message}")]
pub struct ApiError {
#[serde(rename = "type")]
pub error_type: String,

View File

@@ -1,5 +1,5 @@
[package]
name = "assistant_context_editor"
name = "assistant_context"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
@@ -9,7 +9,7 @@ license = "GPL-3.0-or-later"
workspace = true
[lib]
path = "src/assistant_context_editor.rs"
path = "src/assistant_context.rs"
[dependencies]
agent_settings.workspace = true
@@ -21,27 +21,20 @@ client.workspace = true
clock.workspace = true
collections.workspace = true
context_server.workspace = true
editor.workspace = true
feature_flags.workspace = true
fs.workspace = true
futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
indexed_docs.workspace = true
language.workspace = true
language_model.workspace = true
log.workspace = true
multi_buffer.workspace = true
open_ai.workspace = true
ordered-float.workspace = true
parking_lot.workspace = true
paths.workspace = true
picker.workspace = true
project.workspace = true
prompt_store.workspace = true
proto.workspace = true
regex.workspace = true
rope.workspace = true
rpc.workspace = true
serde.workspace = true
serde_json.workspace = true
@@ -50,21 +43,17 @@ smallvec.workspace = true
smol.workspace = true
telemetry_events.workspace = true
text.workspace = true
theme.workspace = true
ui.workspace = true
util.workspace = true
uuid.workspace = true
workspace-hack.workspace = true
workspace.workspace = true
zed_actions.workspace = true
zed_llm_client.workspace = true
[dev-dependencies]
indoc.workspace = true
language_model = { workspace = true, features = ["test-support"] }
languages = { workspace = true, features = ["test-support"] }
pretty_assertions.workspace = true
rand.workspace = true
tree-sitter-md.workspace = true
unindent.workspace = true
workspace = { workspace = true, features = ["test-support"] }

View File

@@ -0,0 +1 @@
../../LICENSE-GPL

View File

@@ -1,5 +1,6 @@
#[cfg(test)]
mod context_tests;
mod assistant_context_tests;
mod context_store;
use agent_settings::AgentSettings;
use anyhow::{Context as _, Result, bail};
@@ -8,7 +9,7 @@ use assistant_slash_command::{
SlashCommandResult, SlashCommandWorkingSet,
};
use assistant_slash_commands::FileCommandMetadata;
use client::{self, proto, telemetry::Telemetry};
use client::{self, Client, proto, telemetry::Telemetry};
use clock::ReplicaId;
use collections::{HashMap, HashSet};
use fs::{Fs, RenameOptions};
@@ -47,6 +48,12 @@ use util::{ResultExt, TryFutureExt, post_inc};
use uuid::Uuid;
use zed_llm_client::CompletionIntent;
pub use crate::context_store::*;
pub fn init(client: Arc<Client>, _: &mut App) {
context_store::init(&client.into());
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct ContextId(String);
@@ -2110,6 +2117,7 @@ impl AssistantContext {
);
}
}
LanguageModelCompletionEvent::RedactedThinking { .. } => {},
LanguageModelCompletionEvent::Text(mut chunk) => {
if let Some(start) = thought_process_stack.pop() {
let end = buffer.anchor_before(message_old_end_offset);
@@ -2338,13 +2346,13 @@ impl AssistantContext {
completion_request.messages.push(request_message);
}
}
let supports_max_mode = if let Some(model) = model {
model.supports_max_mode()
let supports_burn_mode = if let Some(model) = model {
model.supports_burn_mode()
} else {
false
};
if supports_max_mode {
if supports_burn_mode {
completion_request.mode = Some(self.completion_mode.into());
}
completion_request
@@ -2515,6 +2523,12 @@ impl AssistantContext {
}
let message = start_message;
let at_end = range.end >= message.offset_range.end.saturating_sub(1);
let role_after = if range.start == range.end || at_end {
Role::User
} else {
message.role
};
let role = message.role;
let mut edited_buffer = false;
@@ -2549,7 +2563,7 @@ impl AssistantContext {
};
let suffix_metadata = MessageMetadata {
role,
role: role_after,
status: MessageStatus::Done,
timestamp: suffix.id.0,
cache: None,

Some files were not shown because too many files have changed in this diff Show More