Compare commits

...

633 Commits

Author SHA1 Message Date
Richard Feldman
de95efb7bb wip 2025-03-28 10:34:57 -04:00
Richard Feldman
60a7455f12 assistant2: Don't scroll down if user has scrolled up (#27614)
This caused undesirable and unnecessary scrolling, in that if you scroll
up, then as new messages stream into the panel, they jump the scroll bar
back down.

@agu-z and I paired on this and we think this is unnecessary now that we
don't see the Edit button in the UI, but @bennetbo we still see code for
the button in there, so...maybe we do still want this? (If so, we can
revert the second commit and go back to a more conditional way of
scrolling. 😄)

Release Notes:

- N/A

---------

Co-authored-by: Agus <agus@zed.dev>
2025-03-28 10:32:31 -04:00
Marshall Bowers
4315b2fc8a assistant2: Reload profile when making changes to the active profile's tools (#27664)
This PR makes it so the profile is reloaded when making changes to the
active profile's tools.

Release Notes:

- N/A
2025-03-28 14:08:42 +00:00
Marshall Bowers
68d453da52 assistant: Remove /search (#27661)
This PR removes the `/search` command.

This was feature-flagged and was never released to the general public.

Release Notes:

- N/A
2025-03-28 13:40:31 +00:00
Marshall Bowers
20eab9038f assistant: Remove /project (#27660)
This PR removes the `/project` command.

This was feature-flagged and was never released to the general public.

Release Notes:

- N/A
2025-03-28 13:08:16 +00:00
loczek
a201263448 storybook: Fix auto_height_editor story (#27653)
This is a fix for this error when trying to run `auto_height_editor`
story:

```sh
thread 'main' panicked at C:\Users\x\dev\zed\crates\settings\src\settings_store.rs:363:32:
unregistered setting type workspace::workspace_settings::WorkspaceSettings
```

Release Notes:

- N/A
2025-03-28 08:42:44 -04:00
Marshall Bowers
1a4ba59d4e docs: Fix #windows Discord channel name (#27659)
This PR fixes the name of the `#windows` Discord channel to match what
the channel is actually called.

Release Notes:

- N/A
2025-03-28 08:41:07 -04:00
Piotr Osiewicz
24ad97008b language server: Fix restarts sometimes not working for buffers open in go-to-definition view (#27655)
Closes #ISSUE

Release Notes:

- Fixed language server restarts sometimes not restarting a language
server.
2025-03-28 11:46:46 +00:00
Bennet Bo Fenner
f6c81a0595 assistant2: Unify path rendering for file context (#27537)
This ensures that we render path matches the same in both the file
context picker and the messag editor completion provider.

Release Notes:

- N/A
2025-03-28 11:26:54 +01:00
Smit Barmase
7ac51e4c82 project: Add tests for more cases in LSP completions (#27650)
This PR adds two more cases to existing LSP completion cases.

- When text_edit exists:  (New test)
   1. we use text_edit, over insert_text and label

- When edit range exists (and text_edit is None):  (New test)
   1. insert_text is used over label if exists
   2. label is used otherwise

- When not edit range exists (and text_edit is None):  (Existing test)
   1. insert_text is used over label if exists
   2. label is used otherwise

Release Notes:

- N/A
2025-03-28 15:43:18 +05:30
mslzed
c8105863c8 Update docs to include Windows JD (#27649)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-03-28 02:57:07 -07:00
张小白
9f72e05c40 windows: Fix extension uninstall (#18467)
Closes #18153


When calling `uninstall_extension`, the `work_dir` associated with this
`extension` doesn't have its corresponding `FileHandle` properly closed,
preventing the deletion of the `work_dir`. As seen in the image below,
after installing the `toml` extension, `zed.exe` holds two `Handle`s for
the folder `C:\Users\36477\AppData\Local\Zed\extensions\work\toml`.

![Screenshot 2024-09-27
171149](https://github.com/user-attachments/assets/f75f3f6f-9a62-43b5-9450-73ee1ed8e7f9)


Therefore, after deleting `extension_dir` and then calling
`this.update(...)`, `zed.exe` releases these two `Handles`, and only
then can the folder
`C:\Users\36477\AppData\Local\Zed\extensions\work\toml` be deleted. See
the corresponding file handles are closed after calling
`this.update(...)`:

![Screenshot 2024-09-28
132823](https://github.com/user-attachments/assets/476e0494-850a-4af5-b351-899e60ae98f7)

However, if there is a running server of the extension, the error will
persist. At this point, I haven’t found a direct way to terminate all
running servers of the extension. Since this feature might affect the
`LspStore` structure, I paused my work here.


See when `toml` extension is running, we can not delete
`C:\Users\36477\AppData\Local\Zed\extensions\work\toml` since
`C:\Users\36477\AppData\Local\Zed\extensions\work\toml\taplo.exe` is
still running:

![Screenshot 2024-09-28
134709](https://github.com/user-attachments/assets/6801d6e2-2a44-4103-8570-467c507e6e20)



cc @ConradIrwin You're the expert in this area—what are your thoughts?



Release Notes:

- N/A
2025-03-28 13:52:48 +08:00
Marshall Bowers
bb15f4c493 assistant2: Fix tool picker appearance inside modal (#27635)
This PR fixes the tool picker appearance now that there is a header.

Release Notes:

- N/A
2025-03-28 00:10:32 +00:00
Shardul Vaidya
dc6004066d bedrock: Support DeepSeek r1 (#27495)
Release Notes:

- Added support for DeepSeek R1 hosted on AWS Bedrock.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-03-27 19:30:30 -04:00
Marshall Bowers
2863bd1836 icons: Remove ZedAssistant2 icon (#27634)
This PR removes the `ZedAssistant2` icon.

I went to use it as a placeholder icon, but noticed that the icon wasn't
loaded properly due to a name mismatch.

However, since we aren't using it anywhere I'm opting to remove it.

Release Notes:

- N/A
2025-03-27 23:10:42 +00:00
Marshall Bowers
f1ce83b533 context_server: Only look for context server project settings in visible worktrees (#27633)
This PR fixes an issue where we were looking for context server project
settings in _all_ worktrees, not just visible ones.

This meant that if you had a single file worktree open (e.g.,
`settings.json`) this could impact whether context servers defined in
project settings would load.

Release Notes:

- Fixed an issue where context servers defined in project settings would
not be respected in some scenarios.

Co-authored-by: Wilhelm Klopp <wil.klopp@gmail.com>
2025-03-27 22:58:29 +00:00
Richard Feldman
edc7d73643 Set cache breakpoint on second-to-last message (#27632)
Here's a sample `dbg!` of token usage after this change, for a small
agent thread:

```
[crates/assistant2/src/thread.rs:1092:25] &usage = TokenUsage {
    input_tokens: 5354,
    output_tokens: 184,
    cache_creation_input_tokens: 0,
    cache_read_input_tokens: 0,
}
[crates/assistant2/src/thread.rs:1092:25] &usage = TokenUsage {
    input_tokens: 54,
    output_tokens: 132,
    cache_creation_input_tokens: 5518,
    cache_read_input_tokens: 0,
}
[crates/assistant2/src/thread.rs:1092:25] &usage = TokenUsage {
    input_tokens: 54,
    output_tokens: 113,
    cache_creation_input_tokens: 166,
    cache_read_input_tokens: 5518,
}
[crates/assistant2/src/thread.rs:1092:25] &usage = TokenUsage {
    input_tokens: 291,
    output_tokens: 181,
    cache_creation_input_tokens: 147,
    cache_read_input_tokens: 5684,
}
```

Release Notes:

- N/A
2025-03-27 22:32:50 +00:00
Piotr Osiewicz
4839195003 debugger: Remove fake adapter and un-gate GDB (#27557)
This is a clean-up PR in anticipation of introduction of Debugger
Registry. I wanna get rid of DebugAdapterKind (or rather, it being an
enum).
Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Anthony <anthony@zed.dev>
2025-03-27 22:31:58 +00:00
Richard Feldman
56eb650f09 Add Batch tool call for calling multiple tools (#27621)
<img width="620" alt="Screenshot 2025-03-27 at 2 29 13 PM"
src="https://github.com/user-attachments/assets/dd023507-61bc-4722-a095-f65f4b6c746a"
/>

We'll iterate on the UI, but first the goal is to just get it to work at
all so we can see if it's useful in terms of getting correct output
faster.

Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-03-27 18:21:26 -04:00
Richard Feldman
61be869352 Add Open Tool (#27499)
I've seen models try to run `open` in Bash. This is a cross-platform
version of that.

<img width="634" alt="Screenshot 2025-03-26 at 10 27 40 AM"
src="https://github.com/user-attachments/assets/b18cb50f-6e2f-4770-b15c-1040916a420a"
/>

Release Notes:

- N/A
2025-03-27 18:20:59 -04:00
Richard Feldman
7537f0557f Automatically hide "View Panel" notification after refocusing Zed (#27512)
Now if you refocus Zed manually (e.g. cmd-tab), we hide the "View Panel"
notification automatically.

This also fixes a related subscription leak.

Release Notes:

- N/A
2025-03-27 18:20:45 -04:00
Richard Feldman
85740ddaa4 Make serialization backwards-compatible for collab server (#27626)
Sets up the collab server to accept the format of system message that
we'll introduce later for [prompt
caching](https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching).

Release Notes:

- N/A
2025-03-27 18:20:10 -04:00
Finn Evers
6550a96e15 editor: Remove gap between gutter and horizontal scrollbar track (#24887)
Longer write-up, sorry if this got a bit too long.

This PR removes a small gap between the editor gutter and the horizontal
scrollbar, if present, by stretching the scrollbar track the entire
witdth of the editor.


https://github.com/user-attachments/assets/d5c18b03-d1ff-4d48-a3da-5d0fb80ee967

This gap which can be seen in the bottom left of the video can cause
bugs when interacting with it using the cursor, as accidentally clicking
on it would trigger a vertical scroll instead of dragging the horizontal
scroll. Also for cases where themes provide a non-transparent scrollbar
track background, which can be seen in the video, the small gap is
visible whilst scrolling horizontally.

This gap is present because the horizontal editor scrollbar is layouted
based upon the `content_origin`, which offsets the whole layout by the
horizontal gutter margin to the right. However, the scrollbar should be
layouted based upon the editor text bounds to be properly painted over
the entire editor text hitbox.

Here are some comparison images with `scrollbar.track.background` and
`gutter.background` set to red for visibility.

| | Current `main` | With this change | 
| - | - | - |
| Default position / Fully scrolled to the left | <img width="842"
alt="left_main"
src="https://github.com/user-attachments/assets/8b053fc8-5271-4b58-8404-dcabf49bf702"
/> | <img width="842" alt="left_fix"
src="https://github.com/user-attachments/assets/459df723-05d5-4813-a6a4-038f7d662495"
/> |
| Scrolled to the right | <img width="216" alt="scroll_main"
src="https://github.com/user-attachments/assets/9c1fcc0d-fbb4-49af-9645-f258f5a7217b"
/> | <img width="216" alt="scroll_fix"
src="https://github.com/user-attachments/assets/8dd2e585-7802-415b-a05a-fb40a882323e"
/> |

---

#### Small downsight of this approach

Currently, the scrollbar thumb aligns with the indent guides if the
editor is fully scrolled to the left and the track background is
transparent. This is because the indent guides are layouted according to
the content margin.
With this change, however, the scrollbar thumb will shift a few pixels
to the left and will overlap the indent guides if present.

| Current `main` | With this change |
| - | - |
| <img width="295" alt="cur_indent"
src="https://github.com/user-attachments/assets/92753951-6f35-4c39-94eb-21c445f8d2f5"
/> | <img width="381" alt="fix_indent"
src="https://github.com/user-attachments/assets/899d945c-49f8-4117-bc48-52501d55cc33"
/> |

To circumvent this, the scrollbar thumb could be layouted with a small
offset so that the thumb aligns properly with the indent guides whilst
the scrollbar track spans the whole editor width. This would lead to
some questions on how to account for the gap during layouting and
dragging of the thumb though, but might work for a gap that small. Happy
to implement this fix, should that be preferred 😄

(VSCode does not have the indent guide issue, as they do not layout the
text in the editor with any offset unlike Zed does)

Release Notes:

- Removed a small gap between the editor gutter and horizontal
scrollbar.
2025-03-28 03:36:49 +05:30
Peter Tripp
93c0056065 git: Display author not committer in git popover (#27628)
Display Author name/email instead of Committer name/email in git
popover.

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

Prior to v0.173.x, Zed displayed Author name. Regression introduced in:
- https://github.com/zed-industries/zed/pull/24593

Release Notes:

- git: Switch to displaying Git author instead of Git committer in Git
Blame popover.
2025-03-27 17:35:27 -04:00
Smit Barmase
84dd2366bc project: Fix LSP completion to use insertText when constructing default edits (#27630)
Closes #25761 #21603

When `text_edit` is not available we directly fallback to to `label`.
That means, when we have range to replace, we never use `insertText` and
only use it when we haven't found any range.

This PR fixes, this and uses `insertText` as fallback first, and then
`label`.

Release Notes:

- Fixed an issue where accepting LSP snippet completion would insert the
label instead of expanding the snippet.
2025-03-28 03:00:48 +05:30
Ben Kunkle
8e12eb0ab1 keymap: Detect and report errors for uppercase keybindings (#27558)
Closes #25353

Detect keybindings using upper case instead of lowercase, and report an
error

Release Notes:

- N/A
2025-03-27 21:17:43 +00:00
Marshall Bowers
3b158461be assistant2: Add support for forking existing profiles (#27627)
This PR adds support for forking existing profiles from the manage
profiles modal.


https://github.com/user-attachments/assets/5fa9b76c-fafe-4c72-8843-576c4b5ca2f2

Release Notes:

- N/A
2025-03-27 20:17:42 +00:00
Peter Tripp
12a8b850ef Fix Terminal theming issue with background/foreground text (#27617)
- Closes: https://github.com/zed-industries/zed/issues/27427

Release Notes:

- terminal: Fixed an issue where editor theme colors (`text`,
`background`) were incorrectly being uses instead of terminal theme
colors (`terminal.{foreground,background}`)
2025-03-27 16:12:19 -04:00
Michael Sloan
d35f5a4197 Opt-in to markdown parser options and check all options were considered (#27623)
Release Notes:

- N/A
2025-03-27 20:10:06 +00:00
Danilo Leal
1f5d57bece assistant2: Add docs to the notify_when_agent_waiting setting (#27622)
Release Notes:

- N/A
2025-03-27 17:00:59 -03:00
Finn Evers
ca9fb2399e Prevent toggle_dock from opening assistant panel when it is disabled via settings (#27215)
Part of #27171

Follows-up the change in
https://github.com/zed-industries/zed/pull/22346 to consider the case
where the assistant-panel is disabled via settings (as also noted in
[this
comment](https://github.com/zed-industries/zed/pull/22346#issuecomment-2558372412),
Notably, only the explicit case is considered here. Can extend this
change to also cover the implicit case where the button is disabled if
requested.).

Currently, if the user toggles the right dock, the assistant panel will
be shown even if it is disabled via settings, because it has the highest
priority (see
https://github.com/zed-industries/zed/pull/22346#issuecomment-2564890493).
With this change, the assistant panel is no longer activated when
disabled and the dock with the next highest activation order is
activated instead.

I did not opt in to make the priority configurabe, as I agree with
https://github.com/zed-industries/zed/pull/22346#issuecomment-2564890493
that this will most likely rarely be used (the active panel is only none
on the first toggle of the dock, afterwards it remains set for the
remainder of the session).

Release Notes:

- `workspace::ToggleRightDock` will no longer open the assistant panel
when it is disabled via settings.
2025-03-27 19:19:37 +00:00
Ben Kunkle
a360365410 tsx: Insert newline between open and close tags on enter (#27618)
Release Notes:

- Added support for automatically inserting a newline when hitting enter
between opening and closing tags in JSX/TSX
2025-03-27 19:15:12 +00:00
Danilo Leal
df6ee1fc4a assistant2: Add adjustments to OS notification (#27615)
This PR hardcodes the font size for the OS notification and adjusts the
copywriting on the `DoneStreaming` scenario.

1. Reason for the former change is because notifications always have a
fixed width and height, so any responsive design strategy here wouldn't
fully work.
2. Reason for the latter is because when the assistant response is done
streaming, that _can_ mean "changes have been applied" (previous label)
but it can also not mean that. So, I'm making it more generic now.

Release Notes:

- N/A
2025-03-27 15:36:02 -03:00
Richard Feldman
fc99557952 Explicitly prefer find-replace over edit-files (#27503)
`edit-files` is still enabled for now, but this makes it less likely to
be used.

Release Notes:

- N/A
2025-03-27 14:27:28 -04:00
Richard Feldman
52c1e0021c Allow Bash tool to Just Work with more cd inputs (#27501)
Release Notes:

- N/A
2025-03-27 14:27:18 -04:00
Marshall Bowers
6ead57d5ed assistant2: Fix navigation between states in manage profiles modal (#27613)
This PR fixes the navigation between states in the manage profiles
modal, specifically around dismissal.

Release Notes:

- N/A
2025-03-27 17:43:25 +00:00
Marshall Bowers
4a10a0ca77 feature_flags: Remove predict-edits-non-eager-mode feature flag (#27610)
This PR removes the `predict-edits-non-eager-mode` feature flag.

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

Release Notes:

- N/A
2025-03-27 17:25:01 +00:00
Marshall Bowers
cc6d4e3c62 assistant: Remove /auto (#27608)
This PR removes the `/auto` command.

This was feature-flagged and was never released to the general public.

Release Notes:

- N/A
2025-03-27 17:23:32 +00:00
Danilo Leal
3f7c8c97c2 docs: Add improvements to the Completions page (#27612)
Release Notes:

- N/A
2025-03-27 14:18:29 -03:00
Marshall Bowers
ab5ba66b94 feature_flags: Remove predict-edits feature flag (#27605)
This PR removes the `predict-edits` feature flag.

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

Release Notes:

- N/A
2025-03-27 17:01:27 +00:00
Marshall Bowers
a20a534ecf assistant2: Allow dismissing the tool list with the keyboard (#27603)
This PR adds the ability to dismiss the tool list in the profile
configuration modal using the keyboard.

Release Notes:

- N/A
2025-03-27 16:53:59 +00:00
Danilo Leal
5bb979820b docs: Fix link to the subtle mode heading (#27606)
Release Notes:

- N/A
2025-03-27 13:51:32 -03:00
Marshall Bowers
2dee03ebca assistant2: Allow customizing tools for default profiles (#27594)
This PR adds support for customizing the tools for the default profiles.

Release Notes:

- N/A
2025-03-27 15:13:00 +00:00
Danilo Leal
1c7cf1a5c1 docs: Clarify how to turn edit predictions off (#27592)
Closes https://github.com/zed-industries/zed/issues/27590

Release Notes:

- N/A
2025-03-27 11:25:52 -03:00
Agus Zubiaga
f15a241d3e assistant2: Serialize token usage (#27586)
We'll need this for detecting old long threads

Release Notes:

- N/A
2025-03-27 13:38:08 +00:00
Richard Feldman
76d3a9a0f0 Retry on 5xx errors from cloud language model providers (#27584)
Release Notes:

- N/A
2025-03-27 09:35:16 -04:00
Peter Tripp
e6c473a488 html: Update HTML Extension to v0.2.0 (#27548)
Includes:
- https://github.com/zed-industries/zed/pull/27524

Release Notes:

- N/A
2025-03-27 09:00:30 -04:00
张小白
06960670bd windows: Enable collab tests (#27587)
Release Notes:

- N/A
2025-03-27 20:42:22 +08:00
renovate[bot]
71ddb3dad4 Update Rust crate log to v0.4.27 (#27540)
This PR contains the following updates:

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

---

### Release Notes

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

###
[`v0.4.27`](https://redirect.github.com/rust-lang/log/blob/HEAD/CHANGELOG.md#0427---2025-03-24)

[Compare
Source](https://redirect.github.com/rust-lang/log/compare/0.4.26...0.4.27)

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-03-27 10:10:54 +00:00
renovate[bot]
2bf9c472bd Update Rust crate oo7 to v0.4.3 (#27549)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [oo7](https://redirect.github.com/bilelmoussaoui/oo7) | dependencies |
patch | `0.4.1` -> `0.4.3` |

---

### Release Notes

<details>
<summary>bilelmoussaoui/oo7 (oo7)</summary>

###
[`v0.4.3`](https://redirect.github.com/bilelmoussaoui/oo7/releases/tag/0.4.3)

[Compare
Source](https://redirect.github.com/bilelmoussaoui/oo7/compare/0.4.2...0.4.3)

- [cli: Add custom keyring file
support](248d96f225)
- [dbus/secret: Workaround gnome-keyring being non-spec
compliant](cac94b502f)
- [client: Deprecate
Keyring::with_broken_item_cleanup](cce024ba64)
- [cli: Add a new argument for listing all
items](88f5604147)

###
[`v0.4.2`](https://redirect.github.com/bilelmoussaoui/oo7/releases/tag/0.4.2)

#### What's Changed

Relax the condition when validating the file backend keyring secret and
provide various APIs to allow the developer to recover the non-broken
items in a keyring or delete the broken ones.

The root cause of the issue has not been identified but at least apps
will continue to work as normal if the developer uses
`Keyring::with_broken_item_cleanup` instead of `Keyring::new`.

More details can be found in
[#&#8203;207](https://redirect.github.com/bilelmoussaoui/oo7/issues/207)

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 09:54:09 +00:00
Antonio Scandurra
82a06f0ca9 Introduce primitives in GitStore to support reviewing assistant diffs (#27576)
Release Notes:

- N/A
2025-03-27 09:46:31 +00:00
renovate[bot]
cd6b1d32d0 Update Rust crate blake3 to v1.7.0 (#27566)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [blake3](https://redirect.github.com/BLAKE3-team/BLAKE3) |
workspace.dependencies | minor | `1.6.1` -> `1.7.0` |

---

### Release Notes

<details>
<summary>BLAKE3-team/BLAKE3 (blake3)</summary>

###
[`v1.7.0`](https://redirect.github.com/BLAKE3-team/BLAKE3/releases/tag/1.7.0)

[Compare
Source](https://redirect.github.com/BLAKE3-team/BLAKE3/compare/1.6.1...1.7.0)

version 1.7.0

Changes since 1.6.1:

-   The C implementation has gained multithreading support, based on
    Intel's oneTBB library. This works similarly to the Rayon-based
    multithreading used in the Rust implementation. See c/README.md for
details. Contributed by
[@&#8203;silvanshade](https://redirect.github.com/silvanshade)
([#&#8203;445](https://redirect.github.com/BLAKE3-team/BLAKE3/issues/445)).
-   The Rust implementation has gained a WASM SIMD backend, gated by the
`wasm32_simd` Cargo feature. Under Wasmtime on my laptop, this is a 6x
    performance improvement for large inputs. This backend is currently
Rust-only. Contributed by
[@&#8203;monoid](https://redirect.github.com/monoid)
([#&#8203;341](https://redirect.github.com/BLAKE3-team/BLAKE3/issues/341)).
-   Fixed cross-compilation builds targeting Windows with cargo-xwin.
Contributed by [@&#8203;Sporif](https://redirect.github.com/Sporif) and
[@&#8203;toothbrush7777777](https://redirect.github.com/toothbrush7777777)
([#&#8203;230](https://redirect.github.com/BLAKE3-team/BLAKE3/issues/230)).
-   Added `b3sum --tag`, which changes the output format. This is for
    compatibility with GNU checksum tools (which use the same flag) and
    BSD checksum tools (which use the output format this flag turns on).
Contributed by
[@&#8203;leahneukirchen](https://redirect.github.com/leahneukirchen)
([#&#8203;453](https://redirect.github.com/BLAKE3-team/BLAKE3/issues/453))
and [@&#8203;dbohdan](https://redirect.github.com/dbohdan)
([#&#8203;430](https://redirect.github.com/BLAKE3-team/BLAKE3/issues/430)).

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 11:37:20 +02:00
renovate[bot]
5033a2aba0 Update Rust crate image to v0.25.6 (#27539)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [image](https://redirect.github.com/image-rs/image) | dependencies |
patch | `0.25.5` -> `0.25.6` |
| [image](https://redirect.github.com/image-rs/image) |
workspace.dependencies | patch | `0.25.5` -> `0.25.6` |

---

### Release Notes

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

###
[`v0.25.6`](https://redirect.github.com/image-rs/image/blob/HEAD/CHANGES.md#Version-0256)

[Compare
Source](https://redirect.github.com/image-rs/image/compare/v0.25.5...v0.25.6)

Features:

- Improved format detection
([#&#8203;2418](https://redirect.github.com/image-rs/image/pull/2418))
- Implement writing ICC profiles for JPEG and PNG images
([#&#8203;2389](https://redirect.github.com/image-rs/image/pull/2389))

Bug fixes:

- JPEG encoding bugfix
([#&#8203;2387](https://redirect.github.com/image-rs/image/pull/2387))
- Expanded ICO format detection
([#&#8203;2434](https://redirect.github.com/image-rs/image/pull/2434))
- Fixed EXR bug with NaNs
([#&#8203;2381](https://redirect.github.com/image-rs/image/pull/2381))
-   Various documentation improvements

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 11:36:33 +02:00
renovate[bot]
0392ef10cf Update Rust crate bitflags to v2.9.0 (#27565)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [bitflags](https://redirect.github.com/bitflags/bitflags) |
workspace.dependencies | minor | `2.8.0` -> `2.9.0` |

---

### Release Notes

<details>
<summary>bitflags/bitflags (bitflags)</summary>

###
[`v2.9.0`](https://redirect.github.com/bitflags/bitflags/blob/HEAD/CHANGELOG.md#290)

[Compare
Source](https://redirect.github.com/bitflags/bitflags/compare/2.8.0...2.9.0)

#### What's Changed

- `Flags` trait: add `clear(&mut self)` method by
[@&#8203;wysiwys](https://redirect.github.com/wysiwys) in
[https://github.com/bitflags/bitflags/pull/437](https://redirect.github.com/bitflags/bitflags/pull/437)
- Fix up UI tests by
[@&#8203;KodrAus](https://redirect.github.com/KodrAus) in
[https://github.com/bitflags/bitflags/pull/438](https://redirect.github.com/bitflags/bitflags/pull/438)

**Full Changelog**:
https://github.com/bitflags/bitflags/compare/2.8.0...2.9.0

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 11:36:02 +02:00
Antonio Scandurra
7354ef91e1 Make GitRepository::status async and remove cx parameter (#27514)
This lays the groundwork for using `status` as part of the new agent
panel.

Release Notes:

- N/A
2025-03-27 09:05:54 +00:00
张小白
926d10cc45 Move the EventKind::Access filtering before the loop starts (#27569)
Follow up #27498

Release Notes:

- N/A
2025-03-27 15:15:24 +08:00
renovate[bot]
a7697be857 Update Rust crate async-compression to v0.4.22 (#27529)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[async-compression](https://redirect.github.com/Nullus157/async-compression)
| workspace.dependencies | patch | `0.4.21` -> `0.4.22` |

---

### Release Notes

<details>
<summary>Nullus157/async-compression (async-compression)</summary>

###
[`v0.4.22`](https://redirect.github.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#0422---2025-03-25)

[Compare
Source](https://redirect.github.com/Nullus157/async-compression/compare/v0.4.21...v0.4.22)

##### Other

-   Add lz4 encoder/decoder
-   Expose total_in/total_out in DeflateEncoder

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 00:00:41 -04:00
renovate[bot]
97392a23e3 Update Rust crate time to v0.3.41 (#27553)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [time](https://time-rs.github.io)
([source](https://redirect.github.com/time-rs/time)) |
workspace.dependencies | patch | `0.3.40` -> `0.3.41` |

---

### Release Notes

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

###
[`v0.3.41`](https://redirect.github.com/time-rs/time/blob/HEAD/CHANGELOG.md#0341-2025-03-23)

[Compare
Source](https://redirect.github.com/time-rs/time/compare/v0.3.40...v0.3.41)

##### Fixed

- Compatibility with the latest release of `deranged`. This fix is
permanent and covers future
    similar changes upstream.

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 22:55:45 -04:00
renovate[bot]
3f40e0f433 Update Rust crate clap to v4.5.34 (#27530)
This PR contains the following updates:

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

---

### Release Notes

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

###
[`v4.5.34`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4534---2025-03-27)

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

##### Fixes

- *(help)* Don't add extra blank lines with `flatten_help(true)` and
subcommands without arguments

###
[`v4.5.33`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4533---2025-03-26)

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

##### Fixes

- *(error)* When showing the usage of a suggestion for an unknown
argument, don't show the group

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 22:55:18 -04:00
renovate[bot]
3e6d5c0814 Update dependency @tsconfig/node20 to v20.1.5 (#27560)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [@tsconfig/node20](https://redirect.github.com/tsconfig/bases)
([source](https://redirect.github.com/tsconfig/bases/tree/HEAD/bases)) |
[`20.1.4` ->
`20.1.5`](https://renovatebot.com/diffs/npm/@tsconfig%2fnode20/20.1.4/20.1.5)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@tsconfig%2fnode20/20.1.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@tsconfig%2fnode20/20.1.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@tsconfig%2fnode20/20.1.4/20.1.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@tsconfig%2fnode20/20.1.4/20.1.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>tsconfig/bases (@&#8203;tsconfig/node20)</summary>

###
[`v20.1.5`](be6b3bb160...f6e0345911)

[Compare
Source](be6b3bb160...f6e0345911)

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 22:54:49 -04:00
renovate[bot]
2bc91e8c59 Update Rust crate plist to v1.7.1 (#27550)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [plist](https://redirect.github.com/ebarnard/rust-plist) |
dependencies | patch | `1.7.0` -> `1.7.1` |

---

### Release Notes

<details>
<summary>ebarnard/rust-plist (plist)</summary>

###
[`v1.7.1`](https://redirect.github.com/ebarnard/rust-plist/compare/v1.7.0...v1.7.1)

[Compare
Source](https://redirect.github.com/ebarnard/rust-plist/compare/v1.7.0...v1.7.1)

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 22:51:58 -04:00
renovate[bot]
bbc80c78fd Update dependency @slack/webhook to v7.0.5 (#27554)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [@slack/webhook](https://tools.slack.dev/node-slack-sdk/webhook)
([source](https://redirect.github.com/slackapi/node-slack-sdk)) |
[`7.0.4` ->
`7.0.5`](https://renovatebot.com/diffs/npm/@slack%2fwebhook/7.0.4/7.0.5)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/@slack%2fwebhook/7.0.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@slack%2fwebhook/7.0.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@slack%2fwebhook/7.0.4/7.0.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@slack%2fwebhook/7.0.4/7.0.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>slackapi/node-slack-sdk (@&#8203;slack/webhook)</summary>

###
[`v7.0.5`](https://redirect.github.com/slackapi/node-slack-sdk/releases/tag/%40slack/webhook%407.0.5)

[Compare
Source](https://redirect.github.com/slackapi/node-slack-sdk/compare/@slack/webhook@7.0.4...@slack/webhook@7.0.5)

#### What's Changed

This patch release updates the `axios` dependency used to send webhooks
with internal bug fixes.

- fix(webhook): bump axios to 1.8.3 to address CVE-2025-27152 by
[@&#8203;zimeg](https://redirect.github.com/zimeg) in
[https://github.com/slackapi/node-slack-sdk/pull/2173](https://redirect.github.com/slackapi/node-slack-sdk/pull/2173)

**Full Changelog**:
https://github.com/slackapi/node-slack-sdk/compare/[@&#8203;slack/webhook](https://redirect.github.com/slack/webhook)[@&#8203;7](https://redirect.github.com/7).0.4..[@&#8203;slack/webhook](https://redirect.github.com/slack/webhook)[@&#8203;7](https://redirect.github.com/7).0.5
**Milestone**: https://github.com/slackapi/node-slack-sdk/milestone/130

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 22:50:40 -04:00
renovate[bot]
24ab5afa10 Update serde monorepo to v1.0.219 (#27561)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [serde](https://serde.rs)
([source](https://redirect.github.com/serde-rs/serde)) | dependencies |
patch | `1.0.218` -> `1.0.219` |
| [serde](https://serde.rs)
([source](https://redirect.github.com/serde-rs/serde)) |
workspace.dependencies | patch | `1.0.218` -> `1.0.219` |
| [serde_derive](https://serde.rs)
([source](https://redirect.github.com/serde-rs/serde)) |
workspace.dependencies | patch | `1.0.218` -> `1.0.219` |

---

### Release Notes

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

###
[`v1.0.219`](https://redirect.github.com/serde-rs/serde/releases/tag/v1.0.219)

[Compare
Source](https://redirect.github.com/serde-rs/serde/compare/v1.0.218...v1.0.219)

- Prevent `absolute_paths` Clippy restriction being triggered inside
macro-generated code
([#&#8203;2906](https://redirect.github.com/serde-rs/serde/issues/2906),
thanks [@&#8203;davidzeng0](https://redirect.github.com/davidzeng0))

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 22:48:53 -04:00
Marshall Bowers
af8acba353 Remove unneeded inline tables in Cargo.tomls (#27563)
This PR removes some unneeded inline tables from our `Cargo.toml`s.

Release Notes:

- N/A
2025-03-27 02:36:47 +00:00
Marshall Bowers
231e9c2000 assistant2: Add ability to configure tools for profiles in the UI (#27562)
This PR adds the ability to configure tools for a profile in the UI:


https://github.com/user-attachments/assets/16642f14-8faa-4a91-bb9e-1d480692f1f2

Note: Doesn't yet work for customizing tools for the default profiles.

Release Notes:

- N/A
2025-03-27 02:19:45 +00:00
João Marcos
47b94e5ef0 Git: Fix hunks being skipped when staging too quickly (#27552)
Release Notes:

- Git: Fix hunks being skipped when staging too quickly.

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-03-26 23:22:37 +00:00
Cole Miller
29e2e13e6d Fix broken merge (#27551)
This PR fixes main after a semantic merge conflict with
https://github.com/zed-industries/zed/pull/27391.

Release Notes:

- N/A
2025-03-26 23:00:08 +00:00
João Marcos
e635798fe0 Fix crash when staging a hunk that overlaps multiple unstaged hunks (#27545)
Release Notes:

- Git: Fix crash when staging a hunk that overlaps multiple unstaged
hunks.

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-03-26 19:54:51 -03:00
Cole Miller
6924720b35 Move repository state RPC handlers to the GitStore (#27391)
This is another in the series of PRs to make the GitStore own all
repository state and enable better concurrency control for git
repository scans.

After this PR, the `RepositoryEntry`s stored in worktree snapshots are
used only as a staging ground for local GitStores to pull from after
git-related events; non-local worktrees don't store them at all,
although this is not reflected in the types. GitTraversal and other
places that need information about repositories get it from the
GitStore. The GitStore also takes over handling of the new
UpdateRepository and RemoveRepository messages. However, repositories
are still discovered and scanned on a per-worktree basis, and we're
still identifying them by the (worktree-specific) project entry ID of
their working directory.

- [x] Remove WorkDirectory from RepositoryEntry
- [x] Remove worktree IDs from repository-related RPC messages
- [x] Handle UpdateRepository and RemoveRepository RPCs from the
GitStore

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-03-26 18:23:44 -04:00
Thomas Mickley-Doyle
1e8b50f471 Add token usage to LanguageModelTextStream (#27490)
Release Notes:

- N/A

---------

Co-authored-by: Michael Sloan <michael@zed.dev>
2025-03-26 22:21:01 +00:00
Anthony Eid
5f8c53ffe8 Debugger UI: Fix breakpoint rendering in git hunks (#27538)
This PR fixes a bug where breakpoints would be rendered on incorrect
lines when openings a git hunk that contained breakpoints. This also
disables breakpoints from being shown in deleted git hunks as well.

Note: There's some unexpected behavior when using an anchor to get a
display point that is in an open git hunk, where the
`anchor.to_point().col == 0`.

```rust
                let position = multi_buffer_anchor
                    .to_point(&multi_buffer_snapshot)
                    .to_display_point(&snapshot);
```

The above code will return a display point that is one line below where
the anchor actually represents when it's in an opened hunk diff. Which
causes the bug shown below



https://github.com/user-attachments/assets/bd15d02a-3cdc-4c8e-841f-bef238583351


@ConradIrwin Is this expected behavior when calling
`.to_display_point(&snapshot)`?

Release Notes:

- N/A
2025-03-26 18:12:00 -04:00
Smit Barmase
6e82bbf367 Revert "editor: Do not use hide_mouse_while_typing for single line editor" (#27547)
Reverts zed-industries/zed#27536

Looks like hiding cursor on single editor is okay and is default
behavior for other apps.
2025-03-26 22:02:09 +00:00
Marshall Bowers
0ac717c3a8 assistant2: Start on modal for managing profiles (#27546)
This PR starts work on a modal for managing profiles.

Release Notes:

- N/A
2025-03-26 18:01:34 -04:00
Michael Sloan
44aff7cd46 Fix tools' ui_text to use inline code escaping (#27543)
Markdown escaping was added in #27502.

Release Notes:

- N/A
2025-03-26 21:49:51 +00:00
Bennet Bo Fenner
2b5095ac91 assistant2: Fix filtering issue when using @mention completion provider (#27541)
Previously `src` would not show up because it was filtered out:

<img width="466" alt="image"
src="https://github.com/user-attachments/assets/f3802660-ad73-44be-967d-c332466d9aba"
/>

Release Notes:

- N/A
2025-03-26 21:18:25 +00:00
Mikayla Maki
9e02fee98d Align project panel and git panel deletion behavior (#27525)
This change makes the git panel and project panel behave the same, on
Linux and macOS, and adds prompts.

Release Notes:

- Changed the git panel to prompt before restoring a file.
2025-03-26 21:15:24 +00:00
loczek
999ad77a59 workspace: Double click empty pane to open new file (#27521)
Release Notes:

- Added ability to double click on empty pane to open a new file
2025-03-26 14:07:54 -07:00
Smit Barmase
780d0eb427 editor: Do not use hide_mouse_while_typing for single line editor (#27536)
Release Notes:

- N/A
2025-03-27 02:32:16 +05:30
Agus Zubiaga
7b40ab30d7 assistant2: Add scrollbar to active thread (#27534)
This required adding scrollbar support to `list`. Since `list` is
virtualized, the scrollbar height will change as more items are
measured. When the user manually drags the scrollbar, we'll persist the
initial height and offset calculations accordingly to prevent the
scrollbar from moving away from the cursor as new items are measured.

We're not doing this yet, but in the future, it'd be nice to budget some
time each frame to layout unmeasured items so that the scrollbar height
is as accurate as possible.

Release Notes:

- N/A
2025-03-26 18:01:13 -03:00
Kirill Bulatov
0a3c8a6790 Remove project strong reference from git panel's log output editor (#27496)
A readonly buffer built from a static `&str` output does not need rich
project-based capabilities, and leaking projects in global git panel
might be dangerous.

Also adds readonly capability to the buffer, as
`editor.set_read_only(true);` API is a separate thing.

Release Notes:

- N/A
2025-03-26 23:01:03 +02:00
Ben Kunkle
1463b4d201 gpui/blade: Allow forcing use of a specific GPU with ZED_DEVICE_ID env var (#27531)
Workaround for users affected by #25899

Thanks to the work done by @kvark in
https://github.com/kvark/blade/pull/210, we have the ability to tell
Vulkan (through blade) a specific GPU to use.

This will hopefully allow some of the users affected by #25899 to use
Zed by allowing them to use a specific GPU, if the primary/default GPU
will not work

Release Notes:

- Added the ability to specify which GPU Zed uses on Linux by setting
the `ZED_DEVICE_ID` environment variable. You can obtain the device ID
of your GPU by running `lspci -nn | grep VGA` which will output each GPU
on one line like:
  ```
08:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA104
[GeForce RTX 3070] [10de:2484] (rev a1)
  ````
where the device ID here is `2484`. This value is in hexadecimal, so to
force Zed to use this specific GPU you would set the environment
variable like so:
  ```
  ZED_DEVICE_ID=0x2484
  ```
Make sure to export the variable if you choose to define it globally in
a `.bashrc` or similar
2025-03-26 20:32:36 +00:00
Smit Barmase
77856bf017 Hide the mouse when the user is typing in the editor - take 2 (#27519)
Closes #4461

Take 2 on https://github.com/zed-industries/zed/pull/25040. 

Fixes panic caused due to using `setHiddenUntilMouseMoves` return type
to `set` cursor on macOS.

Release Notes:

- Now cursor hides when the user is typing in editor. It will stay
hidden until it is moved again. This behavior is `true` by default, and
can be configured with `hide_mouse_while_typing` in settings.

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
Co-authored-by: Thomas Mickley-Doyle <thomas@zed.dev>
Co-authored-by: Agus <agus@zed.dev>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
Co-authored-by: Angelk90 <angelo.k90@hotmail.it>
2025-03-27 01:58:26 +05:30
Marshall Bowers
848a99c605 assistant2: Rework enabled tool representation (#27527)
This PR reworks how we store enabled tools in the `ToolWorkingSet`.

We now track them based on which tools are explicitly enabled, rather
than by the tools that have been disabled.

Also fixed an issue where switching profiles wouldn't properly set the
right tools.

Release Notes:

- N/A
2025-03-26 20:26:26 +00:00
Peter Tripp
435a36b9f9 html: Improve settings, formatting and user binaries (#27524)
Added support for using `language_server` as HTML formatter.
Added support for finding `vscode-html-language-server` in user's path.

Release Notes:

- N/A
2025-03-26 16:24:37 -04:00
Bennet Bo Fenner
8b3ddcd545 assistant2: Fix \\ appearing for paths in file context picker (#27528)
Closes #ISSUE

Release Notes:

- N/A
2025-03-26 20:22:13 +00:00
Elvis Pranskevichus
13bf179aae python: Show environment name if available (#26741)
Right now the toolchain popup is a nondescript list of duplicate entries
like `Python 3.10.15 (VirtualEnvWrapper)` and one has to look at the
interpreter path to distinguish one virtualenv from another.

Fix this by including the env name as reported by pet, so the entries
looks like `Python 3.10.15 (myproject; VirtualEnvWrapper)`.

Release Notes:

- Python: Improved display of environments in toolchain selector
2025-03-26 21:08:26 +01:00
Marshall Bowers
cdaad2655a assistant2: Add profile selector (#27520)
This PR replaces the tool selector with a new profile selector.

<img width="1394" alt="Screenshot 2025-03-26 at 2 35 42 PM"
src="https://github.com/user-attachments/assets/9631c6e9-9c47-411e-b9fc-5d61ed9ca1fe"
/>

<img width="1394" alt="Screenshot 2025-03-26 at 2 35 50 PM"
src="https://github.com/user-attachments/assets/3abe4e08-d044-4d3f-aa95-f472938452a8"
/>

Release Notes:

- N/A
2025-03-26 18:51:38 +00:00
Michael Sloan
7e4320f587 Fix drawing of 0-width borders when quad has other borders (#27511)
Closes #27485

Release Notes:

- N/A
2025-03-26 11:13:34 -06:00
Agus Zubiaga
130abc8998 assistant2: Encourage diagnostics check (#27510)
Release Notes:

- N/A
2025-03-26 13:42:09 -03:00
Richard Feldman
9db4c8b710 Add Create Directory Tool (#27505)
`mkdir -p` but it works cross-platform and uses project abstractions.

<img width="629" alt="Screenshot 2025-03-26 at 11 02 37 AM"
src="https://github.com/user-attachments/assets/9ef58d53-3343-4c94-a8f3-b82ab942611b"
/>

Release Notes:

- N/A
2025-03-26 11:59:03 -04:00
Marshall Bowers
e67ad1a1b6 extension_host: Rename Extension variants so that the version number components are clearer (#27507)
This PR renames the variants of the `Extension` enum with delimiters
between the version number components so that it's clearer which version
of the extension API they refer to.

Release Notes:

- N/A
2025-03-26 15:54:14 +00:00
Alvaro Parker
82536f5243 Add support for excluding files based on .gitignore (#26636)
Closes: #17543

Release Notes:

- **New Feature:** Introduced the ability to automatically remove files
and directories from the Zed project panel that are specified in
`.gitignore`.
- **Configuration Option:** This behavior can be controlled via the new
`project_panel.hide_gitignore` setting. By setting it to `true`, files
listed in `.gitignore` will be excluded from the project panel.
- **Toggle:** Ability to toggle this setting using the action
`ProjectPanel::ToggleHideGitIgnore`

```json
  "project_panel": {
    "hide_gitignore": true
  },

```

This results in a cleaner and easier to browse project panel for
projects that generate a lot of object files like `xv6-riscv` or `linux`
without needing to tweak `file_scan_exclusions` on `settings.json`

**Preview:**
- With `"project_panel.hide_gitignore": false` (default, this is how zed
currently looks)

![Screenshot From 2025-03-23
12-50-17](https://github.com/user-attachments/assets/15607e73-a474-4188-982a-eed4e0551061)

- With `"project_panel.hide_gitignore": true` 

![Screenshot From 2025-03-23
12-50-27](https://github.com/user-attachments/assets/3e281f92-294c-4133-b5e3-25e17f15bd4d)

- Action `ProjectPanel::ToggleHideGitIgnore`

![Screenshot From 2025-03-23
12-50-55](https://github.com/user-attachments/assets/4d03db33-75ad-471c-814c-098698a8cb38)
2025-03-26 20:57:09 +05:30
Richard Feldman
9eacac62a9 Escape markdown in tools' ui_text (#27502)
Escape markdown in tools' `ui_text`

<img width="628" alt="Screenshot 2025-03-26 at 10 43 23 AM"
src="https://github.com/user-attachments/assets/bb694821-aae7-4ccf-a35a-a3317b0222d5"
/>


Release Notes:

- N/A
2025-03-26 11:27:02 -04:00
Richard Feldman
82b0881dcb Make the "View Panel" focus the assistant panel (#27504)
Release Notes:

- N/A
2025-03-26 11:26:49 -04:00
iyht
0a49ccbebf Allow the keybinding context to detect the terminal vi_mode (#26236)
Release Notes:

- Added support for detecting the vi_mode in the keybinding context. Now
we can define and use the keybinding when the terminal is in vi_mode.


https://github.com/user-attachments/assets/a927b6c9-c634-4739-9502-8457614d9a90
2025-03-26 20:53:23 +05:30
张小白
d232150d67 windows: Fix performance issues after trashing or deleting a folder (#27498)
Closes #25247

Since the upstream `Notify` repo hasn't merged the related PR yet, this
is basically a temporary patch to work around it.

Release Notes:

- N/A
2025-03-26 23:20:09 +08:00
Joseph T. Lyons
9a2dfa687d Bump Zed to v0.181 (#27506)
Release Notes:

-N/A
2025-03-26 11:15:42 -04:00
Bennet Bo Fenner
1e22faebc9 lsp: Check if language server supports workspace/symbol request (#27491)
This ensures that we do not get a bunch of error logs when using the
symbol search:
```
[2025-03-26T13:23:32+01:00 ERROR project] Method not found
[2025-03-26T13:23:32+01:00 ERROR project] Method not found
[2025-03-26T13:23:32+01:00 ERROR project] Method not found
[2025-03-26T13:23:32+01:00 ERROR project] Method not found
[2025-03-26T13:23:32+01:00 ERROR project] Method not found
[2025-03-26T13:23:33+01:00 ERROR project] Method not found
...
```

Release Notes:

- N/A
2025-03-26 13:09:41 +00:00
Danilo Leal
1d9c581ae0 assistant2: Use different icons in the notification popover depending on status (#27493)
Using a check green icon for "success, you're changes are applied" and
the info, muted icon for just "there are news".

<img
src="https://github.com/user-attachments/assets/6b7e06bc-ca03-40fd-8962-7e21f5cd85d9"
width="500"/>
<img
src="https://github.com/user-attachments/assets/347ac8ac-792f-4e18-94d5-69bb9d5270e8"
width="500"/>

Release Notes:

- N/A
2025-03-26 10:03:06 -03:00
Danilo Leal
39af3b434a assistant2: Improve tool card header scrolling affordance (#27492)
Follow up to https://github.com/zed-industries/zed/pull/27489

Added this subtle gradient to the right side of the tool card header so
users know there is more content, suggesting it can be scrolled. Also
took the opportunity to extract out commonly used custom colors in all
of these cards into their own functions to ensure consistency.

Here's the final product:


https://github.com/user-attachments/assets/e44150f9-7751-46c7-8790-149b86cc5e0f

Release Notes:

- N/A
2025-03-26 09:45:51 -03:00
Danilo Leal
64b3eea3cd assistant2: Fix tool label overflowing on card header (#27489)
### Before

<img
src="https://github.com/user-attachments/assets/f56211d8-d60d-4e6c-9c40-af2523f71431"
width="600" />

### After

Now, you can horizontally scroll when the header holds an overflowing
label.


https://github.com/user-attachments/assets/6cb90de3-1db5-4a30-8f23-22221098a233

Release Notes:

- N/A
2025-03-26 09:10:40 -03:00
Bennet Bo Fenner
72318df4b5 lsp: Add support for textDocument/documentSymbol (#27488)
This PR adds support for retrieving the outline of a specific
buffer/document from the LSP.
E.g. for this code (`crates/cli/src/cli.rs`):
```rs
use collections::HashMap;
pub use ipc_channel::ipc;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
pub struct IpcHandshake {
    pub requests: ipc::IpcSender<CliRequest>,
    pub responses: ipc::IpcReceiver<CliResponse>,
}

#[derive(Debug, Serialize, Deserialize)]
pub enum CliRequest {
    Open {
        paths: Vec<String>,
        urls: Vec<String>,
        wait: bool,
        open_new_workspace: Option<bool>,
        env: Option<HashMap<String, String>>,
    },
}

#[derive(Debug, Serialize, Deserialize)]
pub enum CliResponse {
    Ping,
    Stdout { message: String },
    Stderr { message: String },
    Exit { status: i32 },
}

/// When Zed started not as an *.app but as a binary (e.g. local development),
/// there's a possibility to tell it to behave "regularly".
pub const FORCE_CLI_MODE_ENV_VAR_NAME: &str = "ZED_FORCE_CLI_MODE";
```

Rust-analyzer responds with:
```
Symbol: 'IpcHandshake' - Struct - (4:0-8:1) (5:11-5:23)
  Symbol: 'requests' - Field - (6:4-6:44) (6:8-6:16)
  Symbol: 'responses' - Field - (7:4-7:48) (7:8-7:17)
Symbol: 'CliRequest' - Enum - (10:0-19:1) (11:9-11:19)
  Symbol: 'Open' - EnumMember - (12:4-18:5) (12:4-12:8)
    Symbol: 'paths' - Field - (13:8-13:26) (13:8-13:13)
    Symbol: 'urls' - Field - (14:8-14:25) (14:8-14:12)
    Symbol: 'wait' - Field - (15:8-15:18) (15:8-15:12)
    Symbol: 'open_new_workspace' - Field - (16:8-16:40) (16:8-16:26)
    Symbol: 'env' - Field - (17:8-17:44) (17:8-17:11)
Symbol: 'CliResponse' - Enum - (21:0-27:1) (22:9-22:20)
  Symbol: 'Ping' - EnumMember - (23:4-23:8) (23:4-23:8)
  Symbol: 'Stdout' - EnumMember - (24:4-24:30) (24:4-24:10)
    Symbol: 'message' - Field - (24:13-24:28) (24:13-24:20)
  Symbol: 'Stderr' - EnumMember - (25:4-25:30) (25:4-25:10)
    Symbol: 'message' - Field - (25:13-25:28) (25:13-25:20)
  Symbol: 'Exit' - EnumMember - (26:4-26:24) (26:4-26:8)
    Symbol: 'status' - Field - (26:11-26:22) (26:11-26:17)
Symbol: 'FORCE_CLI_MODE_ENV_VAR_NAME' - Constant - (29:0-31:67) (31:10-31:37)
```

We'll use this to reference specific symbols in assistant2

Release Notes:

- N/A
2025-03-26 11:38:22 +00:00
Danilo Leal
d52291bac1 assistant2: Add new icons for create and delete file tool (#27487)
Release Notes:

- N/A
2025-03-26 08:20:09 -03:00
Kirill Bulatov
7462e74fbf Improve editor::CopyAndTrim action's discoverability (#27484)
Follow-up of https://github.com/zed-industries/zed/pull/27206

Add it to the editor context menu and Zed's app menu near the `Copy`
action.

Release Notes:

- N/A
2025-03-26 10:28:32 +00:00
CharlesChen0823
de67d93a92 git: Fix staging file hunks on Windows (#26661)
Closes #26458 

I had checked, and also reference the implementation in vscode.

Release Notes:

- N/A

---------

Co-authored-by: 张小白 <364772080@qq.com>
2025-03-26 15:46:04 +08:00
Anthony Eid
a02f7e5cda Debugger UI: Update tab content and remove red tint from terminated session (#27475)
The new tab content makes it obvious when a session is shutdown so the
red tint is no longer needed.

Release Notes:

- N/A
2025-03-26 07:13:12 +00:00
Anthony Eid
d70ac64fe4 Allow enabling/disabling breakpoints (#27280)
This PR adds the ability to enable/disable breakpoints. It also fixes a
bug where toggling a log breakpoint from the breakpoint context menu
would add a standard breakpoint on top of the log breakpoint instead of
deleting it.

todo: 
- [x] Add `BreakpointState` field Breakpoint that manages if a
breakpoint is active or not
- [x] Don't send disabled breakpoints to DAP servers - in progress
- [x] Half the opacity of disabled breakpoints - in progress
- [x] Add `BreakpointState` to database
- [x] Editor test for enabling/disabling breakpoints
- [ ] Integration Test to make sure we don't send disabled breakpoints
to DAP servers
- [x] Database test to make sure we properly serialize/deserialize
BreakpointState

Release Notes:

- N/A

---------

Co-authored-by: Piotr <piotr@zed.dev>
Co-authored-by: Conrad <conrad@zed.dev>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-03-26 02:06:08 -04:00
Michael Sloan
df583d73b9 Fix corner_radii clamping for Img when actual size is smaller than the container (#27473)
Noticed this when working on #27472

Release Notes:

- N/A
2025-03-26 05:23:27 +00:00
Michael Sloan
e091c5faaf Fix paint_quad behavior change which clamped corner rounding (#27472)
Turns out that git deletion indicator relied on using larger-than-bounds
corner rounding - see
https://github.com/zed-industries/zed/pull/27460#issuecomment-2753174153

Release Notes:

- N/A
2025-03-26 05:09:18 +00:00
Richard Feldman
931a6d6f40 Notify when tool is finished (#27459)
Now if a tool call finishes (or is blocked on confirmation) and the Zed
window is not active, you get a notification popup. You can turn it off
with a setting.

<img width="420" alt="Screenshot 2025-03-25 at 5 19 25 PM"
src="https://github.com/user-attachments/assets/bdf7b6b8-4428-4b46-8b09-e0be140f8a51"
/>
<img width="420 alt="Screenshot 2025-03-25 at 5 18 13 PM"
src="https://github.com/user-attachments/assets/1325e7b8-cd5a-44b9-a82d-5db928ad3cfc"
/>

Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-03-25 20:52:44 -04:00
Finn Evers
a605b66ce1 editor: Do not insert scrollbar hitboxes when scrollbars are disabled (#27467)
This PR fixes an issue where a clickable scrollbar track was inserted in
the editor even when scrollbars were explicitly disabled via the user's
settings. If the user has

```json
"scrollbar": {
    "show": "never"
}
```
in their settings, invisible and interactable scrollbar tracks will be
inserted in the editor if scrollbars would be required, as seen below:


https://github.com/user-attachments/assets/b0d915a9-7a7e-4518-84d8-96d9b15aab12

The bug occurs because we only set the scrollbar contents to be
invisible, which however does not affect the insertion of hitboxes for
the scrollbars.

This PR fixes this behaviour by preventing any scrollbar layouting from
happening when scrollbars are explicitly disabled via the settings:


https://github.com/user-attachments/assets/a3f5725b-aead-4fec-9fd8-e574cf269d45

All existing panels which have configurable scrollbars behave the same
way, see
fb2586a553/crates/outline_panel/src/outline_panel.rs (L4362-L4373)

for example. Following this check, neither a thumb nor a track is
inserted in any case when scrollbars are never to be shown.

Release Notes:

- Fixed invisible scrollbar tracks being inserted into the editor when
scrollbars are explicitly disabled via settings.
2025-03-26 01:44:35 +01:00
Michael Sloan
7376c6f377 Add support for Gemini 2.5 Pro Experimental model (#27468)
Release Notes:

- Added support for Gemini 2.5 Pro Experimental model to Zed AI.

Co-authored-by: Wilhelm Klopp <wil.klopp@gmail.com>
2025-03-26 00:12:10 +00:00
Smit Barmase
ee08776f34 markdown_preview: Fix code block highlight and indentation (#27463)
Closes #23218

Before:
<img width="1463" alt="before"
src="https://github.com/user-attachments/assets/4f77a4e0-61b8-4516-91a4-366f73e24760"
/>

After:
<img width="1463" alt="after"
src="https://github.com/user-attachments/assets/61e2c69d-fa6b-4c52-b1eb-ad7a61e274e0"
/>


Release Notes:

- Fixed issue where code block highlight and indentation in markdown
preview was rendered incorrectly.
2025-03-26 04:14:23 +05:30
Kirill Bulatov
30f7e896cf Add a way to control go to definition fallback (#27426)
Follow-up of https://github.com/zed-industries/zed/pull/9243 and
https://github.com/zed-industries/zed/pull/16512

Release Notes:

- Added a way to control go to definition fallback
2025-03-25 22:13:35 +00:00
Michael Sloan
8c8f50d916 Pull logic for clamping corner rounding radii out of Corners::to_pixels (#27460)
This seems more correct as corners are not necessarily only for rounding
radii.

Also applies clamping after scaling in `paint_quad`, deduplicating that
logic. This also provides a more precise result by doing the clamping
after scaling, avoiding floating point rounding issues (probably a
non-issue).

Release Notes:

- N/A
2025-03-25 21:56:17 +00:00
Ben Kunkle
76192ea93c worktree: Don't open files >= 6GB in size (#27458)
Temporary Workaround For: #27283

This PR can (and should!) be reverted once the underlying inefficiencies
are resolved

Release Notes:

- Files that are 6GB or larger will now not open. This is a temporary
workaround for inefficient handling of large files resulting in
extremely high memory usage, often resulting in system freezing,
requiring a restart of Zed or the entire system.
2025-03-25 16:43:40 -05:00
Danilo Leal
fb2586a553 assistant2: Add some padding top to the checkpoint element (#27456)
Most minimal PR ever.

Release Notes:

- N/A
2025-03-25 21:21:03 +00:00
Peter Tripp
28e0bb5a12 ci: Run nix nightly on any mac runners (#27457)
Nix nightly builds can now run on all macOS runners.

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

Release Notes:

- N/A
2025-03-25 17:15:15 -04:00
Finn Evers
a65ea2708c editor: Refactor scrollbar-related code (#24134)
This PR is primarily an implementation of @osiewicz
[comment](https://github.com/zed-industries/zed/pull/19495#pullrequestreview-2488877957)
in an effort to increase maintainability after the horizontal editor
scrollbar was added in #19495 . I also want to build on these changes in
future PRs to adress some other small bugs.

This primarily does the following:
1. Uses `along` wherever possible
2. Fixes the amount of mouse event listeners attached to the editor when
scrollbars are displayed to 2 instead of 2-4 in case both scrollbars are
displayed.

This can be done since only one scrollbar can be dragged by the cursor
at any given time, so the event listeners now account for that. The
state reflecting the scrollbar dragging state was also updated
accordingly.

It does not change any functionality besides the aforementioned event
listener code as well as some minor bugs which where present after
#19495 , namely:
- One missing `cx.stop_propagation()` (see
[here](a8741dc310/crates/editor/src/element.rs (L4684))
and
[here](a8741dc310/crates/editor/src/element.rs (L4838))
respectively).
- The horizontal scrollbar thumb having a small border on the left side,
which seems to be unintended for the horizontal scrollbar whilst
intended for the vertical one. Since this is a minimal change, I figured
it could be already included in this PR.

This PR admittetly grew quite large over time, however, much of the diff
is just renames to account for the code now working for both axes as
well as moved code. The logic remains (or should at least be)
unaffected. If I should split this into two PRs or remove some of the
changes, please let me know.

Release Notes:

- N/A
2025-03-25 22:08:46 +01:00
Alex van de Griendt
1574a3a2fd python: Add task for running modules (#26462)
Closes #26460

I am new to contributing to Zed (and pretty new to Rust in general). I'm
not too familiar with code style, guidelines etc. so please feel free to
suggest changes/improvements.

This PR adds a run icon to Python files that have a "main" function:
```python
if __name__ == "__main__":
    ...
```

In addition to the gutter icon, there is now also an extra task in the
command palette "run module".

Release Notes:

- Added detection for runnable Python modules
- Added Python-specific task to run a Python file as a module from
inside the project's scope

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2025-03-25 22:08:16 +01:00
Danilo Leal
152432f1d9 title bar: Adjust the onboarding banner component API (#27455)
This PR encapsulates the layout building of the Onboarding Banner
component inside of it, allowing to, at the call site, just pass an
icon, title, and subtitle. The `subtitle` parameter, by default, uses
the `Introducing:` label, which I think will be the one we'll use most
of the time for this specific component.

Release Notes:

- N/A
2025-03-25 17:57:42 -03:00
Marshall Bowers
5953c6167b assistant2: Allow profiles to manage context server tools (#27452)
This PR updates the agent profiles with support for managing context
server tools.

Release Notes:

- N/A
2025-03-25 19:55:59 +00:00
Danilo Leal
aab02a4166 Fix color swatch shrinking in the LSP completion menu (#27450)
### Before

Note how in the second time I try to add a color property, the color
swatch is shrunk.


https://github.com/user-attachments/assets/677a37b2-8ae4-408d-8dea-d9cc4bc2e119

### After


https://github.com/user-attachments/assets/aa902573-1f44-48e1-a396-e22ad5703155

Release Notes:

- Fixed color swatches shrinking upon re-adding a color-related
property.
2025-03-25 16:55:42 -03:00
Michael Sloan
9fc570c4be Remove Pixels: Mul<Pixels, Output = Pixels> impl, add ScaledPixels ops (#27451)
It doesn't make sense to have `Pixels: Mul<Pixels, Output = Pixels>` as
the output should be `Pixels^2` (area), so these impls are removed. All
code where these impls were used are improved by instead multiplying by
`f32` or `usize`.

Also adds math op impls that are present for `Pixels` but absent for
`ScaledPixels`. Adds missing `Mul<Pixels> for usize` to both.

Release Notes:

- N/A
2025-03-25 19:34:26 +00:00
Marshall Bowers
581d67398a Remove ui dependency from assistant_tool and context_server (#27449)
This PR removes the dependency on the `ui` crate from the
`assistant_tool` and `context_server` crates.

These crates were only depending on it for `IconName`, which can now be
depended on from `icons` directly.

Release Notes:

- N/A
2025-03-25 18:37:15 +00:00
Marshall Bowers
4a30b960d4 language_model: Remove dependency on ui (#27448)
This PR removes the dependency on the `ui` crate from the
`language_model` crate.

We were only depending on it to import `IconName`—which now lives in
`icons`—and some re-exported GPUI items.

Release Notes:

- N/A
2025-03-25 18:30:46 +00:00
Marshall Bowers
503bf607c5 Add icons crate (#27447)
This PR adds a new `icons` crate and moves the `IconName` into it.

We have a number of crates that are taking a dependency on `ui` just so
they can talk about icons, which is not ideal.

Release Notes:

- N/A
2025-03-25 18:18:22 +00:00
Agus Zubiaga
46e86f003f assistant tools: Add Tool::icon method instead of matching on name (#27444)
Release Notes:

- N/A
2025-03-25 18:17:36 +00:00
Marshall Bowers
0339d654d2 zed: Make inline assist quick action dispatch an action (#27445)
This PR makes the "Inline Assist" quick action in the quick action bar
work by dispatching the `InlineAssist` action.

This fixes an issue where the button was not working with Assistant 2.

Release Notes:

- N/A
2025-03-25 18:11:30 +00:00
Agus Zubiaga
24d76a64c3 Reapply #27200 after bad conflict resolution (#27446)
Release Notes:

- N/A
2025-03-25 18:06:45 +00:00
Piotr Osiewicz
3ba624391f debugger ui: Make variable values muted by default (#27441)
Closes #ISSUE

Release Notes:

- N/A
2025-03-25 18:43:27 +01:00
Michael Sloan
31e3c13ea9 Add .github/copilot-instructions.md to paths loaded for rules (#27442)
Release Notes:

- N/A
2025-03-25 17:30:37 +00:00
Marshall Bowers
0fec04a1b7 assistant2: Disable "Add Server" button when the required fields are empty (#27440)
This PR updates the add context server modal to disable the "Add Server"
button when the required fields are not filled out.

Release Notes:

- N/A
2025-03-25 17:19:30 +00:00
Richard Feldman
bf255486c0 Add find-replace-file tool, use it by default over edit-files-tool (#27438)
@agu-z and paired on trying out a "one tool call per edit" approach for
editing files. (The previous approach is still available, it's just
unchecked by default for now.)

Release Notes:

- N/A

---------

Co-authored-by: Agus <agus@zed.dev>
2025-03-25 13:12:50 -04:00
Nathan Sobo
cd1e56d6c7 Add support for dashed borders to GPUI (#27139)
Features:

* Scales dash spacing with border width.
* Laying out dashes around rounded corners.
* Varying border widths with rounded corners - now uses an ellipse for the inner edge of the border.
* When there are no rounded corners, each straight border is laid out separately, so that the dashes to meet at the corners.
* All sides of each dash are antialiased.

![image](https://github.com/user-attachments/assets/b3789a98-a5be-4f97-9736-c4e59615afe6)

![image](https://github.com/user-attachments/assets/739bdc57-4580-42c8-bfc3-6e287411a408)

Release Notes:

- N/A

---------

Co-authored-by: Michael Sloan <michael@zed.dev>
Co-authored-by: Ben <ben@zed.dev>
2025-03-25 11:11:04 -06:00
Piotr Osiewicz
2fe2028e20 debugger: Fix typing in active buffer resulting a jump to an active debug line (#27439)
/cc @iamnbutler 

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-03-25 17:08:36 +00:00
Piotr Osiewicz
f9212a001e debugger: Make UI a bit more dense (#27429)
Closes #ISSUE

Release Notes:

- N/A
2025-03-25 17:58:51 +01:00
Joseph T. Lyons
954a56ce0c Update stale issue bot to run on Wednesday (#27437)
Release Notes:

- N/A
2025-03-25 16:40:43 +00:00
Marshall Bowers
275cecb262 assistant2: Add modal for adding context servers (#27434)
This PR adds a modal for adding context servers from the Assistant 2
configuration view:

<img width="1394" alt="Screenshot 2025-03-25 at 12 22 50 PM"
src="https://github.com/user-attachments/assets/52fe194f-7d88-4f3b-aee1-8c6385136e6b"
/>

Release Notes:

- N/A
2025-03-25 16:37:56 +00:00
Richard Feldman
6b7167a32d Gracefully handle models searching for empty glob (#27370)
Sometimes we've seen models provide an empty string for the path search
glob. This assumes they meant "*" when that happens.

Separately, this also removes an unnecessary `clone` of a `String`.

Release Notes:

- N/A
2025-03-25 12:32:20 -04:00
Peter Tripp
430814c0a9 ci: Hide harmless cargo about error from release output (#27433)
Hide harmless errors like this from CI output.

<img width="834" alt="Screenshot 2025-03-25 at 12 07 36"
src="https://github.com/user-attachments/assets/55450812-000d-49a0-9926-cae0df8aa281"
/>

Release Notes:

- N/A
2025-03-25 16:24:05 +00:00
Luke Naylor
5aba5e1b3d Add default settings for LaTeX (#27286)
Closes https://github.com/rzukic/zed-latex/issues/70 where the language
server `texlab` is not used for code formatting when the "cspell"
extension is also installed, because it also provides a language server
for the LaTeX filetype but only for spell checking.

Release Notes:

- Fix conflict between LaTeX and cspell extensions affecting code formatting on save.
2025-03-25 11:36:48 -04:00
Agus Zubiaga
408e157d0f assistant edit tool: Reliability improvements (#27431)
- Add instructions in description to read before editing
- Add instructions in edit prefix to explicitly ask for reads
- Fix `connection error: delay between messages too long` by processing
chunks off a channel

Release Notes:

- N/A
2025-03-25 12:34:01 -03:00
Richard Feldman
2414855121 Gracefully handle bad LLM-generated paths in dir tool (#27202)
Release Notes:

- N/A

Co-authored-by: Ben <ben@zed.dev>
2025-03-25 11:32:27 -04:00
Marshall Bowers
e7f7ed349b assistant2: Add create-file and copy-path tools to the "Code Writer" profile (#27432)
This PR adds the `create-file` and `copy-path` tools to the "Code
Writer" profile.

Release Notes:

- N/A
2025-03-25 15:23:37 +00:00
Ben Kunkle
e273de5490 python: Fix incorrect indenting of except, finally, else, and elif control flow (#27428)
Closes #10832

Note: This PR only fixes the issue where when entering one of `except`,
`finally`, `else`, and `elif` after another block like so:

```python
try:
    for i in range(n):
        pass
except:|
```

The `except` would be indented resulting in the following:

```python
try:
    for i in range(n):
        pass
    except:|
```

This PR does not fix a separate issue in which the indentation is not
corrected from the second example to the first, i.e. if example 2 is
typed verbatim in Zed it will not auto-indent to look like example 1.
Handling of this case would likely require specific logic to handle, or
changes to the tree-sitter grammar for Python, as the current grammar
results in ERROR nodes that obscure the natural structure (cannot tie
the `except` to the `try`)

Release Notes:

- Fixed an issue where `except`, `finally`, `else`, and `elif` control
flow keywords in Python would be incorrectly indented when entered at
the correct level of indentation.
2025-03-25 14:58:41 +00:00
Richard Feldman
7046b9641d Add create-file-tool (#27381)
<img width="627" alt="Screenshot 2025-03-24 at 12 52 04 PM"
src="https://github.com/user-attachments/assets/0e8c061a-11c5-4d60-a694-55575b6c8f5e"
/>

Release Notes:

- N/A
2025-03-25 10:56:41 -04:00
Kirill Bulatov
9b883e02a6 Revert nix package bump attempt (#27425)
Follow-up of
https://github.com/zed-industries/zed/pull/27424/files#r2012188255

Release Notes:

- N/A
2025-03-25 14:54:48 +00:00
Kirill Bulatov
ffee218070 Use newer cargo-bundle without the build error (#27424)
Fixes Nightly builds for macOS.

Release Notes:

- N/A
2025-03-25 14:16:23 +00:00
Naim A.
d9dcc59334 Merge clangd's inactiveRegions with existing diagnostics (#26737)
Closes: https://github.com/zed-industries/zed/issues/13089

This PR attempts to resolve the issues discussed in my previous PR
#26146.

Release Notes:

- Fixed: `inactiveRegions` doesn't replace existing diagnostics anymore
2025-03-25 15:13:53 +02:00
Smit Barmase
8f1023360d extension: Add support for additional_workspace_configuration and additional_initialization_options (#27407)
Closes #22410

With this PR extensions can provide additional workspace configuration
for other LSP Adapters. This allows extensions like Astro, Svelte, Vue,
etc to provide plugins for vtsls typescript server, fixing issues like:
https://github.com/zed-industries/zed/issues/4577,
https://github.com/zed-industries/zed/issues/21697,
https://github.com/zed-industries/zed/issues/26901#issuecomment-2737485096

Todo:

- [x] Test case when extension is installed, does vtsls workspace config
refreshes?

Before:
<img width="450" alt="image"
src="https://github.com/user-attachments/assets/f242167c-5264-44ab-b5a7-8c90eb75c6a1"
/>

After:
<img width="450" alt="image"
src="https://github.com/user-attachments/assets/6a5f1afe-a0e1-4f64-8a95-919b0bf97614"
/>

Release Notes:

- N/A
2025-03-25 18:23:59 +05:30
Danilo Leal
9468b9699e assistant2: Add icon for copy-path tool (#27421)
Release Notes:

- N/A
2025-03-25 12:44:44 +00:00
Nate Butler
f1a9fdddab docs: Improve Git page (#26566)
Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-03-25 09:22:01 -03:00
Jason Lee
d40f8889b9 title_bar: Anchor user menu popover at top right corner (#27329)
Release Notes:

- Improved user menu placement

| Before | After |
| --- | --- |
| ![Pasted image
(2)](https://github.com/user-attachments/assets/fae750fd-d6bc-45d2-9ab0-7a1a1c6262ba)
|
![image](https://github.com/user-attachments/assets/c5d0453f-5bd5-45e2-a56d-e200e90c0a2a)
|
2025-03-25 08:20:50 -03:00
Danilo Leal
6715c8582f assistant2: Remove the Prompt Library section from the settings (#27404) 2025-03-25 07:34:11 -03:00
isomo
baf03e355b copilot: Add Claude 3.7-Sonnet-Thought model to Copilot Chat (#27409)
- Follow-up to: #25529 

Release Notes:

- Added Claude Sonnet 3.7 Thought to GitHub Copilot Chat
2025-03-25 10:56:27 +01:00
Mikayla Maki
35ec4753b4 Make a single re-usable banner component (#27412)
Release Notes:

- Fixed an issue where both the predict edit and git onboarding banners
would both show at the same time.
2025-03-25 07:05:25 +00:00
张小白
b85492bd00 windows: Detect pwsh (#25713)
Closes #22015


Release Notes:

- N/A
2025-03-24 22:31:11 -07:00
Julia Ryan
fca7ce9a14 nix: Cache deps with crane (#27411)
It turns out that on linux crane's `buildDepsOnly` was working fine, so
I'm re-enabling it and will worry about why it's failing on darwin
later.

This should significantly improve the amount of artifact-reuse for the
linux nix builds, which will hopefully bring build times low enough that
we're ok putting it in CI.

Release Notes:

- N/A
2025-03-24 22:24:02 -07:00
Mikayla Maki
42f01cc903 Set edit predictions to default to the Zed provider (#27394)
Release Notes:

- Changed the default edit prediction provider from Copilot to Zed
2025-03-24 20:45:06 -07:00
Ben Kunkle
ffa736e566 zlog: Implement better scope map (#27408)
Another step towards having `zlog` as the default logging solution in
Zed.
The new ScopeMap replaces the previous HashMap based implementation used
for scope lookups to:

A. Reduce complexity
B. Increase speed at which non-enabled logs can be filtered out
C. Provide more granular control over how scopes are determined to be
enabled/disabled,
   and what caching/other speed increase opportunities are available

Release Notes:

- N/A
2025-03-25 03:05:32 +00:00
Richard Feldman
e9e6529df4 Add copy-path tool (#27371)
<img width="631" alt="Screenshot 2025-03-24 at 11 01 10 AM"
src="https://github.com/user-attachments/assets/7e144619-83d0-4455-8d80-cc7ec6a7b03e"
/>

Release Notes:

- N/A
2025-03-24 21:21:55 -04:00
Piotr Osiewicz
3205ae3884 debugger/lldb: Remove xcrun-based lldb-dap binary lookup (#27405)
Closes #ISSUE
/cc @RemcoSmitsDev 

Release Notes:

- N/A
2025-03-25 00:21:09 +00:00
Danilo Leal
03102b4d7e assistant: Allow opening the Prompt Library via the command palette (#27368)
Also took the opportunity to rename the action to something that would
be clearer in the command palette, from `DeployPromptLibrary` to
`OpenPromptLibrary`.

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-03-24 21:00:33 -03:00
Danilo Leal
c32dece1b8 assistant2: Add tiny design improvements (#27399)
Really small stuff, like adding icons and adjusting colors.

Release Notes:

- N/A
2025-03-24 21:00:21 -03:00
Remco Smits
bcfc9e4437 debugger: Improve variable list keyboard navigation (#27308)
This PR improves the keyboard navigation for the variable list. 
Before this PR, if you want to open/close nested variables, you had to
use the right/left & up/down arrow keys.
Now you can step through with just only using your left/right arrow
keys, this feels a bit more natural and more similar to how other
editors allow you to navigate through variables.

This PR also fixes the following issues:
- Allow selecting a scope to be the start of your selection
- Allow selecting previous item if the first item is selected

-----


https://github.com/user-attachments/assets/aff0b133-97be-4c09-8ee6-b11495ad5568

Release Notes:

- N/A
2025-03-25 00:36:09 +01:00
Marshall Bowers
a52095f558 copilot: Switch to official @github/copilot-language-server (#27401)
This PR updates Copilot to use the official
[`@github/copilot-language-server`](https://github.com/github/copilot-language-server-release).

It's [available on
npm](https://www.npmjs.com/package/@github/copilot-language-server), so
we're installing it from there.

I tested it out locally and it seemed to be working as expected.

Release Notes:

- Updated Copilot to use the official
[`@github/copilot-language-server`](https://github.com/github/copilot-language-server-release).
2025-03-24 23:35:29 +00:00
Finn Evers
be83c5e1c5 migrator: Add migration for settings changed prior to migrator-introduction (#27375)
This PR updates two existing settings to use the settings migrator
instead of a manually implemented visitor. Both of these settings were
changed prior to the introduction of automatic migrations and the
visitor ensured that the settings were kept backwards compatible. See
https://github.com/zed-industries/zed/pull/22200 and
https://github.com/zed-industries/zed/pull/22364 respectively.

WIth this change, existing user configurations are updated accordingly
and the corresponding settings can derive `Deserialize` again.

I also added tests for the replacement of settings values, as there was
no test for this behaviour. Additionally, I added a seperate test for
the existing migration of `always_show_close_button`, since that
migration updated both the key and value.

Release Notes:

- N/A
2025-03-25 04:35:01 +05:30
Conrad Irwin
46d67a33c7 Don't assume that the excerpt can be found (#27395)
Release Notes:

- Fix (rare) panic in the project diff view
2025-03-24 16:12:26 -06:00
João Marcos
10c04afc81 Fix regression in do_completion changes (#27396)
Caused by #27313

Release Notes:

- N/A
2025-03-24 21:53:03 +00:00
Marshall Bowers
920eda07a5 zed_extension_api: Fork new version of extension API (#27390)
This PR forks a new version of the `zed_extension_api` in preparation
for new changes.

Release Notes:

- N/A
2025-03-24 17:06:05 -04:00
Marshall Bowers
a469c0e261 assistant2: Add move-path tool to the "Code Writer" profile (#27389)
This PR adds the `move-path` tool to the "Code Writer" profile.

Release Notes:

- N/A
2025-03-24 20:22:26 +00:00
Richard Feldman
5d05c4aa70 Remove Lua scripting tool (#27388)
We decided to take this out for now. It doesn't seem necessary, and it
complicates the code a lot. We can always put it back later if desired.

Release Notes:

- N/A
2025-03-24 15:58:07 -04:00
Peter Tripp
5465198d0d docs: Improve Haskell documentation (#27372)
Closes: https://github.com/zed-industries/zed/issues/27365

Release Notes:

- N/A
2025-03-24 15:33:02 -04:00
João Marcos
bbc7fcc54f Fix crash when toggling deleted hunk (#27138)
Release Notes:

- Fix rare crash when toggling deleted hunks.

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-03-24 19:25:56 +00:00
João Marcos
11552cc0bd Git: reload index before reading it (#27386)
This is one of the causes for race conditions, but isn't a specific bug fix by itself.

Release Notes:

- N/A

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-03-24 19:03:57 +00:00
Bennet Bo Fenner
699369995b assistant2: Rework @mentions (#26983)
https://github.com/user-attachments/assets/167f753f-2775-4d31-bfef-55565e61e4bc

Release Notes:

- N/A
2025-03-24 19:32:52 +01:00
Richard Feldman
4a5f89aded Make system prompt be more explicit about root paths (#27383)
## Before

<img width="627" alt="Screenshot 2025-03-24 at 12 55 15 PM"
src="https://github.com/user-attachments/assets/349d7025-e65e-4107-86ae-45eb321003b3"
/>

## After

<img width="627" alt="Screenshot 2025-03-24 at 12 52 04 PM"
src="https://github.com/user-attachments/assets/0e8c061a-11c5-4d60-a694-55575b6c8f5e"
/>

Release Notes:

- N/A
2025-03-24 14:00:16 -04:00
Piotr Osiewicz
e661a0afd6 python: Enable subroot detection for pylsp and pyright (#27364)
This is not going to fully fix the multi-root story, as we still need to
weave venvs through. Hence, no release note just yet.

Release Notes:

- N/A
2025-03-24 18:56:18 +01:00
张小白
57ffd6d78d Update windows-rs (#27379)
Release Notes:

- N/A
2025-03-24 16:29:18 +00:00
Anthony Eid
350c1e41d2 Serialize breakpoints when changing the state of a breakpoint (#27373)
This fixes a rare bug where a breakpoint isn't saved in the database
when a user toggles a breakpoint and immediately exits out of zed.

Release Notes:

- N/A

Co-authored-by: Piotr Osiewicz <peterosiewicz@gmail.com>
2025-03-24 12:25:17 -04:00
tidely
a23e293ca2 Upgrade xkbcommon to v0.8.0 (#27376)
Switch back to xkbcommon releases after
https://github.com/rust-x-bindings/xkbcommon-rs/pull/54 got merged in
0.8.0

Release Notes:

- N/A
2025-03-24 18:13:46 +02:00
Ben Kunkle
f2be201495 jsx_tag_auto_close: Fix <Foo.Bar> component auto-close (#27374)
- **support alternate tag name node names to fix autoclosing of
`<Foo.Bar>` style tags in TSX**
- **remove checks against close tag name while checking if tag is
closed**
- **move jsx tag auto close tests into jsx_tag_auto_close.rs**

Closes #27335

Release Notes:

- Fixed an issue with JSX tag auto-close where components containing a
`.` access like `<Foo.Bar>` would be auto-closed as `</>` instead of
`</Foo.Bar>`
2025-03-24 15:43:06 +00:00
Richard Feldman
43712285bf Add move_path tool (#27366)
<img width="629" alt="Screenshot 2025-03-24 at 10 06 39 AM"
src="https://github.com/user-attachments/assets/b099fcc0-b2f4-44ee-8c8f-416808363689"
/>

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-03-24 14:45:19 +00:00
Acaibrid
f38ee81e83 Treat bun.lock as JSONC (#27359)
Closes #27355 
This PR treat `bun.lock` file as `.jsonc`
note: 
[bun.lock](https://bun.sh/blog/bun-lock-text-lockfile) is a lockfile of
bun.js

Release Notes:

- Updated `bun.lock` files to be recognized as JSONC.
2025-03-24 09:46:23 -04:00
Alvaro Parker
ddf8d07f02 Update cosmic text to 0.13.2 (#27362)
Related #14222

Release Notes:

- This PR updates `cosmic-text` dependency on `gpui` crate from `0.11.2`
to 0.13.2`. This decreases RAM usage zed depending on the amount of
monospace fonts installed on the system. On Arch Linux with `nerd-fonts`
package installed (which provides around 2000 monospaced fonts), it
decreases ram usage from ~800mb to around ~300mb.

- Updated `cosmic-text` to `0.13.2` on `gpui` crate
2025-03-24 13:37:41 +00:00
Danilo Leal
7db9077835 assistant2: Polish the thinking card (#27363)
Mostly just adjusting spacing and making it consistent with how we
display other tool calls.

<img
src="https://github.com/user-attachments/assets/85892006-9029-4cb8-b805-bebe4232e458"
width="600px" />

Release Notes:

- N/A
2025-03-24 10:08:49 -03:00
Nils Koch
4e33aaa55c markdown_preview: Fix preview not rendering bullet points beginning with HTML (#27018)
Closes #26631

Release Notes:

- Fixed markdown preview not rendering bullet points beginning with HTML

Before:
<img width="822" alt="before"
src="https://github.com/user-attachments/assets/102a7eac-fcc2-457b-9587-4a021800841d"
/>


After:
<img width="822" alt="after"
src="https://github.com/user-attachments/assets/8544b318-0efa-46c3-bd9b-c6c21444fab7"
/>

Note: I thought it would make sense to return `true` in `is_text_like`
for `Event::InlineMath` and `Event::DisplayMath` as well, but it looks
like rendering inline math is not supported at all at the moment:


4402e033a4/crates/markdown/src/parser.rs (L108)
2025-03-24 13:57:06 +01:00
Thorben Kröger
d253d46fdf Make python's file, line output clickable in terminal (#26903)
Closes #16004.


![image](https://github.com/user-attachments/assets/73cfe9da-5575-4616-9ed0-99fcb3ab61f5)

Python formats file and line number references in the form `File
"file.py", line 8"`
I'm not a CPython expert, but from a quick look, they appear to come
from:
-
80e00ecc39/Python/traceback.c (L613)
-
80e00ecc39/Python/traceback.c (L927)
I am not aware of the possiblity to also encode the column information.

Release Notes:

- File, line references from Python, like 'File "file.py", line 8' are
now clickable in the terminal

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-03-24 12:36:14 +00:00
Junseong Park
07727f939e Update docs for some settings (#27293)
- update tooltips of `auto_install_extensions` , `active_pane_modifiers`
- update docs of `auto_install_extensions`, `active_pane_modifiers`,
`buffer_line_height`

Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2025-03-24 13:05:06 +02:00
AltCode
08e8109e79 docs: Add Erlang language server documentation (#27346)
This PR updates the Erlang docs to list the two language servers the
extension offers support for, as well as how to switch from `erlang_ls`
to `erlang-language-platform`.

Release Notes:

- N/A
2025-03-24 11:34:21 +02:00
Andy Waite
f19e1e3b5f docs: Document how to use project-specific settings for Ruby LSP (#27310)
I think it's helpful to illustrate how some settings can be added to the
project settings file rather than being global.

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-03-23 14:54:55 -04:00
Andy Waite
8294b9f674 docs: Add tip for viewing LSP logs when configuring Ruby (#27318)
Ruby LSP displays important information in its logs upon startup, such
as which formatter is detected. Being able to see this helps a lot when
configuring or troubleshooting.

cc @vitallium 

Release Notes:

- N/A
2025-03-22 18:32:16 -04:00
Andy Waite
352c71f8a6 docs: Update Minitest example to clarify it only works with Rails (#27311)
The example in the docs works with Rails but not plain minitest.

There are workarounds such as the [`m`](https://github.com/qrush/m) gem,
or adding ActiveSupport to a non-Rails project, but I feel they are
beyond the scope of the docs here.

Release Notes:

- N/A
2025-03-22 18:30:34 -04:00
João Marcos
9f0b09007b Rename LSP function and simplify tests (#27313)
While working on a fix I found opportunities to improve readability, but
it's a big rename diff, so I'm landing separately.

Release Notes:

- N/A
2025-03-22 19:23:11 +00:00
chbk
f4d1e7901c Improve Regex syntax highlighting (#25332)
Release Notes:

  - Improved Regex syntax highlighting

| Zed 0.174.6 | With this PR |
| --- | --- |
|
![Image](https://github.com/user-attachments/assets/2a0bb1d9-cbba-490c-b2be-08c94bc79517)
|
![Image](https://github.com/user-attachments/assets/b19f3871-ee83-4165-a026-207bdbbcad44)
|

- `(?P=`, `<`: `punctuation.bracket`
- `group_name`: `property` -> `label`
- `^`, `$`: `string.escape` -> `operator`
- `\b`, `\B`, `\k`: `string.escape` -> `keyword.operator`
- added `regex` scope to target regex tokens specifically

```js
regex = /^(?<group>[!\.\?])\b[a-z]+word\k<group>$/g
```

---------

Co-authored-by: João Marcos <marcospb19@hotmail.com>
2025-03-22 16:11:19 -03:00
Smit Barmase
044eb7b990 extensions_ui : Scroll to top on filter change (#27305)
Closes #27100


https://github.com/user-attachments/assets/0fc1409d-01c1-4caa-a2ed-762c8951930f

Release Notes:

- N/A
2025-03-22 18:38:01 +05:30
Smit Barmase
d96a50b029 extensions_ui: Add scrollbar (#27303)
This PR adds scrollbar to extensions page. 

For now haven't added setting to hide or configure this scrollbar, can
be handled later.

<img width="1258" alt="image"
src="https://github.com/user-attachments/assets/0d260051-5e4a-4e3f-9738-b5c5a988419e"
/>


Release Notes:

- Added scrollbar to extensions page.
2025-03-22 17:56:36 +05:30
Smit Barmase
b5e5959339 terminal: Make alternate_scroll on by default (#27302)
Most terminal emulators, like macOS Terminal, Alacritty, and Ghostty,
have alternate scroll turned on by default. I think it makes sense for
the Zed terminal to do the same and make it more of an opt-out feature.

Release Notes:

- N/A
2025-03-22 17:56:19 +05:30
João Marcos
9918b6cade Scroll to follow expanding part of editor::SelectLargerSyntaxNode (#27295)
When the selection grows both ways, the new code prioritizes the top
part instead of bottom one, this is usually more helpful considering
that most programming language grammars tend to define tokens right
before large delimited blocks, and rarely after (because humans and
parsers read from top to bottom).

Also, revert selection when convenient, so you have more control over
what you're selecting, looking at the selection `head` is commonly more
convenient than at the `tail`.

Release Notes:

- Improve scrolling of `editor::SelectLargerSyntaxNode` for better
visibility.
2025-03-22 09:06:13 +00:00
AidanV
fa677bdc38 vim: Single quote mark (#27231)
Closes #22398

Release Notes:

- vim: Adds `'` and `"` marks (last location jumped from in the current
buffer, and location when last exiting a buffer)

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-03-22 05:45:57 +00:00
AidanV
d82b547596 vim: View Marks (#26885)
Closes #26884

Release Notes:

- vim: Added `:marks` which brings up list of current marks
- confirming on selected mark in the view jumps to that mark

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-03-22 04:46:04 +00:00
Richard Feldman
4c86cda909 Prompt before running some tools (#27284)
Also includes some fixes for how the Lua tool was being generated.

<img width="644" alt="Screenshot 2025-03-21 at 6 26 18 PM"
src="https://github.com/user-attachments/assets/51bd1685-5b3f-4ed3-b11e-6fa8017847d4"
/>


Release Notes:

- N/A

---------

Co-authored-by: Ben <ben@zed.dev>
Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Joseph T. Lyons <JosephTLyons@gmail.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com>
2025-03-22 00:05:34 -04:00
Conrad Irwin
90649fbc89 Automatically expand context for inline assistant to nearest block (#27282)
Release Notes:

- Inline assistant will now expand empty selections to the block under
the cursor.

Co-authored-by: andrew j <andjones100@gmail.com>
2025-03-21 21:24:27 -06:00
Ben Kunkle
c783fd072f zed: Add --system-specs arg (#27285)
Adds the `--system-specs` flag to the Zed binary, so that users who wish
to report issues can retrieve their system specs, even if Zed is failing
to launch

Still TODO:
- [x] Test and do best effort GPU info detection on Linux
- [ ] Modify GitHub issue templates to tell users that the flag is
available if they are unable to launch Zed

Release Notes:

- Added the `--system-specs` flag to the Zed binary (not the cli!), to
retrieve the system specs we ask for in GitHub issues without needing to
open Zed
2025-03-22 02:56:25 +00:00
Martin Fischer
85a761cb2b markdown_preview: Fix rendering image not at all or too often (#25592)
Before MarkdownParagraphChunk::Image was pushed for every Text event if
we're currently inside an image. This was wrong since pulldown-cmark
parses `![](foo)` as:

Start(Image { link_type: Inline, dest_url: "foo", title: "", id: "" })
    End(Image)

If there is no alt text, no Text event is emitted. Which caused images
without any alt text not to be rendered at all.

For alt texts containing inline formatting this was even more obviously
broken since e.g. `![foo *bar* baz](foo)` gets parsed as:

Start(Image { link_type: Inline, dest_url: "foo", title: "", id: "" })
      Text(Borrowed("foo "))
      Start(Emphasis)
        Text(Borrowed("bar"))
      End(Emphasis)
      Text(Borrowed(" baz"))
    End(Image)

which for this example caused the image to appear 3 times in the
preview.

This commit fixes these two bugs which have existed since the
introduction of the image previews in
96854c68ea.

Release Notes:

- Fixed images in the markdown preview appearing not at all or too
often.
2025-03-21 22:17:42 +01:00
Anthony Eid
739f45eb23 Clear breakpoints action (#27254)
This PR adds an action that clears all breakpoints and notifies any
active DAPs.

todo
- [x] Implement clear functionality
- [x] Write an integration test for this

Release Notes:

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

---------

Co-authored-by: Piotr Osiewicz <peterosiewicz@gmail.com>
2025-03-21 20:18:08 +00:00
Ben Kunkle
16ad7424d6 zlog: Init (#27273)
Scaffolding for a revised way of logging in Zed. Very WIP, but the idea
is to allow maintainers to tell users to paste
```json
{
    "log": {
        "project.format": "trace"
    }
}
```
into their settings so that even trace logs are emitted for the log
statements emitted from a logger under the `project.format` scope.

The plan is to eventually implement the `Log` trait from the `log` crate
instead of just wrapping the `log` crate, which will simplify the
implementation greatly, and remove our need for both the `env_logger`
and `simplelog` crates.
Additionally, work will be done to transition to using the scoped
logging APIs throughout the app, focusing on bug hotspots to start
(currently, scoped logging is only used in the format codepath).

Release Notes:

- N/A
2025-03-21 20:08:03 +00:00
Danilo Leal
b32c792b68 assistant2: Polish spacing and alignment (#27264)
Release Notes:

- N/A

---------

Co-authored-by: Nathan Sobo <1789+nathansobo@users.noreply.github.com>
2025-03-21 16:17:32 -03:00
Sunli
1db621dc50 Refactor TextLayout to use Rc<RefCell> instead of Arc<Mutex> for improved performance (#27177)
Since `TextLayout` is not shared by multiple threads, changing it to
`Rc<RefCell<T>>` should improve performance.

I also found several codes with the same problem. If you think this
change is beneficial, I will continue to improve it in the subsequent
PR. 🙂

Release Notes:

- N/A
2025-03-21 15:06:22 -04:00
Nate Butler
6143da95fc editor: Fix regression in git label colors due to status color changes (#27272)
This PR fixes the new awkward-looking git status labels due to the
change in version control colors. We want to enable styling version
control colors distinctly from other statuses, but these colors aren't
great for labels as they are meant to be quite high contrast.

We may need to split version control colors into a primary color and a
text color if we want to improve theming this overall.

| Before | After |
|--------|-------|
| ![CleanShot 2025-03-21 at 14 12
22@2x](https://github.com/user-attachments/assets/fadb93b1-06b6-44cc-bf16-7e1279166ed0)
| ![CleanShot 2025-03-21 at 14 12
49@2x](https://github.com/user-attachments/assets/262ffc23-60b9-4cee-8a2b-9e864130912f)
|

Release Notes:

- Fixes a regression in git status colors in the project panel
2025-03-21 18:49:52 +00:00
João Marcos
7ced1b7a90 Fix strikethrough and underline in Linux (#27267)
Follow up to #26827 and #24721, which introduced a bug in Linux.

|before|now|
|---|---|

|![image](https://github.com/user-attachments/assets/6471502d-bf92-4808-ad42-9e0c66569d4f)|!![image](https://github.com/user-attachments/assets/ae45510a-8bc9-4f89-90a0-7496842fecb6)|


Release Notes:

- N/A

Co-authored-by: Jason Lee <huacnlee@gmail.com>
2025-03-21 17:17:25 +00:00
Antonio Scandurra
0e9e2d70cd Delete unused checkpoints (#27260)
Release Notes:

- N/A
2025-03-21 16:39:01 +00:00
Bennet Bo Fenner
a52e2f9553 Show claude-3-7-sonnet-thinking model for all users (#27256)
Release Notes:

- N/A
2025-03-21 17:23:36 +01:00
Conrad Irwin
a551a6139c Bump up default timeout (#27250)
Release Notes:

- Extended timeout used when connecting to remote instances
2025-03-21 10:04:39 -06:00
Danilo Leal
c394a3a890 Adjust multibuffer header fold button size (#27253)
This PR adjust the size of the fold button on the multibuffer header.
Had to make the `height` method public on the Button Like to pull that
off without other major changes.

| Before | After |
|--------|--------|
| ![CleanShot 2025-03-21 at 12  11
40@2x](https://github.com/user-attachments/assets/003b2965-b1cc-43ad-8528-2bd11cf0f9cc)
| ![CleanShot 2025-03-21 at 12  11
28@2x](https://github.com/user-attachments/assets/d4927b72-3f41-4c4b-9813-49e676170419)
|

Release Notes:

- N/A
2025-03-21 12:49:54 -03:00
Peter Tripp
1cca2e37b0 keymap: Remove backspace/delete as shortcuts for git::RestoreFile (#27257)
- See also: https://github.com/zed-industries/zed/pull/27004

Release Notes:

- N/A
2025-03-21 15:48:12 +00:00
Marshall Bowers
0de5c2ed53 assistant2: Order agent profiles in the order they are defined in settings (#27255)
This PR updates the ordering of the agent profiles in the tool selector
to respect the order they are defined in in the settings instead of
sorting them alphabetically.

This gives the user more control, and allows them to order the profiles
as they desire.

Release Notes:

- N/A
2025-03-21 11:38:02 -04:00
Kirill Bulatov
6397872c49 Persist editor folds between restarts (#27252)
Part of https://github.com/zed-industries/zed/issues/11626


https://github.com/user-attachments/assets/276cca5f-dd87-4496-b1b8-40b211f65aa7

Folds restoration between editor reopens will follow later

Release Notes:

- Started to persist editor folds between restarts
2025-03-21 15:28:11 +00:00
Anthony Eid
93bd32b425 Fix toggling breakpoints not working when text anchor isn't at start (#27249)
This fixes a bug where breakpoint's were unable to be toggled if the
text::Anchor representing the breakpoint position was not at the
beginning of a line.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-03-21 11:21:05 -04:00
Marshall Bowers
f119550838 assistant2: Define built-in agent profiles in the default settings (#27251)
This PR moves the definitions of the built-in agent profiles into the
default `settings.json`.

It also changes the behavior of how this setting is treated when merging
settings such that the set of profiles will be merged. This is so users
don't clobber the built-in profiles when adding profiles of their own.

Release Notes:

- N/A
2025-03-21 15:11:45 +00:00
Marshall Bowers
4e93e38b0a assistant2: Sort tools in the tool selector by ID (#27247)
This PR makes it so the tools in the tool selector are sorted by ID so
that they have a deterministic order.

Release Notes:

- N/A
2025-03-21 14:41:27 +00:00
Piotr Osiewicz
05aa8880a4 project: Track manifest locations per unique manifest locator (#27194)
This pull request paves way for exposing manifest tracking to
extensions.
- Project tree was renamed to manifest tree to better reflect it's
intent (and avoid confusion).
- Language server adapters now provide a name of their *manifest
locator*. If multiple language servers refer to the same locator, the
locating code will run just once for a given path.

Release Notes:

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

---------

Co-authored-by: Anthony <anthony@zed.dev>
2025-03-21 15:22:36 +01:00
Marshall Bowers
6bced3a834 assistant2: Ensure scripting tool gets disabled when switching profiles (#27244)
This PR fixes an issue where the scripting tool wasn't being disabled
when switching to a profile that did not have it enabled.

Release Notes:

- N/A
2025-03-21 14:22:00 +00:00
Antonio Scandurra
e14ebcf267 Show "Restore Checkpoint" only when there were changes (#27243)
Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-03-21 15:10:43 +01:00
Peter Tripp
9d965bc98a Change default Markdown soft_wrap to "bounded" (#27205)
- Follow-up to: https://github.com/zed-industries/zed/pull/26247

Previously with defaults meant that Markdown would softwrap even if you
had available window space (soft_wrap occurred at default
`preferred_line_length` of 80).

Release Notes:

- Changed Markdown default to soft_wrap at window width instead of
preferred_line_length
2025-03-21 10:00:01 -04:00
Piotr Osiewicz
579868110b lsp: Fix workspace folders being cleared when new set is the same as the old one (#27242)
Release Notes:

- N/A
2025-03-21 13:47:06 +00:00
Bennet Bo Fenner
a709d4c7c6 assistant: Add support for claude-3-7-sonnet-thinking (#27085)
Closes #25671

Release Notes:

- Added support for `claude-3-7-sonnet-thinking` in the assistant panel

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-03-21 12:29:07 +00:00
Kirill Bulatov
2ffce4f516 Add non-blob columns to SQLite (#27236) 2025-03-21 12:04:59 +02:00
Smit Barmase
33fc1f4af2 languages: Fix JS/TS imports not showing correct suggestions after using period (#27235)
Closes #21728

This PR improves autocomplete for imports for all kinds of javascript
and typescript files.

Adds `.` as `completion_query_characters` which will make it act like
word for auto completion context. This allows capturing compete
`format.` as query.

Before:
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/849fb342-db73-48e7-a9d8-93f0e5a14b58"
/>

After:
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/e3ac3272-3217-4bcd-857f-4a83afc5980e"
/>

Release Notes:

- Improved autocomplete suggestions for JavaScript and TypeScript
imports.
2025-03-21 15:13:31 +05:30
David Barsky
7ade7d8e45 lsp-config: Allow setting a server's environment variables (#27213)
Closes https://github.com/zed-industries/zed/issues/14334, allowing
users to set environment variables for a language server binary like:

```json
"lsp": {
  "rust-analyzer": {
    "binary": {
      "path": "/Users/dbarsky/.cargo/bin/rust-analyzer",
      "env": {
        "RA_PROFILE": "*>100"
      }
    },
  }
}
```

The newly introduced environment variables are merged with the shell
environment. Perhaps more controversially, I've _also_ removed the
trimming/`stderr:`-prefixing of language server logs. This because
rust-analyzer has some nice, tree-shaped profiling built-in, and it
prevents us from printing profiles like this:

<details>
<img width="1147" alt="Screenshot 2025-03-20 at 12 09 14 PM"
src="https://github.com/user-attachments/assets/b7066651-6394-492b-b745-906c66d3c7b2"
/>
</details>

Release Notes:

- Added the ability to set a language server's environment variables.
- Removed the `stderr`-prefix of a language server's stderr logs.
2025-03-21 09:15:41 +02:00
Danilo Leal
8f86cd758a assistant2: Add design refinements (#27160)
Release Notes:

- N/A

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-03-21 03:19:41 -03:00
迷渡
962709f42c docs: Change render function's return type (#27229) 2025-03-20 22:48:22 -06:00
Cole Miller
cf7d639fbc Migrate most callers of git-related worktree APIs to use the GitStore (#27225)
This is a pure refactoring PR that goes through all the git-related APIs
exposed by the worktree crate and minimizes their use outside that
crate, migrating callers of those APIs to read from the GitStore
instead. This is to prepare for evacuating git repository state from
worktrees and making the GitStore the new source of truth.

Other drive-by changes:

- `project::git` is now `project::git_store`, for consistency with the
other project stores
- the project panel's test module has been split into its own file

Release Notes:

- N/A

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-03-21 00:10:17 -04:00
Smit Barmase
9134630841 extensions: Add copy author info button in context menu (#27221)
Closes #26108

Add "Copy Author Info" button to extension context menu.

Release Notes:

- Added option to copy extension author's name and email from extension
context menu.
2025-03-21 03:45:06 +05:30
Cole Miller
bc1c0a2297 Separate repository state synchronization from worktree synchronization (#27140)
This PR updates our DB schemas and wire protocol to separate the
synchronization of git statuses and other repository state from the
synchronization of worktrees. This paves the way for moving the code
that executes git status updates out of the `worktree` crate and onto
the new `GitStore`. That end goal is motivated by two (related) points:

- Disentangling git status updates from the worktree's
`BackgroundScanner` will allow us to implement a simpler concurrency
story for those updates, hopefully fixing some known but elusive bugs
(upstream state not updating after push; statuses getting out of sync in
remote projects).
- By moving git repository state to the project-scoped `GitStore`, we
can get rid of the duplication that currently happens when two worktrees
are associated with the same git repository.

Co-authored-by: Max <max@zed.dev>

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-03-20 18:07:03 -04:00
Marshall Bowers
700af63c45 assistant2: Watch settings for changes to profiles (#27219)
This PR makes it so we watch the settings and update when the profiles
change.

Release Notes:

- N/A
2025-03-20 21:12:58 +00:00
Marshall Bowers
4b5df2189b assistant2: Allow creating agent profiles via settings (#27216)
This PR adds support for creating new agent profiles via the settings:

```json
{
  "assistant": {
    "profiles": {
      "lua": {
        "name": "Lua",
        "tools": {
          "lua-interpreter": true
        }
      },
      "lua-thinking": {
        "name": "Lua + Thinking",
        "tools": {
          "lua-interpreter": true,
          "thinking": true
        }
      }
    }
  }
}
```

Release Notes:

- N/A
2025-03-20 20:30:07 +00:00
Finn Evers
48b1a43f5e docs: Fix rendering of keybind in languages.md (#27217)
This fixes a broken keybind in the language extension docs: [Language
metadata](https://zed.dev/docs/extensions/languages#language-metadata) >
`line_comments`.

Release Notes:

- N/A
2025-03-20 20:25:54 +00:00
Kirill Bulatov
9609e04bb2 Add a way to copy with the selections trimmed (#27206)
No default binding currently, `cmd/ctr-shift-c` seem somewhat natural
but those are occupied by the collab panel.


https://github.com/user-attachments/assets/702cc52a-a4b7-4f2c-bb7f-12ca0c66faeb


Release Notes:

- Added a way to copy with the selections trimmed

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
2025-03-20 19:58:51 +00:00
Anthony Eid
a74f2bb18b Reuse values from last debug panel inert state if they exist (#27211)
This should allow the team to iterate faster when using the debug panel
to set up a session

Release Notes:

- N/A
2025-03-20 18:53:11 +00:00
Remco Smits
ac452799b0 debugger: Fix shutdown issues (#27071)
This PR fixes a few issues around shutting down a debug adapter.

The first issue I discovered was when I shut down all sessions via
`shutdown all adapters` command. We would still fetch the threads
request again, because we receive a thread event that indicated that it
exited. But this will always time out because the debug adapter is
already shutdown at this point, so by updating the check so we don't
allow fetching a request when the session is terminated fixes the issue.

The second issue fixes a bug where we would always shut down the parent
session, when a child session is terminated. This was reintroduced by
the big refactor. This is not something we want, because you could
receive multiple StartDebugging reverse requests, so if one child is
shutting down that does not mean the other ones should have been
shutting down as well.
Issue was original fixed in
https://github.com/RemcoSmitsDev/zed/pull/80#issuecomment-2573943661.


## TODO:
- [x] Add tests

Release Notes:

- N/A
2025-03-20 18:32:37 +00:00
Smit Barmase
7b80cd865d Show more possible matches in code context completion (#27199)
Closes #24794

We now don't filter matches provided by the fuzzy matcher, as it already
performs most of the filtering for us. Instead, the custom logic we
previously used for filtering is now used to partition, where before
discarded matches will be appended at end of list.

Before - Filtering out matches with higher fuzzy score
<img width="400" alt="image"
src="https://github.com/user-attachments/assets/7f9d66a2-0921-499c-af8a-f1e530da50b1"
/>

After - Changing filter to partition instead, and appending remaining
items at the end
<img width="400" alt="image"
src="https://github.com/user-attachments/assets/45848f70-ed51-4935-976c-6c16c5b5777b"
/>


Release Notes:

- Improved LSP auto complete to show more possible matches.

---------

Co-authored-by: Peter Tripp <petertripp@gmail.com>
2025-03-20 23:46:20 +05:30
Joseph T. Lyons
7931b1d345 Pre-fill body of email with system specs (#27210)
I think we still want to be able to easily capture system spec info from
users. They can decide if they want to include it or not.

Release Notes:

- N/A
2025-03-20 18:09:10 +00:00
Marshall Bowers
27ebedf517 gpui: Make App::get_name return an Option (#27209)
This PR makes `App::get_name` return an `Option` instead of panicking if
the name is not set.

We'll let the caller be responsible for dealing with the absence of a
name.

Release Notes:

- N/A
2025-03-20 17:56:27 +00:00
Marshall Bowers
f9f5126d2c assistant2: Uniquely identify context server entries in configuration view (#27207)
This PR gives each context server entry in the configuration view a
unique element ID.

This fixes some issues where the disclosures and switches weren't
working properly due to element ID collisions.

Release Notes:

- N/A
2025-03-20 17:37:15 +00:00
Agus Zubiaga
6408ae81d1 assistant2: Return no-edits response to architect model (#27200)
Sometimes the editor model returns no search/replace blocks. This
usually happens when the architect model calls the edit tool before
reading any files. When this happens, we'll now return the raw response
from the editor model to the architect model so it can recover
accordingly.

Release Notes:

- N/A
2025-03-20 14:29:34 -03:00
Marshall Bowers
c60a7034c8 context_server: Interpret context server command paths relative to the extension's work dir (#27201)
This PR fixes an issues where the commands returned from context server
extensions were being used as-is instead of interpreting them relative
to the extension's work dir.

Release Notes:

- Fixed an issue with context server paths not being interpreted
relative to the extension's work dir.

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Thomas Mickley-Doyle <tmickleydoyle@gmail.com>
2025-03-20 16:36:40 +00:00
Antonio Scandurra
7feb50fafe Add UI feedback for checkpoint restoration (#27203)
Release Notes:

- N/A

Co-authored-by: Agus Zubiaga <hi@aguz.me>
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2025-03-20 16:35:44 +00:00
Antonio Scandurra
f365b80814 Avoid polluting branch list and restore parent commit when using checkpoints (#27191)
Release Notes:

- N/A
2025-03-20 15:00:23 +00:00
Joseph T. Lyons
d0641a38a4 Rework feedback modal (#27186)
After our last community sync, we came to the conclusion that feedback
being sent outside of email is difficult to reply to. Our decision was
to use the old, tried and true email system, so that we can better
respond to people asking questions.

<img width="392" alt="SCR-20250320-igub"
src="https://github.com/user-attachments/assets/f1d01771-30eb-4b6f-b031-c68ddaac5700"
/>

Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-03-20 10:28:43 -04:00
Agus Zubiaga
2e8c0ff244 assistant edit tool: Report when file is empty or doesn't exist (#27190)
Instead of just reporting a search match failure, we'll now indicate
whether the file is empty or exists to help the model recover better
from bad edits.


Release Notes:

- N/A
2025-03-20 10:42:10 -03:00
Danilo Leal
4421bdd12e assistant: Dismiss model picker upon selection (#27162)
This PR makes the model picker close when you click on a new item.

Release Notes:

- N/A
2025-03-20 10:22:49 -03:00
Peter Tripp
aa2fe9cce1 Add additional git-blame-ignore-revs (#27189)
Release Notes:

- N/A
2025-03-20 09:17:56 -04:00
Richard Feldman
e3578fc44a Display what the tool is doing (#27120)
<img width="639" alt="Screenshot 2025-03-19 at 4 56 47 PM"
src="https://github.com/user-attachments/assets/b997f04d-4aff-4070-87b1-ffdb61019bd1"
/>

Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-03-20 09:16:39 -04:00
Kirill Bulatov
aae81fd54c Notify about broken task file contents (#27185)
Closes https://github.com/zed-industries/zed/issues/23783


https://github.com/user-attachments/assets/df019f68-a76b-4953-967a-a35ed21206ab

Release Notes:

- Added notifications when invalid tasks.json/debug.json is saved
2025-03-20 13:06:10 +00:00
Kirill Bulatov
de99febd9b debugger: Ensure both debug and regular global tasks are correctly merged (#27184)
Follow-up of https://github.com/zed-industries/zed/pull/13433
Closes https://github.com/zed-industries/zed/issues/27124
Closes https://github.com/zed-industries/zed/issues/27066

After this change, both old global task source, `tasks.json` and new,
`debug.json` started to call for the same task update method:


14920ab910/crates/project/src/task_inventory.rs (L414)

erasing previous declarations.

The PR puts this data under different paths instead and adjusts the code
around it.

Release Notes:

- Fixed custom tasks not shown
2025-03-20 12:51:26 +00:00
Kirill Bulatov
5bef32f3ed When determining Python task context, do not consider worktree-less files as an error (#27183)
Makes Python plugin to output


![image](https://github.com/user-attachments/assets/4960bc48-21b7-4392-82b9-18bfd1dd9cd0)

for standalone Python files now, instead of nothing as now.

Before the change, no task context was created for the standalone file
due to `VariableName::RelativeFile` lookup considered as an error.
Now, Zed continues and constructs whatever possible context instead.

That `pytest` task seems odd, as the logic fixed here needs a relative
path (hence, a worktree) to consider unit tests.
We do not have variables at the moment the associated tasks are queried
for:


14920ab910/crates/languages/src/python.rs (L359-L363)


14920ab910/crates/languages/src/python.rs (L417-L446)

so we cannot filter this the same way the PR does.
Maybe, we can use a `VariableName::RelativeFile` instead of
`VariableName::File` there?

Release Notes:

- Show tasks from Python plugin for standalone files
2025-03-20 12:44:07 +00:00
Smit Barmase
23e8519057 Add completion_query_characters in language (#27175)
Closes #18581

Now characters for completing query and word characters, which are
responsible for selecting words by double clicking or navigating, are
different. This fixes a bunch of things:

For settings.json, this improves completions to treat the whole string
as a completion query, instead of just the last word. We now added
"space" as a completion query character without it being a word
character.

For keymap.json, this improves selecting part of an action as the ":"
character is only a completion character and not a word character. So,
completions would still trigger on ":" and query capture will treat ":"
as a word, but for actions like selections and navigation, ":" will be
treated as punctuation.

Before:
Unnecessary related suggestions as query is only the last word which is
"d".
<img width="300" alt="image"
src="https://github.com/user-attachments/assets/8199a715-7521-49dd-948b-e6aaed04c488"
/>

Double clicking `ToggleFold` selects the whole action:
<img width="300" alt="image"
src="https://github.com/user-attachments/assets/c7f91a6b-06d5-45b6-9d59-61a1b2deda71"
/>

After:
Now query is "one d" and it shows only matched ones.
<img width="300" alt="image"
src="https://github.com/user-attachments/assets/1455dfbc-9906-42e8-b8aa-b3f551194ca2"
/>

Double clicking `ToggleFold` only selects part of the action, which is
more refined behavior.
<img width="300" alt="image"
src="https://github.com/user-attachments/assets/34b1c3c2-184f-402f-9dc8-73030a8c370f"
/>

Release Notes:

- Improved autocomplete suggestions in `settings.json`, now whole string
is queried instead of just last word of string, which filters out lot of
false positives.
- Improved selection of action in `keymap.json`, where now you can
double click to only select certain part of action, instead of selecting
whole action.

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-03-20 16:45:35 +05:30
Michael Sloan
1180b6fbc7 Initial support for AI assistant rules files (#27168)
Release Notes:

- N/A

---------

Co-authored-by: Danilo <danilo@zed.dev>
Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Thomas <thomas@zed.dev>
2025-03-20 08:30:04 +00:00
renovate[bot]
14920ab910 Update swatinem/rust-cache digest to 9d47c6a (#27121)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [swatinem/rust-cache](https://redirect.github.com/swatinem/rust-cache)
| action | digest | `f0deed1` -> `9d47c6a` |

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-20 07:58:22 +02:00
renovate[bot]
000b981cb4 Update Rust crate rustls-platform-verifier to v0.5.1 (#27136)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[rustls-platform-verifier](https://redirect.github.com/rustls/rustls-platform-verifier)
| workspace.dependencies | patch | `0.5.0` -> `0.5.1` |

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-20 07:52:49 +02:00
renovate[bot]
c9bff6e762 Update Rust crate sea-orm to v1.1.7 (#27137)
This PR contains the following updates:

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

---

### Release Notes

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

###
[`v1.1.7`](https://redirect.github.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#117---2025-03-02)

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

##### New Features

- Support nested entities in `FromQueryResult`
[https://github.com/SeaQL/sea-orm/pull/2508](https://redirect.github.com/SeaQL/sea-orm/pull/2508)

```rust

#[derive(FromQueryResult)]
struct Cake {
    id: i32,
    name: String,
    #[sea_orm(nested)]
    bakery: Option<CakeBakery>,
}

#[derive(FromQueryResult)]
struct CakeBakery {
    #[sea_orm(from_alias = "bakery_id")]
    id: i32,
    #[sea_orm(from_alias = "bakery_name")]
    title: String,
}

let cake: Cake = cake::Entity::find()
    .select_only()
    .column(cake::Column::Id)
    .column(cake::Column::Name)
    .column_as(bakery::Column::Id, "bakery_id")
    .column_as(bakery::Column::Name, "bakery_name")
    .left_join(bakery::Entity)
    .order_by_asc(cake::Column::Id)
    .into_model()
    .one(&ctx.db)
    .await?
    .unwrap();

assert_eq!(
    cake,
    Cake {
        id: 1,
        name: "Cake".to_string(),
        bakery: Some(CakeBakery {
            id: 20,
            title: "Bakery".to_string(),
        })
    }
);
```

- Support nested entities in `DerivePartialModel`
[https://github.com/SeaQL/sea-orm/pull/2508](https://redirect.github.com/SeaQL/sea-orm/pull/2508)

```rust

#[derive(DerivePartialModel)] // FromQueryResult is no longer needed
#[sea_orm(entity = "cake::Entity", from_query_result)]
struct Cake {
    id: i32,
    name: String,
    #[sea_orm(nested)]
    bakery: Option<Bakery>,
}

#[derive(DerivePartialModel)]

#[sea_orm(entity = "bakery::Entity", from_query_result)]
struct Bakery {
    id: i32,
    #[sea_orm(from_col = "Name")]
    title: String,
}

// same as previous example, but without the custom selects
let cake: Cake = cake::Entity::find()
    .left_join(bakery::Entity)
    .order_by_asc(cake::Column::Id)
    .into_partial_model()
    .one(&ctx.db)
    .await?
    .unwrap();

assert_eq!(
    cake,
    Cake {
        id: 1,
        name: "Cake".to_string(),
        bakery: Some(CakeBakery {
            id: 20,
            title: "Bakery".to_string(),
        })
    }
);
```

- Derive also `IntoActiveModel` with `DerivePartialModel`
[https://github.com/SeaQL/sea-orm/pull/2517](https://redirect.github.com/SeaQL/sea-orm/pull/2517)

```rust

#[derive(DerivePartialModel)]
#[sea_orm(entity = "cake::Entity", into_active_model)]
struct Cake {
    id: i32,
    name: String,
}

assert_eq!(
    Cake {
        id: 12,
        name: "Lemon Drizzle".to_owned(),
    }
    .into_active_model(),
    cake::ActiveModel {
        id: Set(12),
        name: Set("Lemon Drizzle".to_owned()),
        ..Default::default()
    }
);
```

- Added `SelectThree`
[https://github.com/SeaQL/sea-orm/pull/2518](https://redirect.github.com/SeaQL/sea-orm/pull/2518)

```rust
// Order -> (many) Lineitem -> Cake
let items: Vec<(order::Model, Option<lineitem::Model>, Option<cake::Model>)> =
    order::Entity::find()
        .find_also_related(lineitem::Entity)
        .and_also_related(cake::Entity)
        .order_by_asc(order::Column::Id)
        .order_by_asc(lineitem::Column::Id)
        .all(&ctx.db)
        .await?;
```

##### Enhancements

- Support complex type path in `DeriveIntoActiveModel`
[https://github.com/SeaQL/sea-orm/pull/2517](https://redirect.github.com/SeaQL/sea-orm/pull/2517)

```rust

#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "<fruit::Entity as EntityTrait>::ActiveModel")]
struct Fruit {
    cake_id: Option<Option<i32>>,
}
```

- Added `DatabaseConnection::close_by_ref`
[https://github.com/SeaQL/sea-orm/pull/2511](https://redirect.github.com/SeaQL/sea-orm/pull/2511)

```rust
pub async fn close(self) -> Result<(), DbErr> { .. } // existing
pub async fn close_by_ref(&self) -> Result<(), DbErr> { .. } // new
```

##### House Keeping

- Cleanup legacy `ActiveValue::Set`
[https://github.com/SeaQL/sea-orm/pull/2515](https://redirect.github.com/SeaQL/sea-orm/pull/2515)

###
[`v1.1.6`](https://redirect.github.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#116---2025-02-24)

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

##### New Features

- Support PgVector (under feature flag `postgres-vector`)
[https://github.com/SeaQL/sea-orm/pull/2500](https://redirect.github.com/SeaQL/sea-orm/pull/2500)

```rust
// Model

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "image_model")]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false)]
    pub id: i32,
    pub embedding: PgVector,
}
 
// Schema
sea_query::Table::create()
    .table(image_model::Entity.table_ref())
    .col(ColumnDef::new(Column::Id).integer().not_null().primary_key())
    .col(ColumnDef::new(Column::Embedding).vector(None).not_null())
    ..

// Insert
ActiveModel {
    id: NotSet,
    embedding: Set(PgVector::from(vec![1., 2., 3.])),
}
.insert(db)
.await?
```

- Added `Insert::exec_with_returning_keys` &
`Insert::exec_with_returning_many` (Postgres only)

```rust
assert_eq!(
    Entity::insert_many([
        ActiveModel { id: NotSet, name: Set("two".into()) },
        ActiveModel { id: NotSet, name: Set("three".into()) },
    ])
    .exec_with_returning_many(db)
    .await
    .unwrap(),
    [
        Model { id: 2, name: "two".into() },
        Model { id: 3, name: "three".into() },
    ]
);

assert_eq!(
    cakes_bakers::Entity::insert_many([
        cakes_bakers::ActiveModel {
            cake_id: Set(1),
            baker_id: Set(2),
        },
        cakes_bakers::ActiveModel {
            cake_id: Set(2),
            baker_id: Set(1),
        },
    ])
    .exec_with_returning_keys(db)
    .await
    .unwrap(),
    [(1, 2), (2, 1)]
);
```

- Added `DeleteOne::exec_with_returning` &
`DeleteMany::exec_with_returning`
[https://github.com/SeaQL/sea-orm/pull/2432](https://redirect.github.com/SeaQL/sea-orm/pull/2432)

##### Enhancements

- Expose underlying row types (e.g. `sqlx::postgres::PgRow`)
[https://github.com/SeaQL/sea-orm/pull/2265](https://redirect.github.com/SeaQL/sea-orm/pull/2265)
- \[sea-orm-cli] Added `acquire-timeout` option
[https://github.com/SeaQL/sea-orm/pull/2461](https://redirect.github.com/SeaQL/sea-orm/pull/2461)
- \[sea-orm-cli] Added `with-prelude` option
[https://github.com/SeaQL/sea-orm/pull/2322](https://redirect.github.com/SeaQL/sea-orm/pull/2322)
- \[sea-orm-cli] Added `impl-active-model-behavior` option
[https://github.com/SeaQL/sea-orm/pull/2487](https://redirect.github.com/SeaQL/sea-orm/pull/2487)

##### Bug Fixes

- Fixed `seaography::register_active_enums` macro
[https://github.com/SeaQL/sea-orm/pull/2475](https://redirect.github.com/SeaQL/sea-orm/pull/2475)

##### House keeping

- Remove `futures` crate, replace with `futures-util`
[https://github.com/SeaQL/sea-orm/pull/2466](https://redirect.github.com/SeaQL/sea-orm/pull/2466)

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-20 07:52:35 +02:00
renovate[bot]
9fd2d064ee Update Rust crate mimalloc to v0.1.44 (#27131)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

##### Changes

-   Mimalloc v2.2.2

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-20 07:48:35 +02:00
renovate[bot]
11425cf5f1 Update Rust crate unindent to v0.2.4 (#27151)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [unindent](https://redirect.github.com/dtolnay/indoc) |
workspace.dependencies | patch | `0.2.3` -> `0.2.4` |

---

### Release Notes

<details>
<summary>dtolnay/indoc (unindent)</summary>

###
[`v0.2.4`](https://redirect.github.com/dtolnay/indoc/releases/tag/0.2.4)

[Compare
Source](https://redirect.github.com/dtolnay/indoc/compare/0.2.3...0.2.4)

-   Update to Syn 0.13

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-20 07:42:39 +02:00
renovate[bot]
b54c92079f Update Rust crate winresource to v0.1.20 (#27152)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [winresource](https://redirect.github.com/BenjaminRi/winresource) |
build-dependencies | patch | `0.1.19` -> `0.1.20` |

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-20 07:42:20 +02:00
renovate[bot]
3bbdc546ec Update Rust crate time to v0.3.40 (#27147)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [time](https://time-rs.github.io)
([source](https://redirect.github.com/time-rs/time)) |
workspace.dependencies | patch | `0.3.37` -> `0.3.40` |

---

### Release Notes

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

###
[`v0.3.40`](https://redirect.github.com/time-rs/time/blob/HEAD/CHANGELOG.md#0340-2025-03-18)

[Compare
Source](https://redirect.github.com/time-rs/time/compare/v0.3.39...v0.3.40)

##### Added

- Visibility modifiers may now be added to the `mod` generated by
`time::sere::format_description!`.

###
[`v0.3.39`](https://redirect.github.com/time-rs/time/blob/HEAD/CHANGELOG.md#0339-2025-03-06)

[Compare
Source](https://redirect.github.com/time-rs/time/compare/v0.3.38...v0.3.39)

##### Fixed

-   Doc tests run successfully with the default feature set.
-   wasm builds work again.

Both of these were regressions in v0.3.38 and are now checked in CI.

###
[`v0.3.38`](https://redirect.github.com/time-rs/time/blob/HEAD/CHANGELOG.md#0338-2025-03-05)

[Compare
Source](https://redirect.github.com/time-rs/time/compare/v0.3.37...v0.3.38)

##### Added

- The `[year]` component (in format descriptions) now supports a `range`
modifier, which can be
either `standard` or `extended`. The default is `extended` for backwards
compatibility. This is
intended as a manner to opt *out* of the extended range when the
`large-dates` feature is enabled.
When the `large-dates` feature is not enabled, the modifier has no
effect.
- `UtcDateTime`, which is semantically equivalent to an `OffsetDateTime`
with UTC as its offset. The
advantage is that it is the same size as a `PrimitiveDateTime` and has
improved operability with
    well-known formats.

    As part of this, there were some other additions:

- `utc_datetime!` macro, which is similar to the `datetime!` macro but
constructs a `UtcDateTime`.
    -   `PrimitiveDateTime::as_utc`
    -   `OffsetDateTime::to_utc`
    -   `OffsetDateTime::checked_to_utc`
- `time::serde::timestamp::milliseconds_i64`, which is a module to
serialize/deserialize timestamps
as the Unix timestamp. The pre-existing module does this as an `i128`
where an `i64` would
    suffice. This new module should be preferred.

##### Changed

- `error::Format` has had its `source()` implementation changed to no
longer return a boxed value
from the `ComponentRange` variant. If you were explicitly expecting
this, you will need to update
    your code. The method API remains unchanged.
-   `[year repr:century]` supports single-digit values.
-   All `format_into` methods accept `?Sized` references.

##### Miscellaneous

- Some non-exhaustive enum variants that are no longer used have been
modified to be statically
proven as uninhabited. The relevant fields are doc-hidden and not
semver-guaranteed to remain as
    such, though it is unlikely to change.
-   An unnecessary check when parsing RFC 2822 has been removed.
- Various methods have had their implementations changed, resulting in
significant performance
    gains. Among the methods changed are
    -   `util::is_leap_year`
    -   `util::weeks_in_year`
    -   `Month::length`
    -   `Date::to_calendar_date`
    -   `Date::month`
    -   `Date::day`
    -   `Date::from_julian_day`
    -   `Date::to_julian_day`
    -   other methods that call into these methods

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-19 23:36:33 -04:00
renovate[bot]
e4e3ce6a38 Update Rust crate serde_repr to v0.1.20 (#27146)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [serde_repr](https://redirect.github.com/dtolnay/serde-repr) |
workspace.dependencies | patch | `0.1.19` -> `0.1.20` |

---

### Release Notes

<details>
<summary>dtolnay/serde-repr (serde_repr)</summary>

###
[`v0.1.20`](https://redirect.github.com/dtolnay/serde-repr/releases/tag/0.1.20)

[Compare
Source](https://redirect.github.com/dtolnay/serde-repr/compare/0.1.19...0.1.20)

-   Documentation improvements

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-19 23:35:25 -04:00
renovate[bot]
8cd96cbf59 Update Rust crate serde_json to v1.0.140 (#27144)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [serde_json](https://redirect.github.com/serde-rs/json) | dependencies
| patch | `1.0.139` -> `1.0.140` |
| [serde_json](https://redirect.github.com/serde-rs/json) |
workspace.dependencies | patch | `1.0.139` -> `1.0.140` |

---

### Release Notes

<details>
<summary>serde-rs/json (serde_json)</summary>

###
[`v1.0.140`](https://redirect.github.com/serde-rs/json/releases/tag/v1.0.140)

[Compare
Source](https://redirect.github.com/serde-rs/json/compare/v1.0.139...v1.0.140)

-   Documentation improvements

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-19 23:35:21 -04:00
Ben Kunkle
274124256d Fix code action formatters creating separate transaction (#26311)
Closes #24588
Closes #25419

Restructures `LspStore.format_local` a decent bit in order to make how
the transaction history is preserved more clear, and in doing so fix
various bugs with how the transaction history is handled during a format
request (especially when formatting in remote dev)

Release Notes:

- Fixed an issue that prevented formatting from working when working
with remote dev
- Fixed an issue when using code actions as a format step where the
edits made by the code actions would not be grouped with the other
format edits in the undo history
2025-03-19 20:59:43 -05:00
renovate[bot]
1cf252f8eb Update Rust crate semver to v1.0.26 (#27143)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [semver](https://redirect.github.com/dtolnay/semver) |
workspace.dependencies | patch | `1.0.25` -> `1.0.26` |

---

### Release Notes

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

###
[`v1.0.26`](https://redirect.github.com/dtolnay/semver/releases/tag/1.0.26)

[Compare
Source](https://redirect.github.com/dtolnay/semver/compare/1.0.25...1.0.26)

-   Documentation improvements

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-20 01:20:45 +00:00
Julia Ryan
e46c72f4a8 nix: Add nightly build job with cachix (#27014)
I'll be using this to `nix run github:zed-industries/zed/nightly` and
get an up-to-date and cached nightly build.

It'll also serve as a way to warn me when the nix build is broken,
rather than having to wait for users to report it.

Eventually and depending on the build time of the nix builds, we may
want to consider putting a nix build in CI (#17458) to prevent
breakages, but for now a best-effort nightly build that doesn't block
the job if it fails is a good start.

Resolve #19937

Release Notes:

- N/A
2025-03-20 00:16:06 +00:00
renovate[bot]
63f656faae Update Rust crate async-compression to v0.4.21 (#27122)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[async-compression](https://redirect.github.com/Nullus157/async-compression)
| workspace.dependencies | patch | `0.4.20` -> `0.4.21` |

---

### Release Notes

<details>
<summary>Nullus157/async-compression (async-compression)</summary>

###
[`v0.4.21`](https://redirect.github.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#0421---2025-03-15)

[Compare
Source](https://redirect.github.com/Nullus157/async-compression/compare/v0.4.20...v0.4.21)

##### Fixed

- When flate encoding, do not mark internal state as flushed if it ran
out of buffer space.
- Add debug assertion in `produce` method to check buffer capacity in
implementations for `BufWriter`.

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-19 18:36:27 -04:00
renovate[bot]
31b8c36479 Update Rust crate async-std to v1.13.1 (#27127)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [async-std](https://async.rs)
([source](https://redirect.github.com/async-rs/async-std)) |
dependencies | patch | `1.13.0` -> `1.13.1` |

---

### Release Notes

<details>
<summary>async-rs/async-std (async-std)</summary>

###
[`v1.13.1`](https://redirect.github.com/async-rs/async-std/blob/HEAD/CHANGELOG.md#1131---2025-02-21)

[Compare
Source](https://redirect.github.com/async-rs/async-std/compare/v1.13.0...v1.13.1)

`async-std` has officially been discontinued. We recommend that all
users and
libraries migrate to the excellent
[`smol`](https://redirect.github.com/smol-rs/smol/)
project.

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-19 18:35:56 -04:00
Agus Zubiaga
dfdca540ec assistant2: Handle empty tool results by providing placeholder text (#27130)
This is surprising, but the Anthropic API returns a 400 if a tool output
is an empty string because it thinks we're attaching a `tool use`
without a corresponding `tool result`, but we are not, it's just empty
(which seems totally reasonable) 🙃

Release Notes:

- N/A
2025-03-19 22:30:49 +00:00
renovate[bot]
14c036931d Update Rust crate async-trait to v0.1.88 (#27128)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [async-trait](https://redirect.github.com/dtolnay/async-trait) |
workspace.dependencies | patch | `0.1.87` -> `0.1.88` |

---

### Release Notes

<details>
<summary>dtolnay/async-trait (async-trait)</summary>

###
[`v0.1.88`](https://redirect.github.com/dtolnay/async-trait/releases/tag/0.1.88)

[Compare
Source](https://redirect.github.com/dtolnay/async-trait/compare/0.1.87...0.1.88)

- Fix lifetime bounding on generic parameters that have cfg
([#&#8203;289](https://redirect.github.com/dtolnay/async-trait/issues/289))

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-19 22:17:00 +00:00
Mikayla Maki
5387ae9ed8 Add documentation for secondary modifier (#27129)
Follow up to: https://github.com/zed-industries/zed/pull/26390

Release Notes:

- N/A
2025-03-19 22:05:33 +00:00
Angelo Verlain Shema
c30fb5f1ec Use shell script language for APKBUILD files (#27099)
`APKBUILD` files are similar to `PKGBUILD` used by arch linux, but are
used to build alpine linux packages:
https://wiki.alpinelinux.org/wiki/APKBUILD_Reference

Release Notes:

- Added recognition for `APKBUILD` files as "Shell Script".
2025-03-19 22:00:44 +00:00
renovate[bot]
f7e2b7b679 Update actions/upload-artifact digest to ea165f8 (#27115)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/upload-artifact](https://redirect.github.com/actions/upload-artifact)
| action | digest | `4cec3d8` -> `ea165f8` |

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-19 17:55:06 -04:00
renovate[bot]
b3bf3e2d53 Update cloudflare/wrangler-action digest to da0e0df (#27116)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[cloudflare/wrangler-action](https://redirect.github.com/cloudflare/wrangler-action)
| action | digest | `392082e` -> `da0e0df` |

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-19 17:54:25 -04:00
renovate[bot]
1cc59b317c Update actions/setup-node digest to cdca736 (#27108)
This PR contains the following updates:

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

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-19 17:52:19 -04:00
Marshall Bowers
efd3f8a8f1 assistant2: Add initial concept of profiles (#27123)
This PR adds the initial concept of agent profiles to Assistant 2.

Right now these are just collections of tools that can quickly be
enabled together:


https://github.com/user-attachments/assets/7c7f9cc8-a5e5-492f-96f7-79697bbf3d72

There are currently two profiles:

- `Read-only` - Consists only of tools that do not perform writes.
- `Code Writer` - Consists of all tools for writing code, with the
exception of the `lua-interpreter`.

Release Notes:

- N/A
2025-03-19 21:48:14 +00:00
Marshall Bowers
930dba4a7f Upgrade thiserror to v2.0 (#27117)
This PR upgrades `thiserror` to v2.0.

We were still on v1.0, but a number of our dependencies have already
moved to v2.0.

Release Notes:

- N/A
2025-03-19 20:47:38 +00:00
renovate[bot]
7cfd919523 Pin actions/checkout action to 11bd719 (#27107)
This PR contains the following updates:

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

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-19 16:46:51 -04:00
Peter Tripp
edd1b48e7c ci: Send emails for weekly release (#27102)
Release Notes:

- N/A
2025-03-19 16:16:34 -04:00
Michael Sloan
3ec69a5bc0 Make getting keybinding for display more efficient (#27046)
No longer iterates all the matching bindings, and no longer clones the
result.

Release Notes:

- N/A
2025-03-19 20:15:33 +00:00
Antonio Scandurra
33faa66e35 Start on a Git-based review flow (#27103)
Release Notes:

- N/A

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
2025-03-19 19:00:21 +00:00
Marshall Bowers
68262fe7e4 theme: Add fallback colors for version_control.<variant> properties (#27104)
This PR adds fallback colors for the `version_control.<variant>` theme
properties.

This fixes the colors when themes do not provide the properties.

Related to  https://github.com/zed-industries/zed/pull/26951.

Release Notes:

- Added fallback colors for the `version_control.<variant>` theme
properties.
2025-03-19 18:48:35 +00:00
Joseph T. Lyons
2491426be7 Fix release notes API call with heredoc syntax (#27096)
Release Notes:

- N/A
2025-03-19 13:50:46 -04:00
Marshall Bowers
4487dc1064 assistant2: Add a button to open the extensions view to install more context servers (#27095)
This PR adds a new button in the Assistant configuration view to open
the extensions view pre-filtered to extensions that provide context
servers.


https://github.com/user-attachments/assets/3bc77507-c8b8-4bc6-8a17-ab5d8b3b7c8a

Release Notes:

- N/A
2025-03-19 17:48:42 +00:00
Piotr Osiewicz
e03edc2a76 debugger: Do not allow setting breakpoints in buffers without file storage (#27094)
Closes #ISSUE

Release Notes:

- N/A
2025-03-19 18:40:31 +01:00
Marshall Bowers
d722067000 extensions_ui: Add ability to open the extensions view with a pre-selected filter (#27093)
This PR adds the ability to open the extensions view via the `zed:
extensions` action with a pre-selected filter.

The "Install Themes" and "Install Icon Themes" buttons in their
respective selectors take advantage of this to set the filter when
opening the view:


https://github.com/user-attachments/assets/2e345c0f-418a-47b6-811e-cabae6c616d1

Release Notes:

- N/A
2025-03-19 17:26:46 +00:00
Kirill Bulatov
d51cd15e4d Remove an unused field in Diagnostic from zed.proto (#27091)
Release Notes:

- N/A
2025-03-19 17:15:43 +00:00
loczek
ef14bc8e76 docs: Add better snippets documentation (#26853)
Improved snippets docs

Release Notes:

- N/A
2025-03-19 18:05:05 +01:00
Marshall Bowers
9fe243efa5 gpui: Update doc comment for App::new (#27089)
This PR updates the doc comment for the `App::new` method.

Release Notes:

- N/A
2025-03-19 16:51:19 +00:00
Max Brunsfeld
74a39c7263 Make FakeGitRepository behave more like a real git repository (#26961)
This PR reworks the `FakeGitRepository` type that we use for testing git
interactions, to make it more realistic. In particular, the `status`
method now derives the Git status from the differences between HEAD, the
index, and the working copy. This way, if you modify a file in the
`FakeFs`, the Git repository's `status` method will reflect that
modification.

Release Notes:

- N/A

---------

Co-authored-by: Junkui Zhang <364772080@qq.com>
2025-03-19 16:04:27 +00:00
Agus Zubiaga
5f398071b2 assistant2: Skip tool uses without a matching tool result (#27082)
Anthropic API doesn't allow `tool_use` messages without a corresponding
`tool_result`, so we'll skip those when building a request. I'll
separately investigate why we are sending request before the tool result
as that might lead to separate issues, but that might take a while and
this is currently very frustrating.

Release Notes:

- N/A
2025-03-19 15:54:57 +00:00
Marshall Bowers
410a942d57 assistant2: Add ability to start and stop context servers (#27080)
This PR adds the ability to start and stop context servers from within
the configuration view in the Assistant panel:


https://github.com/user-attachments/assets/93c3a7cb-d799-4286-88ba-c13cc26e959a

Release Notes:

- N/A
2025-03-19 15:37:48 +00:00
Joseph T. Lyons
06ffdc6791 Bump Zed to v0.180 (#27083)
Release Notes:

-N/A
2025-03-19 11:33:30 -04:00
Marshall Bowers
394215599a assistant2: Fix broken merge (#27081)
This PR fixes a broken merge caused by
https://github.com/zed-industries/zed/pull/26987 landing after
https://github.com/zed-industries/zed/pull/26758.

Release Notes:

- N/A
2025-03-19 15:26:19 +00:00
Richard Feldman
e8a40085de Allow tools to read unsaved buffers (#26987)
If the tool asks to read a path, we don't need to verify whether that
path exists on disk; an unsaved buffer with that path is fine.

Release Notes:

- N/A
2025-03-19 14:59:10 +00:00
Richard Feldman
6303751325 Record token usage telemetry (#26962)
<img width="1103" alt="Screenshot 2025-03-17 at 9 47 32 PM"
src="https://github.com/user-attachments/assets/947cf33d-4464-4305-8ff0-3630529d2f81"
/>


Release Notes:

- N/A
2025-03-19 10:47:46 -04:00
Antonio Scandurra
3edf930007 Revert "Start tracking edits performed by the agent" (#27077)
Reverts zed-industries/zed#27064
2025-03-19 15:33:08 +01:00
Jakub Čermák
584a70ca5e Refactor Git panel styling & status colors for consistency (#26951)
Closes #26847

Release Notes:

- Updated Git panel background to use panel_background instead of
ElevationIndex::Surface.bg(cx) for consistency with other panels.
- Removed redundant GitStatusColors struct from status.rs and refactored
to use existing theme colors.
- Adjusted Color enum mappings in color.rs to reference
version_control_* colors instead of status() for better alignment with
the theme system.
- Cleaned up unused or redundant code.
2025-03-19 10:26:36 -04:00
Smit Barmase
2230f3b09d editor: Preserve expand excerpt down button position (#27058)
When you press the "Expand Excerpt Down" button, the editor will scroll
up by the same amount to keep the button in same place. This allows you
to expand the excerpt rapidly without moving your mouse.

Before:


https://github.com/user-attachments/assets/376350ac-6f21-4ce0-a383-b2c9ca4f45bb

After:


https://github.com/user-attachments/assets/4fba4173-5f01-4220-990a-65820ac40cf5

Release Notes:

- Improved "Expand Excerpt Down" so the button stays in place, allowing
rapid expansion without moving the mouse.
2025-03-19 19:54:52 +05:30
5brian
84a8d48178 vim: Fix space not handling non-ascii characters (#27053)
Closes #26806

Changes: Clips the new point with `Bias::Right` like in
`saturating_right`

Release Notes:

- vim: Fixed `space` not handling non-ascii characters
2025-03-19 07:28:50 -06:00
Antonio Scandurra
ac5dafc6b2 Start tracking edits performed by the agent (#27064)
Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-03-19 13:07:25 +00:00
Piotr Osiewicz
23686aa394 debugger: Do not use Disclosure for attach button (#27068)
Closes #ISSUE

Release Notes:

- N/A
2025-03-19 14:01:33 +01:00
Danilo Leal
3874d315ec assistant2: Adjust text and padding alignment between messages (#27067)
Ensuring that text between the "you" messages align with text in the
assistant response. This also creates a nice subtle hierarchy effect
where the "you" message card is wider than the message, making it
slightly easier to tell them apart.

<img
src="https://github.com/user-attachments/assets/616c1776-ca51-454e-9d52-e480bf26c843"
width="600px" />

Release Notes:

- N/A
2025-03-19 09:43:33 -03:00
Agus Zubiaga
1d33bfde37 assistant edit tool: Replace with flexible indentation (#27039)
Sometimes the model produces SEARCH queries that don't match the
indentation of the source file exactly.

When we can't find an exact match, we'll now attempt to match the lines
while being more flexible about the leading whitespace as long as all
lines are consistently offset from the source, and extend the leading
whitespace in the REPLACE string accordingly.

Release Notes:

- N/A
2025-03-19 09:39:00 -03:00
Piotr Osiewicz
9377ef9817 feature_flags: Do not enable feature flags by default in dev builds (#27065)
Closes #ISSUE

Release Notes:

- N/A
2025-03-19 12:20:26 +00:00
Piotr Osiewicz
c3b5046347 editor: Do not use breakpoint color for run indicators (#27063)
Closes #ISSUE

Release Notes:

- N/A
2025-03-19 11:54:14 +00:00
Piotr Osiewicz
44fff08ed6 util: Include path to asset in panic message from asset_str (#27059)
Somebody on Discord ran into issues with running the debugger which goes
down to an unwrap in asset_str. Let's print a path that was accessed.

Release Notes:

- N/A
2025-03-19 11:09:51 +00:00
Anthony Eid
d4daa0a3a2 Show debug console evaluation response (#27050)
We weren't incrementing the output token when getting responses from the
debug evaluation request which caused some output to not be displayed.
(Usually the evaluation response, but that could cascade into other
output events not showing)


Release Notes:
- N/A

Co-authored-by: Remco Smits <djsmits12@gmail.com>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-03-19 05:37:32 +00:00
Conrad Irwin
81582cd7f3 Don't render breakpoint indicators on top of expand arrows (#27048)
Closes #ISSUE

cc @Anthony-Eid. One thing I noticed while doing this is that we do an
invalid cast here from DisplayPoint.row to MultiBufferRow. These are not
the same if you have soft-wrap enabled (or anything else in the display
map that's not in the editor).

Release Notes:

- N/A
2025-03-19 05:00:41 +00:00
Ryan Hawkins
0f5a3afe94 Support built-in Zed prompts for all platforms (#26201)
This pull request does two things:

1. Adds a setting to force Zed to use the built-in prompts, instead of
the system provided ones. I've personally found the system prompts on
macOS often fail to respond to keyboard input, are slow to render
initially, and don't match Zed's style.
2. Makes the previously Linux-only Zed provided prompts available to
everybody using the above setting.

Release Notes:
- Added support for a built-in prompting system, regardless of platform.
Use the new `use_system_prompts` setting to control whether to use the
system provided prompts or Zed's built-in system. Note that on Linux,
this setting has no effect, as Linux doesn't have a system prompting
mechanism.
2025-03-18 22:27:09 -06:00
CharlesChen0823
382f9f6151 language_tools: Fix buffer search keeping focusing when pressing enter in vim mode (#26266)
Closes #25643 

Release Notes:

- Fixed buffer search keep focus when pressing enter in vim mode

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-03-19 04:25:29 +00:00
5brian
15d2420031 workspace::Open: Fix trapped cursor/selection on update (#25402)
Closes #ISSUE

Issue: Selection index does not reset when the matches update, which can
lead to the selection getting trapped when that index does not exist in
the next matches.


https://github.com/user-attachments/assets/d3fab23f-750c-47fb-bd3b-a0c42f214c83

This is in workspace::Open with   "use_system_path_prompts": false

Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-03-18 22:19:11 -06:00
CharlesChen0823
026c7274d9 workspace: Add function to save new file in directory nearest tab (#22563)
Closes #15685

Release Notes:

- save new file in directory neasrest tab

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-03-19 03:41:04 +00:00
Mikayla Maki
1aefa5178b Move "async move" a few characters to the left in cx.spawn() (#26758)
This is the core change:
https://github.com/zed-industries/zed/pull/26758/files#diff-044302c0d57147af17e68a0009fee3e8dcdfb4f32c27a915e70cfa80e987f765R1052

TODO:
- [x] Use AsyncFn instead of Fn() -> Future in GPUI spawn methods
- [x] Implement it in the whole app
- [x] Implement it in the debugger 
- [x] Glance at the RPC crate, and see if those box future methods can
be switched over. Answer: It can't directly, as you can't make an
AsyncFn* into a trait object. There's ways around that, but they're all
more complex than just keeping the code as is.
- [ ] Fix platform specific code

Release Notes:

- N/A
2025-03-19 02:09:02 +00:00
João Marcos
7f2e3fb5bd Fix git stage race condition with delayed fs events (#27036)
This PR adds a failing test `test_staging_hunks_with_delayed_fs_event`
and makes it pass

Also skips a queued read for git diff states if another read was
requested (less work)

This still doesn't catch all race conditions, but the PR is getting long
so I'll yield this and start another branch

Release Notes:

- N/A
2025-03-18 22:44:36 -03:00
Agus Zubiaga
68a572873b assistant edit tool: Improve bad search output (#27012)
When we failed to match a search string, we were reporting the replace
string as not found, this confuses the model and can make it go into a
doom loop. This PR fixes that improves the error output in general to
help it recover faster.

Release Notes:

- N/A
2025-03-18 21:53:20 -03:00
Piotr Osiewicz
c042a02cf4 debugger: First slight pass at UI (#27034)
- Collapse Launch and Attach into a single split button
- Fix code actions indicator being colored red.

Release Notes:

- N/A
2025-03-19 00:15:48 +00:00
Julia Ryan
73ac3d9a99 nix: Fix LDFLAGS rpath (#26912)
By default stdenv strips all unused rpaths, but we use a few libraries
that are `dlopen`'d so we need to stop it from removing those. The
[`dontPatchELF`
flag](https://ryantm.github.io/nixpkgs/stdenv/stdenv/#var-stdenv-dontPatchELF)
disables that and makes the nix build work on wayland again.

Fix #26905
Close #26864

Release Notes:

- N/A
2025-03-18 17:04:27 -07:00
Peter Tripp
2269f996f7 Add more shortcuts for delete/restore in Git Panel (#27004)
Release Notes:

- N/A
2025-03-18 18:52:28 -04:00
Marshall Bowers
e9033a75ac assistant2: Remove unneeded debug logging (#27030)
This PR removes the debug logging added in
https://github.com/zed-industries/zed/pull/23722, as we no longer need
it.

Release Notes:

- N/A
2025-03-18 22:12:04 +00:00
Marshall Bowers
a2ae6a1c77 assistant2: Add tool lists for each context server (#27029)
This PR updates the list of context servers with the ability to view the
tools provided by the context server:

<img width="1394" alt="Screenshot 2025-03-18 at 5 53 05 PM"
src="https://github.com/user-attachments/assets/4ffe93dd-f9e9-44e7-877f-656ebf45a326"
/>

Release Notes:

- N/A
2025-03-18 22:04:47 +00:00
Jason Lee
985ac4e5f2 gpui: Reduce window.refresh to improve cache hit of the cached views (#25009)
Release Notes:

- Improved performance when using the scroll wheel and some other mouse
interactions.

Based on some cache details about GPUI `AnyView::cached` that I found in
the discussion of
https://github.com/zed-industries/zed/discussions/24260#discussioncomment-12135749,
and combined with the optimization points found in actual applications.

This change may have some scenarios that I have not considered, so I
just make a draft to put forward my ideas first for discussion.

From my analysis, `AnyView::cached` will always invalid by Div's mouse
events, because of it called `window.refresh`. I understand that (mouse
move event) this is because the interface changes related to hover and
mouse_move will be affected style, so `window.refresh` is required.
Since Div does not have the `entity_id` of View, it is impossible to
know which View should be refreshed, so the entire window can only be
refreshed.

With this change, we can reduce a lot of `render` method calls on
ScrollWheel or Mouse Event.
2025-03-18 14:52:20 -07:00
Kirill Bulatov
89ae4ca9a3 Fix debugger docs a bit (#27026)
Tried adding a custom debugging tasks to discover two more required
properties missing from the docs.

Release Notes:

- N/A
2025-03-18 21:46:11 +00:00
Marshall Bowers
1d4afe6daa assistant2: Add context server list to configuration view (#27028)
This PR adds a context server list to the configuration view in
Assistant2:

<img width="1394" alt="Screenshot 2025-03-18 at 5 26 23 PM"
src="https://github.com/user-attachments/assets/58bf3920-1e35-4cb8-a32a-5ae9f98ce387"
/>

Release Notes:

- N/A
2025-03-18 21:41:39 +00:00
Joseph T. Lyons
777c88bcea Clean up community_release_actions file (#27027)
Release Notes:

- N/A
2025-03-18 21:29:22 +00:00
Kirill Bulatov
959a024861 Omit json-language-server from the scope_opt_in_language_servers (#27023)
Follow-up of https://github.com/zed-industries/zed/pull/26574/files

After that PR, settings.json stopped giving completions when `"` was
typed as a key:

https://github.com/user-attachments/assets/5ff03863-024c-4c28-a7cd-8ef48a1695d8

This goes down to 


fb12863999/crates/language/src/language.rs (L1736-L1748)

which was empty before the PR, hence leading to lower `true` branch.
Now, when typing `"`, there's no scope according to 


fb12863999/crates/project/src/lsp_store.rs (L4529-L4532)

return result.

Removing `json-language-server` from `scope_opt_in_language_servers`
seems to preserve the `:` fix and restore the completions behavior.


Release Notes:

- N/A
2025-03-18 21:08:43 +00:00
Joseph T. Lyons
ed510b5e93 Remove unused AssistantThreadFeedback event (#27021)
It looks like:

- https://github.com/zed-industries/zed/pull/26780

accidentally added a new event type, `AssistantThreadFeedback`, using
the old event system, that it didn't end up actually using, as the code
actually relies on using the newer (preferred) `telemetry::event!()`.

Release Notes:

- N/A
2025-03-18 20:39:54 +00:00
Peter Tripp
674c572a28 ci: Run stalebot checks multiple times to ensure completion (#27017)
Stalebot has a maximum operations-per-run which is set at 1000. As a
result it may require multiple runs to successfully complete.

This morning it took [three
runs](https://github.com/zed-industries/zed/actions/runs/13921563707/attempts/1)
so set it to run three times two hours apart to avoid hitting github API
limits.

Release Notes:

- N/A
2025-03-18 16:34:24 -04:00
Martin Fischer
4a39fc2644 gpui: Provide workaround for AMD Linux driver bug (#26890)
There apparently is some amdgpu/radv bug that rendering with
multisample anti-aliasing (MSAA) results in a crash when the bounds
of a triangle list exceed 1024px, which in Zed happens with the default
buffer font size when you select a line with more than 144 characters.

This crash has been reported as #26143.

This commit introduces a workaround: you can set the
ZED_PATH_SAMPLE_COUNT=0
environment variable to disable MSAA and the error message we print
when a GPU crash is encountered with radv now suggests trying this
environment
variable as a workaround and links the respective issue.

Sidenote: MSAA was introduced in
f08b1d78ec
so you didn't run into this driver bug with versions < 0.173.8.

Release Notes:

- Added a workaround for an AMD Linux driver bug that causes Zed to
crash when selecting long lines.
2025-03-18 20:11:09 +00:00
Agus Zubiaga
48fe134408 assistant edit tool: Create file when search/replace is empty (#27009)
We used to fail when this happened, but we saw the model use it as a way
to create empty files, which makes sense.

Release Notes:

- N/A
2025-03-18 18:35:11 +00:00
Cole Miller
22b8662275 Fix syntax highlighting of git commit messages (#26988)
- Load syntax colors into commit message editors
- Fix name mismatches that were preventing the git commit grammar and
language config from being matched up

Release Notes:

- Fixed git commit messages not being syntax-highlighted
2025-03-18 18:18:56 +00:00
Marshall Bowers
cc36cd9768 extensions_ui: Add ability to filter extensions by category (#27005)
This PR adds the ability to filter the list of extensions by category:


https://github.com/user-attachments/assets/ea7b518e-4769-4e2e-8bbe-e75f9f01edf9

Release Notes:

- Added the ability to filter the list of extensions by category.
2025-03-18 17:59:58 +00:00
KyleBarton
628a61d929 docs: Specify the command for activating prompt library from the command palette (#27007)
Quickfix of the docs as I read through and get familiar with the
assistant interface.
`prompt-library: toggle` does not appear to be a live command in
`cmd-shift-p` - instead I see `assistant: deploy prompt library`. This
change to the docs reflects that. It also notes that this command can
only be activated from within the assistant panel (the command is not
accessible from a standard editor panel).

Release Notes:

- N/A
2025-03-18 13:56:22 -04:00
Cole Miller
7f23875c5e Fold git merge messages into commit editor placeholder text (#26992)
This PR changes the git commit message editors to surface git's
suggested merge message, if any, as placeholder text, as opposed to
"real" buffer text as was previously the case.

Release Notes:

- Changed git commit message editors to use placeholder text for git's
suggested merge messages
2025-03-18 17:21:20 +00:00
Cole Miller
e7bba1c252 Improvements to interactive hard wrap behavior (#26953)
Release Notes:

- Fixed involuntary joining of lines when typing in the commit message
editor
- Fixed being unable to type whitespace after a comment character at the
start of a line in the commit message editor
2025-03-18 17:05:08 +00:00
Remco Smits
41a60ffecf Debugger implementation (#13433)
###  DISCLAIMER

> As of 6th March 2025, debugger is still in development. We plan to
merge it behind a staff-only feature flag for staff use only, followed
by non-public release and then finally a public one (akin to how Git
panel release was handled). This is done to ensure the best experience
when it gets released.

### END OF DISCLAIMER 

**The current state of the debugger implementation:**


https://github.com/user-attachments/assets/c4deff07-80dd-4dc6-ad2e-0c252a478fe9


https://github.com/user-attachments/assets/e1ed2345-b750-4bb6-9c97-50961b76904f

----

All the todo's are in the following channel, so it's easier to work on
this together:
https://zed.dev/channel/zed-debugger-11370

If you are on Linux, you can use the following command to join the
channel:
```cli
zed https://zed.dev/channel/zed-debugger-11370 
```

## Current Features

- Collab
  - Breakpoints
    - Sync when you (re)join a project
    - Sync when you add/remove a breakpoint
  - Sync active debug line
  - Stack frames
    - Click on stack frame
      - View variables that belong to the stack frame
      - Visit the source file
    - Restart stack frame (if adapter supports this)
  - Variables
  - Loaded sources
  - Modules
  - Controls
    - Continue
    - Step back
      - Stepping granularity (configurable)
    - Step into
      - Stepping granularity (configurable)
    - Step over
      - Stepping granularity (configurable)
    - Step out
      - Stepping granularity (configurable)
  - Debug console
- Breakpoints
  - Log breakpoints
  - line breakpoints
  - Persistent between zed sessions (configurable)
  - Multi buffer support
  - Toggle disable/enable all breakpoints
- Stack frames
  - Click on stack frame
    - View variables that belong to the stack frame
    - Visit the source file
    - Show collapsed stack frames
  - Restart stack frame (if adapter supports this)
- Loaded sources
  - View all used loaded sources if supported by adapter.
- Modules
  - View all used modules (if adapter supports this)
- Variables
  - Copy value
  - Copy name
  - Copy memory reference
  - Set value (if adapter supports this)
  - keyboard navigation
- Debug Console
  - See logs
  - View output that was sent from debug adapter
    - Output grouping
  - Evaluate code
    - Updates the variable list
    - Auto completion
- If not supported by adapter, we will show auto-completion for existing
variables
- Debug Terminal
- Run custom commands and change env values right inside your Zed
terminal
- Attach to process (if adapter supports this)
  - Process picker
- Controls
  - Continue
  - Step back
    - Stepping granularity (configurable)
  - Step into
    - Stepping granularity (configurable)
  - Step over
    - Stepping granularity (configurable)
  - Step out
    - Stepping granularity (configurable)
  - Disconnect
  - Restart
  - Stop
- Warning when a debug session exited without hitting any breakpoint
- Debug view to see Adapter/RPC log messages
- Testing
  - Fake debug adapter
    - Fake requests & events

---

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Anthony <anthony@zed.dev>
Co-authored-by: Piotr Osiewicz <peterosiewicz@gmail.com>
Co-authored-by: Piotr <piotr@zed.dev>
2025-03-18 12:55:25 -04:00
Marshall Bowers
ed4e654fdf assistant_tools: Add fetch tool (#26999)
This PR adds a new `fetch` tool to the set of tools the Assistant has
available.

This tool accepts a URL and fetches the content as Markdown.

<img width="1394" alt="Screenshot 2025-03-18 at 11 52 21 AM"
src="https://github.com/user-attachments/assets/e5bcde14-a0dd-4835-9d42-8f45def68f4d"
/>

<img width="1394" alt="Screenshot 2025-03-18 at 11 52 37 AM"
src="https://github.com/user-attachments/assets/3bcce4f5-f61b-40d7-8b30-2c673ce3c06a"
/>

Release Notes:

- N/A
2025-03-18 16:25:51 +00:00
Cole Miller
baaafddbeb worktree: Fix tracking of git status scans and re-enable tests (#26926)
Closes #ISSUE

Release Notes:

- N/A
2025-03-18 12:23:46 -04:00
Marshall Bowers
b70f21c08d assistant_tools: Rename RegexSearchTool module to match the others (#27001)
This PR renames the `RegexSearchTool` module to `regex_search_tool.rs`
to match the other tools.

Release Notes:

- N/A
2025-03-18 16:20:15 +00:00
Agus Zubiaga
5615be51cc assistant edit tool: Revert fuzzy matching (#26996)
#26935 is leading to bad edits, so let's revert it for now. I'll bring
back a version of this, but it'll likely just focus on indentation
instead of making the whole search fuzzy.

Release Notes: 

- N/A
2025-03-18 13:08:09 -03:00
Richard Feldman
06e9f0e309 Paginate regex and path search tools (#26997)
<img width="630" alt="Screenshot 2025-03-18 at 10 50 17 AM"
src="https://github.com/user-attachments/assets/0aee5367-402a-405a-8676-f2f8af425b1e"
/>

Release Notes:

- N/A
2025-03-18 15:44:41 +00:00
Marshall Bowers
41a2be7e54 assistant2: Keep the tool selector open when toggling tools (#26994)
This PR makes it so the tool selector will stay open when toggling tools
instead of closing after each selection:


https://github.com/user-attachments/assets/eb987785-cfb5-4b07-8d63-510fbd9d9bf1

This involved making a change to `ContextMenu` to allow it to rebuild
its menu items after each confirmation in order for them to reflect
their selected/unselected status. I intend to clean up the `ContextMenu`
API a bit at a later point, but that is out of scope for this PR.

Release Notes:

- N/A
2025-03-18 15:30:05 +00:00
Cole Miller
e38ae423f1 Add missing commit event reporting (#26990)
cc @morgankrey 

Release Notes:

- N/A
2025-03-18 14:52:32 +00:00
Peter Tripp
68bb3bd5eb Add more shortcuts for editor::OrganizeImports (#26932)
Follow-up to:
- https://github.com/zed-industries/zed/pull/25793

Release Notes:

- N/A
2025-03-18 10:51:12 -04:00
Richard Feldman
122e73f152 Allow read-file tool to read a subset of a file (#26966)
Release Notes:

- N/A
2025-03-18 10:03:15 -04:00
Smit Barmase
4b775505f5 migrator: Fix case where users see migration banner despite no diff changes (#26982)
Fixes edge case where after carrying out all migrations if final text is
same as existing text, we don't need to ask user to do anything, despite
migrations edits are being applied internally. E.g. A -> B - > C -> A

Release Notes:

- N/A
2025-03-18 19:24:24 +05:30
Marshall Bowers
a9f7c0549c docs: Use correct name for Intelephense license file (#26986)
This PR updates the Intelephense section of the PHP docs to use the
correct name for the license file.

Intelephense uses British English:

<img width="1185" alt="Screenshot 2025-03-18 at 8 30 20 AM"
src="https://github.com/user-attachments/assets/a675e854-bedf-4f70-bf8f-90488d196242"
/>

Release Notes:

- N/A
2025-03-18 12:31:57 +00:00
Kirill Bulatov
ac617e278e Keep and filter word completions on input, if the menu is open (#26979)
Follow-up of https://github.com/zed-industries/zed/pull/26410

Release Notes:

- N/A
2025-03-18 13:19:32 +02:00
Bennet Bo Fenner
26f4b2a491 assistant2: Combine file & directory picker (#26975)
In the process of adding `@mentions` we realized that we do not want to
make a distinction between Files & Directories in the UI, therefore this
PR combines the File & Directory pickers into a unified version



https://github.com/user-attachments/assets/f3bf189c-8b69-4f5f-90ce-0b83b12dbca3

(Ignore the `@mentions`, they are broken also on main)

Release Notes:

- N/A
2025-03-18 09:49:25 +00:00
Sheik Althaf
fdcacb3849 typescript: Add highlighting for Angular inline components (#26553)
Closes #ISSUE

Release Notes:

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

Before
<img width="1004" alt="image"
src="https://github.com/user-attachments/assets/9a611e8d-e00e-4dc7-b4c9-bd76fec95525"
/>

After
<img width="936" alt="Image"
src="https://github.com/user-attachments/assets/b83d3309-1aab-492c-a2f1-c45cd19e6bcc"
/>
2025-03-18 09:27:48 +00:00
Michael Sloan
f61d3d28e0 Use futures::future::join_all instead of futures::stream in assistant_eval (#26974)
Release Notes:

- N/A
2025-03-18 08:22:18 +00:00
tidely
a5621662b2 Update to git2 0.20.1 (#26972)
Switch back to git2 releases after
https://github.com/rust-lang/git2-rs/pull/1120 got merged in 0.20.1

Release Notes:

- N/A
2025-03-18 10:05:15 +02:00
Michael Sloan
b6198ad516 Add Ord and PartialOrd impls for gpui entity types (#26968)
Motivation is to be able to use entities as TreeMap keys.

Release Notes:

- N/A

Co-authored-by: Nathan <nathan@zed.dev>
2025-03-18 06:20:21 +00:00
Conrad Irwin
5210d9e8b4 Tidier multibuffer (#26954)
Makes multibuffer headers less close to the top of the file.

Moves multibuffer line numbers one em to the right to make space for the
expand excerpt button on large line numbers.

Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-03-17 22:26:27 -06:00
Michael Sloan
1139904ef5 Remove unnecessary conditional definition of FS_WATCH_LATENCY (#26967)
This was added in #8343 to make it only visible for tests. #9189 then
made it visible regardless of `test-support`, so the definitions became
identical.

Release Notes:

- N/A
2025-03-17 22:10:16 -06:00
Joseph T. Lyons
b4ef3791bb Send stable release notes email (#26964)
Release Notes:

- N/A
2025-03-17 23:25:41 -04:00
Cole Miller
88907eeb38 git: Always zero panel's entry counts when clearing entries (#26924)
Keep the panel's state consistent even when we transition to having no
active repository.

Release Notes:

- N/A
2025-03-17 22:54:21 -04:00
Marshall Bowers
cd5d7e82d0 collab: Make account age-related fields required in LlmTokenClaims (#26959)
This PR makes the account age-related fields required in
`LlmTokenClaims`.

We've also removed the account age check from the LLM token issuance
endpoint, instead having it solely be enforced in the `POST /completion`
endpoint.

This change will be safe to deploy at ~8:01PM EDT.

Release Notes:

- N/A
2025-03-17 19:54:44 -04:00
Marshall Bowers
0851842d2c collab: Defer account age check to POST /completion endpoint (#26956)
This PR defers the account age check to the `POST /completion` endpoint
instead of doing it when an LLM token is generated.

This will allow us to lift the account age restriction for using Edit
Prediction.

Note: We're still temporarily performing the account age check when
issuing the LLM token until this change is deployed and the LLM tokens
have had a chance to cycle.

Release Notes:

- N/A
2025-03-17 22:42:29 +00:00
Marshall Bowers
1397e01735 collab: Clean up LLM token creation (#26955)
This PR cleans up the LLM token creation a bit.

We now pass in the entire list of feature flags to the
`LlmTokenClaims::create` method to prevent having a bunch of confusable
`bool` parameters.

Release Notes:

- N/A
2025-03-17 22:25:43 +00:00
Max Brunsfeld
2b2b9c1624 Make repo and branch popovers extend up from their trigger buttons (#26950)
Previously, when clicking on the branch, the popover would obscure the
button you just clicked, which was awkward.

Release Notes:

- Improved the placement of the repo and branch picker popovers in the
git panel.
- Added a 'SelectRepo' action that opens the repository selector in a
modal.
2025-03-17 15:05:17 -07:00
Agus Zubiaga
a05066cd83 assistant edit tool: Track read buffers and notify model of user edits (#26952)
When the model reads file, we'll track the version it read, and let it
know if the user makes edits to the buffer. This helps prevent edit
failures because it'll know to re-read the file before.

Release Notes:

- N/A
2025-03-17 21:50:16 +00:00
Smit Barmase
cb439e672d editor: Fix navigate back for locations opened via preview item (#26943)
Closes #25458

When navigating code from a preview tab with
`enable_preview_from_code_navigation` set to `true`, "Go Back" from a
newly opened tab could focus on the tab to the right instead of
returning to the original preview tab.

Before, we killed the existing preview tab before opening a new one,
which breaking history as the new tab had no reference to the old one.
This caused navigation to shift to the next tab on the right.

Now, we first add the new tab at the preview index, and then kill the
existing preview tab. This preserves the history by linking new preview
tab to existing tab.

Release Notes:

- Fixes an issue where navigating code from a preview tab with
`enable_preview_from_code_navigation` set to `true`, "Go Back" from a
newly opened tab could focus on the tab to the right instead of
returning to the original preview tab.
2025-03-18 00:59:36 +05:30
Mostafa Mahmoud
6b0a282c9c docs: Fix wrong html-like tags shortcut for Vim (#26792)
Release Notes:

- N/A
2025-03-17 12:40:07 -06:00
Conrad Irwin
25772b8777 Fix sticky header in last buffer of a multibuffer (#26944)
This also simplifies our code to stop generating a last excerpt boundary
that we always ignore.

Closes #ISSUE

Release Notes:

- N/A
2025-03-17 18:39:57 +00:00
Agus Zubiaga
94b63808e0 assistant edit tool: Fuzzy match search block (#26935)
Release Notes:

- N/A

Co-authored-by: Antonio Scandurra <me@as-cii.com>
2025-03-17 18:33:20 +00:00
Cole Miller
798af67dc1 Disable the other flaky tests (#26942)
I thought it might be just `test_file_status` this time, but it seems to
be all four of the tests that we were previously seeing issues with.

Release Notes:

- N/A
2025-03-17 18:10:42 +00:00
Anthony Eid
db1d2defa5 Sync git button states between project diff & git panel (#26938)
Closes #ISSUE

Release Notes:

- Git action buttons are now synced between the project diff and git
panel

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Piotr Osiewicz <peterosiewicz@gmail.com>
2025-03-17 14:08:32 -04:00
Max Brunsfeld
430bd83e4d Don't open the commit editor when staging last hunk (#26939)
Closes #26880

Release Notes:

- Removed a behavior where staging the last hunk in the project diff
would open the commit modal.
2025-03-17 10:58:04 -07:00
Cole Miller
dbe5399fc4 Remove disabling effect on the stage and unstage toolbar buttons (#26936)
Closes #26883

Release Notes:

- N/A
2025-03-17 13:48:04 -04:00
João Marcos
aba242d576 Document gutter_debounce (#26940)
Release Notes:

- N/A
2025-03-17 14:47:37 -03:00
Mikayla Maki
ddc210abfc Add website docs for the hunk_style variants (#26937)
Follow up to https://github.com/zed-industries/zed/pull/26816

Release Notes:

- N/A
2025-03-17 10:36:31 -07:00
Jakub Charvat
65994c0576 Add git.hunk_style setting for gutter hollow hunk behavior (#26816)
This is a follow up to #26809, introducing `git.hunk_style` setting to
control whether staged or unstaged hunks are shown as hollow.

Reused `GitHunkStyleSetting` which was left over from #26504.

Release Notes:

- Added `git.hunk_style` setting to control whether staged or unstaged
hunks are hollow.
2025-03-17 10:24:49 -07:00
João Marcos
011f823f33 Move buffer diff storage from BufferStore to GitStore (#26795)
Release Notes:

- N/A

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: max <max@zed.dev>
2025-03-17 17:02:32 +00:00
Marco Roth
3d1ae68f83 docs: Remove duplicate indent_guides setting in Project panel options (#26927)
Just a small docs pull request to remove the duplicate `indent_guides`
setting in the Project panel options section of the docs. There is also
another `indent_guides` on
[L2585](https://github.com/zed-industries/zed/pull/26927/files#diff-c2decf822f3085926bd23bdf175700222dfd8193d48ea39908d4bb1d1a7c6240R2584).
~~Let me know if you prefer to remove the other instance, thank you!~~

Edit: I just realized it's the old setting, `indent_guides` now expects
a object.
2025-03-17 16:22:39 +00:00
Agus Zubiaga
1f62274a89 assistant edit tool: Return applied actions back to main model (#26810)
We'll now include the search/replace block that got applied as part of
the tool output. We think this will help the model have a better idea of
how the file changed and prevent later edit failures.

Release Notes:

- N/A
2025-03-17 13:21:35 -03:00
Cole Miller
c2f62d261b Disable flaky file status test again (#26925)
Failure on an unrelated commit:
https://github.com/zed-industries/zed/actions/runs/13903012863/job/38899239052

Release Notes:

- N/A
2025-03-17 15:44:55 +00:00
khayyam
7d433a30ec git_hosting_providers: Allow configuring additional hosting providers via settings (#26879)
Release Notes:

- Added a new `git_hosting_providers` setting for configuring custom Git
hosting providers.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-03-17 15:39:52 +00:00
Max Brunsfeld
52567f4b72 Eliminate unnecessary macros in languages crate (#26813)
I vibe coded this in Zed, dawg.

This avoids a number of cases where we created multiple copies of the
same lsp adapter. Now we clone the Arcs.

Release Notes:

- N/A
2025-03-17 08:13:32 -07:00
Richard Feldman
a0ee84d3ac Use the main thread less on search tool (#26732)
Release Notes:

- N/A
2025-03-17 16:02:22 +01:00
Danilo Leal
6cac0b33dc docs: Add marker to signal which languages are built into Zed (#26913)
I saw over the weekend some social media posts that indicated people
didn't know which languages are included in Zed by default. We do say
that on each language-specific page, but I figured having this
high-level view on the languages page wouldn't hurt.

Release Notes:

- N/A
2025-03-17 11:27:52 -03:00
Marshall Bowers
45606abfdb git_hosting_providers: Refactor constructors (#26919)
This PR refactors the constructors for the various Git hosting providers
to facilitate adding support for more self-hosted variants.

Release Notes:

- N/A
2025-03-17 13:46:58 +00:00
Marshall Bowers
8ba6ce43ac git_hosting_providers: Fix incorrect name for SourceHut (#26915)
This PR fixes an issue where the SourceHut Git hosting provider was
using the wrong name.

Release Notes:

- N/A
2025-03-17 13:32:23 +00:00
Agus Zubiaga
040d42fc24 assistant tools: Fix running tests locally (#26914)
Without this, we running into the following error:

```
Running into this when running tests. Is this  
dyld[45041]: Library not loaded: @rpath/WebRTC.framework/WebRTC
  Referenced from: <B2EA63A5-994E-3FB0-A74B-C9C4F7E5C1EF> /Users/aguz/zed/zed/target/debug/deps/assistant_tools-522d7745dd439dfb
  Reason: no LC_RPATH's found
```

Thanks Piotr!

Release Notes:

- N/A
2025-03-17 13:32:19 +00:00
Julia Ryan
22d905dc03 nix: Allow auto-update in the devshell (#26911)
Our direnv integration was making zed refuse to auto-update when you had
the zed repo open with the devshell active. This was happening even when
you used a non-nix build of zed, which actually should be able to
auto-update.

I'm a bit unsure of why we check for the `ZED_UPDATE_EXPLANATION` env
var [both at build time _and_ at
runtime](2828dcb67b/crates/auto_update/src/auto_update.rs (L149)),
but I can see an argument for why people might want that so I'll just do
the less intrusive change for now and leave the var out of the devshell.

Release Notes:

- N/A
2025-03-17 04:49:56 -07:00
0x2CA
bf735da3f2 Support extended keys on Mac (F20-F35) (#26899)
Closes #4640

About the support limit of Fn:

Mac F1-F35
Win F1-F24
Linux F1-F35
Terminal F1-F20

Release Notes:

- Improved support for extended keyboards on Mac (F20-F35)
2025-03-17 12:56:46 +02:00
Antonio Scandurra
210d8d5530 Allow cancellation of tool uses (#26906)
Release Notes:

- N/A
2025-03-17 09:53:18 +00:00
Color Fuzzy
a0f995d2ae Support SSH usernames which contain @ symbols (#25314)
Closes #25246

Release Notes:

- SSH: Improved handling of multiple `@` in connection strings: e.g.
`ssh jim.lv@es2@10.220.67.57@11.239.1.231` improving support of jump
hosts running JumpServer.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-03-17 05:10:21 +00:00
Piotr Osiewicz
8f560daec2 chore: Extract http-client-tls crate (#26895)
http-client pulled in rustls which in turn meant that gpui depended on
rustls/aws-lc-sys. This commit extracts http-client-tls crate to
separate the http-client and tls dependencies.

Closes #ISSUE

Release Notes:

- N/A
2025-03-17 02:36:37 +00:00
Richard Feldman
d5bb12631a Delete tool uses paths instead of globs (#26715)
Also made `run` avoid doing work on the main thread.

Release Notes:

- N/A
2025-03-16 11:58:25 +01:00
Kirill Bulatov
8a31dcaeb0 Use textDocument/codeLens data in the actions menu when applicable #2 (#26848)
Re-applies what's been reverted in
https://github.com/zed-industries/zed/pull/26832 with an action-related
fix in
64b5d37d32

Before, actions were resolved only if `data` is present and either of
the possible fields is empty:

e842b4eade/crates/project/src/lsp_store.rs (L1632-L1633)

But Zed resolves completions and inlays once, unconditionally, and the
reverted PR applied the same strategy to actions.
That did not work despite the spec not forbidding `data`-less actions to
be resolved.

Soon, it starts to work due to
https://github.com/rust-lang/rust-analyzer/pull/19369 but it seems safer
to restore the original filtering code.

Code lens have no issues with `data`-less resolves:

220d913cbc/crates/rust-analyzer/src/handlers/request.rs (L1618-L1620)

so the same approach as completions and inlays is kept: resolve once.


Release Notes:

- N/A
2025-03-15 20:09:32 +00:00
Michael Sloan
ef91e7afae Minor optimization of line number length logic (#26845)
In `layout_excerpt_gutter`, compute max line number length once instead
of for every row

In `max_line_number_width`, use ilog10 instead of converting to floats
and back

Release Notes:

- N/A
2025-03-15 19:28:50 +00:00
Michael Sloan
c220fb387d Fix panic when providing 0 to ilog10 in line number length logic (#26844)
Introduced in #24428

Release Notes:

- N/A
2025-03-15 19:10:54 +00:00
Smit Barmase
adbde210fd termina: Fix text selection for first line scrolls up (#26842)
Closes #21626
 
Now scroll will only happen when cursor goes beyond the bounds of
terminal.
 
 Before:
 


https://github.com/user-attachments/assets/9ac48e80-d0e0-44c9-87ad-14ed748de78d


 After:


https://github.com/user-attachments/assets/c697c1fc-a6d2-4b9a-aad4-5b0c79837c2a
 
Release Notes:

- Fixed an issue where selecting the first line in the terminal would
cause it to scroll.
2025-03-15 22:33:16 +05:30
Jason Lee
b81a1ad91d gpui: Fix text underline width (#26827)
Release Notes:

- N/A 

Fix #24721 mistake to make sure underline width same as the text.

## Before


![image](https://github.com/user-attachments/assets/1fe6a8c2-517f-41be-bdf0-0ee777b7f8aa)

## After

<img width="912" alt="image"
src="https://github.com/user-attachments/assets/222b5dcb-c0fb-4ec1-8e23-d68247621375"
/>
2025-03-15 09:18:11 -07:00
Peter Tripp
5f390f1bf8 Initial PyLSP documentation (#26835)
Closes https://github.com/zed-industries/zed/issues/26820

Release Notes:

- N/A
2025-03-15 11:03:35 -04:00
Richard Hao
c282acbe65 terminal: Don’t include line breaks for soft wrap in Assistant terminal context (#25415)
> Detects and combines wrapped lines into single logical lines, more
accurately representing the actual terminal content.


```shell
perl -i -pe \
    's/"vscode-languageserver(\/node)?"/"\@zed-industries\/vscode-languageserver$1"/g' packages/css/lib/node/cssServerMain.js
```

<img width="518" alt="image"
src="https://github.com/user-attachments/assets/52d9327c-c381-4e5f-a676-0cf84c824388"
/>

<img width="1314" alt="image"
src="https://github.com/user-attachments/assets/0a32e1f9-7e95-482e-9beb-2e8a6c40584c"
/>




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

Release Notes:

- Fixed a bug where context for the terminal assistant would add line
breaks in the presence of soft wrapped lines.

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-03-15 14:28:26 +00:00
Marshall Bowers
021d6584cc Revert "Use textDocument/codeLens data in the actions menu when applicable (#26811)" (#26832)
This reverts commit b61171f152.

This PR reverts #26811, as it has broken `rust-analyzer` code actions.

With this commit reverted my code actions are working again. 

Release Notes:

- Community: Reverted https://github.com/zed-industries/zed/pull/26811.
2025-03-15 14:14:29 +00:00
Marshall Bowers
b547cd1c70 ci: Remove migration_checks as a required check (#26833)
This PR removes the `migration_checks` job as a required check.

This was not required before, and we shouldn't make it required, as
there are cases where we need to bypass it, as is the case in
https://github.com/zed-industries/zed/pull/26832.

Release Notes:

- N/A
2025-03-15 13:59:25 +00:00
张小白
8f841d1ab7 Revert unintended Cargo.lock changes (#26830)
This PR reverts some of the changes made to `Cargo.lock` in #25702. In
that PR, several crate versions were unintentionally downgraded,
including `aws-lc-rs`, which has caused release builds to fail on
Windows again.

Release Notes:

- N/A
2025-03-15 21:08:55 +08:00
Jason Lee
4b153e7f7f gpui: Fix line_through, underline position when used text center or right (#24721)
Release Notes:

- N/A

---

| Before | After |
| --- | --- |
| <img width="912" alt="image"
src="https://github.com/user-attachments/assets/0640ac85-ee5d-4707-b866-997e36608c18"
/> | <img width="912" alt="image"
src="https://github.com/user-attachments/assets/caf84477-a7bc-4c22-a9e6-f44c3b6f86ef"
/> |
 
And fix the `line_through` doc link.
2025-03-15 11:44:51 +02:00
Kirill Bulatov
b61171f152 Use textDocument/codeLens data in the actions menu when applicable (#26811)
Similar to how tasks are fetched via LSP, also queries for document's
code lens and filters the ones with the commands, supported in server
capabilities.

Whatever's left and applicable to the range given, is added to the
actions menu:


![image](https://github.com/user-attachments/assets/6161e87f-f4b4-4173-8bf9-30db5e94b1ce)

This way, Zed can get more actions to run, albeit neither r-a nor vtsls
seem to provide anything by default.

Currently, there are no plans to render code lens the way as in VSCode,
it's just the extra actions that are show in the menu.

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

As part of the attempts to use rust-analyzer LSP data about the
runnables, I've explored a way to get this data via standard LSP.

When particular experimental client capabilities are enabled (similar to
how clangd does this now), r-a starts to send back code lens with the
data needed to run a cargo command:

```
{"jsonrpc":"2.0","id":48,"result":{"range":{"start":{"line":0,"character":0},"end":{"line":98,"character":0}},"command":{"title":"▶︎ Run Tests","command":"rust-analyzer.runSingle","arguments":[{"label":"test-mod tests::ecparser","location":{"targetUri":"file:///Users/someonetoignore/work/ec4rs/src/tests/ecparser.rs","targetRange":{"start":{"line":0,"character":0},"end":{"line":98,"character":0}},"targetSelectionRange":{"start":{"line":0,"character":0},"end":{"line":98,"character":0}}},"kind":"cargo","args":{"environment":{"RUSTC_TOOLCHAIN":"/Users/someonetoignore/.rustup/toolchains/1.85-aarch64-apple-darwin"},"cwd":"/Users/someonetoignore/work/ec4rs","overrideCargo":null,"workspaceRoot":"/Users/someonetoignore/work/ec4rs","cargoArgs":["test","--package","ec4rs","--lib"],"executableArgs":["tests::ecparser","--show-output"]}}]}}}
```

This data is passed as is to VSCode task processor, registered in


60cd01864a/editors/code/src/main.ts (L195)

where it gets eventually executed as a VSCode's task, all handled by the
r-a's extension code.

rust-analyzer does not declare server capabilities for such tasks, and
has no `workspace/executeCommand` handle, and Zed needs an interactive
terminal output during the test runs, so we cannot ask rust-analyzer
more than these descriptions.

Given that Zed needs experimental capabilities set to get these lens:

60cd01864a/editors/code/src/client.ts (L318-L327)

and that the lens may contain other odd tasks (e.g. docs opening or
references lookup), a protocol extension to get runnables looks more
preferred than lens:
https://rust-analyzer.github.io/book/contributing/lsp-extensions.html#runnables

This PR does not include any work on this direction, limiting to the
general code lens support.

As a proof of concept, it's possible to get the lens and even attempt to
run it, to no avail:

![image](https://github.com/user-attachments/assets/56950880-d387-48f9-b865-727f97b5633b)


Release Notes:

- Used `textDocument/codeLens` data in the actions menu when applicable
2025-03-15 09:50:32 +02:00
张小白
0b492c11de Use line_endings macro for the edit tool tests (#26642)
This aligns with how we handle other tests on Windows.

Release Notes:

- N/A
2025-03-15 14:16:10 +08:00
AidanV
265caed15e vim: Add global marks (#25702)
Closes https://github.com/zed-industries/zed/issues/13111

Release Notes:

- vim: Added global marks `'[A-Z]`
- vim: Added persistence for global (and local) marks. When re-opening
the same workspace your previous marks will be available.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-03-15 05:58:34 +00:00
Ryan Hawkins
148131786f Reveal always_included entries in Project Panel (#26197)
If the user has the `auto_reveal` option enabled, as well as
`file_scan_inclusions` and opens a file that is gitignored but is also
set to be always included, that file won't be revealed in the project
panel. I've personally found this annoying, as the project panel can
provide useful context on where you are in a codebase. It also just
feels weird for it to be out of sync with the editor state.

Release Notes:

- Fixed the interaction between `auto_reveal`, `file_scan_inclusions`,
and `.gitignore` within the Project Panel. Files that are always
included will now be auto-revealed in the Project Panel, even if those
files are also gitignored.
2025-03-15 01:42:11 +00:00
Jakub Charvat
7c1405db37 Update rendering of gutter diff hunks to show whether a hunk is staged or not (#26809)
In the gutter, it seems more intuitive to me for the unstaged hunks to
be hollow, indicating an action left to complete, and the staged hunks
to be filled. I therefore flipped the style of expanded hunks to match
the gutter icons. Is that acceptable? And would it be a breaking change?
If it is not acceptable, then 058dc216d5
contains the opposite behaviour, it is not a problem to revert to it.

In the following images, the first hunk is always ~unstaged~ staged and
the second is ~staged~ unstaged.

<img width="138" alt="image"
src="https://github.com/user-attachments/assets/35927069-da90-424a-8988-a4eb984d865f"
/>
<img width="133" alt="image"
src="https://github.com/user-attachments/assets/4edd0e0d-a2b5-453a-8172-47684e065c82"
/>

<br />
<img width="143" alt="image"
src="https://github.com/user-attachments/assets/2f295944-81aa-45f3-a103-c13b92bc2aba"
/>
<img width="133" alt="image"
src="https://github.com/user-attachments/assets/35248218-7104-4059-8742-ae0e54da6c6b"
/>


Release Notes:

- Improved gutter diff hunks to show whether a hunk is staged
2025-03-14 16:49:53 -07:00
Finn Evers
96b747e31d editor: Disable edit predictions in read-only buffers (#26804)
Closes #26797

Release Notes:

- Fixed edit predictions appearing in read-only buffers.

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-03-14 23:15:49 +00:00
Michael Sloan
7a888de9f5 Add initial implementation of evaluating changes generated by the assistant (#26799)
Release Notes:

- N/A

---------

Co-authored-by: Richard Feldman <oss@rtfeldman.com>
Co-authored-by: Thomas <thomas@zed.dev>
2025-03-14 23:10:25 +00:00
Finn Evers
e9b4fa1465 rust: Follow-up fixes for attribute highlighting (#26172)
Closes #26124

This PR fixes some more cases of improper attribute highlights for rust.

In #25501 I tried to address the regression in highlighting rust
attributes which were introduced by #25333 . However, I failed to
properly check all cases of attribute highlights as shown in the linked
issue - really sorry for that! Thus, this is a follow-up fix aiming to
resolve the issues the previous PR did not cover.

The changes do not affect any highlighting shown in the [previous
PR](https://github.com/zed-industries/zed/pull/25501):

| `main` | <img width="719" alt="main-working"
src="https://github.com/user-attachments/assets/9aa0e611-7bda-4b50-9335-c87da4c38057"
/> |
| --- | --- |
| This PR | <img width="719" alt="PR-working"
src="https://github.com/user-attachments/assets/605b275c-1d68-4bd7-97c6-251d7614a7ed"
/> |

But resolves the mentioned regressions in the linked issue:

| `main` | <img width="371" alt="main_broken"
src="https://github.com/user-attachments/assets/ebbb47b7-7945-41e0-b030-2fe3f2198653"
/> |
| --- | --- |
| This PR | <img width="371" alt="PR_broken"
src="https://github.com/user-attachments/assets/fa97408b-e1d6-4d99-81c1-cfb8073961a4"
/> |

Again, sorry for not checking this more thoroughly.


Release Notes:

- Fixed attributes in Rust being improperly highlighted.

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-03-14 23:02:45 +00:00
Devzeth
ead60d1857 docs: Add documentation for icon theme (#25973)
Adds documentation for the icon theme setting (mostly based on the
documentation from theme but adjusted for icon theme).

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-03-14 22:39:11 +00:00
Cole Miller
768dfc8b6b Reinstate failing worktree tests (#26733)
Just debugging for now

Release Notes:

- N/A
2025-03-14 22:20:24 +00:00
Cole Miller
f2f9c786da Fix the feedback modal (#26793)
Closes #26787

Release Notes:

- Fixed a bug that prevented typing in the in-app feedback form
2025-03-14 17:55:52 -04:00
Smit Barmase
e5d2678d94 editor: Disable selection highlights for single line editor (#26805)
Fixes the selection highlight appearing in single-line editors like the
file picker, command palette, etc.

Release Notes:

- Fixed selection highlight appearing in input fields like the file
picker, command palette, etc.
2025-03-15 03:02:40 +05:30
Smit Barmase
3ad9074e63 editor: Fix auto-closing quotes after word character (#26803)
Closes #14349

When typing quotes immediately after a word character, it resulted in
auto-closing the quote.

```js
const thing = this is text^;
```

Typing a quote resulted in `this is text""^;` which is not correct, and
should be `this is text"^;`.

This PR changes logic for auto close:

1. We now prevent auto-closing in case of brackets where start == end
when they're typed immediately after a word character. i.e. For, ``` `,
", ' ```.
2. Other bracket pairs like `{}, (), etc` continue to auto-close
regardless of preceding character. So, `func^` to `func()^` will keep
working.
3. Auto-closing in other contexts like after spaces, punctuation, etc.
will still work.

Before:

![before](https://github.com/user-attachments/assets/6be02c95-4c71-488b-901d-b7b98c4170a4)

After:

![after](https://github.com/user-attachments/assets/680ece4d-20cb-428c-b430-846da3a2d643)

Release Notes:

- Fixed auto-paired quotes being inserted when typing a quote
immediately next to a word character.
2025-03-15 02:46:57 +05:30
Richard Feldman
f40b22c02a Add action log to thinking tool (#26802)
Release Notes:

- N/A
2025-03-14 20:44:36 +00:00
Richard Feldman
8490d0d4ef Add thinking tool (#26675)
Release Notes:

- N/A
2025-03-14 16:26:22 -04:00
Finn Evers
afd0da97b9 language_selector: Improve lookup for language icons (#26376)
This PR fixes a rare case where icons could be missing in the language
selector.

Currently, whilst looking up an icon, all file suffixes starting with a
dot are filtered out. While this works fine for some languages, there
are some languages having only file suffixes starting with a dot, e.g.
the "Git Attributes" language provided from the "Git Firefly" extension.
This results in no icon being displayed in the list, as shown in the
screenshots below.

To solve this, we can just simply remove the check for this special case
as well as the construction of an artificial file name in the code, as
both are not needed. A simple path just consisting of the extension is
sufficient, as we currently do not differentiate between file names and
file suffixes during an icon lookup. see the relevant code below:


013a646799/crates/file_icons/src/file_icons.rs (L23-L52)

As the first lookup is directly done using the entire file name and then
checked against all suffixes, we actually do not have to construct an
artificial file name at all. Should that produce no match, we check for
a hidden file right after, so we do not have to filter hidden file names
out.

With this fix, nothing changes for "normal" file suffixes, for some
cases where languges provide entire file names as a path suffix, the
matching might improve, and for languages with only hidden associated
file names, the initially described issue is resolved.

I do believe the behavior of matching icons to languages could be
improved in general. Fowever, I do think this is beyond the scope of
this change.

| Current main | <img width="546" alt="main"
src="https://github.com/user-attachments/assets/5c3c9fdc-cadf-4e44-9667-2530374aa0d2"
/> |
| --- | --- |
| This PR |<img width="546" alt="PR"
src="https://github.com/user-attachments/assets/82e59108-e31f-4ca9-8bbd-b9fd2b34feb0"
/>|

Aditionally, in 4395f78fb2 I refactored
the code which acquires the label and icon for a match, since I found it
a bit hard to read initially. The majority of this diff comes from this
change. Should that not be wanted, I can revert that change.

Release Notes:

- Fixed a rare case where languages had no associated icon in the
language selector.
2025-03-14 20:13:59 +00:00
Agus Zubiaga
1bf1c7223f assistant edit tool: Fix editing files in context (#26751)
When the user attached context in the thread, the editor model request
would fail because its tool use wouldn't be removed properly leading to
an API error.

Also, after an edit, we'd keep the old file snapshot in the context.
This would make the model think that the edits didn't apply and make it
go in a loop.

Release Notes:

- N/A
2025-03-14 17:07:43 -03:00
0x2CA
ba8b9ec2c7 gpui: Add interval in pattern (#26459)
Closes #ISSUE

[git: Use font size to determine pattern slash width
#26446](https://github.com/zed-industries/zed/pull/26446)

This PR only uses font size as the slant line width, and here it further
uses line height as the slant line interval control.

before


![image](https://github.com/user-attachments/assets/a8f2406e-5eed-4528-a9a2-867513613fc7)


now


![image](https://github.com/user-attachments/assets/9b8ccca9-8023-4cb2-a6fe-0e42e19642a4)

big line height


![image](https://github.com/user-attachments/assets/4498e858-4f25-432c-80ee-355726d9c41b)


Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-03-14 12:51:09 -07:00
Ben Kunkle
685536c27e editor: Change order of format and timeout futures (#26796)
Very small change, simply changing the order of the futures we pass to
`select_biased!` so that if the format request and the timeout resolve
at the same time (highly unlikely) we choose the format request instead
of choosing the timeout and throwing away our work!

Release Notes:

- N/A
2025-03-14 18:42:00 +00:00
Smit Barmase
ae017c3f96 file_finder: Fix panic when file name contains new line (#26791)
Closes #26777

This PR fixes a panic when a file name contains a newline and a
multi-byte character like 👋 (4 bytes in UTF-8). The issue was in the
regex not considering newlines in file names, causing it to match only
the latter part of the file name.

For example:

```
 left: PathWithPosition { path: "ab", row: None, column: None } // matched
 right: PathWithPosition { path: "ab\ncd", row: None, column: None } // actual file name
```


This resulted in incorrect index calculation later in the code, which
went unnoticed until now due to the lack of tests with file names
containing newlines.

We discovered this issue when a panic occurred due to incorrect index
calculation while trying to get the index of a multi-byte character.
After the newline fix, the index calculation is always correct, even in
the case of multi-byte characters.

Release Notes:

- Fixed an issue where file names with newlines and multi-byte
characters could cause a crash in certain cases.
2025-03-14 22:50:33 +05:30
João Marcos
f587e95a7e Add seed argument to #[gpui::test] attribute macro (#26764)
This PR introduces the arguments `seed` and `seeds` to `gpui::test`,
e.g.:
- `#[gpui::test(seed = 10)]`
- `#[gpui::test(seeds(10, 20, 30, 40))]`

Which allows us to run a test against a specific seed value without
slowing
down our tests like `iterations` does with high values.

This was motivated by a diff hunk test that only fails in a 400+ seed,
but is
slow to run 400+ times for every `cargo test`.

If your test failed with a specific seed, you can now add the `seed` arg
to
increase the chances of detecting a regression.

There are now three ways of setting seeds, the `SEED` env var,
`iterations`,
and the args this PR adds. See docs in `gpui::test`.

---

I also relaxed the limitation on `retries` not working with
`iterations`, as
that seemed unnecessary.

Release Notes:

- N/A
2025-03-14 13:40:02 -03:00
Danilo Leal
83dfdb0cfe assistant2: Add "running" status feedback in the disclosure (#26786)
Just a tiny bit of polish here, so that if the user expands the
disclosure, an equivalent loading state is at the response container.

<img
src="https://github.com/user-attachments/assets/a2ecb7f4-c9ea-4a14-8a60-9f7f2983a1a1"
width="600px" />

Release Notes:

- N/A
2025-03-14 12:31:26 -03:00
Kirill Bulatov
566c5f91a7 Refine word completions (#26779)
Follow-up of https://github.com/zed-industries/zed/pull/26410

* Extract word completions into their own, `editor::ShowWordCompletions`
action so those could be triggered independently of completions
* Assign `ctrl-shift-space` binding to this new action
* Still keep words returned along the completions as in the original PR,
but:
* Tone down regular completions' fallback logic, skip words when the
language server responds with empty list of completions, but keep on
adding words if nothing or an error were returned instead
    * Adjust the defaults to wait for LSP completions infinitely
* Skip "words" with digits such as `0_usize` or `2.f32` from completion
items, unless a completion query has digits in it

Release Notes:

- N/A
2025-03-14 15:18:55 +00:00
Danilo Leal
21057e3af7 assistant2: Refine thread design (#26783)
Just some light design polish while we're in-flight with this.

<img
src="https://github.com/user-attachments/assets/40a68fe6-f37e-4df1-b669-824c7dd8ff11"
width="600px" />

---

Release Notes:

- N/A
2025-03-14 12:09:24 -03:00
Antonio Scandurra
f68a475eca Introduce rating for assistant threads (#26780)
Release Notes:

- N/A

---------

Co-authored-by: Richard Feldman <oss@rtfeldman.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-03-14 14:41:50 +00:00
Smit Barmase
c62210b178 copilot: Handle sign out when copilot language server is not running (#26776)
When copilot is not being used as the edit prediction provider and you
open a fresh Zed instance, we don’t run the copilot language server.
This is because copilot chat is purely handled via oauth token and
doesn’t require the language server.

In this case, if you click sign out, instead of asking the language
server to sign out (which isn’t running), we can manually clear the
config directory, which contains the oauth tokens. We already watch this
directory, and if the token is not found, we update the sign-in status.

Release Notes:

- N/A
2025-03-14 19:41:27 +05:30
Danilo Leal
ad14dcc57b assistant2: Truncate thread title in context picker (#26775)
Similar issue as in https://github.com/zed-industries/zed/pull/26721.

Release Notes:

- N/A
2025-03-14 11:03:57 -03:00
Smit Barmase
b9432dbe42 macOS: Disable fullscreen window tabbing (take 2) (#26774)
Take 2 on https://github.com/zed-industries/zed/pull/26600. Now, it
doesn't break remote development.

Instead of using it in `build_classes`, it's now used in the `open`
method while creating a window. I found similar usage in other places
over internet.

Release Notes:

- Fixed issue where Zed would show mac native tabs when opening new
fullscreen windows on macOS.
2025-03-14 19:13:01 +05:30
Kamal Ahmad
41c373eff1 gpui: Add support for text in SVGs (#26335)
Closes #21319
Before: 

![image](https://github.com/user-attachments/assets/f75d7d59-75b1-4836-ae3b-6a1f526a5833)
After:

![image](https://github.com/user-attachments/assets/5fa28a6d-c417-4777-99f8-2a17edf759a0)

Use fontdb to load system fonts and pass it to resvg renderer. This adds
a small increase in startup time (around 30ms on my Linux system to
traverse fonts on a cold start). In the future once cosmic-text bumps
their version of fontdb we could clone the Database from
CosmicTextSystem

Release Notes: 
- Added: support for rendering text in SVGs
2025-03-14 08:25:11 -05:00
Smit Barmase
6a95ec6a64 copilot: Decouple copilot sign in from edit prediction settings (#26689)
Closes #25883

This PR allows you to use copilot chat for assistant without setting
copilot as the edit prediction provider.


[copilot.webm](https://github.com/user-attachments/assets/fecfbde1-d72c-4c0c-b080-a07671fb846e)

Todos:
- [x] Remove redudant "copilot" key from settings
- [x] Do not disable copilot LSP when `edit_prediction_provider` is not
set to `copilot`
- [x] Start copilot LSP when:
  - [x]  `edit_prediction_provider` is set to `copilot`
  - [x] Copilot sign in clicked from assistant settings
- [x] Handle flicker for frame after starting LSP, but before signing in
caused due to signed out status
- [x] Fixed this by adding intermediate state for awaiting signing in in
sign out enum
- [x] Handle cancel button should sign out from `copilot` (existing bug)
- [x] Handle modal dismissal should sign out if not in signed in state
(existing bug)

Release Notes:

- You can now sign into Copilot from assistant settings without making
it your edit prediction provider. This is useful if you want to use
Copilot chat while keeping a different provider, like Zed, for
predictions.
- Removed the `copilot` key from `features` in settings. Use
`edit_prediction_provider` instead.
2025-03-14 15:10:56 +05:30
Anthony Eid
8d7b021f92 Fix editor's outline view confirm not working before any queries have (#26761)
## Summary
This PR fixes a minor bug where editor's outline view wouldn't move the
cursor on confirm before any outline queries have been made.

### Before 

https://github.com/user-attachments/assets/6ccca0c1-c0fa-46cb-b700-28a666d62ce8

### After

https://github.com/user-attachments/assets/d508e20b-90fb-471a-b974-431205501c89

Release Notes:

- Fixes bug where editor's outline view wouldn't move cursor on confirm
action
2025-03-14 07:19:43 +00:00
Conrad Irwin
798a34bfc2 Show git toasts for 10s (#26714)
Release Notes:

- N/A
2025-03-13 22:51:07 -06:00
Conrad Irwin
a4a9f6bd07 Merge excerpts in project diff (#26739)
This adds code to merge excerpts when you expand them and they would
overlap. It is only enabled for callers who use the
`set_excerpts_for_path` API for multibuffers (which is currently just
project diff), as other users of multibuffer care too much about the
exact excerpts that they have.

Release Notes:

- N/A
2025-03-13 22:50:42 -06:00
Conrad Irwin
bfe4c40f73 Revert "Disable automatic window tabbing (cherry-pick #26600) (#26652)" (#26749)
This reverts commit 391eb380b5.

For some reason that is very unclear to me, this broke ssh'ing into
macOS remotes.
The remote process aborts with:

```
-------------------------------------
Translated Report (Full Report Below)
-------------------------------------

Process:               zed-remote-server-dev-build [78088]
Path:                  /Users/USER/*/zed-remote-server-dev-build
Identifier:            zed-remote-server-dev-build
Version:               ???
Code Type:             ARM-64 (Native)
Parent Process:        launchd [1]
Responsible:           iTerm2 [62245]
User ID:               501

Date/Time:             2025-03-13 19:30:37.6827 -0600
OS Version:            macOS 15.3.1 (24D70)
Report Version:        12
Anonymous UUID:        3A9631EB-5468-8CA4-7A0F-E36C3FF9D04F

Sleep/Wake UUID:       C935AE4C-E06A-4F6D-BE97-101E4E03482F

Time Awake Since Boot: 910000 seconds
Time Since Wake:       1265 seconds

System Integrity Protection: enabled

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000

Termination Reason:    Namespace OBJC, Code 1 

Application Specific Information:
crashed on child side of fork pre-exec


Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	       0x18653fc6c __abort_with_payload + 8
1   libsystem_kernel.dylib        	       0x186565eb8 abort_with_payload_wrapper_internal + 104
2   libsystem_kernel.dylib        	       0x186565e50 abort_with_reason + 32
3   libobjc.A.dylib               	       0x1861dc040 _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 128
4   libobjc.A.dylib               	       0x1861dbfc0 _objc_fatal(char const*, ...) + 44
5   libobjc.A.dylib               	       0x1861c1674 performForkChildInitialize(objc_class*, objc_class*) + 400
6   libobjc.A.dylib               	       0x1861a67f0 initializeNonMetaClass + 592
7   libobjc.A.dylib               	       0x1861c4a3c initializeAndMaybeRelock(objc_class*, objc_object*, locker_mixin<lockdebug::lock_mixin<objc_lock_base_t>>&, bool) + 164
8   libobjc.A.dylib               	       0x1861a5f98 lookUpImpOrForward + 304
9   libobjc.A.dylib               	       0x1861a5b84 _objc_msgSend_uncached + 68
10  zed-remote-server-dev-build   	       0x104f9ec4c _$LT$$LP$$RP$$u20$as$u20$objc..message..MessageArguments$GT$::invoke::hf68c58806f4b5702 + 56
11  zed-remote-server-dev-build   	       0x104f9d4c8 objc::message::platform::send_unverified::h2ec8392957fd6551 + 120
12  zed-remote-server-dev-build   	       0x104e5631c cocoa::appkit::NSPasteboard::generalPasteboard::h68122d7f32549cba + 512
13  zed-remote-server-dev-build   	       0x104e3b3b4 gpui::platform::mac::platform::MacPlatform::new::hb68d7ae2c5fdea7e + 336
14  zed-remote-server-dev-build   	       0x104e48008 gpui::platform::current_platform::h931999673c8c6468 + 28
15  zed-remote-server-dev-build   	       0x104ee4284 gpui::app::Application::headless::h3bffec62c65240ce + 32
16  zed-remote-server-dev-build   	       0x1023746ac remote_server::unix::execute_run::h7ac8de1a7e257f61 + 1200
17  zed-remote-server-dev-build   	       0x102368e1c remote_server::main::h42e4b18462b32dcf + 252 (main.rs:56)
18  zed-remote-server-dev-build   	       0x10236717c core::ops::function::FnOnce::call_once::h8534244cea12c898 + 16 (function.rs:250)
19  zed-remote-server-dev-build   	       0x102368154 std::sys::backtrace::__rust_begin_short_backtrace::h22fd48e0f46eb10b + 12 (backtrace.rs:152)
20  zed-remote-server-dev-build   	       0x10236bf74 std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::hf8bd0081bf8d785b + 16 (rt.rs:195)
21  zed-remote-server-dev-build   	       0x105723d20 std::rt::lang_start_internal::h5f91760815528aa2 + 1092
22  zed-remote-server-dev-build   	       0x10236bf50 std::rt::lang_start::hb88fe48ac1498ea6 + 60 (rt.rs:194)
23  zed-remote-server-dev-build   	       0x10236b67c main + 36
24  dyld                          	       0x1861f4274 start + 2840
```

Which is not even (apparently) on the line that calls this function.

To reproduce this, run `ZED_BUILD_REMOTE_SERVER=true cargo run
ssh://127.0.0.1/~/`.

Release Notes:

- N/A
2025-03-13 20:55:22 -06:00
Ben Kunkle
daa16bcf42 cli: Support opening anonymous file descriptors via the cli on MacOS and Linux (#26744)
Closes #4770

(really closes issue described in [this
comment](https://github.com/zed-industries/zed/issues/4770#issuecomment-2258728884)
on #4770)

Only implemented for MacOS and Linux for now as I have no way to test on
Windows or BSD.
PRs welcome!

Release Notes:

- Added support for reading from anonymous file descriptors (e.g.
created as part of process substitution) on MacOS and Linux
2025-03-13 20:53:47 -05:00
renovate[bot]
22ad7b17c5 Update Rust crate clap to v4.5.32 (#26592)
This PR contains the following updates:

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

---

### Release Notes

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

###
[`v4.5.32`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4532---2025-03-10)

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

##### Features

-   Add `Error::remove`

##### Documentation

-   *(cookbook)* Switch from `humantime` to `jiff`
-   *(tutorial)* Better cover required vs optional

##### Internal

-   Update `pulldown-cmark`

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwMC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-13 23:36:04 +00:00
renovate[bot]
728a5eb388 Update Rust crate ctor to v0.4.1 (#26593)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [ctor](https://redirect.github.com/mmastrac/rust-ctor) |
workspace.dependencies | patch | `0.4.0` -> `0.4.1` |

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwMC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 01:17:43 +02:00
renovate[bot]
8d8e5d3635 Update Rust crate mdbook to v0.4.47 (#26611)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [mdbook](https://redirect.github.com/rust-lang/mdBook) | dependencies
| patch | `0.4.45` -> `0.4.47` |

---

### Release Notes

<details>
<summary>rust-lang/mdBook (mdbook)</summary>

###
[`v0.4.47`](https://redirect.github.com/rust-lang/mdBook/blob/HEAD/CHANGELOG.md#mdBook-0447)

[Compare
Source](https://redirect.github.com/rust-lang/mdBook/compare/v0.4.46...v0.4.47)


[v0.4.46...v0.4.47](https://redirect.github.com/rust-lang/mdBook/compare/v0.4.46...v0.4.47)

##### Fixed

-   Fixed search not showing up in sub-directories.
[#&#8203;2586](https://redirect.github.com/rust-lang/mdBook/pull/2586)

###
[`v0.4.46`](https://redirect.github.com/rust-lang/mdBook/blob/HEAD/CHANGELOG.md#mdBook-0446)

[Compare
Source](https://redirect.github.com/rust-lang/mdBook/compare/v0.4.45...v0.4.46)


[v0.4.45...v0.4.46](https://redirect.github.com/rust-lang/mdBook/compare/v0.4.45...v0.4.46)

##### Changed

- The `output.html.hash-files` config option has been added to add
hashes to static filenames to bust any caches when a book is updated.
`{{resource}}` template tags have been added so that links can be
properly generated to those files.
[#&#8203;1368](https://redirect.github.com/rust-lang/mdBook/pull/1368)

##### Fixed

-   Playground links for Rust 2024 now set the edition correctly.
[#&#8203;2557](https://redirect.github.com/rust-lang/mdBook/pull/2557)

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwMC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 01:17:26 +02:00
renovate[bot]
a05a480ed9 Update Rust crate rsa to v0.9.8 (#26619)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [rsa](https://redirect.github.com/RustCrypto/RSA) |
workspace.dependencies | patch | `0.9.7` -> `0.9.8` |

---

### Release Notes

<details>
<summary>RustCrypto/RSA (rsa)</summary>

###
[`v0.9.8`](https://redirect.github.com/RustCrypto/RSA/blob/HEAD/CHANGELOG.md#098-2025-03-12)

[Compare
Source](https://redirect.github.com/RustCrypto/RSA/compare/v0.9.7...v0.9.8)

##### Added

-   Doc comments to specify the `rand` version ([#&#8203;473])

[#&#8203;473]: https://redirect.github.com/RustCrypto/RSA/pull/473

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwMC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 01:17:14 +02:00
renovate[bot]
d141fa027e Update Rust crate schemars to v0.8.22 (#26626)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [schemars](https://graham.cool/schemars/)
([source](https://redirect.github.com/GREsau/schemars)) |
workspace.dependencies | patch | `0.8.21` -> `0.8.22` |

---

### Release Notes

<details>
<summary>GREsau/schemars (schemars)</summary>

###
[`v0.8.22`](https://redirect.github.com/GREsau/schemars/blob/HEAD/CHANGELOG.md#0822---2025-02-25)

[Compare
Source](https://redirect.github.com/GREsau/schemars/compare/v0.8.21...v0.8.22)

##### Fixed:

- Fix compatibility with rust 2024 edition
([https://github.com/GREsau/schemars/pull/378](https://redirect.github.com/GREsau/schemars/pull/378))

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwMC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 01:16:57 +02:00
Michael Sloan
8e0e291bd5 Track cumulative token usage in assistant2 when using anthropic API (#26738)
Release Notes:

- N/A
2025-03-13 22:56:16 +00:00
Conrad Irwin
e3c0f56a96 New excerpt controls (#24428)
Release Notes:

- Multibuffers now use less vertical space for excerpt boundaries.
Additionally the expand up/down arrows are hidden at the start and end
of the buffers

---------

Co-authored-by: Nate Butler <iamnbutler@gmail.com>
Co-authored-by: Zed AI <claude-3.5-sonnet@zed.dev>
2025-03-13 15:52:47 -06:00
Conrad Irwin
3935e8343a Allow parsing commits when we can't resolve the permalink (#26709)
Closes #26577

Release Notes:

- git: Fix showing commit messages for all repos
2025-03-13 15:41:08 -06:00
renovate[bot]
0c84170071 Update Rust crate quote to v1.0.40 (#26618)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [quote](https://redirect.github.com/dtolnay/quote) |
workspace.dependencies | patch | `1.0.38` -> `1.0.40` |

---

### Release Notes

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

###
[`v1.0.40`](https://redirect.github.com/dtolnay/quote/releases/tag/1.0.40)

[Compare
Source](https://redirect.github.com/dtolnay/quote/compare/1.0.39...1.0.40)

- Optimize construction of lifetime tokens
([#&#8203;293](https://redirect.github.com/dtolnay/quote/issues/293),
thanks [@&#8203;aatifsyed](https://redirect.github.com/aatifsyed))

###
[`v1.0.39`](https://redirect.github.com/dtolnay/quote/releases/tag/1.0.39)

[Compare
Source](https://redirect.github.com/dtolnay/quote/compare/1.0.38...1.0.39)

-   Documentation improvements

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjE5NC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-13 23:14:13 +02:00
renovate[bot]
a38687d278 Update Rust crate libc to v0.2.171 (#26604)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

##### Added

- Android: Add `if_nameindex`/`if_freenameindex` support
([#&#8203;4247](https://redirect.github.com/rust-lang/libc/pull/4247))
- Apple: Add missing proc types and constants
([#&#8203;4310](https://redirect.github.com/rust-lang/libc/pull/4310))
- BSD: Add `devname`
([#&#8203;4285](https://redirect.github.com/rust-lang/libc/pull/4285))
- Cygwin: Add PTY and group API
([#&#8203;4309](https://redirect.github.com/rust-lang/libc/pull/4309))
- Cygwin: Add support
([#&#8203;4279](https://redirect.github.com/rust-lang/libc/pull/4279))
- FreeBSD: Make `spawn.h` interfaces available on all FreeBSD-like
systems
([#&#8203;4294](https://redirect.github.com/rust-lang/libc/pull/4294))
- Linux: Add `AF_XDP` structs for all Linux environments
([#&#8203;4163](https://redirect.github.com/rust-lang/libc/pull/4163))
- Linux: Add SysV semaphore constants
([#&#8203;4286](https://redirect.github.com/rust-lang/libc/pull/4286))
- Linux: Add `F_SEAL_EXEC`
([#&#8203;4316](https://redirect.github.com/rust-lang/libc/pull/4316))
- Linux: Add `SO_PREFER_BUSY_POLL` and `SO_BUSY_POLL_BUDGET`
([#&#8203;3917](https://redirect.github.com/rust-lang/libc/pull/3917))
- Linux: Add `devmem` structs
([#&#8203;4299](https://redirect.github.com/rust-lang/libc/pull/4299))
- Linux: Add socket constants up to `SO_DEVMEM_DONTNEED`
([#&#8203;4299](https://redirect.github.com/rust-lang/libc/pull/4299))
- NetBSD, OpenBSD, DragonflyBSD: Add `closefrom`
([#&#8203;4290](https://redirect.github.com/rust-lang/libc/pull/4290))
- NuttX: Add `pw_passwd` field to `passwd`
([#&#8203;4222](https://redirect.github.com/rust-lang/libc/pull/4222))
- Solarish: define `IP_BOUND_IF` and `IPV6_BOUND_IF`
([#&#8203;4287](https://redirect.github.com/rust-lang/libc/pull/4287))
- Wali: Add bindings for `wasm32-wali-linux-musl` target
([#&#8203;4244](https://redirect.github.com/rust-lang/libc/pull/4244))

##### Changed

- AIX: Use `sa_sigaction` instead of a union
([#&#8203;4250](https://redirect.github.com/rust-lang/libc/pull/4250))
- Make `msqid_ds.__msg_cbytes` public
([#&#8203;4301](https://redirect.github.com/rust-lang/libc/pull/4301))
- Unix: Make all `major`, `minor`, `makedev` into `const fn`
([#&#8203;4208](https://redirect.github.com/rust-lang/libc/pull/4208))

##### Deprecated

- Linux: Deprecate obsolete packet filter interfaces
([#&#8203;4267](https://redirect.github.com/rust-lang/libc/pull/4267))

##### Fixed

- Cygwin: Fix strerror_r
([#&#8203;4308](https://redirect.github.com/rust-lang/libc/pull/4308))
- Cygwin: Fix usage of f!
([#&#8203;4308](https://redirect.github.com/rust-lang/libc/pull/4308))
- Hermit: Make `stat::st_size` signed
([#&#8203;4298](https://redirect.github.com/rust-lang/libc/pull/4298))
- Linux: Correct values for `SI_TIMER`, `SI_MESGQ`, `SI_ASYNCIO`
([#&#8203;4292](https://redirect.github.com/rust-lang/libc/pull/4292))
- NuttX: Update `tm_zone` and `d_name` fields to use `c_char` type
([#&#8203;4222](https://redirect.github.com/rust-lang/libc/pull/4222))
- Xous: Include the prelude to define `c_int`
([#&#8203;4304](https://redirect.github.com/rust-lang/libc/pull/4304))

##### Other

- Add labels to FIXMEs
([#&#8203;4231](https://redirect.github.com/rust-lang/libc/pull/4231),
[#&#8203;4232](https://redirect.github.com/rust-lang/libc/pull/4232),
[#&#8203;4234](https://redirect.github.com/rust-lang/libc/pull/4234),
[#&#8203;4235](https://redirect.github.com/rust-lang/libc/pull/4235),
[#&#8203;4236](https://redirect.github.com/rust-lang/libc/pull/4236))
- CI: Fix "cannot find libc" error on Sparc64
([#&#8203;4317](https://redirect.github.com/rust-lang/libc/pull/4317))
- CI: Fix "cannot find libc" error on s390x
([#&#8203;4317](https://redirect.github.com/rust-lang/libc/pull/4317))
- CI: Pass `--no-self-update` to `rustup update`
([#&#8203;4306](https://redirect.github.com/rust-lang/libc/pull/4306))
- CI: Remove tests for the `i586-pc-windows-msvc` target
([#&#8203;4311](https://redirect.github.com/rust-lang/libc/pull/4311))
- CI: Remove the `check_cfg` job
([#&#8203;4322](https://redirect.github.com/rust-lang/libc/pull/4312))
- Change the range syntax that is giving `ctest` problems
([#&#8203;4311](https://redirect.github.com/rust-lang/libc/pull/4311))
- Linux: Split out the stat struct for gnu/b32/mips
([#&#8203;4276](https://redirect.github.com/rust-lang/libc/pull/4276))

##### Removed

- NuttX: Remove `pthread_set_name_np`
([#&#8203;4251](https://redirect.github.com/rust-lang/libc/pull/4251))

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjE5NC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-13 23:13:58 +02:00
renovate[bot]
b75b308459 Update Rust crate env_logger to v0.11.7 (#26603)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [env_logger](https://redirect.github.com/rust-cli/env_logger) |
workspace.dependencies | patch | `0.11.6` -> `0.11.7` |

---

### Release Notes

<details>
<summary>rust-cli/env_logger (env_logger)</summary>

###
[`v0.11.7`](https://redirect.github.com/rust-cli/env_logger/blob/HEAD/CHANGELOG.md#0117---2025-03-10)

[Compare
Source](https://redirect.github.com/rust-cli/env_logger/compare/v0.11.6...v0.11.7)

##### Internal

-   Replaced `humantime` with `jiff`

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjE5NC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-13 23:13:43 +02:00
Cole Miller
dffa725c7d worktree: Disable flaky test_file_status test (#26729)
See also:
- https://github.com/zed-industries/zed/pull/26684
- https://github.com/zed-industries/zed/pull/26710

Release Notes:

- N/A
2025-03-13 21:09:16 +00:00
Marshall Bowers
22f1429f97 assistant2: Prevent sending messages when the button is disabled (#26722)
This PR updates the `Chat` action handler to prevent sending messages in
the states when the submit button is disabled.

Release Notes:

- N/A
2025-03-13 20:49:21 +00:00
KyleBarton
6bdd2cf7db Consider the colon to be a word character when inside a string in JSON (#26574)
Partially addresses #25698

Part of why autocomplete suggestions for `keymap.json` aren't great is
because `:` is (correctly) considered a punctuation character, rather
than a word character, in JSON. But since `::` is part of the name of
zed commands, it means that the autocomplete context window loses
context after the user types colon:

Suggestion here is to use overrides for JSON and JSONC such that colon
is considered a word character when it's inside a string. This improves
the experience:

I believe this is more broadly correct anyway, since `:` loses it's
punctuation meaning when inside a string.

Hope this is helpful!

Release Notes:

- Improved autocomplete for keymap.json by treating `::` like word characters when inside a string.
2025-03-13 16:21:34 -04:00
Cole Miller
a7f3b22051 Don't render "Initialize Repository" button when no worktrees (#26713)
Closes #26676  

Release Notes:

- Fixed the git panel to not show an "Initialize Repositories" button in
empty projects
2025-03-13 16:17:23 -04:00
Cole Miller
f3703fa8be Use system git for committing (#26705)
Closes #26472

Release Notes:

- On macOS, switched to using the system's git binary to create commits.
This fixes issues that some users were seeing with pre-commit hooks.
Compatibility note: after this change, it is no longer possible to
commit from Zed unless git is installed.
2025-03-13 16:14:28 -04:00
Marshall Bowers
a0be6c8cb2 assistant2: Consider tool use as part of the "streaming" state (#26723)
This PR updates the `Thread::is_streaming` method so that it includes
tool use in the "streaming" state.

This will prevent the streaming indicator from disappearing when we're
doing tool use.

Release Notes:

- N/A
2025-03-13 20:11:44 +00:00
Cole Miller
b5a7fb13c3 Remove github issue template for git beta and improve related CI (#26707)
Remove the git beta issue template.
Improve ci.yml `job_spec` so that changes like this will not require CI in the future.
Improve ci.yml `job_spec` ensuring `output.run_license` exported for Cargo.lock.

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-03-13 16:07:32 -04:00
Danilo Leal
2183fc674d assistant2: Truncate context pill labels (#26721)
To solve a problem that mostly happens if the pill is of kind `Thread`
and the corresponding thread has a super long title.

<img
src="https://github.com/user-attachments/assets/4ee8038d-9467-41a9-9b30-76019d0b9c0b"
width="500px"/>

Release Notes:

- N/A
2025-03-13 16:56:49 -03:00
renovate[bot]
0ad5979f19 Update Rust crate proc-macro2 to v1.0.94 (#26612)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

-   Documentation improvements

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjE5NC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-13 15:38:02 -04:00
Peter Tripp
ed1938dd9a worktree: Disable flaky tests (test_write_file, test_git_status_postprocessing) (#26710)
Comment out flaky tests:
- `worktree_tests::test_write_file`
- `worktree_tests::test_git_status_postprocessing`

Job links:
- windows fail:
https://github.com/zed-industries/zed/actions/runs/13841766606/job/38730766252
- macos fail:
https://github.com/zed-industries/zed/actions/runs/13841766606/job/38730764118

That
[commit](85384fb9c6)
was a non-op script change, but in the [prior
commit](00359271d1)
[windows/macos
pass](https://github.com/zed-industries/zed/actions/runs/13841135221).

Similar experience with `worktree_tests::test_write_file` on both macOS
windows too.

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

Release Notes:

- N/A
2025-03-13 15:16:30 -04:00
Peter Tripp
f7927d3fa4 ci: Fix 'Run Tests' not always running (#26685)
Follow up to:
- https://github.com/zed-industries/zed/pull/26551

We need the "Tests Pass" step to run `if: always()`. 
Turns out when it's 'skipped', it counts as 'passing' with respect to
required status checks.


Release Notes:

- N/A
2025-03-13 19:02:59 +00:00
Conrad Irwin
8361c32a34 Fix flicker when reverting last hunk from the project diff view (#26706)
Closes #26696

Closes #ISSUE

Release Notes:

- git: Fix flicker when reverting last hunk in project diff view
2025-03-13 18:49:18 +00:00
Agus Zubiaga
2edadd9352 bash tool: Rename working_directory to cd and improve command wrap (#26702)
This helps its do the right thing

Release Notes:

- N/A

Co-authored-by: Antonio Scandurra <me@as-cii.com>
2025-03-13 18:29:25 +00:00
Joseph T. Lyons
85384fb9c6 Update issue response script to only consider replies from staff (#26703)
Release Notes:

- N/A

Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com>
2025-03-13 14:15:38 -04:00
João Marcos
00359271d1 git: Fix race condition when [un]staging hunks in quick succession (#26422)
- [x] Fix `[un]stage` hunk operations cancelling pending ones
  - [x] Add test
- [ ] bugs I stumbled upon (try to repro again before merging)
  - [x] holding `git::StageAndNext` skips hunks randomly 
    - [x] Add test
  - [x] restoring a file keeps it in the git panel
- [x] Double clicking on `toggle staged` fast makes Zed disagree with
`git` CLI
- [x] checkbox shows ✔️ (fully staged) after a single
stage

Release Notes:

- N/A

---------

Co-authored-by: Cole <cole@zed.dev>
Co-authored-by: Max <max@zed.dev>
2025-03-13 10:41:04 -07:00
Ben Kunkle
18fcdf1d2c terminal: Fix issues with highlighted ranges of paths (#26695)
Fixes a few problems,

- Uses `Boundary::Grid` instead of `Boundary::Cursor` for highlighted
range adjustments.

This fixes quite a few wierd behaviors around highlighting paths that
had to be scrolled into view (i.e. were in the terminal history)
including the issue described in the release notes as well as a
regression caused by #26401 where the highlight range would span from
the start of the path to the cursor location in the shell prompt

- Strips all trailing `:`s from the paths, updating the highlighted
range accordingly.

This worked fine before and is just a visual improvement.


Release Notes:

- Fixed an issue where file paths in the terminal surrounded by `()` or
`[]` would not be highlighted properly
2025-03-13 12:25:20 -05:00
renovate[bot]
55c927b039 Update Rust crate async-trait to v0.1.87 (#26578)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [async-trait](https://redirect.github.com/dtolnay/async-trait) |
workspace.dependencies | patch | `0.1.86` -> `0.1.87` |

---

### Release Notes

<details>
<summary>dtolnay/async-trait (async-trait)</summary>

###
[`v0.1.87`](https://redirect.github.com/dtolnay/async-trait/releases/tag/0.1.87)

[Compare
Source](https://redirect.github.com/dtolnay/async-trait/compare/0.1.86...0.1.87)

-   Documentation improvements

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjE5NC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-03-13 17:07:01 +00:00
Marshall Bowers
1be3f81920 assistant2: Include the thread summary in the Markdown representation (#26693)
This PR adds the thread's summary (if it has one) as a heading in the
Markdown representation.

Release Notes:

- N/A
2025-03-13 16:59:35 +00:00
Cole Miller
2eb4d6b7eb Fix being unable to put a cursor after trailing deletion hunks (#26621)
Closes #26541

Release Notes:

- Fixed a bug that prevented putting the cursor after a deletion hunk at
the end of a file, in the absence of trailing newlines

---------

Co-authored-by: Max <max@zed.dev>
2025-03-13 16:56:54 +00:00
Ben Kunkle
25f407baab settings: Auto-update JSON schemas for settings when extensions are un/installed (#26633)
Because of #26562, it is now possible to subscribe to extension update
events within the LSP store, where we can then update the Schemas sent
to the JSON LSP resulting in dynamic updates to the auto-complete
suggestions and diagnostics in settings. Notably, this means newly
installed languages and (icon) themes will auto-complete correctly as
soon as the extension is installed.

Closes #15436

Release Notes:

- Fixed an issue where autocomplete suggestions and diagnostics for
languages and (icon) themes in settings would not update when the
extension with which they were added was installed or uninstalled
2025-03-13 16:50:07 +00:00
Marshall Bowers
79874872cb assistant2: Add ability to open the active thread as Markdown (#26690)
This PR adds a new `assistant2: open active thread as markdown` action
that opens up the active thread in a Markdown representation:

<img width="1394" alt="Screenshot 2025-03-13 at 12 25 33 PM"
src="https://github.com/user-attachments/assets/363baaaa-c74b-4e93-af36-a3e04a114af0"
/>

Release Notes:

- N/A
2025-03-13 12:39:01 -04:00
Marshall Bowers
95208a6576 worktree: Disable flaky test_git_repository_status test (#26684)
This PR disables the flaky `test_git_repository_status` test.

Release Notes:

- N/A
2025-03-13 12:06:44 -04:00
Danilo Leal
1034d1a6b5 docs: Add section about Edit Prediction modes (#26683)
To go along the upcoming blog post as well as the new menu item options
(https://github.com/zed-industries/zed/pull/26680).

Release Notes:

- N/A
2025-03-13 12:48:39 -03:00
Danilo Leal
d4eab557b2 edit prediction: Add eager and subtle modes toggle to menu (#26680)
Now, users can toggle the display modes for Edit Prediction via the UI.

<img
src="https://github.com/user-attachments/assets/974cd3cc-43b4-46ba-9ce5-b2345ef3323d"
width="600px"/>

Release Notes:

- N/A
2025-03-13 12:46:22 -03:00
Nate Butler
b75964a636 Revert "ui: Color cleanup (#26673)" (#26681)
This reverts commit 6767e98e00.

Somehow that PR automerged itself even with failed CI checks.

Release Notes:

- N/A
2025-03-13 15:40:57 +00:00
Peter Tripp
87cdb68cca ci: Use smaller windows runners (#26674)
Let's see if the speed of `windows-2025-32` for `windows_tests` is
fast-enough for PRs and everywhere else use `windows-2025-16`. Leaving
`windows_clippy` unchanged with `windows-2025-16`.

Release Notes:

- N/A
2025-03-13 15:39:12 +00:00
Kirill Bulatov
b0b65420f6 Do not repeat proposed LSP completions in the word completions (#26682)
Follow-up of https://github.com/zed-industries/zed/pull/26410

Release Notes:

- N/A
2025-03-13 15:37:46 +00:00
Agus Zubiaga
8ec0309645 assistant edit tool: Use buffer search and replace in background (#26679)
Instead of getting the whole text from the buffer, replacing with
`String::replace`, and getting a whole diff, we'll now use `SearchQuery`
to get a range, diff only that range, and apply it (all in the
background).

When we match zero strings, we'll record a "bad search", keep going and
report it to the model at the end.

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
2025-03-13 12:25:49 -03:00
Nate Butler
6767e98e00 ui: Color cleanup (#26673)
This PR cleans up some color & elevation misc.

### Don't allow deriving Color from Hsla

The point of the [ui::Color] enum is to encourage consistent color
usage, and the the Color::Custom case is really only meant for cases
where we have no other choice.

`impl From<Hsla> for Color` encourages blindly passing colors into
`Color::Custom` – with this in place we might as well remove the entire
`Color` enum.

The usages that were updated due to this removal were for colors that
already exist in the Color enum, making it even more clear that it
didn't make sense to have this.

### `ElevationIndex` -> `Elevation`

This name would make more sense if we had an `Elevation` in the first
place. The new name is more clear.

#### `Button::elevation`

As part of this change I also updated button's `layer` method to
`elevation`, since it takes an elevation. This method still has the
following issue:

You want to use `Button::elevation` when it's default colors are
invisible on the layer you are rendering the button on. However, current
this method uses the elevation's `bg` color, rather than it's
`on_elevation_bg`.

Ideally when you use `Button::elevation` you want to pass the elevation
you are _on_, not choosing one that will show up the elevation you are
on.

This change will be in a separate PR, as it likely will have widespread
visual impact across the app.

Release Notes:

- N/A
2025-03-13 15:18:40 +00:00
Antonio Scandurra
8cf5af1a84 Introduce DiagnosticsTool (#26670)
Release Notes:

- N/A
2025-03-13 14:53:00 +01:00
Albin Kocheril Chacko
247ee880d2 Fix typo in default.json (#26666)
minor typo fix

Release Notes:

- N/A
2025-03-13 13:39:28 +00:00
Nate Butler
2e217759c0 gruvbox: version_control_ -> version_control. (#26665)
Missed this in PR #26606 

Before:

![CleanShot 2025-03-13 at 08 58
59@2x](https://github.com/user-attachments/assets/021df4b1-5a70-4fae-a109-9b8bb35949e3)

After:

![CleanShot 2025-03-13 at 08 59
22@2x](https://github.com/user-attachments/assets/01dca26d-77ec-4a54-8b7c-aa2fb160ff7d)

Release Notes:

- theme: Fixed an issue where version control colors weren't applying
correctly. (again)
2025-03-13 13:13:35 +00:00
Danilo Leal
0a0c163692 assistant2: Use icons for tool call status communication (#26617)
It was hard to catch the running & pending states, though. When running,
it will appear as a spinning arrow circle icon.

<img
src="https://github.com/user-attachments/assets/dbf1bc0a-6fa3-41c6-bcd7-2226e89c87b4"
width="500px" />

Release Notes:

- N/A
2025-03-13 10:01:20 -03:00
Antonio Scandurra
e80df25386 Iterate on tools some more (#26663)
Release Notes:

- N/A
2025-03-13 12:42:02 +00:00
Danilo Leal
d9590f3f0e docs: Improve introduction to Edit Prediction (#26620)
As I was writing a blog post about Edit Prediction, I realized we didn't
have a great section in the docs I could link to talking about
configuring it. We weren't: 1) explicitly exposing the settings code to
add Zed as the edit prediction provider, and 2) not showing an image of
the title bar banner.

Release Notes:

- N/A
2025-03-13 09:03:49 -03:00
Antonio Scandurra
4ecd1b5174 Fix bad cd sometimes used by BashTool and set edit model temperature to 0 (#26656)
Release Notes:

- N/A
2025-03-13 10:47:00 +00:00
Antonio Scandurra
70c973f6c3 Fix issues in EditFilesTool, ListDirectoryTool and BashTool (#26647)
Release Notes:

- N/A
2025-03-13 09:41:27 +00:00
Stanislav Alekseev
e842b4eade macOS: Disable automatic window tabbing in fullscreen mode (#26600)
Fixes #26534 (this time for real)

Release Notes:

- Fixed issue where Zed would behave weirdly when opening new fullscreen
windows by disabling window tabbing

Apple docs:
https://developer.apple.com/documentation/appkit/nswindow/allowsautomaticwindowtabbing
2025-03-13 12:45:01 +05:30
Agus Zubiaga
606aa7a78c Edit tool debugging (#26637)
Adds an `debug: edit tool` action that opens a new view which will help
us debug the edit tool internals. As the edit tool runs, the log
displays:

- Instructions provided by the main model
- Response stream from the editor model
- Parsed edit blocks
- Tool output provided back to main model

The log automatically records all edit tool interactions for staff, so
if you notice something weird, you can debug it retroactively without
having to open the debug tool first. We may want to limit the number of
recorded requests later.

I have a few more ideas for it, but this seems like a good starting
point.


https://github.com/user-attachments/assets/c61f5ce8-08b1-4500-accb-db2a480eb3ab


Release Notes:

- N/A
2025-03-13 04:03:01 +00:00
Mikayla Maki
0081b816fe Fix a bug where the modal layer could not be dismissed by the mouse 2025-03-12 16:44:16 -07:00
Peter Tripp
21949bcf1a ci: Fix tests not-running on main (#26613)
Follow-up to #26551 

Fix for tests being skipped on main.
Also fetch less history: [example
run](https://github.com/zed-industries/zed/actions/runs/13822318758/job/38670334893)

Release Notes:

- N/A
2025-03-12 19:16:23 -04:00
renovate[bot]
ee7ed6d5b8 Update Rust crate anyhow to v1.0.97 (#26576)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

-   Documentation improvements

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjE5NC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-03-12 23:01:04 +00:00
Marshall Bowers
07b67c1bd3 assistant2: Add ability to enable/disable all tools from a context server (#26610)
This PR adds an option to enable/disable all tools from a specific
context server:

<img width="1297" alt="Screenshot 2025-03-12 at 5 55 45 PM"
src="https://github.com/user-attachments/assets/af6c169e-0462-4a99-9bec-48fbf83dd08a"
/>

Release Notes:

- N/A
2025-03-12 22:14:31 +00:00
Mikayla Maki
f116b44ae8 Rename the editor::ToggleGitBlame action to git::Blame (#26565)
Release Notes:

- Git Beta: Renamed `editor::ToggleGitBlame` to `git::Blame`

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-03-12 22:12:42 +00:00
Nate Butler
43ab7fe0e2 theme: Fix incorrect version control keys in One themes (#26606)
While the `.{variants}` of the theme keys _were_ incorrect, they are
actually more consistent with our current theme keys (thanks AI!) So we
will keep theme, and fix the incorrect usages in the one themes and
elsewhere.

Old description:
> 
> This PR fixes an issue where we specified the incorrect theme keys
(thanks AI!) > in the theme schema. The following keys have been changed
to their correct > versions:
> 
> | Before                        | After                   |
> |-------------------------------|-------------------------|
> | version_control.added         | version_control_added   |
> | version_control.deleted       | version_control_deleted |
> | version_control.modified      | version_control_modified|
> | version_control.renamed       | version_control_renamed |
> | version_control.conflict      | version_control_conflict|
> | version_control.ignored       | version_control_ignored |
> 
> Please use the after versions in your themes, as they are correct! 
> 
> We won't be adding secondary keys to fix this automatically as git
only > officially launched today.
> 
> Due to this change, we've also updated the version control keys in the
One > themes to keep the default diff hunks looks from changing.

Closes #26572

Release Notes:

- theme: Fixed an issue where version control colors weren't applying
correctly.
2025-03-12 22:07:04 +00:00
Richard Feldman
6044773043 Add path search glob tool (#26567)
<img width="638" alt="Screenshot 2025-03-12 at 1 33 31 PM"
src="https://github.com/user-attachments/assets/f29b9dae-59eb-4d7a-bc26-aa4721cb829a"
/>

Release Notes:

- N/A
2025-03-12 22:00:54 +00:00
Conrad Irwin
81af2c0bed Fix overflow in create branch label (#26591)
Closes #ISSUE

Release Notes:

- N/A
2025-03-12 21:55:31 +00:00
Peter Tripp
ab199fda47 ci: GitHub actions refactor (#26551)
Refactor GitHub actions CI workflow.
- Single combined 'tests_pass' action so we only need one mandatory
check for merge queue
- Add new `job_spec` job which determines what needs to be run (+5secs)
  - Do not run full CI for docs only changes (~30secs vs 10+mins)
- Only run `script/generate-licenses` if Cargo.lock changed (saves
~23secs on mac_test)
- Move prettier /docs check to ci.yml and remove docs.yml 
- Run Windows tests on every PR commit
- Added new Windows runners named to reflect their OS/capacity
(windows-2025-64, windows-2025-32, windows-2025-16)

Release Notes:

- N/A
2025-03-12 17:32:38 -04:00
Marshall Bowers
e60e8f3a0a assistant_tool: Reduce locking in ToolWorkingSet (#26605)
This PR updates the `ToolWorkingSet` to reduce the amount of locking we
need to do.

A number of the methods have had corresponding versions moved to the
`ToolWorkingSetState` so that we can take out the lock once and do a
number of operations without needing to continually acquire and release
the lock.

Release Notes:

- N/A
2025-03-12 21:26:26 +00:00
brian tan
edeed7b619 workspace::Open: Highlight fuzzy matches (#26320)
Partial: https://github.com/zed-industries/zed/issues/15398

Changes:
Adds highlighting to the matches when using `"use_system_path_prompts":
false`

| before | after |
|---|---|

|![image](https://github.com/user-attachments/assets/60a385a0-abb0-49c5-935c-e71149161562)|![image](https://github.com/user-attachments/assets/d66ce980-cea9-4c22-8e6a-9720344be39a)|

Release Notes:

- N/A
2025-03-12 22:54:38 +02:00
Richard Feldman
9be7934f12 Add Bash tool (#26597)
<img width="636" alt="Screenshot 2025-03-12 at 4 24 18 PM"
src="https://github.com/user-attachments/assets/6f317031-f495-4a5a-8260-79a56b10d628"
/>

<img width="634" alt="Screenshot 2025-03-12 at 4 24 36 PM"
src="https://github.com/user-attachments/assets/27283432-4f94-49f3-9d61-a0a9c737de40"
/>


Release Notes:

- N/A
2025-03-12 20:51:29 +00:00
Peter Tripp
009b90291e Fix formatting in linux.md (#26598)
Merge queue did not require docs tests to pass:
-
https://github.com/zed-industries/zed/actions/runs/13820880465/job/38665664419

This will be fixed with:
- https://github.com/zed-industries/zed/pull/26551

cc: @ConradIrwin 

Release Notes:

- N/A
2025-03-12 16:33:11 -04:00
Michael Kaplan
8b17dc66f6 docs: Document linker issue & workarounds with GCC >= 14 (#26579)
Closes #24880

documents issues with aws-lc-rs and gcc >=14 on linux and provides a
workaround until the issues are fixed in aws-lc-rs
2025-03-12 20:26:08 +00:00
Conrad Irwin
de07b712fd Fix message on push (#26588)
Instead of saying "Successfully pushed new branch" we say "Pushed x to
y"

Release Notes:

- N/A
2025-03-12 20:18:28 +00:00
Richard Feldman
be8f3b3791 Add delete-path tool (#26590)
Release Notes:

- N/A
2025-03-12 20:16:26 +00:00
Richard Feldman
3131b0459f Return which files were touched in the edit tool (#26564)
<img width="631" alt="Screenshot 2025-03-12 at 12 56 43 PM"
src="https://github.com/user-attachments/assets/9ab84a53-829a-4943-ae76-b1d97ee31f55"
/>

<img width="908" alt="Screenshot 2025-03-12 at 12 57 12 PM"
src="https://github.com/user-attachments/assets/bd246231-6c92-4266-b61e-5293adfe2ba0"
/>

Release Notes:

- N/A
2025-03-12 15:56:23 -04:00
Marshall Bowers
3ec323ce0d uiua: Extract to zed-extensions/uiua repository (#26587)
This PR extracts the Uiua extension to the
[zed-extensions/uiua](https://github.com/zed-extensions/uiua)
repository.

Release Notes:

- N/A
2025-03-12 19:55:37 +00:00
Conrad Irwin
c8b782d870 git: Hard wrap in editor (#26507)
This adds the ability for the editor to implement hard wrap (similar to
"textwidth" in vim).

If you are typing and your line extends beyond the limit, a newline is
inserted before the most recent space on the line. If you are otherwise
editing the line, pasting, etc. then you will need to manually rewrap.

Release Notes:

- git: Commit messages are now wrapped "as you type" to 72 characters.
2025-03-12 13:48:13 -06:00
Conrad Irwin
7bca15704b Git on main thread (#26573)
This moves spawning of the git subprocess to the main thread. We're not
yet
sure why, but when we spawn a process using GCD's background queues,
sub-processes like git-credential-manager fail to open windows.

This seems to be fixable either by using the main thread, or by using a
standard background thread,
but for now we use the main thread.


Release Notes:

- Git: Fix git-credential-manager

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2025-03-12 19:39:30 +00:00
Kirill Bulatov
5268e74315 Properly handle goto single file worktrees during terminal cmd-clicks (#26582)
Closes https://github.com/zed-industries/zed/issues/26431
Follow-up of https://github.com/zed-industries/zed/pull/26174

`path_with_position.path.strip_prefix(&worktree_root)` used in the PR is
wrong for cases of single-file worktrees, where it will return empty
paths that will result in incorrect project and FS entries accessed.

Release Notes:

- Fixed goto single file worktrees during terminal cmd-clicks
2025-03-12 19:38:21 +00:00
Kirill Bulatov
91c209900b Support word-based completions (#26410)
Closes https://github.com/zed-industries/zed/issues/4957


https://github.com/user-attachments/assets/ff491378-376d-48ec-b552-6cc80f74200b

Adds `"completions"` language settings section, to configure LSP and
word completions per language.
Word-based completions may be turned on never, always (returned along
with the LSP ones), and as a fallback if no LSP completion items were
returned.

Future work:

* words are matched with the same fuzzy matching code that the rest of
the completions are

This might worsen the completion menu's usability even more, and will
require work on better completion sorting.

* completion entries currently have no icons or other ways to indicate
those are coming from LSP or from word search, or from something else

* we may work with language scopes more intelligently, group words by
them and distinguish during completions

Release Notes:

- Supported word-based completions

---------

Co-authored-by: Max Brunsfeld <max@zed.dev>
2025-03-12 21:27:10 +02:00
Anthony Eid
74c29f1818 Fix unstage/stage in project diff not working when git panel isn't open (#26575)
Closes #ISSUE

Release Notes:

- Fix Bug where unstage/stage all in project diff wouldn't work while
git panel was closed

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-03-12 19:07:51 +00:00
Marshall Bowers
5858e61327 purescript: Extract to zed-extensions/purescript repository (#26571)
This PR extracts the PureScript extension to the
[zed-extensions/purescript](https://github.com/zed-extensions/purescript)
repository.

Release Notes:

- N/A
2025-03-12 18:42:12 +00:00
Martim Aires de Sousa
21cf2e38c5 Fix pane magnification causing mouse to drag tabs unexpectedly (#26383)
Previously, if a user clicked a button and moved the cursor out before
releasing, the click event was correctly prevented, but the pending
mouse-down state remained.
This caused unintended drags when the UI shifted due to magnification
settings.

Now, mouse-up clears the pending state:
- If over the button → clear state and trigger click handlers.
- If outside the button → clear state without triggering a click.

This avoids accidental drags while preserving expected click behavior.

Closes #24600

Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-03-12 13:32:42 -05:00
Marshall Bowers
a3ca5554fd zig: Extract to zed-extensions/zig repository (#26569)
This PR extracts the Zig extension to the
[zed-extensions/zig](https://github.com/zed-extensions/zig) repository.

Release Notes:

- N/A
2025-03-12 18:28:26 +00:00
Marshall Bowers
acf9b22466 extension: Add ExtensionEvents for listening to extension-related events (#26562)
This PR adds a new `ExtensionEvents` event bus that can be used to
listen for extension-related events throughout the app.

Today you need to have a handle to the `ExtensionStore` (which entails
depending on `extension_host`) in order to listen for extension events.

With this change subscribers only need to depend on `extension`, which
has a leaner dependency graph.

Release Notes:

- N/A
2025-03-12 17:01:52 +00:00
Joseph T. Lyons
ffcd023f83 Bump Zed to v0.179 (#26563)
Release Notes:

-N/A
2025-03-12 12:53:37 -04:00
Antonio Scandurra
6259ad559b Add RegexSearchTool (#26555)
Release Notes:

- N/A
2025-03-12 16:23:15 +00:00
Nate Butler
8d259a9dbe git_ui: Update Project Diff empty state design (#26554)
Title

Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
2025-03-12 12:21:47 -04:00
Danilo Leal
010c5a2c4e docs: Update the Git page (#26530)
So it reflects the new set of features supported starting from v0.177.

Release Notes:

- N/A
2025-03-12 09:20:39 -07:00
Mikayla Maki
45b126a977 git: Add an onboarding and banner flow (#26518)
TODO:

- [ ] Hide the reset onboarding action (only useful for development,
uncomment:
https://github.com/zed-industries/zed/pull/26518/files#diff-f0ce01d9a3df30f60c64b6f9906c54aa0191246a58dbf5297ee321575a180879R96)
- [x] Get a designer to replace the modal background (@danilo-leal)

Release Notes:

- Added a small onboarding banner for the git launch

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
2025-03-12 16:17:47 +00:00
Agus Zubiaga
5f74297576 Fix edit tool tests on windows (#26552)
Assertions on the parsed system prompt should use CRLF on Windows. I
didn't see it before because I was testing on my Windows VM from a
shared folder I cloned on macOS.

Release Notes:

- N/A
2025-03-12 15:52:51 +00:00
Antonio Scandurra
349f57381f Add ListDirectoryTool (#26549)
Release Notes:

- N/A
2025-03-12 15:17:12 +00:00
Antonio Scandurra
41eb586ec8 Remove list_worktrees and use relative paths instead (#26546)
Release Notes:

- N/A
2025-03-12 15:06:04 +00:00
Smit Barmase
6bf6fcaa51 macOS: Fix window turning black on fullscreen mode (#26547)
Closes #26534

Recently, we fixed a title bar transparency issue that only occurred on
macOS 15.3 and later. PR:
https://github.com/zed-industries/zed/pull/26403

However, this seems to have broken multi-window fullscreen behavior on
earlier macOS versions. This PR adds versioning so that the title bar
transparency fix only applies to macOS 15.3.0 and later.

No release notes, as this bug only exists on main right now.  

Release Notes:

- N/A

Co-authored-by: MrSubidubi <dev@bahn.sh>
2025-03-12 20:29:27 +05:30
Marshall Bowers
6e89537830 assistant2: Add an option to enable/disable all tools (#26544)
This PR adds an option to enable or disable all tools in the tool
selector.

<img width="1297" alt="Screenshot 2025-03-12 at 10 40 28 AM"
src="https://github.com/user-attachments/assets/9125bdfb-5b54-461c-a065-2882a8585a67"
/>

Release Notes:

- N/A
2025-03-12 14:53:38 +00:00
Agus Zubiaga
669c6a3d5e assistant edit tool: Do not include \r in old/new str (#26542)
#26538 fixed part of the issue, but it would keep trailing carriage
returns in the old/new strings. The model is unlikely to produce those,
but we might as well support them.

Release Notes:

- N/A
2025-03-12 11:34:40 -03:00
Nils Koch
910531bc33 Check if additional git provider is not the original git provider (#26533)
Release Notes:

- N/A

Yesterday I worked on https://github.com/zed-industries/zed/pull/26482
and noticed afterwards that we have duplicated hosting providers if the
git remote host is "gitlab.com" and after the PR also for "github.com".
This is not a big problem, since the original providers are registered
first and therefore we first find a match with the original providers,
but I think we should address this nevertheless.

We initialize every hosting provider with the defaults here:

b008b2863e/crates/git_hosting_providers/src/git_hosting_providers.rs (L15-L24)

After that, we also register additional hosting providers:

b008b2863e/crates/git_hosting_providers/src/git_hosting_providers.rs (L30-L43)

If we do not check if the additional provider is not the original
provider, we will register the same provider twice.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-03-12 10:25:31 -04:00
Kirill Bulatov
690f26cf8b Disable clangd's inactiveRegions support (#26539)
Disables https://github.com/zed-industries/zed/pull/26146 until a better
way to add diagnostics is found.
Overall, the PR had made changes that are worth keeping instead of
reverting, such as finally extracting out r-a's language server logic
into an `_ext.rs` file.

Release Notes:

- N/A
2025-03-12 14:20:05 +00:00
Agus Zubiaga
6b56fee6b0 assistant edit tool: Support \r\n around markers (#26538)
This should fix the tests on Windows

Release Notes:

- N/A
2025-03-12 11:00:16 -03:00
Cole Miller
d94001f445 git: Fix placeholder dots in untracked files (#26537)
This regressed at some point.

Release Notes:

- N/A
2025-03-12 13:50:25 +00:00
Antonio Scandurra
6bcfc4014b Introduce a system prompt for the new assistant (#26536)
This should be less eager in terms of invoking tools. But we should keep
iterating on it as we add more tools.

Also, this disables the Lua interpreter by default (it can still be
enabled manually from the tools icon).

Release Notes:

- N/A

---------

Co-authored-by: Richard Feldman <oss@rtfeldman.com>
2025-03-12 13:48:53 +00:00
Agus Zubiaga
47a89ad243 assistant: Edit files tool (#26506)
Exposes a new "edit files" tool that the model can use to apply
modifications to files in the project. The main model provides
instructions and the tool uses a separate "editor" model (Claude 3.5 by
default) to generate search/replace blocks like Aider does:

````markdown
mathweb/flask/app.py
```python
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
````

The search/replace blocks are parsed and applied as they stream in. If a
block fails to parse, the tool will apply the other edits and report an
error pointing to the part of the input where it occurred. This should
allow the model to fix it.


Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
2025-03-12 12:30:47 +00:00
Antonio Scandurra
f3f97895a9 Improve script tool description and add lines iterator to Lua file objects (#26529)
Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-03-12 07:58:11 +00:00
Antonio Scandurra
30afba50a9 Start tracking diffs in ScriptingSession (#26463)
The diff is not exposed yet, but we'll take care of that next.

Release Notes:

- N/A
2025-03-12 08:32:29 +01:00
Mikayla Maki
036c123488 Add git init button (#26522)
Because why not

Release Notes:

- N/A
2025-03-12 07:25:19 +00:00
Mikayla Maki
050f5f6723 Hide generate commit message button when assistant is disabled (#26519)
Release Notes:

- Git Beta: Fixed the generate commit message button still showing when
the assistant is disabled.
2025-03-12 05:55:41 +00:00
Cole Miller
2cd970f137 git: Remove hunk style setting (#26504) 2025-03-12 00:35:34 -04:00
Cole Miller
d6255fb3d2 git: Prevent up and down motions leaking out of the commit editor (#26501)
Closes #ISSUE

Release Notes:

- Git Beta: fixed an issue where pressing `up` or `down` in the git
panel's commit message editor would change the selected status entry
2025-03-12 00:01:08 -04:00
Nils Koch
f9a66ecaed Add detection of self hosted GitHub enterprise instances (#26482)
This PR does not close an issue, but it is an issue and and fix in one.
I hope this is ok, but please let me know if you prefer me to open an
issue before.

Release Notes:

- Add "copy permalink" action for self-hosted GitHub enterprise
instances

# Issue
### Related issues:
* https://github.com/zed-industries/zed/issues/26393
* https://github.com/zed-industries/zed/issues/11043

When you try to copy a permalink from a self-hosted GitHub enterprise
instance, you get the following error:

<img width="383" alt="permalink"
src="https://github.com/user-attachments/assets/b32338a7-a2d7-48fc-86bf-ade1d32ed1f7"
/>

You also cannot open a PR or commit when you hover over a git blame:


https://github.com/user-attachments/assets/a5491ce7-270b-412f-b9ac-027ec020b028


### Reproduce
If you do not have access to a self-hosted GitHub instance, you can
change the remote url of any git repo:
```
git remote set-url origin git@github.mycorp.com:nilskch/zed.git
```

With the fix, permalinks still won't bring you to a valid website, but
you can verify that they are correctly created.

# Solution

Currently, we only support detecting self-hosted GitLab instances, but
not self-hosted GitHub instances. We detect GitLab instances by checking
if "gitlab" is part of the git URL.

This PR adds the same logic to detect self-hosted GitHub enterprise
instances (by checking if "github" is in the URL).

This solution is not ideal, since self-hosted GitHub or GitLab instances
might not contain the word "github" or "gitlab". #26393 proposes adding
a setting that would allow users to map specific domains to their
corresponding git provider types. This mapping would help Zed correctly
identify the appropriate git instance, even if "gitlab" or "github" are
not part of the URL.

This PR does not implement the offered solution, but I added a TODO
where the fix for #26393 has to make changes.
2025-03-11 21:46:17 -06:00
Cole Miller
cfb9a4beb0 Fix git panel entries getting cut off (#26499)
Closes #26497 

Release Notes:

- N/A
2025-03-11 23:43:36 +00:00
Marshall Bowers
9902cd54ce extension_host: Remove restriction of extension API v0.3.0 to development builds (#26498)
Forgot to do this in #26495.

Release Notes:

- N/A
2025-03-11 23:22:31 +00:00
Marshall Bowers
96510b72b8 zed_extension_api: Release v0.3.0 (#26495)
This PR releases v0.3.0 of the Zed extension API.

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

Release Notes:

- N/A
2025-03-11 22:54:44 +00:00
Cristiano Pantea
a364a13458 Fix panel not resizing after external file deletion (#26378)
Previously, when a file was deleted externally and the warning prompt
was dismissed with "Close", the panel remained but was empty, leaving an
unused split space.

This happened because pane.remove_item(...) was being called with
close_pane_if_empty set to false, preventing the panel from being
removed even when it had no remaining items.

This fix changes the third boolean parameter to true, ensuring that the
panel is removed if it becomes empty, allowing the layout to properly
resize.

Closes #23904

Release Notes:

- N/A
2025-03-11 22:52:55 +00:00
Nate Butler
09a4cfd307 git_ui: Panel Horizontal Scroll (#26402)
Known Issues:
- When items can horizontal scroll, the right selected border is hidden

TODO:
- [ ] Width calculation is off
- [ ] When scrollbars should autohide they don't until hovering the
panel
- [ ] When switching to and from scrollbar track being visible we are
missing a notify somewhere.

Release Notes:

- Git Panel: Added horizontal scrolling in the git panel

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Cole Miller <cole@zed.dev>
2025-03-11 15:47:39 -07:00
Conrad Irwin
5d66c3db85 Git panel editor scroll (#26465)
Release Notes:

- N/A
2025-03-11 16:27:47 -06:00
Conrad Irwin
28f33d0103 Fix conflict marker in project diff view (#26466)
Closes #ISSUE

Release Notes:

- N/A

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-03-11 16:27:25 -06:00
Marshall Bowers
55a90f576a ui: Split up ContextMenu::render into smaller methods (#26489)
This PR refactors the `ContextMenu::render` method to extract a couple
smaller methods from it.

The existing `render` method was suffering from its size, with some of
the `match` arms not being able to be formatted with `rustfmt`.

Release Notes:

- N/A
2025-03-11 22:26:22 +00:00
Kirill Bulatov
8d6abf6537 Improve terminal hover tooltips (#26487)
Follow-up of https://github.com/zed-industries/zed/pull/26174

* Fixes `./path/foo.bar` not properly parsed as valid open target
* Shows full open target's path in cmd-hover tooltips

Before:

<img width="864" alt="before_1"
src="https://github.com/user-attachments/assets/2575b887-6c4d-486e-8e92-dd76aedf8103"
/>
<img width="864" alt="before_2"
src="https://github.com/user-attachments/assets/ded1f203-523c-4b75-afe9-fe541c785798"
/>

After:

<img width="864" alt="after_1"
src="https://github.com/user-attachments/assets/c50d9ba3-5dfb-4cfb-aed6-00e6fa6f088e"
/>
<img width="864" alt="after_2"
src="https://github.com/user-attachments/assets/0cdc8f34-7faa-4aab-87f3-dc0c8b499842"
/>

Release Notes:



- N/A
2025-03-12 00:17:12 +02:00
Cole Miller
04961a0186 Tweak stage/unstage-and-next to start a commit instead of wrapping in the project diff editor (#26434)
Release Notes:

- Git Beta: improved the stage-and-next and unstage-and-next actions in
the project diff editor to start a commit after acting on the last hunk
2025-03-11 18:17:04 -04:00
Mikayla Maki
fd7ab20ea4 Don't clobber the user's upstream settings (#26486)
It's not clobbering time :(

Release Notes:

- Git Beta: Fixed a bug where our push button would always overwrite the
current branch's upstream
2025-03-11 22:02:22 +00:00
Nate Butler
7019aca59d git_ui: Truncate long repository and branch names for respective selectors in panel (#26483)
This PR fixes a long repo name pushing the branch selector off the
screen, as well as just generally truncating them down in a way smarter
than a fixed character limit when long.

| Before | After |
|---------|-----------|
| ![CleanShot 2025-03-11 at 17 21
31@2x](https://github.com/user-attachments/assets/8762b5a7-883c-4080-a6cf-e8007c4737e7)
| ![CleanShot 2025-03-11 at 17 21
44@2x](https://github.com/user-attachments/assets/c3904c29-d939-445f-b700-5bf73f257256)
|


Release Notes:

- Git Panel: Smart truncate long branch and repository names in their
respective selectors
2025-03-11 21:58:36 +00:00
Marshall Bowers
d43bcc04db assistant2: Remove "Tools" switch (#26485)
This PR removes the "Tools" switch from Assistant 2, as we can manage
tools from the tool selector now.

Release Notes:

- N/A
2025-03-11 21:46:51 +00:00
Julia Ryan
2b94a35aaa Rework git toasts (#26420)
The notifications from git output could take up variable amounts of
screen space, and they were quite obnoxious when a git command printed
lots of output, such as fetching many new branches or verbose push
hooks.

This change makes the push/pull/fetch buttons trigger a small
notification toast, based on the output of the command that was ran. For
errors or commands with more output the user may want to see, there's an
"Open Log" button which opens a new buffer with the output of that
command.

It also uses this behavior for long error notifications for other git
commands like `commit` and `checkout`. The output of those commands can
be quite long due to arbitrary githooks running.

Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-03-11 21:39:29 +00:00
Marshall Bowers
e8208643bb assistant2: Show scripting tool in the tool selector (#26484)
This PR adds the scripting tool to the tool selector.

Release Notes:

- N/A
2025-03-11 21:35:39 +00:00
Ben Kunkle
a90f80725f settings: Enable JSX tag auto-close by default (#26481)
Based on conversation with @maxbrunsfeld. Enabling Tag auto closing by
default so that it is discoverable for new and existing users

Release Notes:

- Made it so JSX tag auto-closing is automatically enabled in supported
languages
2025-03-11 21:01:41 +00:00
Marshall Bowers
4e6c37d23b assistant2: Add tool selector (#26480)
This PR adds a tool selector to Assistant 2 to facilitate customizing
the tools that the model sees:

<img width="1297" alt="Screenshot 2025-03-11 at 4 25 31 PM"
src="https://github.com/user-attachments/assets/7a656343-83bc-4546-9430-6a5f7ff1fd08"
/>

Release Notes:

- N/A
2025-03-11 20:50:18 +00:00
Peter Tripp
0cf6259fec Make nano save (ctrl-o) work by-default in terminal (linux) (#26479)
Closes: https://github.com/zed-industries/zed/issues/15770

Release Notes:

- Make nano save (`ctrl-o`) work by-default in terminal (linux)
2025-03-11 20:29:02 +00:00
Kirill Bulatov
5cb5e92185 Bump aws-lc-rs to fix Windows release builds (#26477)
Closes https://github.com/zed-industries/zed/discussions/24816

https://github.com/aws/aws-lc-rs/releases/tag/v1.12.6 release includes a
fix for https://github.com/aws/aws-lc-rs/issues/707

Release Notes:

- N/A
2025-03-11 19:43:14 +00:00
Marshall Bowers
da61a28839 assistant_tool: Fix inaccurate parameter name (#26473)
This PR fixes an inaccurate parameter name in the
`ToolWorkingSet::insert` method.

Release Notes:

- N/A
2025-03-11 19:15:03 +00:00
Marshall Bowers
efdb769f9b terraform: Extract to zed-extensions/terraform repository (#26475)
This PR extracts the Terraform extension to the
[zed-extensions/terraform](https://github.com/zed-extensions/terraform)
repository.

Release Notes:

- N/A
2025-03-11 19:10:51 +00:00
Marshall Bowers
9cce5a650e assistant_tool: Add a source to the Tool trait (#26471)
This PR adds a `source` method to the `Tool` trait.

This will allow us to track where a tool is coming from.

Release Notes:

- N/A
2025-03-11 19:10:48 +00:00
Piotr Osiewicz
2021ca5bff terraform: Do not add each string constraint to the outline (#26453)
Closes #26336

Release Notes:

- N/A
2025-03-11 19:46:18 +01:00
Peter Tripp
1771250b04 Add 'Open Remote...' to File Menu (#26288)
Added some spacers while I was at it.

Release Notes:

- Added 'Open Remote...' to File menu
2025-03-11 14:18:13 +00:00
张小白
18259c0fd4 chore: Bump windows crate version (#26455)
Closes #ISSUE

Release Notes:

- N/A
2025-03-11 21:14:36 +08:00
Smit Barmase
41ddd1cc97 editor: Fix text selection not visible on text background (#26454)
Closes #25014

Previously, we painted in the order: highlights -> text background ->
text -> etc. This caused text selection to be invisible when the text
had a background.

This PR changes the painting order to: text background -> highlights ->
text -> etc.

Before:


https://github.com/user-attachments/assets/5d9647c4-3ab2-4960-b6b9-e399882a0c50

After:


https://github.com/user-attachments/assets/c699f5b9-4077-45f8-85e5-86c89130eb71

Release Notes:

- Fixed an issue where text selection was not visible on top of a text
background in the editor.
2025-03-11 18:43:11 +05:30
Smit Barmase
e175878008 macOS: Remove multi-keystroke rendering in title of menu item (#26448)
Closes #25483

Currently, macOS doesn't support showing multi-keystroke shortcuts in
menu items. We can use an attributed string to differentiate them, but
that breaks consistency with traditional shortcuts.

This PR removes the hack of concatenating the multi-keystroke shortcut
to the title, as it looked a bit janky.

Release Notes:

- N/A
2025-03-11 18:42:02 +05:30
张小白
1cfbfc199c windows: Fix tests (#26450)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-03-11 11:43:24 +00:00
张小白
f59f2caf7e Fix tests on Windows (#26449)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-03-11 11:17:48 +00:00
Agus Zubiaga
401342c6ec assistant: Display edits from scripts in panel (#26441)
https://github.com/user-attachments/assets/a486ff2a-4aa1-4c0d-be6c-1dea2a8d60c8
 
- [x] Track buffer changes in `ScriptingSession`
- [x] Show edited files in thread

Reviewing diffs and displaying line counts will be part of an upcoming
PR.

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
2025-03-11 10:12:52 +00:00
Cole Miller
0df1e4a489 Address out-of-bounds panic in inline completion button (#26394)
Closes #26350

Release Notes:

- Git Beta: Fixed a panic that could occur when using the project diff
2025-03-11 03:04:56 -04:00
Cole Miller
9bd3e156f5 Fix enter binding in git panel's commit editor on Linux (#26427)
Closes #26110 

Release Notes:

- Git Beta: fixed being unable to enter newline in the git panel's
commit editor on Linux
2025-03-11 00:26:13 -04:00
Conrad Irwin
42c655751b Show a disabled stage all button for no entries (#26436)
Closes #ISSUE

Release Notes:

- N/A
2025-03-10 22:25:33 -06:00
Conrad Irwin
ff1d78df3b Go back to "create branch" in the list (#26433)
Closes #ISSUE

Release Notes:

- N/A
2025-03-11 04:24:52 +00:00
Conrad Irwin
c2e4fdf63d Git commit modal branch list (#26417)
Closes #26273

Release Notes:

- git: Fixes opening the branch selector in the commit modal with
cmd-option-b
- git: Truncates the branch selector in the commit modal
2025-03-10 22:10:52 -06:00
Agus Zubiaga
bf11b888c3 scripting tool: Use project buffers in io.open (#26425)
This PR makes `io.open` use our own implementation again, but instead of
the real filesystem, it will now use the project's to check file
metadata and perform read and writes using project buffers.

This also cleans up the `io.open` implementation by splitting it into
multiple methods, adds tests for various File I/O patterns, and fixes a
few bugs in read formats.

Release Notes:

- N/A
2025-03-11 00:52:16 -03:00
Angelk90
d562f58e76 git_ui: Show more information in the branch picker (#25359)
Final product:

![CleanShot 2025-02-26 at 9  08
17@2x](https://github.com/user-attachments/assets/e5db1932-b2c6-4b32-ab67-ef0a0d19f022)

Release Notes:

- Added more information about Git branches to the branch picker.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-03-10 21:05:29 -06:00
Conrad Irwin
94e4aa626d Use current upstream for permalink to line (#26398)
Release Notes:

- git: Copy permalink to line now uses the upstream of the current
branch instead of "origin"
2025-03-10 20:53:46 -06:00
lydiandy
8ceba89d81 ui: Fix error code in button comment (#26423)
Closes #ISSUE

Release Notes:
ui: Fix error code in button comment.

- N/A *or* Added/Fixed/Improved ...
2025-03-11 02:15:39 +00:00
Conrad Irwin
c37d6d5fed Unwind deprecated permalinks code (#26395)
Release Notes:

- N/A
2025-03-10 19:57:10 -06:00
Max Brunsfeld
1a3597d726 Fix race conditions in updating buffer diffs on git changes (#26409)
Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
2025-03-10 16:52:18 -07:00
Kirill Bulatov
c747cccde3 Revert "Return back a proper resolved value (#26406)" (#26419)
This reverts commit 1f8b14f4f1.

Release Notes:

- N/A
2025-03-10 23:33:41 +00:00
Kirill Bulatov
d81e7683ea Use proper order of Completion::Source field to have sane default (#26416)
Follow-up of https://github.com/zed-industries/zed/pull/26300

Release Notes:

- N/A
2025-03-10 18:58:21 -04:00
edwloef
8b29ee6033 Add variable.special color to Gruvbox themes (#26271)
This adds the `variable.special` color to the Gruvbox family of themes,
which colors special variables (in Rust's case `self`) differently than
normal ones. The colors were taken from the old Gruvbox `variable`
highlighting (see https://github.com/zed-industries/zed/pull/25464).

before:

![image](https://github.com/user-attachments/assets/3f329ac0-fbdf-480c-9074-5db99591f4e1)

![image](https://github.com/user-attachments/assets/43efdddd-7daf-440f-8c11-d6279330912a)

after:

![image](https://github.com/user-attachments/assets/052c05b8-55c5-495a-a9cc-a5f73aa5aa00)

![image](https://github.com/user-attachments/assets/f598b75f-8d2d-4710-b804-9282de9f8d15)

fixes half of https://github.com/zed-industries/zed/issues/26206. Since
I don't use the Ayu themes I'd prefer someone who knows what looks good
there does those changes.

Release Notes:

- Gruvbox themes: Added a color for `@variable.special` syntax
highlights.
2025-03-10 18:43:18 -04:00
Conrad Irwin
96a75e08af Fix panic opening branch picker in commit modal (#26407)
Closes #ISSUE

Release Notes:

- N/A
2025-03-10 16:36:23 -06:00
Marshall Bowers
06cbff6714 assistant2: Remove excess padding around scripting tool inputs (#26412)
This PR removes some excess padding around the rendered scripting tool
inputs.

Release Notes:

- N/A
2025-03-10 22:29:50 +00:00
Marshall Bowers
ce05813e7c assistant2: Render scripting tool inputs when opening past threads (#26408)
This PR makes it so we render the scripting tool inputs to Markdown when
opening past threads.

Release Notes:

- N/A
2025-03-10 22:11:36 +00:00
Conrad Irwin
4d1d8d6d78 Git commit modal command (#26405)
Fix KeyBinding::for_action() to use the active focus handle instead of
what was
rendered last.

This makes the UI consistently chose the cmd-escape binding for close
(because escape in the editor is editor::Cancel?),
so force it to be "escape"

Release Notes:

- git: Fixed escape tooltip in commit modal
2025-03-10 16:10:53 -06:00
Kirill Bulatov
1f8b14f4f1 Return back a proper resolved value (#26406)
Follow-up of https://github.com/zed-industries/zed/pull/26300


https://github.com/zed-industries/zed/pull/26300/files#diff-a3da3181e4ab4f73aa1697d7b6dc0caa0c17b2a187fb83b076dfc0234ec91f54L16900
changed the snippets' `resolved` value but it should have not.

Release Notes:

- N/A
2025-03-10 23:48:04 +02:00
Marshall Bowers
082cc6184c assistant2: Persist scripting tool uses in saved threads (#26404)
This PR makes it so the scripting tool uses are persisted to and
restored from saved threads.

Release Notes:

- N/A
2025-03-10 21:42:23 +00:00
Smit Barmase
6cfc4dc857 gpui: Fix transparent titlebar in fullscreen mode on macOS (#26403)
Closes #23735

This PR fixes an issue where Zed shows a transparent title bar in
fullscreen mode on macOS instead of the default gray one.

When switching to fullscreen mode, we change the title bar appearance to
opaque. When exiting fullscreen mode, we check the existing
`appears_transparent` flag that we pass to gpui to decide whether to
change the title bar back to transparent or not.

Note: Regardless of the `appears_transparent` flag, gpui should always
show an opaque title bar in fullscreen mode to prevent a broken
appearance, as macOS always displays the title bar in fullscreen mode
upon mouse interaction.


https://github.com/user-attachments/assets/211fb185-239b-454e-ac7f-b93b25d33805

Release Notes:

- Fixed issue where Zed showed transparent titlebar in fullscreen mode
on macOS.
2025-03-11 03:02:52 +05:30
Ben Kunkle
b9c48685e8 terminal: Support trailing :description or error message after file path (#26401)
Closes #25086

Release Notes:

- Fixed a bug where file paths in the built in terminal of the format
`path/to/file.ext:row:col:description or error message` would not be
correctly identified as file paths due to the colon & additional text at
the end
2025-03-10 16:20:48 -05:00
Marshall Bowers
570c396e84 assistant2: Remove unneeded pub on field (#26399)
This PR removes an unneeded `pub` on a field in the `ContextStrip`, as
it was never accessed externally.

Release Notes:

- N/A
2025-03-10 20:55:53 +00:00
Ben Kunkle
5fd034e604 docs: Add documentation for using debuggers with Zed (#26391)
Just some basic documentation for using debuggers in Zed development.
Goes over configuring cargo to include full debug info, attaching to an
instance of Zed, and using a debugger to debug panics and crashes

Release Notes:

- N/A
2025-03-10 15:51:51 -05:00
Cole Miller
63dab5f891 Add a missing notify when updating the project diff (#26396)
Closes #ISSUE

Release Notes:

- Git Beta: Fixed a bug that caused the project diff not to update in
response to git-related events

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-03-10 16:47:35 -04:00
Marshall Bowers
a2d6df3ed6 scripting_tool: Fix formatting of tool description (#26397)
This PR fixes the formatting of the scripting tool description, as it
had acquired some strange whitespaces.

Release Notes:

- N/A
2025-03-10 20:35:02 +00:00
Mikayla Maki
30e86ac939 Add a "secondary" meta key to GPUI keystroke parsing (#26390)
"secondary" means "cmd" on macOS and "ctrl" on not macOS.

Release Notes:

- Added a "secondary" meta key to the zed keystroke parser, which maps
to 'cmd' on macOS and 'ctrl' off of macOS
2025-03-10 13:32:13 -07:00
Nate Butler
976fc3ee97 git_ui: Design Polish (#26361)
Polish PR

- [ ] Horizontal scrollbar for git panel
- [ ] Allow shift clicking a checkbox in any section to stage the whole
section
- [ ] Clean up design of no changes/pending push state in panel
- [x] Ensure checkbox placeholder dot is centered in the checkbox
- [x] Improve spacing between elements in git panel entries
- [x] Update git branch icon to match branch selector text when disabled
- [x] Truncate last commit message less aggressively in panel
- [x] Clean up new panel header design
- [x] Remove `_background` version control keys (backgrounds are derived
from the foreground colors)

### Previous message truncation:

Before:

![CleanShot 2025-03-10 at 11 54
32@2x](https://github.com/user-attachments/assets/46b18f66-bb5c-435e-a0da-6cc931bd8a15)

After:

![CleanShot 2025-03-10 at 11 55
24@2x](https://github.com/user-attachments/assets/fcf688c7-b949-41a2-a7b8-1a198eb7fa4a)

### Make branch icon match when menu is disabled

Before:

![CleanShot 2025-03-10 at 12 02
14@2x](https://github.com/user-attachments/assets/1990f4b3-c2f0-4e02-89ad-211aaebb3821)

After:

![CleanShot 2025-03-10 at 12 02
53@2x](https://github.com/user-attachments/assets/9b1caf65-c48f-44c9-924b-484892fb543f)

Release Notes:

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

---------

Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-03-10 13:19:02 -07:00
Conrad Irwin
63091459d8 Allow too many arguments (#26375)
This is nearly half of our #allows, and seems like something we happily
break whenever we need

Release Notes:

- N/A
2025-03-10 13:38:30 -06:00
Conrad Irwin
659fae70f8 Remove GitUiFeatureFlag and enable panel unconditionally (#26386)
Release Notes:

- git: Enable for everyone
2025-03-10 13:38:07 -06:00
Marshall Bowers
02e970192f assistant2: Improve Lua script rendering (#26389)
This PR improves the rendering of Lua scripts provided to the scripting
tool.

We now render them in code blocks with syntax highlighting:

<img width="1297" alt="Screenshot 2025-03-10 at 2 40 51 PM"
src="https://github.com/user-attachments/assets/def65b5c-86a8-490f-aaa5-5cc1687fe01e"
/>

Release Notes:

- N/A
2025-03-10 18:54:03 +00:00
Julia Ryan
5ecc67f2ef Remove --frozen flag for cargo-about (#26385)
This was added to support the nix build but accidentally broke our
bundling. I'll try to re-add it in a way that works for both in the
future.

Release Notes:

- N/A
2025-03-10 18:35:59 +00:00
Conrad Irwin
73dfb10c16 Scroll project diff into view always (#26379)
Closes #ISSUE

Release Notes:

- N/A
2025-03-10 12:28:07 -06:00
Marshall Bowers
e513e81046 assistant2: Decouple scripting tool from the Tool trait (#26382)
This PR decouples the scripting tool from the `Tool` trait while still
allowing it to be used as a tool from the model's perspective.

This will allow us to evolve the scripting tool as more of a first-class
citizen while still retaining the ability to have the model call it as a
regular tool.

Release Notes:

- N/A
2025-03-10 17:57:03 +00:00
Agus Zubiaga
2fc4dec58f assistant: Use tool interface for scripts (#26377)
We decided to expose scripting as tools again. We are aware of the UX
downsides of doing so, but we want to focus on getting it working well
first, and the model seems to make better use of it as an actual tool.

In the future, the tools API might support streaming. If it doesn't and
we need to ship, we can consider reverting this.

Release Notes:

- N/A
2025-03-10 13:59:31 -03:00
Conrad Irwin
3891381d3e Git keyboard shortcuts (#26374)
Closes #26040

Release Notes:

- git: Add keyboard shortcuts (when the panel is open) for fetch `ctrl-g
ctrl-g`, pull `ctrl-g down`, push `ctrl-g up`, force-push `ctrl-g
shift-up`, open diff `ctrl-g d`
2025-03-10 10:46:53 -06:00
Cole Miller
b91e929086 git: Pass project environment to git binary invocations (#26301)
Closes #26213 

Release Notes:

- Git Beta: pass down environment variables from project to git
operations
2025-03-10 12:12:46 -04:00
Cole Miller
013a646799 git_ui: Branch picker improvements (#26287)
- Truncate branch names based on the width of the picker
- Use a footer for "Create branch" instead of a picker entry

Still to do:

- [x] Select the footer button when no matches and run the create logic
on `enter`
- [x] Make it possible to quickly select the footer button from the
keyboard when there are matches

Release Notes:

- Git Beta: Removed limitation that made it impossible to create a
branch from the branch picker when it too closely resembled an existing
branch name
2025-03-10 11:39:01 -04:00
Marshall Bowers
ed52e759d7 docs: Fix language links (#26368)
This PR fixes some language links in the docs.

The Shell Script page wasn't being linked from `SUMMARY.md`, so no page
was being generated.

There were also some differences in the language lists in the sidebar
and on the top-level languages page.

Release Notes:

- N/A
2025-03-10 15:22:51 +00:00
Richard Feldman
6da099a9d7 Unsandbox Lua scripts (#26365)
Per a conversation with @nathansobo, have the Lua scripts run
unsandboxed for now (while this feature is behind the staff feature
flag).

Release Notes:

- N/A
2025-03-10 11:04:37 -04:00
Smit Barmase
5f159bc95e go_to_line: Fix goto line + mouse click jumps to previous scroll position (#26362)
Closes #20658

Now, when the "Go to Line" palette is open:  
- Clicking on the editor will dismiss the palette without changing the
scroll position. (PR change)
- Pressing Enter will jump to the line number entered in the palette.
(Unchanged)
- Pressing Escape will jump back to the previous cursor location.
(Unchanged)

Release Notes:

- Fixed an issue where clicking the editor with the mouse while the "Go
to Line" palette is open would cause it to jump to the previous scroll
position.
2025-03-10 20:33:07 +05:30
Marshall Bowers
a4462577bf Sort Cargo.tomls (#26367)
This PR sorts some `Cargo.toml`s that had become unsorted.

Release Notes:

- N/A
2025-03-10 14:48:21 +00:00
João Marcos
c147b58558 Remove redundant checks in do_stage_or_unstage_and_next (#26364)
Release Notes:

- N/A
2025-03-10 14:23:17 +00:00
Devzeth
84fe1bfe9b Recognize ixx as part of the cpp suffix (#26333)
Adds "ixx" as path suffix to be recognized for c++. 

> ixx documentation
https://learn.microsoft.com/en-us/cpp/cpp/modules-cpp?view=msvc-

I've also added it to the icon file. 

Release Notes:

- N/A
2025-03-10 09:10:29 -05:00
Danilo Leal
657d7a911d Add logo for wgsl (WebGPU Shading Language) (#26360)
Was dabbling on the shaders these past few days and felt like we could
have the WGSL logo. This is based on the logo found on the GPU Web
repository: https://github.com/gpuweb/gpuweb/tree/main/logo

Release Notes:

- N/A
2025-03-10 09:44:19 -03:00
Danilo Leal
ee05cc3ad9 Add a line numbers toggle to the editor controls menu (#26318)
Closes https://github.com/zed-industries/zed/issues/26305

<img
src="https://github.com/user-attachments/assets/795029ad-128a-471f-9adf-c0ef26319bbf"
width="400px" />

Release Notes:

- N/A
2025-03-10 09:28:46 -03:00
Smit Barmase
5ed144f9d2 macOS: Add support for external file managers to open directory in Zed (#26357)
Closes #25421

This PR adds support for external file managers to show Zed as an option
in the "Open With" context menu for directories on macOS.

<img width="350" alt="image"
src="https://github.com/user-attachments/assets/c52acd48-73c4-47be-8683-6950e0371b73"
/>


Release Notes:

- Added support for opening folders in Zed from third-party macOS file
managers like Path Finder and Super Charge through their "Open With"
menu.
2025-03-10 15:21:39 +05:30
Julia Ryan
2a862b3c54 nix: Disable checks and remove crane workaround (#26356)
The checkPhase was failing for me in darwin so I turned it off. I think
eventually we'll want to use a separate derivation for tests (which
crane has a helper for).

Crane also solved our issue with spaces in paths so I bumped the flake
to pick up that fix and removed our workaround: ipetkov/crane#808.

Release Notes:

- N/A
2025-03-10 02:00:57 -07:00
Julia Ryan
4a7c84f490 Fix nix build (#26270)
This PR includes lots of small fixes to get our `build.nix` and
`shell.nix` back to a working state.

I've tested this by running `cargo run` (inside the devshell) and `nix
run` on x86 nixos and arm64 darwin machines. I'd appreciate it if others
could test building inside the devshell to double-check that it's not
just working because I happen to have some system-level packages
installed, as well as seeing if it works on other platforms (non-nixos
linux, arm linux, x86 darwin).

I couldn't get the full test suite (`cargo nextest run --workspace`)
passing in the devshell on darwin, but they _are_ all passing on nixos.
nixpkgs [disables some of our
tests](92d11f06d5/pkgs/by-name/ze/zed-editor/package.nix (L226-L234))
that apparently fail or are flakey on hydra, but they don't know why.
I'm going to punt on debugging those for now, especially given that they
seem to be working for me. I'm also unsure of whether we actually want
the nix checkPhase to run the full test suite (it's currently not
passing `--workspace`) given that we have separate CI that should
enforce that those pass on all PRs.

Here's an overview of the changes made:
- Fix our `generate-licenses` script
- Relaxes the `cargo-about` version requirement slightly so it doesn't
try to install an older binary when the nixpkgs one is newer than our
requirement
- Add a workaround for [this cargo-about
issue](https://github.com/zed-industries/zed/issues/19971) obviating the
need for the patching done in the nixpkgs package
- Set the new `--frozen` flag to avoid network access/mutating the
lockfile
- Use dynamic webrtc lib from nixpkgs, and fixes up the build script in
webrtc-sys that hardcodes it to be statically linked.
- Use `inputsFrom` in `shell.nix` and avoid duplicating everything from
`build.nix`
- Add a temporary workaround for an [upstream crane
bug](https://github.com/ipetkov/crane/issues/808).
- Fix shebangs in our `script` dir to not hard-code `/bin/bash`

There are still a bunch of issues that aren't resolved here, I'll make a
tracking issue for those and try to land this first just to get back to
an unbroken state. Eventually among other things I'd like to use a
`libgit2` from `staticPkgs` and musl cross compilation to build the
remote server under nix, and then add that as a separate flake output
and include it in the shell's `inputsFrom` list.

Thanks @niklaskorz, @GaetanLepage, @bbigras and all the other nixpkgs
maintainers that have kept the `zed-editor` package working and up to
date! I seriously considered just making our flake `overrideAttrs` the
package in nixpkgs given how well maintained it is.

Thanks @WeetHet for your volunteer maintinance of this flake. I
referenced #24953 while working on these fixes, and I'd love to
collaborate on adding some of those pieces like treefmt and a github
action. If you're interested I'd really appreciate some help debugging
why crane's `buildDepsOnly` isn't working for us. I'm assuming it'd make
our `nix build` times go way down from the improved dep caching if we
could get it working.

Thanks @rrbutani for all the help on this PR 💙.

Release Notes:

- N/A

---------

Co-authored-by: Rahul Butani <rrbutani@users.noreply.github.com>
Co-authored-by: Rahul Butani <rr.butani@gmail.com>
2025-03-10 01:06:11 -07:00
Mikayla Maki
230e2e4107 Restore git panel header (#26354)
Let's play around with it. This should not be added to tomorrow's
preview.

Release Notes:

- Git Beta: Added a panel header with an open diff and stage/unstage all
buttons.
2025-03-10 07:08:10 +00:00
Richard Hao
d732b8ba0f git: Disable commit message generation when commit not possible (#26329)
## Issue:

- `Generate Commit Message` will generate a random message if there are
no changes.
<img width="614" alt="image"
src="https://github.com/user-attachments/assets/c16cadac-01af-47c0-a2db-a5bbf62f84bb"
/>


## After Fixed:

- `Generate Commit Message` will be disabled if commit is not possible
<img width="610" alt="image"
src="https://github.com/user-attachments/assets/5ea9ca70-6fa3-4144-ab4e-be7a986d5496"
/>


## Release Notes:

- Fixed: Disable commit message generation when commit is not possible
2025-03-09 23:45:25 -07:00
Michael Sloan
7c3eecc9c7 Add support for querying file outline in assistant script (#26351)
Release Notes:

- N/A
2025-03-10 05:26:17 +00:00
Max Brunsfeld
fff37ab823 Follow-up fixes for recent multi buffer optimizations (#26345)
I realized that the optimization broke multi buffer syncing after buffer
reparses.

Release Notes:

- N/A
2025-03-09 22:15:38 -07:00
Kirill Bulatov
8a7a78fafb Avoid modifying the LSP message before resolving it (#26347)
Closes https://github.com/zed-industries/zed/issues/21277

To the left is current Zed, right is the improved version.
3rd message, from Zed, to resolve the item, does not have `textEdit` on
the right side, and has one on the left.
Seems to not influence the end result though, but at least Zed behaves
more appropriate now.

<img width="1727" alt="image"
src="https://github.com/user-attachments/assets/ca1236fd-9ce2-41ba-88fe-1f3178cdcbde"
/>


Instead of modifying the original LSP completion item, store completion
list defaults and apply them when the item is requested (except `data`
defaults, needed for resolve).

Now, the only place that can modify the completion items is this method,
and Python impl seems to be the one doing it:


ca9c3af56f/crates/languages/src/python.rs (L182-L204)

Seems ok to leave untouched for now.

Release Notes:

- Fixed LSP completion items modified before resolve request
2025-03-10 00:12:53 +02:00
Ben Kunkle
6de3ac3e17 Revert "Highlight super and this as keywords in JS/TS/TSX" (#26342)
Reverts zed-industries/zed#25135

This approach was not the best as explained in the response to the
original PR. Likely, the better approach is to create a newer specific
scope for these kinds of variables under the `@variable` prefix so that
themes can control these pseudo-keywords specifically
2025-03-09 16:11:37 +00:00
Smit Barmase
5aae3bdc69 copilot: Fix missing sign-out button when Zed is the edit prediction provider (#26340)
Closes #25884

Added a sign-out button for Copilot in Assistant settings, allowing
sign-out even when copilot is disabled.

<img width="500" alt="image"
src="https://github.com/user-attachments/assets/43fc97ad-f73c-49e1-a7b6-a3910434d661"
/>



Release Notes:

- Added a sign-out button for Copilot in Assistant settings.
2025-03-09 21:39:14 +05:30
Agus Zubiaga
e298301b40 assistant: Make scripting a first-class concept instead of a tool (#26338)
This PR makes refactors the scripting functionality to be a first-class
concept of the assistant instead of a generic tool, which will allow us
to build a more customized experience.

- The tool prompt has been slightly tweaked and is now included as a
system message in all conversations. I'm getting decent results, but now
that it isn't in the tools framework, it will probably require more
refining.

- The model will now include an `<eval ...>` tag at the end of the
message with the script. We parse this tag incrementally as it streams
in so that we can indicate that we are generating a script before we see
the closing `</eval>` tag. Later, this will help us interpret the script
as it arrives also.

- Threads now hold a `ScriptSession` entity which manages the state of
all scripts (from parsing to exited) in a centralized way, and will
later collect all script operations so they can be displayed in the UI.

- `script_tool` has been renamed to `assistant_scripting` 

- Script source now opens in a regular read-only buffer  

Note: We still need to handle persistence properly

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-03-09 09:01:49 +00:00
brian tan
ed6bf7f161 diagnostics: Fix losing focus when activating from diagnostics view (#25517)
Closes #25509

Changes:
- If active item is already diagnostics, don't try to focus it again.

Instead of not focusing, should it just not activate instead? Something
like:

            if !workspace
                .active_item(cx)
                .map(|item| item.item_id() == existing.item_id())
                .unwrap_or(false)
            {
workspace.activate_item(&existing, true, true, window, cx);
            }


Release Notes:

- N/A
2025-03-08 22:17:20 +00:00
Smit Barmase
f14d6670ba copilot: Fix onboarding into Copilot requires Zed restart (#26330)
Closes #25594

This PR fixes an issue where signing into Copilot required restarting
Zed.

Copilot depends on an OAuth token that comes from either `hosts.json` or
`apps.json`. Initially, both files don't exist. If neither file is
found, we fallback to watching `hosts.json` for updates. However, if the
auth process creates `apps.json`, we won't receive updates from it,
causing the UI to remain outdated.

This PR fixes that by watching the parent `github-copilot` directory
instead, which will always contain one of those files along with an
additional version file.

I have tested this on macOS and Linux Wayland.

Release Notes:

- Fixed an issue where signing into Copilot required restarting Zed.
2025-03-09 03:19:09 +05:30
Joseph T. Lyons
22d9b5d8ca Update key binding documentation (#26321)
Release Notes:

- N/A
2025-03-08 01:03:52 -05:00
875 changed files with 82762 additions and 31216 deletions

View File

@@ -19,6 +19,10 @@
# https://github.com/zed-industries/zed/pull/2394
eca93c124a488b4e538946cd2d313bd571aa2b86
# 2024-02-15 Format YAML files
# https://github.com/zed-industries/zed/pull/7887
a161a7d0c95ca7505bf9218bfae640ee5444c88b
# 2024-02-25 Format JSON files in assets/
# https://github.com/zed-industries/zed/pull/8405
ffdda588b41f7d9d270ffe76cab116f828ad545e

View File

@@ -1,51 +0,0 @@
name: Git Beta
description: There is a bug related to new Git features in Zed
type: "Bug"
labels: [git]
title: "Git Beta: <a short description of the Git bug>"
body:
- type: textarea
attributes:
label: Summary
description: Describe the bug with a one line summary, and provide detailed reproduction steps
value: |
<!-- Please insert a one line summary of the issue below -->
<!-- Include all steps necessary to reproduce from a clean Zed installation. Be verbose -->
Steps to trigger the problem:
1.
2.
3.
Actual Behavior:
Expected Behavior:
validations:
required: true
- type: textarea
id: environment
attributes:
label: Zed Version and System Specs
description: 'Open Zed, and in the command palette select "zed: Copy System Specs Into Clipboard"'
placeholder: |
Output of "zed: Copy System Specs Into Clipboard"
validations:
required: true
- type: textarea
attributes:
label: If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
description: |
macOS: `~/Library/Logs/Zed/Zed.log`
Linux: `~/.local/share/zed/logs/Zed.log` or $XDG_DATA_HOME
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
value: |
<details><summary>Zed.log</summary>
<!-- Click below this line and paste or drag-and-drop your log-->
```
```
<!-- Click above this line and paste or drag-and-drop your log--></details>
validations:
required: false

View File

@@ -10,7 +10,7 @@ runs:
cargo install cargo-nextest --locked
- name: Install Node
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
with:
node-version: "18"

View File

@@ -16,7 +16,7 @@ runs:
run: cargo install cargo-nextest --locked
- name: Install Node
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
with:
node-version: "18"

View File

@@ -23,9 +23,53 @@ env:
RUST_BACKTRACE: 1
jobs:
job_spec:
name: Decide which jobs to run
if: github.repository_owner == 'zed-industries'
outputs:
run_tests: ${{ steps.filter.outputs.run_tests }}
run_license: ${{ steps.filter.outputs.run_license }}
runs-on:
- ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
# 350 is arbitrary; ~10days of history on main (5secs); full history is ~25secs
fetch-depth: ${{ github.ref == 'refs/heads/main' && 2 || 350 }}
- name: Fetch git history and generate output filters
id: filter
run: |
if [ -z "$GITHUB_BASE_REF" ]; then
echo "Not in a PR context (i.e., push to main/stable/preview)"
COMPARE_REV=$(git rev-parse HEAD~1)
else
echo "In a PR context comparing to pull_request.base.ref"
git fetch origin "$GITHUB_BASE_REF" --depth=350
COMPARE_REV=$(git merge-base "origin/${GITHUB_BASE_REF}" HEAD)
fi
# Specify anything which should skip full CI in this regex:
# - docs/
# - .github/ISSUE_TEMPLATE/
# - .github/workflows/ (except .github/workflows/ci.yml)
SKIP_REGEX='^(docs/|\.github/(ISSUE_TEMPLATE|workflows/(?!ci)))'
if [[ $(git diff --name-only $COMPARE_REV ${{ github.sha }} | grep -vP "$SKIP_REGEX") ]]; then
echo "run_tests=true" >> $GITHUB_OUTPUT
else
echo "run_tests=false" >> $GITHUB_OUTPUT
fi
if [[ $(git diff --name-only $COMPARE_REV ${{ github.sha }} | grep '^Cargo.lock') ]]; then
echo "run_license=true" >> $GITHUB_OUTPUT
else
echo "run_license=false" >> $GITHUB_OUTPUT
fi
migration_checks:
name: Check Postgres and Protobuf migrations, mergability
if: github.repository_owner == 'zed-industries'
needs: [job_spec]
if: |
github.repository_owner == 'zed-industries' &&
needs.job_spec.outputs.run_tests == 'true'
timeout-minutes: 60
runs-on:
- self-hosted
@@ -69,6 +113,7 @@ jobs:
style:
timeout-minutes: 60
name: Check formatting and spelling
needs: [job_spec]
if: github.repository_owner == 'zed-industries'
runs-on:
- buildjet-8vcpu-ubuntu-2204
@@ -76,6 +121,21 @@ jobs:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9
- name: Prettier Check on /docs
working-directory: ./docs
run: |
pnpm dlx prettier@${PRETTIER_VERSION} . --check || {
echo "To fix, run from the root of the zed repo:"
echo " cd docs && pnpm dlx prettier@${PRETTIER_VERSION} . --write && cd .."
false
}
env:
PRETTIER_VERSION: 3.5.0
# To support writing comments that they will certainly be revisited.
- name: Check for todo! and FIXME comments
run: script/check-todos
@@ -91,7 +151,10 @@ jobs:
macos_tests:
timeout-minutes: 60
name: (macOS) Run Clippy and tests
if: github.repository_owner == 'zed-industries'
needs: [job_spec]
if: |
github.repository_owner == 'zed-industries' &&
needs.job_spec.outputs.run_tests == 'true'
runs-on:
- self-hosted
- test
@@ -123,7 +186,9 @@ jobs:
- name: Check licenses
run: |
script/check-licenses
script/generate-licenses /tmp/zed_licenses_output
if [[ "${{ needs.job_spec.outputs.run_license }}" == "true" ]]; then
script/generate-licenses /tmp/zed_licenses_output
fi
- name: Check for new vulnerable dependencies
if: github.event_name == 'pull_request'
@@ -154,7 +219,10 @@ jobs:
linux_tests:
timeout-minutes: 60
name: (Linux) Run Clippy and tests
if: github.repository_owner == 'zed-industries'
needs: [job_spec]
if: |
github.repository_owner == 'zed-industries' &&
needs.job_spec.outputs.run_tests == 'true'
runs-on:
- buildjet-16vcpu-ubuntu-2204
steps:
@@ -167,7 +235,7 @@ jobs:
clean: false
- name: Cache dependencies
uses: swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2
uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-provider: "buildjet"
@@ -203,9 +271,12 @@ jobs:
build_remote_server:
timeout-minutes: 60
name: (Linux) Build Remote Server
if: github.repository_owner == 'zed-industries'
needs: [job_spec]
if: |
github.repository_owner == 'zed-industries' &&
needs.job_spec.outputs.run_tests == 'true'
runs-on:
- buildjet-16vcpu-ubuntu-2204
- buildjet-8vcpu-ubuntu-2204
steps:
- name: Add Rust to the PATH
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
@@ -216,7 +287,7 @@ jobs:
clean: false
- name: Cache dependencies
uses: swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2
uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-provider: "buildjet"
@@ -239,21 +310,12 @@ jobs:
windows_clippy:
timeout-minutes: 60
name: (Windows) Run Clippy
if: github.repository_owner == 'zed-industries'
runs-on: hosted-windows-2
needs: [job_spec]
if: |
github.repository_owner == 'zed-industries' &&
needs.job_spec.outputs.run_tests == 'true'
runs-on: windows-2025-16
steps:
# Temporarily Collect some metadata about the hardware behind our runners.
- name: GHA Runner Info
run: |
Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -Uri "http://169.254.169.254/metadata/instance/compute?api-version=2023-07-01" |
ConvertTo-Json -Depth 10 |
jq "{ vm_size: .vmSize, location: .location, os_disk_gb: (.storageProfile.osDisk.diskSizeGB | tonumber), rs_disk_gb: (.storageProfile.resourceDisk.size | tonumber / 1024) }"
@{
Cores = (Get-CimInstance Win32_Processor).NumberOfCores
vCPUs = (Get-CimInstance Win32_Processor).NumberOfLogicalProcessors
RamGb = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)
cpuid = (Get-CimInstance Win32_Processor).Name.Trim()
} | ConvertTo-Json
# more info here:- https://github.com/rust-lang/cargo/issues/13020
- name: Enable longer pathnames for git
run: git config --system core.longpaths true
@@ -272,7 +334,7 @@ jobs:
Copy-Item -Path "${{ github.workspace }}" -Destination "${{ env.ZED_WORKSPACE }}" -Recurse
- name: Cache dependencies
uses: swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2
uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
workspaces: ${{ env.ZED_WORKSPACE }}
@@ -306,21 +368,13 @@ jobs:
windows_tests:
timeout-minutes: 60
name: (Windows) Run Tests
if: ${{ github.repository_owner == 'zed-industries' && (github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'windows')) }}
runs-on: hosted-windows-2
needs: [job_spec]
if: |
github.repository_owner == 'zed-industries' &&
needs.job_spec.outputs.run_tests == 'true'
# Use bigger runners for PRs (speed); smaller for async (cost)
runs-on: ${{ github.event_name == 'pull_request' && 'windows-2025-32' || 'windows-2025-16' }}
steps:
# Temporarily Collect some metadata about the hardware behind our runners.
- name: GHA Runner Info
run: |
Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -Uri "http://169.254.169.254/metadata/instance/compute?api-version=2023-07-01" |
ConvertTo-Json -Depth 10 |
jq "{ vm_size: .vmSize, location: .location, os_disk_gb: (.storageProfile.osDisk.diskSizeGB | tonumber), rs_disk_gb: (.storageProfile.resourceDisk.size | tonumber / 1024) }"
@{
Cores = (Get-CimInstance Win32_Processor).NumberOfCores
vCPUs = (Get-CimInstance Win32_Processor).NumberOfLogicalProcessors
RamGb = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)
cpuid = (Get-CimInstance Win32_Processor).Name.Trim()
} | ConvertTo-Json
# more info here:- https://github.com/rust-lang/cargo/issues/13020
- name: Enable longer pathnames for git
run: git config --system core.longpaths true
@@ -339,7 +393,7 @@ jobs:
Copy-Item -Path "${{ github.workspace }}" -Destination "${{ env.ZED_WORKSPACE }}" -Recurse
- name: Cache dependencies
uses: swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2
uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
workspaces: ${{ env.ZED_WORKSPACE }}
@@ -372,13 +426,49 @@ jobs:
Remove-Item -Path "${{ env.CARGO_HOME }}/config.toml" -Force
}
tests_pass:
name: Tests Pass
runs-on: ubuntu-latest
needs:
- job_spec
- style
- migration_checks
- linux_tests
- build_remote_server
- macos_tests
- windows_clippy
- windows_tests
if: always()
steps:
- name: Check all tests passed
run: |
# Check dependent jobs...
RET_CODE=0
# Always check style
[[ "${{ needs.style.result }}" != 'success' ]] && { RET_CODE=1; echo "style tests failed"; }
# Only check test jobs if they were supposed to run
if [[ "${{ needs.job_spec.outputs.run_tests }}" == "true" ]]; then
[[ "${{ needs.macos_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "macOS tests failed"; }
[[ "${{ needs.linux_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "Linux tests failed"; }
[[ "${{ needs.windows_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "Windows tests failed"; }
[[ "${{ needs.windows_clippy.result }}" != 'success' ]] && { RET_CODE=1; echo "Windows clippy failed"; }
[[ "${{ needs.build_remote_server.result }}" != 'success' ]] && { RET_CODE=1; echo "Remote server build failed"; }
fi
if [[ "$RET_CODE" -eq 0 ]]; then
echo "All tests passed successfully!"
fi
exit $RET_CODE
bundle-mac:
timeout-minutes: 120
name: Create a macOS bundle
runs-on:
- self-hosted
- bundle
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
if: |
startsWith(github.ref, 'refs/tags/v')
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
needs: [macos_tests]
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
@@ -392,7 +482,7 @@ jobs:
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
steps:
- name: Install Node
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
with:
node-version: "18"
@@ -436,14 +526,14 @@ jobs:
mv target/x86_64-apple-darwin/release/Zed.dmg target/x86_64-apple-darwin/release/Zed-x86_64.dmg
- name: Upload app bundle (aarch64) to workflow run if main branch or specific label
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
with:
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.dmg
path: target/aarch64-apple-darwin/release/Zed-aarch64.dmg
- name: Upload app bundle (x86_64) to workflow run if main branch or specific label
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
with:
name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.dmg
@@ -468,7 +558,9 @@ jobs:
name: Linux x86_x64 release bundle
runs-on:
- buildjet-16vcpu-ubuntu-2004
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
if: |
startsWith(github.ref, 'refs/tags/v')
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
needs: [linux_tests]
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
@@ -485,7 +577,7 @@ jobs:
run: ./script/linux && ./script/install-mold 2.34.0
- name: Determine version and release channel
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
if: startsWith(github.ref, 'refs/tags/v')
run: |
# This exports RELEASE_CHANNEL into env (GITHUB_ENV)
script/determine-release-channel
@@ -494,15 +586,19 @@ jobs:
run: script/bundle-linux
- name: Upload Linux bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: |
github.ref == 'refs/heads/main'
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
with:
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
path: target/release/zed-*.tar.gz
- name: Upload Linux remote server to workflow run if main branch or specific label
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: |
github.ref == 'refs/heads/main'
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
with:
name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.gz
path: target/zed-remote-server-linux-x86_64.gz
@@ -523,7 +619,9 @@ jobs:
name: Linux arm64 release bundle
runs-on:
- buildjet-16vcpu-ubuntu-2204-arm
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
if: |
startsWith(github.ref, 'refs/tags/v')
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
needs: [linux_tests]
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
@@ -540,7 +638,7 @@ jobs:
run: ./script/linux
- name: Determine version and release channel
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
if: startsWith(github.ref, 'refs/tags/v')
run: |
# This exports RELEASE_CHANNEL into env (GITHUB_ENV)
script/determine-release-channel
@@ -549,15 +647,19 @@ jobs:
run: script/bundle-linux
- name: Upload Linux bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: |
github.ref == 'refs/heads/main'
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
with:
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
path: target/release/zed-*.tar.gz
- name: Upload Linux remote server to workflow run if main branch or specific label
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: |
github.ref == 'refs/heads/main'
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
with:
name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.gz
path: target/zed-remote-server-linux-aarch64.gz
@@ -575,7 +677,9 @@ jobs:
auto-release-preview:
name: Auto release preview
if: ${{ startsWith(github.ref, 'refs/tags/v') && endsWith(github.ref, '-pre') && !endsWith(github.ref, '.0-pre') }}
if: |
startsWith(github.ref, 'refs/tags/v')
&& endsWith(github.ref, '-pre') && !endsWith(github.ref, '.0-pre')
needs: [bundle-mac, bundle-linux-x86_x64, bundle-linux-aarch64]
runs-on:
- self-hosted

View File

@@ -1,7 +1,7 @@
name: "Close Stale Issues"
on:
schedule:
- cron: "0 11 * * 2"
- cron: "0 7,9,11 * * 3"
workflow_dispatch:
jobs:

View File

@@ -13,11 +13,12 @@ jobs:
id: get-release-url
run: |
if [ "${{ github.event.release.prerelease }}" == "true" ]; then
URL="https://zed.dev/releases/preview/latest"
URL="https://zed.dev/releases/preview/latest"
else
URL="https://zed.dev/releases/stable/latest"
URL="https://zed.dev/releases/stable/latest"
fi
echo "::set-output name=URL::$URL"
echo "URL=$URL" >> $GITHUB_OUTPUT
- name: Get content
uses: 2428392/gh-truncate-string-action@b3ff790d21cf42af3ca7579146eedb93c8fb0757 # v1.4.1
id: get-content
@@ -33,3 +34,35 @@ jobs:
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
content: ${{ steps.get-content.outputs.string }}
send_release_notes_email:
if: github.repository_owner == 'zed-industries' && !github.event.release.prerelease
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Check if release was promoted from preview
id: check-promotion-from-preview
run: |
VERSION="${{ github.event.release.tag_name }}"
PREVIEW_TAG="${VERSION}-pre"
if git rev-parse "$PREVIEW_TAG" > /dev/null 2>&1; then
echo "was_promoted_from_preview=true" >> $GITHUB_OUTPUT
else
echo "was_promoted_from_preview=false" >> $GITHUB_OUTPUT
fi
- name: Send release notes email
if: steps.check-promotion-from-preview.outputs.was_promoted_from_preview == 'true'
run: |
TAG="${{ github.event.release.tag_name }}"
echo \"${{ toJSON(github.event.release.body) }}\" > release_body.txt
jq -n --arg tag "$TAG" --rawfile body release_body.txt '{version: $tag, markdown_body: $body}' \
> release_data.json
curl -X POST "https://zed.dev/api/send_release_notes_email" \
-H "Authorization: Bearer ${{ secrets.RELEASE_NOTES_API_TOKEN }}" \
-H "Content-Type: application/json" \
-d @release_data.json

View File

@@ -22,7 +22,7 @@ jobs:
version: 9
- name: Setup Node
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
with:
node-version: "20"
cache: "pnpm"

View File

@@ -37,35 +37,35 @@ jobs:
mdbook build ./docs --dest-dir=../target/deploy/docs/
- name: Deploy Docs
uses: cloudflare/wrangler-action@392082e81ffbcb9ebdde27400634aa004b35ea37 # v3
uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy target/deploy --project-name=docs
- name: Deploy Install
uses: cloudflare/wrangler-action@392082e81ffbcb9ebdde27400634aa004b35ea37 # v3
uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: r2 object put -f script/install.sh zed-open-source-website-assets/install.sh
- name: Deploy Docs Workers
uses: cloudflare/wrangler-action@392082e81ffbcb9ebdde27400634aa004b35ea37 # v3
uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy .cloudflare/docs-proxy/src/worker.js
- name: Deploy Install Workers
uses: cloudflare/wrangler-action@392082e81ffbcb9ebdde27400634aa004b35ea37 # v3
uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy .cloudflare/docs-proxy/src/worker.js
- name: Preserve Wrangler logs
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: wrangler_logs

View File

@@ -1,39 +0,0 @@
name: Docs
on:
pull_request:
paths:
- "docs/**"
push:
branches:
- main
jobs:
check_formatting:
name: "Check formatting"
if: github.repository_owner == 'zed-industries'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9
- name: Prettier Check on /docs
working-directory: ./docs
run: |
pnpm dlx prettier@${PRETTIER_VERSION} . --check || {
echo "To fix, run from the root of the zed repo:"
echo " cd docs && pnpm dlx prettier@${PRETTIER_VERSION} . --write && cd .."
false
}
env:
PRETTIER_VERSION: 3.5.0
- name: Check for Typos with Typos-CLI
uses: crate-ci/typos@8e6a4285bcbde632c5d79900a7779746e8b7ea3f # v1.24.6
with:
config: ./typos.toml
files: ./docs/

View File

@@ -18,7 +18,7 @@ jobs:
version: 9
- name: Setup Node
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
with:
node-version: "20"
cache: "pnpm"

View File

@@ -22,7 +22,7 @@ jobs:
clean: false
- name: Cache dependencies
uses: swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2
uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-provider: "github"

View File

@@ -23,7 +23,7 @@ jobs:
- buildjet-16vcpu-ubuntu-2204
steps:
- name: Install Node
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
with:
node-version: "18"

View File

@@ -71,7 +71,7 @@ jobs:
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
steps:
- name: Install Node
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
with:
node-version: "18"
@@ -170,6 +170,57 @@ jobs:
- name: Upload Zed Nightly
run: script/upload-nightly linux-targz
bundle-nix:
timeout-minutes: 60
name: (${{ matrix.system.os }}) Nix Build
continue-on-error: true
strategy:
fail-fast: false
matrix:
system:
- os: x86 Linux
runner: buildjet-16vcpu-ubuntu-2204
install_nix: true
- os: arm Mac
runner: [macOS, ARM64, test]
install_nix: false
- os: arm Linux
runner: buildjet-16vcpu-ubuntu-2204-arm
install_nix: true
if: github.repository_owner == 'zed-industries'
runs-on: ${{ matrix.system.runner }}
needs: tests
env:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
GIT_LFS_SKIP_SMUDGE: 1 # breaks the livekit rust sdk examples which we don't actually depend on
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
# on our macs we manually install nix. for some reason the cachix action is running
# under a non-login /bin/bash shell which doesn't source the proper script to add the
# nix profile to PATH, so we manually add them here
- name: Set path
if: ${{ ! matrix.system.install_nix }}
run: |
echo "/nix/var/nix/profiles/default/bin" >> $GITHUB_PATH
echo "/Users/administrator/.nix-profile/bin" >> $GITHUB_PATH
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
if: ${{ matrix.system.install_nix }}
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
with:
name: zed-industries
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- run: nix build
- run: nix-collect-garbage -d
update-nightly-tag:
name: Update nightly tag
if: github.repository_owner == 'zed-industries'

19
.zed/debug.json Normal file
View File

@@ -0,0 +1,19 @@
[
{
"label": "Debug Zed with LLDB",
"adapter": "lldb",
"program": "$ZED_WORKTREE_ROOT/target/debug/zed",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT"
},
{
"label": "Debug Zed with GDB",
"adapter": "gdb",
"program": "$ZED_WORKTREE_ROOT/target/debug/zed",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT",
"initialize_args": {
"stopAtBeginningOfMainSubprogram": true
}
}
]

1476
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@ members = [
"crates/assistant",
"crates/assistant2",
"crates/assistant_context_editor",
"crates/assistant_eval",
"crates/assistant_settings",
"crates/assistant_slash_command",
"crates/assistant_slash_commands",
@@ -36,6 +37,10 @@ members = [
"crates/context_server_settings",
"crates/copilot",
"crates/credentials_provider",
"crates/dap",
"crates/dap_adapters",
"crates/debugger_tools",
"crates/debugger_ui",
"crates/db",
"crates/deepseek",
"crates/diagnostics",
@@ -64,6 +69,8 @@ members = [
"crates/gpui_tokio",
"crates/html_to_markdown",
"crates/http_client",
"crates/http_client_tls",
"crates/icons",
"crates/image_viewer",
"crates/indexed_docs",
"crates/inline_completion",
@@ -118,7 +125,6 @@ members = [
"crates/rope",
"crates/rpc",
"crates/schema_generator",
"crates/scripting_tool",
"crates/search",
"crates/semantic_index",
"crates/semantic_version",
@@ -154,6 +160,7 @@ members = [
"crates/ui",
"crates/ui_input",
"crates/ui_macros",
"crates/ui_prompt",
"crates/util",
"crates/util_macros",
"crates/vim",
@@ -164,6 +171,8 @@ members = [
"crates/zed",
"crates/zed_actions",
"crates/zeta",
"crates/zlog",
"crates/zlog_settings",
#
# Extensions
@@ -174,15 +183,11 @@ members = [
"extensions/html",
"extensions/perplexity",
"extensions/proto",
"extensions/purescript",
"extensions/ruff",
"extensions/slash-commands-example",
"extensions/snippets",
"extensions/terraform",
"extensions/test-extension",
"extensions/toml",
"extensions/uiua",
"extensions/zig",
#
# Tooling
@@ -210,6 +215,7 @@ assets = { path = "crates/assets" }
assistant = { path = "crates/assistant" }
assistant2 = { path = "crates/assistant2" }
assistant_context_editor = { path = "crates/assistant_context_editor" }
assistant_eval = { path = "crates/assistant_eval" }
assistant_settings = { path = "crates/assistant_settings" }
assistant_slash_command = { path = "crates/assistant_slash_command" }
assistant_slash_commands = { path = "crates/assistant_slash_commands" }
@@ -237,7 +243,11 @@ context_server = { path = "crates/context_server" }
context_server_settings = { path = "crates/context_server_settings" }
copilot = { path = "crates/copilot" }
credentials_provider = { path = "crates/credentials_provider" }
dap = { path = "crates/dap" }
dap_adapters = { path = "crates/dap_adapters" }
db = { path = "crates/db" }
debugger_ui = { path = "crates/debugger_ui" }
debugger_tools = { path = "crates/debugger_tools" }
deepseek = { path = "crates/deepseek" }
diagnostics = { path = "crates/diagnostics" }
buffer_diff = { path = "crates/buffer_diff" }
@@ -264,6 +274,8 @@ gpui_macros = { path = "crates/gpui_macros" }
gpui_tokio = { path = "crates/gpui_tokio" }
html_to_markdown = { path = "crates/html_to_markdown" }
http_client = { path = "crates/http_client" }
http_client_tls = { path = "crates/http_client_tls" }
icons = { path = "crates/icons" }
image_viewer = { path = "crates/image_viewer" }
indexed_docs = { path = "crates/indexed_docs" }
inline_completion = { path = "crates/inline_completion" }
@@ -318,7 +330,6 @@ reqwest_client = { path = "crates/reqwest_client" }
rich_text = { path = "crates/rich_text" }
rope = { path = "crates/rope" }
rpc = { path = "crates/rpc" }
scripting_tool = { path = "crates/scripting_tool" }
search = { path = "crates/search" }
semantic_index = { path = "crates/semantic_index" }
semantic_version = { path = "crates/semantic_version" }
@@ -354,6 +365,7 @@ toolchain_selector = { path = "crates/toolchain_selector" }
ui = { path = "crates/ui" }
ui_input = { path = "crates/ui_input" }
ui_macros = { path = "crates/ui_macros" }
ui_prompt = { path = "crates/ui_prompt" }
util = { path = "crates/util" }
util_macros = { path = "crates/util_macros" }
vim = { path = "crates/vim" }
@@ -364,13 +376,15 @@ worktree = { path = "crates/worktree" }
zed = { path = "crates/zed" }
zed_actions = { path = "crates/zed_actions" }
zeta = { path = "crates/zeta" }
zlog = { path = "crates/zlog" }
zlog_settings = { path = "crates/zlog_settings" }
#
# External crates
#
aho-corasick = "1.1"
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty.git", rev = "03c2907b44b4189aac5fdeaea331f5aab5c7072e" }
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty.git", branch = "add-hush-login-flag" }
any_vec = "0.14"
anyhow = "1.0.86"
arrayvec = { version = "0.7.4", features = ["serde"] }
@@ -402,6 +416,7 @@ bytes = "1.0"
cargo_metadata = "0.19"
cargo_toml = "0.21"
chrono = { version = "0.4", features = ["serde"] }
circular-buffer = "1.0"
clap = { version = "4.4", features = ["derive"] }
cocoa = "0.26"
cocoa-foundation = "0.2.0"
@@ -410,6 +425,7 @@ core-foundation = "0.9.3"
core-foundation-sys = "0.8.6"
ctor = "0.4.0"
dashmap = "6.0"
dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "bfd4af0" }
derive_more = "0.99.17"
dirs = "4.0"
ec4rs = "1.1"
@@ -421,8 +437,7 @@ fork = "0.2.0"
futures = "0.3"
futures-batch = "0.6.1"
futures-lite = "1.13"
# TODO: get back to regular versions when https://github.com/rust-lang/git2-rs/pull/1120 is released
git2 = { git = "https://github.com/rust-lang/git2-rs", rev = "a3b90cb3756c1bb63e2317bf9cfa57838178de5c", default-features = false }
git2 = { version = "0.20.1", default-features = false }
globset = "0.4"
handlebars = "4.3"
heed = { version = "0.21.0", features = ["read-txn-no-tls"] }
@@ -455,6 +470,7 @@ mlua = { version = "0.10", features = ["lua54", "vendored", "async", "send"] }
nanoid = "0.4"
nbformat = { version = "0.10.0" }
nix = "0.29"
open = "5.0.0"
num-format = "0.4.4"
ordered-float = "2.1.1"
palette = { version = "0.7.5", default-features = false, features = ["std"] }
@@ -524,7 +540,7 @@ sys-locale = "0.3.1"
sysinfo = "0.31.0"
take-until = "0.2.0"
tempfile = "3.9.0"
thiserror = "1.0.29"
thiserror = "2.0.12"
tiktoken-rs = "0.6.0"
time = { version = "0.3", features = [
"macros",
@@ -565,6 +581,7 @@ unindent = "0.2.0"
unicode-segmentation = "1.10"
unicode-script = "0.5.7"
url = "2.2"
urlencoding = "2.1.2"
uuid = { version = "1.1.2", features = ["v4", "v5", "v7", "serde"] }
wasmparser = "0.221"
wasm-encoder = "0.221"
@@ -597,12 +614,12 @@ features = [
]
[workspace.dependencies.windows]
version = "0.58"
version = "0.61"
features = [
"implement",
"Foundation_Collections",
"Foundation_Numerics",
"Storage",
"Storage_Search",
"Storage_Streams",
"System_Threading",
"UI_StartScreen",
"UI_ViewManagement",
@@ -623,9 +640,11 @@ features = [
"Win32_System_Com_StructuredStorage",
"Win32_System_Console",
"Win32_System_DataExchange",
"Win32_System_IO",
"Win32_System_LibraryLoader",
"Win32_System_Memory",
"Win32_System_Ole",
"Win32_System_Pipes",
"Win32_System_SystemInformation",
"Win32_System_SystemServices",
"Win32_System_Threading",
@@ -751,5 +770,9 @@ new_ret_no_self = { level = "allow" }
should_implement_trait = { level = "allow" }
let_underscore_future = "allow"
# in Rust it can be very tedious to reduce argument count without
# running afoul of the borrow checker.
too_many_arguments = "allow"
[workspace.metadata.cargo-machete]
ignored = ["bindgen", "cbindgen", "prost_build", "serde", "component", "linkme"]

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-right-left"><path d="m16 3 4 4-4 4"/><path d="M20 7H4"/><path d="m8 21-4-4 4-4"/><path d="M4 17h16"/></svg>

After

Width:  |  Height:  |  Size: 316 B

View File

@@ -0,0 +1,3 @@
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.4 2.6H5.75C5.75 2.50717 5.71312 2.41815 5.64749 2.35251C5.58185 2.28688 5.49283 2.25 5.4 2.25V2.6ZM2.6 2.25C2.4067 2.25 2.25 2.4067 2.25 2.6C2.25 2.7933 2.4067 2.95 2.6 2.95V2.25ZM5.05 5.4C5.05 5.5933 5.2067 5.75 5.4 5.75C5.5933 5.75 5.75 5.5933 5.75 5.4H5.05ZM2.35252 5.15251C2.21583 5.2892 2.21583 5.5108 2.35252 5.64748C2.4892 5.78417 2.7108 5.78417 2.84749 5.64748L2.35252 5.15251ZM5.4 2.25H2.6V2.95H5.4V2.25ZM5.05 2.6V5.4H5.75V2.6H5.05ZM5.15252 2.35251L2.35252 5.15251L2.84749 5.64748L5.64749 2.84748L5.15252 2.35251Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 650 B

1
assets/icons/brain.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-brain"><path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"/><path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"/><path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"/><path d="M17.599 6.5a3 3 0 0 0 .399-1.375"/><path d="M6.003 5.125A3 3 0 0 0 6.401 6.5"/><path d="M3.477 10.896a4 4 0 0 1 .585-.396"/><path d="M19.938 10.5a4 4 0 0 1 .585.396"/><path d="M6 18a4 4 0 0 1-1.967-.516"/><path d="M19.967 17.484A4 4 0 0 1 18 18"/></svg>

After

Width:  |  Height:  |  Size: 718 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clipboard"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg>

After

Width:  |  Height:  |  Size: 358 B

1
assets/icons/cog.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-cog"><path d="M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z"/><path d="M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z"/><path d="M12 2v2"/><path d="M12 22v-2"/><path d="m17 20.66-1-1.73"/><path d="M11 10.27 7 3.34"/><path d="m20.66 17-1.73-1"/><path d="m3.34 7 1.73 1"/><path d="M14 12h8"/><path d="M2 12h2"/><path d="m20.66 7-1.73 1"/><path d="m3.34 17 1.73-1"/><path d="m17 3.34-1 1.73"/><path d="m11 13.73-4 6.93"/></svg>

After

Width:  |  Height:  |  Size: 608 B

1
assets/icons/debug.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bug"><path d="m8 2 1.88 1.88"/><path d="M14.12 3.88 16 2"/><path d="M9 7.13v-1a3.003 3.003 0 1 1 6 0v1"/><path d="M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6"/><path d="M12 20v-9"/><path d="M6.53 9C4.6 8.8 3 7.1 3 5"/><path d="M6 13H2"/><path d="M3 21c0-2.1 1.7-3.9 3.8-4"/><path d="M20.97 5c0 2.1-1.6 3.8-3.5 4"/><path d="M22 13h-4"/><path d="M17.2 17c2.1.1 3.8 1.9 3.8 4"/></svg>

After

Width:  |  Height:  |  Size: 615 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle"><circle cx="12" cy="12" r="10"/></svg>

After

Width:  |  Height:  |  Size: 257 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-step-forward"><line x1="6" x2="6" y1="4" y2="20"/><polygon points="10,4 20,12 10,20"/></svg>

After

Width:  |  Height:  |  Size: 295 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-unplug"><path d="m19 5 3-3"/><path d="m2 22 3-3"/><path d="M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z"/><path d="M7.5 13.5 10 11"/><path d="M10.5 16.5 13 14"/><path d="m12 6 6 6 2.3-2.3a2.4 2.4 0 0 0 0-3.4l-2.6-2.6a2.4 2.4 0 0 0-3.4 0Z"/></svg>

After

Width:  |  Height:  |  Size: 474 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-off"><path d="m2 2 20 20"/><path d="M8.35 2.69A10 10 0 0 1 21.3 15.65"/><path d="M19.08 19.08A10 10 0 1 1 4.92 4.92"/></svg>

After

Width:  |  Height:  |  Size: 334 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-circle"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/></svg>

After

Width:  |  Height:  |  Size: 275 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-pause"><rect x="14" y="4" width="4" height="16" rx="1"/><rect x="6" y="4" width="4" height="16" rx="1"/></svg>

After

Width:  |  Height:  |  Size: 313 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-rotate-ccw"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>

After

Width:  |  Height:  |  Size: 302 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-undo-dot"><path d="M21 17a9 9 0 0 0-15-6.7L3 13"/><path d="M3 7v6h6"/><circle cx="12" cy="17" r="1"/></svg>

After

Width:  |  Height:  |  Size: 310 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-from-dot">
<path d="m5 15 7 7 7-7"/>
<path d="M12 8v14"/>
<circle cx="12" cy="3" r="1"/>
</svg>

After

Width:  |  Height:  |  Size: 313 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-from-dot">
<path d="m3 10 9-8 9 8"/>
<path d="M12 17V2"/>
<circle cx="12" cy="21" r="1"/>
</svg>

After

Width:  |  Height:  |  Size: 314 B

View File

@@ -0,0 +1,5 @@
<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-redo-dot">
<circle cx="12" cy="17" r="1"/>
<path d="M21 7v6h-6"/>
<path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7"/>
</svg>

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square"><rect width="18" height="18" x="3" y="3" rx="2"/></svg>

After

Width:  |  Height:  |  Size: 266 B

View File

@@ -0,0 +1,4 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5 8.5L7.5 11.5M7.5 11.5L4.5 8.5M7.5 11.5L7.5 5.5" stroke="black" stroke-linecap="square"/>
<path d="M5 3.5L10 3.5" stroke="black"/>
</svg>

After

Width:  |  Height:  |  Size: 248 B

View File

@@ -0,0 +1,4 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.5 6.5L7.5 3.5M7.5 3.5L10.5 6.5M7.5 3.5V9.5" stroke="black" stroke-linecap="square"/>
<path d="M5 11.5H10" stroke="black"/>
</svg>

After

Width:  |  Height:  |  Size: 238 B

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.0001 1.33334H4.00008C3.64646 1.33334 3.30732 1.47382 3.05727 1.72387C2.80722 1.97392 2.66675 2.31305 2.66675 2.66668V13.3333C2.66675 13.687 2.80722 14.0261 3.05727 14.2762C3.30732 14.5262 3.64646 14.6667 4.00008 14.6667H12.0001C12.3537 14.6667 12.6928 14.5262 12.9429 14.2762C13.1929 14.0261 13.3334 13.687 13.3334 13.3333V4.66668L10.0001 1.33334Z" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 8H10" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 10V6" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 762 B

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.0001 1.33334H4.00008C3.64646 1.33334 3.30732 1.47382 3.05727 1.72387C2.80722 1.97392 2.66675 2.31305 2.66675 2.66668V13.3333C2.66675 13.687 2.80722 14.0261 3.05727 14.2762C3.30732 14.5262 3.64646 14.6667 4.00008 14.6667H12.0001C12.3537 14.6667 12.6928 14.5262 12.9429 14.2762C13.1929 14.0261 13.3334 13.687 13.3334 13.3333V4.66668L10.0001 1.33334Z" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.66659 6.5L6.33325 9.83333" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.33325 6.5L9.66659 9.83333" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 804 B

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.5 13L1.5 5H11.5L6.5 13Z" fill="black"/>
<path d="M14 9H9L11.5 5L14 9Z" fill="black" fill-opacity="0.75"/>
<path d="M9 9L14 9L11.5 13L9 9Z" fill="black" fill-opacity="0.65"/>
<path d="M14 5L15.25 7L12.75 7L14 5Z" fill="black" fill-opacity="0.5"/>
<path d="M14 9L12.75 7H15.25L14 9Z" fill="black" fill-opacity="0.55"/>
</svg>

After

Width:  |  Height:  |  Size: 432 B

View File

@@ -0,0 +1,40 @@
<svg width="400" height="120" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="tilePattern" width="124" height="24" patternUnits="userSpaceOnUse">
<svg width="124" height="24" viewBox="0 0 124 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.2">
<path d="M16.666 12.0013L11.9993 16.668L7.33268 12.0013" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 7.33464L12 16.668" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M29 8.33464C29.3682 8.33464 29.6667 8.03616 29.6667 7.66797C29.6667 7.29978 29.3682 7.0013 29 7.0013C28.6318 7.0013 28.3333 7.29978 28.3333 7.66797C28.3333 8.03616 28.6318 8.33464 29 8.33464ZM29 9.66797C30.1046 9.66797 31 8.77254 31 7.66797C31 6.5634 30.1046 5.66797 29 5.66797C27.8954 5.66797 27 6.5634 27 7.66797C27 8.77254 27.8954 9.66797 29 9.66797Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M35 8.33464C35.3682 8.33464 35.6667 8.03616 35.6667 7.66797C35.6667 7.29978 35.3682 7.0013 35 7.0013C34.6318 7.0013 34.3333 7.29978 34.3333 7.66797C34.3333 8.03616 34.6318 8.33464 35 8.33464ZM35 9.66797C36.1046 9.66797 37 8.77254 37 7.66797C37 6.5634 36.1046 5.66797 35 5.66797C33.8954 5.66797 33 6.5634 33 7.66797C33 8.77254 33.8954 9.66797 35 9.66797Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M29 16.9987C29.3682 16.9987 29.6667 16.7002 29.6667 16.332C29.6667 15.9638 29.3682 15.6654 29 15.6654C28.6318 15.6654 28.3333 15.9638 28.3333 16.332C28.3333 16.7002 28.6318 16.9987 29 16.9987ZM29 18.332C30.1046 18.332 31 17.4366 31 16.332C31 15.2275 30.1046 14.332 29 14.332C27.8954 14.332 27 15.2275 27 16.332C27 17.4366 27.8954 18.332 29 18.332Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.334 9H29.6673V11.4615C30.2383 11.1443 31.0005 11 32.0007 11H33.6675C34.0356 11 34.334 10.7017 34.334 10.3333V9H35.6673V10.3333C35.6673 11.4378 34.7723 12.3333 33.6675 12.3333H32.0007C30.8614 12.3333 30.3692 12.5484 30.1298 12.7549C29.9016 12.9516 29.7857 13.2347 29.6673 13.742V15H28.334V9Z" fill="white"/>
<path d="M48.668 8.66406H55.3346V15.3307" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M48.668 15.3307L55.3346 8.66406" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M76.5871 9.40624C76.8514 9.14195 77 8.78346 77 8.40965C77 8.03583 76.8516 7.67731 76.5873 7.41295C76.323 7.14859 75.9645 7.00005 75.5907 7C75.2169 6.99995 74.8584 7.14841 74.594 7.4127L67.921 14.0874C67.8049 14.2031 67.719 14.3456 67.671 14.5024L67.0105 16.6784C66.9975 16.7217 66.9966 16.7676 67.0076 16.8113C67.0187 16.8551 67.0414 16.895 67.0734 16.9269C67.1053 16.9588 67.1453 16.9815 67.1891 16.9925C67.2328 17.0035 67.2788 17.0024 67.322 16.9894L69.4985 16.3294C69.6551 16.2818 69.7976 16.1964 69.9135 16.0809L76.5871 9.40624Z" stroke="white" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M74 8L76 10" stroke="white" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M70.3877 7.53516V6.53516" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M73.5693 16.6992V17.6992" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M66.3877 10.5352H67.3877" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M77.5693 13.6992H76.5693" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M68.3877 8.53516L67.3877 7.53516" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M75.5693 15.6992L76.5693 16.6992" stroke="white" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M87.334 11.9987L92.0007 7.33203L96.6673 11.9987" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M92 16.6654V7.33203" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M117 12C117 10.6739 116.473 9.40215 115.536 8.46447C114.598 7.52678 113.326 7 112 7C110.602 7.00526 109.261 7.55068 108.256 8.52222L107 9.77778" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M107 7V9.77778H109.778" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M107 12C107 13.3261 107.527 14.5979 108.464 15.5355C109.402 16.4732 110.674 17 112 17C113.398 16.9947 114.739 16.4493 115.744 15.4778L117 14.2222" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M114.223 14.2188H117V16.9965" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>
</pattern>
<linearGradient id="fade" y2="1" x2="0">
<stop offset="0" stop-color="white" stop-opacity=".52"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<mask id="fadeMask" maskContentUnits="objectBoundingBox">
<rect width="1" height="1" fill="url(#fade)"/>
</mask>
</defs>
<rect width="100%" height="100%" fill="url(#tilePattern)" mask="url(#fadeMask)"/>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -1,6 +1,6 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 4H8" stroke="black" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 10L11 10" stroke="black" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="4" cy="10" r="1.875" stroke="black" stroke-width="1.75"/>
<circle cx="10" cy="4" r="1.875" stroke="black" stroke-width="1.75"/>
<path d="M3 4H8" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 10L11 10" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="4" cy="10" r="1.875" stroke="black" stroke-width="1.5"/>
<circle cx="10" cy="4" r="1.875" stroke="black" stroke-width="1.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 446 B

View File

@@ -1,5 +0,0 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.27772 1.38585L4.39187 4.07909C4.34653 4.21692 4.26946 4.34219 4.16685 4.44479C4.06425 4.5474 3.93898 4.62447 3.80115 4.66981L1.10791 5.55566L3.80115 6.44151C3.93898 6.48685 4.06425 6.56392 4.16685 6.66653C4.26946 6.76913 4.34653 6.8944 4.39187 7.03223L5.27772 9.72547L6.16357 7.03223C6.20891 6.8944 6.28598 6.76913 6.38859 6.66653C6.49119 6.56392 6.61646 6.48685 6.7543 6.44151L9.44753 5.55566L6.7543 4.66981C6.61646 4.62447 6.49119 4.5474 6.38859 4.44479C6.28598 4.34219 6.20891 4.21692 6.16357 4.07909L5.27772 1.38585Z" fill="black" fill-opacity="0.15" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.35938 12.3555C8.35938 12.0664 8.52734 11.8086 9.00781 11.3594L10.2031 10.2344C10.6094 9.85156 10.7891 9.60156 10.7891 9.34375C10.7891 9.05469 10.5781 8.85938 10.2734 8.85938C10.0391 8.85938 9.87109 8.95312 9.66406 9.21094C9.42578 9.50781 9.25391 9.60938 8.99219 9.60938C8.61719 9.60938 8.35156 9.35938 8.35156 9.01172C8.35156 8.25 9.26953 7.57812 10.3594 7.57812C11.4961 7.57812 12.3438 8.26172 12.3438 9.17969C12.3438 9.75391 12.0391 10.3008 11.418 10.8516L10.4961 11.6719V11.7344H11.8047C12.2578 11.7344 12.5391 11.9766 12.5391 12.3711C12.5391 12.7656 12.2656 13 11.8047 13H9.08203C8.65234 13 8.35938 12.7383 8.35938 12.3555Z" fill="black"/>
<path d="M11.0834 1.38585V3.71918M9.91675 2.55248H12.2501" stroke="black" stroke-opacity="0.75" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -30,6 +30,13 @@
"ctrl-0": ["zed::ResetBufferFontSize", { "persist": false }],
"ctrl-,": "zed::OpenSettings",
"ctrl-q": "zed::Quit",
"f4": "debugger::Start",
"f5": "debugger::Continue",
"shift-f5": "debugger::Stop",
"f6": "debugger::Pause",
"f7": "debugger::StepOver",
"cmd-f11": "debugger::StepInto",
"shift-f11": "debugger::StepOut",
"f11": "zed::ToggleFullScreen",
"ctrl-alt-z": "edit_prediction::RateCompletions",
"ctrl-shift-i": "edit_prediction::ToggleMenu"
@@ -46,7 +53,9 @@
"context": "Prompt",
"bindings": {
"left": "menu::SelectPrevious",
"right": "menu::SelectNext"
"right": "menu::SelectNext",
"h": "menu::SelectPrevious",
"l": "menu::SelectNext"
}
},
{
@@ -107,6 +116,7 @@
"ctrl-a": "editor::SelectAll",
"ctrl-l": "editor::SelectLine",
"ctrl-shift-i": "editor::Format",
"alt-shift-o": "editor::OrganizeImports",
// "cmd-shift-left": ["editor::SelectToBeginningOfLine", {"stop_at_soft_wraps": true, "stop_at_indent": true }],
// "ctrl-shift-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
@@ -123,7 +133,9 @@
"alt-g b": "editor::ToggleGitBlame",
"menu": "editor::OpenContextMenu",
"shift-f10": "editor::OpenContextMenu",
"ctrl-shift-e": "editor::ToggleEditPrediction"
"ctrl-shift-e": "editor::ToggleEditPrediction",
"f9": "editor::ToggleBreakpoint",
"shift-f9": "editor::EditLogBreakpoint"
}
},
{
@@ -183,7 +195,7 @@
"ctrl-shift-g": "search::SelectPreviousMatch",
"ctrl-alt-/": "assistant::ToggleModelSelector",
"ctrl-k h": "assistant::DeployHistory",
"ctrl-k l": "assistant::DeployPromptLibrary",
"ctrl-k l": "assistant::OpenPromptLibrary",
"new": "assistant::NewChat",
"ctrl-t": "assistant::NewChat",
"ctrl-n": "assistant::NewChat"
@@ -362,6 +374,7 @@
"ctrl-k ctrl-0": "editor::FoldAll",
"ctrl-k ctrl-j": "editor::UnfoldAll",
"ctrl-space": "editor::ShowCompletions",
"ctrl-shift-space": "editor::ShowWordCompletions",
"ctrl-.": "editor::ToggleCodeActions",
"ctrl-k r": "editor::RevealInFileManager",
"ctrl-k p": "editor::CopyPath",
@@ -393,6 +406,7 @@
"alt-shift-open": "projects::OpenRemote",
"alt-ctrl-shift-o": "projects::OpenRemote",
"alt-ctrl-shift-b": "branches::OpenRecent",
"alt-shift-enter": "toast::RunAction",
"ctrl-~": "workspace::NewTerminal",
"save": "workspace::Save",
"ctrl-s": "workspace::Save",
@@ -731,28 +745,52 @@
"up": "menu::SelectPrevious",
"down": "menu::SelectNext",
"enter": "menu::Confirm",
"alt-y": "git::StageFile",
"alt-shift-y": "git::UnstageFile",
"ctrl-alt-y": "git::ToggleStaged",
"space": "git::ToggleStaged",
"ctrl-space": "git::StageAll",
"ctrl-shift-space": "git::UnstageAll",
"tab": "git_panel::FocusEditor",
"shift-tab": "git_panel::FocusEditor",
"escape": "git_panel::ToggleFocus",
"ctrl-enter": "git::Commit",
"alt-enter": "menu::SecondaryConfirm"
"alt-enter": "menu::SecondaryConfirm",
"delete": ["git::RestoreFile", { "skip_prompt": false }],
"backspace": ["git::RestoreFile", { "skip_prompt": false }],
"shift-delete": ["git::RestoreFile", { "skip_prompt": false }],
"ctrl-backspace": ["git::RestoreFile", { "skip_prompt": false }],
"ctrl-delete": ["git::RestoreFile", { "skip_prompt": false }]
}
},
{
"context": "GitCommit > Editor",
"bindings": {
"escape": "menu::Cancel",
"enter": "editor::Newline",
"ctrl-enter": "git::Commit",
"alt-l": "git::GenerateCommitMessage"
}
},
{
"context": "GitPanel",
"use_key_equivalents": true,
"bindings": {
"ctrl-g ctrl-g": "git::Fetch",
"ctrl-g up": "git::Push",
"ctrl-g down": "git::Pull",
"ctrl-g shift-up": "git::ForcePush",
"ctrl-g d": "git::Diff",
"ctrl-g backspace": "git::RestoreTrackedFiles",
"ctrl-g shift-backspace": "git::TrashUntrackedFiles",
"ctrl-space": "git::StageAll",
"ctrl-shift-space": "git::UnstageAll"
}
},
{
"context": "GitDiff > Editor",
"bindings": {
"ctrl-enter": "git::Commit"
"ctrl-enter": "git::Commit",
"ctrl-space": "git::StageAll",
"ctrl-shift-space": "git::UnstageAll"
}
},
{
@@ -767,6 +805,7 @@
"escape": "git_panel::FocusChanges",
"tab": "git_panel::FocusChanges",
"shift-tab": "git_panel::FocusChanges",
"enter": "editor::Newline",
"ctrl-enter": "git::Commit",
"alt-up": "git_panel::FocusChanges",
"alt-l": "git::GenerateCommitMessage"
@@ -840,21 +879,22 @@
"alt-b": ["terminal::SendText", "\u001bb"],
"alt-f": ["terminal::SendText", "\u001bf"],
// Overrides for conflicting keybindings
"ctrl-b": ["terminal::SendKeystroke", "ctrl-b"],
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
"ctrl-e": ["terminal::SendKeystroke", "ctrl-e"],
"ctrl-o": ["terminal::SendKeystroke", "ctrl-o"],
"ctrl-w": ["terminal::SendKeystroke", "ctrl-w"],
"ctrl-shift-a": "editor::SelectAll",
"find": "buffer_search::Deploy",
"ctrl-shift-f": "buffer_search::Deploy",
"ctrl-shift-l": "terminal::Clear",
"ctrl-shift-w": "pane::CloseActiveItem",
"ctrl-e": ["terminal::SendKeystroke", "ctrl-e"],
"up": ["terminal::SendKeystroke", "up"],
"pageup": ["terminal::SendKeystroke", "pageup"],
"down": ["terminal::SendKeystroke", "down"],
"pagedown": ["terminal::SendKeystroke", "pagedown"],
"escape": ["terminal::SendKeystroke", "escape"],
"enter": ["terminal::SendKeystroke", "enter"],
"ctrl-b": ["terminal::SendKeystroke", "ctrl-b"],
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
"shift-pageup": "terminal::ScrollPageUp",
"shift-pagedown": "terminal::ScrollPageDown",
"shift-up": "terminal::ScrollLineUp",

View File

@@ -14,6 +14,13 @@
{
"use_key_equivalents": true,
"bindings": {
"f4": "debugger::Start",
"f5": "debugger::Continue",
"shift-f5": "debugger::Stop",
"f6": "debugger::Pause",
"f7": "debugger::StepOver",
"f11": "debugger::StepInto",
"shift-f11": "debugger::StepOut",
"home": "menu::SelectFirst",
"shift-pageup": "menu::SelectFirst",
"pageup": "menu::SelectFirst",
@@ -31,13 +38,13 @@
"enter": "menu::Confirm",
"ctrl-enter": "menu::SecondaryConfirm",
"cmd-enter": "menu::SecondaryConfirm",
"cmd-escape": "menu::Cancel",
"ctrl-escape": "menu::Cancel",
"ctrl-c": "menu::Cancel",
"escape": "menu::Cancel",
"alt-shift-enter": "menu::Restart",
"cmd-shift-w": "workspace::CloseWindow",
"shift-escape": "workspace::ToggleZoom",
"cmd-escape": "menu::Cancel",
"cmd-o": "workspace::Open",
"cmd-=": ["zed::IncreaseBufferFontSize", { "persist": false }],
"cmd-+": ["zed::IncreaseBufferFontSize", { "persist": false }],
@@ -148,6 +155,8 @@
"cmd-\"": "editor::ExpandAllDiffHunks",
"cmd-alt-g b": "editor::ToggleGitBlame",
"cmd-i": "editor::ShowSignatureHelp",
"f9": "editor::ToggleBreakpoint",
"shift-f9": "editor::EditLogBreakpoint",
"ctrl-f12": "editor::GoToDeclaration",
"alt-ctrl-f12": "editor::GoToDeclarationSplit",
"ctrl-cmd-e": "editor::ToggleEditPrediction"
@@ -232,7 +241,7 @@
"cmd-shift-g": "search::SelectPreviousMatch",
"cmd-alt-/": "assistant::ToggleModelSelector",
"cmd-k h": "assistant::DeployHistory",
"cmd-k l": "assistant::DeployPromptLibrary",
"cmd-k l": "assistant::OpenPromptLibrary",
"cmd-t": "assistant::NewChat",
"cmd-n": "assistant::NewChat"
}
@@ -278,7 +287,9 @@
"context": "MessageEditor > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "assistant2::Chat"
"enter": "assistant2::Chat",
"cmd-g d": "git::Diff",
"shift-escape": "git::ExpandCommitEditor"
}
},
{
@@ -466,6 +477,7 @@
// Using `ctrl-space` in Zed requires disabling the macOS global shortcut.
// System Preferences->Keyboard->Keyboard Shortcuts->Input Sources->Select the previous input source (uncheck)
"ctrl-space": "editor::ShowCompletions",
"ctrl-shift-space": "editor::ShowWordCompletions",
"cmd-.": "editor::ToggleCodeActions",
"cmd-k r": "editor::RevealInFileManager",
"cmd-k p": "editor::CopyPath",
@@ -514,6 +526,7 @@
"ctrl-~": "workspace::NewTerminal",
"cmd-s": "workspace::Save",
"cmd-k s": "workspace::SaveWithoutFormat",
"alt-shift-enter": "toast::RunAction",
"cmd-shift-s": "workspace::SaveAs",
"cmd-shift-n": "workspace::NewWindow",
"ctrl-`": "terminal_panel::ToggleFocus",
@@ -694,6 +707,16 @@
"ctrl-]": "assistant::CycleNextInlineAssist"
}
},
{
"context": "Prompt",
"use_key_equivalents": true,
"bindings": {
"left": "menu::SelectPrevious",
"right": "menu::SelectNext",
"h": "menu::SelectPrevious",
"l": "menu::SelectNext"
}
},
{
"context": "ProjectSearchBar && !in_replace",
"use_key_equivalents": true,
@@ -754,6 +777,14 @@
"space": "project_panel::Open"
}
},
{
"context": "VariableList",
"use_key_equivalents": true,
"bindings": {
"left": "variable_list::CollapseSelectedEntry",
"right": "variable_list::ExpandSelectedEntry"
}
},
{
"context": "GitPanel && ChangesList",
"use_key_equivalents": true,
@@ -763,28 +794,28 @@
"cmd-up": "menu::SelectFirst",
"cmd-down": "menu::SelectLast",
"enter": "menu::Confirm",
"cmd-alt-y": "git::ToggleStaged",
"space": "git::ToggleStaged",
"cmd-shift-space": "git::StageAll",
"ctrl-shift-space": "git::UnstageAll",
"cmd-y": "git::StageFile",
"cmd-shift-y": "git::UnstageFile",
"alt-down": "git_panel::FocusEditor",
"tab": "git_panel::FocusEditor",
"shift-tab": "git_panel::FocusEditor",
"escape": "git_panel::ToggleFocus",
"cmd-enter": "git::Commit"
"cmd-enter": "git::Commit",
"backspace": ["git::RestoreFile", { "skip_prompt": false }],
"delete": ["git::RestoreFile", { "skip_prompt": false }],
"cmd-backspace": ["git::RestoreFile", { "skip_prompt": true }],
"cmd-delete": ["git::RestoreFile", { "skip_prompt": true }]
}
},
{
"context": "GitDiff > Editor",
"use_key_equivalents": true,
"bindings": {
"cmd-enter": "git::Commit"
}
},
{
"context": "AskPass > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "menu::Confirm"
"cmd-enter": "git::Commit",
"cmd-ctrl-y": "git::StageAll",
"cmd-ctrl-shift-y": "git::UnstageAll"
}
},
{
@@ -800,11 +831,27 @@
"alt-tab": "git::GenerateCommitMessage"
}
},
{
"context": "GitPanel",
"use_key_equivalents": true,
"bindings": {
"ctrl-g ctrl-g": "git::Fetch",
"ctrl-g up": "git::Push",
"ctrl-g down": "git::Pull",
"ctrl-g shift-up": "git::ForcePush",
"ctrl-g d": "git::Diff",
"ctrl-g backspace": "git::RestoreTrackedFiles",
"ctrl-g shift-backspace": "git::TrashUntrackedFiles",
"cmd-ctrl-y": "git::StageAll",
"cmd-ctrl-shift-y": "git::UnstageAll"
}
},
{
"context": "GitCommit > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::Newline",
"escape": "menu::Cancel",
"cmd-enter": "git::Commit",
"alt-tab": "git::GenerateCommitMessage"
}

View File

@@ -3,7 +3,14 @@
"bindings": {
"ctrl-alt-s": "zed::OpenSettings",
"ctrl-{": "pane::ActivatePreviousItem",
"ctrl-}": "pane::ActivateNextItem"
"ctrl-}": "pane::ActivateNextItem",
"ctrl-f2": "debugger::Stop",
"f6": "debugger::Pause",
"f7": "debugger::StepInto",
"f8": "debugger::StepOver",
"shift-f8": "debugger::StepOut",
"f9": "debugger::Continue",
"alt-shift-f9": "debugger::Start"
}
},
{
@@ -31,6 +38,7 @@
"shift-alt-up": "editor::MoveLineUp",
"shift-alt-down": "editor::MoveLineDown",
"ctrl-alt-l": "editor::Format",
"ctrl-alt-o": "editor::OrganizeImports",
"shift-f6": "editor::Rename",
"ctrl-alt-left": "pane::GoBack",
"ctrl-alt-right": "pane::GoForward",
@@ -48,7 +56,9 @@
"ctrl-home": "editor::MoveToBeginning",
"ctrl-end": "editor::MoveToEnd",
"ctrl-shift-home": "editor::SelectToBeginning",
"ctrl-shift-end": "editor::SelectToEnd"
"ctrl-shift-end": "editor::SelectToEnd",
"ctrl-f8": "editor::ToggleBreakpoint",
"ctrl-shift-f8": "editor::EditLogBreakpoint"
}
},
{

View File

@@ -2,7 +2,14 @@
{
"bindings": {
"cmd-{": "pane::ActivatePreviousItem",
"cmd-}": "pane::ActivateNextItem"
"cmd-}": "pane::ActivateNextItem",
"ctrl-f2": "debugger::Stop",
"f6": "debugger::Pause",
"f7": "debugger::StepInto",
"f8": "debugger::StepOver",
"shift-f8": "debugger::StepOut",
"f9": "debugger::Continue",
"alt-shift-f9": "debugger::Start"
}
},
{
@@ -29,6 +36,7 @@
"shift-alt-up": "editor::MoveLineUp",
"shift-alt-down": "editor::MoveLineDown",
"cmd-alt-l": "editor::Format",
"ctrl-alt-o": "editor::OrganizeImports",
"shift-f6": "editor::Rename",
"cmd-[": "pane::GoBack",
"cmd-]": "pane::GoForward",
@@ -45,7 +53,9 @@
"cmd-home": "editor::MoveToBeginning",
"cmd-end": "editor::MoveToEnd",
"cmd-shift-home": "editor::SelectToBeginning",
"cmd-shift-end": "editor::SelectToEnd"
"cmd-shift-end": "editor::SelectToEnd",
"ctrl-f8": "editor::ToggleBreakpoint",
"ctrl-shift-f8": "editor::EditLogBreakpoint"
}
},
{

View File

@@ -0,0 +1,34 @@
You are an AI assistant integrated into a text editor. Your goal is to do one of the following two things:
1. Help users answer questions and perform tasks related to their codebase.
2. Answer general-purpose questions unrelated to their particular codebase.
It will be up to you to decide which of these you are doing based on what the user has told you. When unclear, ask clarifying questions to understand the user's intent before proceeding.
You should only perform actions that modify the users system if explicitly requested by the user:
- If the user asks a question about how to accomplish a task, provide guidance or information, and use read-only tools (e.g., search) to assist. You may suggest potential actions, but do not directly modify the users system without explicit instruction.
- If the user clearly requests that you perform an action, carry out the action directly without explaining why you are doing so.
- The editing actions you perform might produce errors or warnings. At the end of your changes, check whether you introduced any problems, and fix them before providing a summary of the changes you made.
- Do not fix errors unrelated to your changes unless the user explicitly asks you to do so.
Be concise and direct in your responses.
The user has opened a project that contains the following root directories/files. Whenever you specify a path in the project, it must be a relative path which begins with one of these root directories/files:
{{#each worktrees}}
- `{{root_name}}` (absolute path: `{{abs_path}}`)
{{/each}}
{{#if has_rules}}
There are rules that apply to these root directories:
{{#each worktrees}}
{{#if rules_file}}
`{{root_name}}/{{rules_file.rel_path}}`:
``````
{{{rules_file.text}}}
``````
{{/if}}
{{/each}}
{{/if}}

View File

@@ -25,7 +25,7 @@
// Features that can be globally enabled or disabled
"features": {
// Which edit prediction provider to use.
"edit_prediction_provider": "copilot"
"edit_prediction_provider": "zed"
},
// The name of a font to use for rendering text in the editor
"buffer_font_family": "Zed Plex Mono",
@@ -136,6 +136,11 @@
// Whether to use the system provided dialogs for Open and Save As.
// When set to false, Zed will use the built-in keyboard-first pickers.
"use_system_path_prompts": true,
// Whether to use the system provided dialogs for prompts, such as confirmation
// prompts.
// When set to false, Zed will use its built-in prompts. Note that on Linux,
// this option is ignored and Zed will always use the built-in prompts.
"use_system_prompts": true,
// Whether the cursor blinks in the editor.
"cursor_blink": true,
// Cursor shape for the default editor.
@@ -150,6 +155,8 @@
//
// Default: not set, defaults to "bar"
"cursor_shape": null,
// Determines whether the mouse cursor is hidden when typing in an editor or input box.
"hide_mouse_while_typing": true,
// How to highlight the current line in the editor.
//
// 1. Don't highlight the current line:
@@ -179,6 +186,11 @@
// Whether to show the signature help after completion or a bracket pair inserted.
// If `auto_signature_help` is enabled, this setting will be treated as enabled also.
"show_signature_help_after_edits": false,
// What to do when go to definition yields no results.
//
// 1. Do nothing: `none`
// 2. Find references for the same symbol: `find_all_references` (default)
"go_to_definition_fallback": "find_all_references",
// Whether to show wrap guides (vertical rulers) in the editor.
// Setting this to true will show a guide at the 'preferred_line_length' value
// if 'soft_wrap' is set to 'preferred_line_length', and will show any
@@ -324,6 +336,8 @@
"code_actions": true,
// Whether to show runnables buttons in the gutter.
"runnables": true,
// Whether to show breakpoints in the gutter.
"breakpoints": true,
// Whether to show fold buttons in the gutter.
"folds": true
},
@@ -336,14 +350,14 @@
"active_line_width": 1,
// Determines how indent guides are colored.
// This setting can take the following three values:
///
//
// 1. "disabled"
// 2. "fixed"
// 3. "indent_aware"
"coloring": "fixed",
// Determines how indent guide backgrounds are colored.
// This setting can take the following two values:
///
//
// 1. "disabled"
// 2. "indent_aware"
"background_coloring": "disabled"
@@ -402,8 +416,8 @@
// Time to wait after scrolling the buffer, before requesting the hints,
// set to 0 to disable debouncing.
"scroll_debounce_ms": 50,
/// A set of modifiers which, when pressed, will toggle the visibility of inlay hints.
/// If the set if empty or not all the modifiers specified are pressed, inlay hints will not be toggled.
// A set of modifiers which, when pressed, will toggle the visibility of inlay hints.
// If the set if empty or not all the modifiers specified are pressed, inlay hints will not be toggled.
"toggle_on_modifiers_press": {
"control": false,
"shift": false,
@@ -415,6 +429,8 @@
"project_panel": {
// Whether to show the project panel button in the status bar
"button": true,
// Whether to hide the gitignore entries in the project panel.
"hide_gitignore": false,
// Default width of the project panel.
"default_width": 240,
// Where to dock the project panel. Can be 'left' or 'right'.
@@ -440,7 +456,7 @@
"scrollbar": {
// When to show the scrollbar in the project panel.
// This setting can take five values:
///
//
// 1. null (default): Inherit editor settings
// 2. Show the scrollbar if there's important information or
// follow the system's configured behavior (default):
@@ -455,7 +471,7 @@
},
// Which files containing diagnostic errors/warnings to mark in the project panel.
// This setting can take the following three values:
///
//
// 1. Do not mark any files:
// "off"
// 2. Only mark files with errors:
@@ -512,7 +528,7 @@
"scrollbar": {
// When to show the scrollbar in the project panel.
// This setting can take five values:
///
//
// 1. null (default): Inherit editor settings
// 2. Show the scrollbar if there's important information or
// follow the system's configured behavior (default):
@@ -547,7 +563,7 @@
"git_panel": {
// Whether to show the git panel button in the status bar.
"button": true,
// Where to the git panel. Can be 'left' or 'right'.
// Where to show the git panel. Can be 'left' or 'right'.
"dock": "left",
// Default width of the git panel.
"default_width": 360,
@@ -555,6 +571,12 @@
//
// Default: icon
"status_style": "icon",
// What branch name to use if init.defaultBranch
// is not set
//
// Default: main
"fallback_branch_name": "main",
"scrollbar": {
// When to show the scrollbar in the git panel.
//
@@ -594,7 +616,53 @@
"provider": "zed.dev",
// The model to use.
"model": "claude-3-5-sonnet-latest"
}
},
// The model to use when applying edits from the assistant.
"editor_model": {
// The provider to use.
"provider": "zed.dev",
// The model to use.
"model": "claude-3-5-sonnet-latest"
},
"default_profile": "code-writer",
"profiles": {
"read-only": {
"name": "Read-only",
"tools": {
"diagnostics": true,
"fetch": true,
"list-directory": true,
"now": true,
"path-search": true,
"read-file": true,
"regex-search": true,
"thinking": true
}
},
"code-writer": {
"name": "Code Writer",
"tools": {
"bash": true,
"batch-tool": true,
"copy-path": true,
"create-file": true,
"delete-path": true,
"diagnostics": true,
"find-replace-file": true,
"edit-files": false,
"fetch": true,
"list-directory": true,
"move-path": true,
"now": true,
"path-search": true,
"read-file": true,
"regex-search": true,
"thinking": true
}
}
},
// Shows a notification when the agent needs confirmation before running an edit tool call or when that's concluded.
"notify_when_agent_waiting": true
},
// The settings for slash commands.
"slash_commands": {
@@ -673,7 +741,7 @@
// Which files containing diagnostic errors/warnings to mark in the tabs.
// Diagnostics are only shown when file icons are also active.
// This setting only works when can take the following three values:
///
//
// 1. Do not mark any files:
// "off"
// 2. Only mark files with errors:
@@ -841,12 +909,20 @@
// How git hunks are displayed visually in the editor.
// This setting can take two values:
//
// 1. Show unstaged hunks with a transparent background (default):
// "hunk_style": "transparent"
// 2. Show unstaged hunks with a pattern background:
// "hunk_style": "pattern"
"hunk_style": "staged_border"
// 1. Show unstaged hunks filled and staged hunks hollow:
// "hunk_style": "staged_hollow"
// 2. Show unstaged hunks hollow and staged hunks filled:
// "hunk_style": "unstaged_hollow"
"hunk_style": "staged_hollow"
},
// The list of custom Git hosting providers.
"git_hosting_providers": [
// {
// "provider": "github",
// "name": "BigCorp GitHub",
// "base_url": "https://code.big-corp.com"
// }
],
// Configuration for how direnv configuration should be loaded. May take 2 values:
// 1. Load direnv configuration using `direnv export json` directly.
// "load_direnv": "direct"
@@ -953,7 +1029,7 @@
// "alternate_scroll": "on",
// 2. Default alternate scroll mode to off
// "alternate_scroll": "off",
"alternate_scroll": "off",
"alternate_scroll": "on",
// Set whether the option key behaves as the meta key.
// May take 2 values:
// 1. Rely on default platform handling of option key, on macOS
@@ -1009,7 +1085,7 @@
"scrollbar": {
// When to show the scrollbar in the terminal.
// This setting can take five values:
///
//
// 1. null (default): Inherit editor settings
// 2. Show the scrollbar if there's important information or
// follow the system's configured behavior (default):
@@ -1080,6 +1156,32 @@
"auto_install_extensions": {
"html": true
},
// Controls how completions are processed for this language.
"completions": {
// Controls how words are completed.
// For large documents, not all words may be fetched for completion.
//
// May take 3 values:
// 1. "enabled"
// Always fetch document's words for completions along with LSP completions.
// 2. "fallback"
// Only if LSP response errors or times out, use document's words to show completions.
// 3. "disabled"
// Never fetch or complete document's words for completions.
// (Word-based completions can still be queried via a separate action)
//
// Default: fallback
"words": "fallback",
// Whether to fetch LSP completions or not.
//
// Default: true
"lsp": true,
// When fetching LSP completions, determines how long to wait for a response of a particular server.
// When set to 0, waits indefinitely.
//
// Default: 0
"lsp_fetch_timeout_ms": 0
},
// Different settings for specific languages.
"languages": {
"Astro": {
@@ -1170,11 +1272,19 @@
"allowed": true
}
},
"LaTeX": {
"format_on_save": "on",
"formatter": "language_server",
"language_servers": ["texlab", "..."],
"prettier": {
"allowed": false
}
},
"Markdown": {
"format_on_save": "off",
"use_on_type_format": false,
"allow_rewrap": "anywhere",
"soft_wrap": "bounded",
"soft_wrap": "editor_width",
"prettier": {
"allowed": true
}
@@ -1300,8 +1410,7 @@
},
// Settings for auto-closing of JSX tags.
"jsx_tag_auto_close": {
// // Whether to auto-close JSX tags.
// "enabled": true
"enabled": true
},
// LSP Specific settings.
"lsp": {
@@ -1407,6 +1516,12 @@
// }
// ]
"ssh_connections": [],
// Configures context servers for use in the Assistant.
"context_servers": {}
"context_servers": {},
"debugger": {
"stepping_granularity": "line",
"save_breakpoints": true,
"button": true
}
}

View File

@@ -0,0 +1,32 @@
[
{
"label": "Debug active PHP file",
"adapter": "php",
"program": "$ZED_FILE",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT"
},
{
"label": "Debug active Python file",
"adapter": "python",
"program": "$ZED_FILE",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT"
},
{
"label": "Debug active JavaScript file",
"adapter": "javascript",
"program": "$ZED_FILE",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT"
},
{
"label": "JavaScript debug terminal",
"adapter": "javascript",
"request": "launch",
"cwd": "$ZED_WORKTREE_ROOT",
"initialize_args": {
"console": "integratedTerminal"
}
}
]

View File

@@ -6,15 +6,7 @@
{
"name": "Gruvbox Dark",
"appearance": "dark",
"accents": [
"#cc241dff",
"#98971aff",
"#d79921ff",
"#458588ff",
"#b16286ff",
"#689d6aff",
"#d65d0eff"
],
"accents": ["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"],
"style": {
"border": "#5b534dff",
"border.variant": "#494340ff",
@@ -105,9 +97,9 @@
"terminal.ansi.bright_white": "#fbf1c7ff",
"terminal.ansi.dim_white": "#b0a189ff",
"link_text.hover": "#83a598ff",
"version_control_added": "#b7bb26ff",
"version_control_modified": "#f9bd2fff",
"version_control_deleted": "#fb4a35ff",
"version_control.added": "#b7bb26ff",
"version_control.modified": "#f9bd2fff",
"version_control.deleted": "#fb4a35ff",
"conflict": "#f9bd2fff",
"conflict.background": "#572e10ff",
"conflict.border": "#754916ff",
@@ -383,6 +375,11 @@
"font_style": null,
"font_weight": null
},
"variable.special": {
"color": "#83a598ff",
"font_style": null,
"font_weight": null
},
"variant": {
"color": "#83a598ff",
"font_style": null,
@@ -394,15 +391,7 @@
{
"name": "Gruvbox Dark Hard",
"appearance": "dark",
"accents": [
"#cc241dff",
"#98971aff",
"#d79921ff",
"#458588ff",
"#b16286ff",
"#689d6aff",
"#d65d0eff"
],
"accents": ["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"],
"style": {
"border": "#5b534dff",
"border.variant": "#494340ff",
@@ -493,9 +482,9 @@
"terminal.ansi.bright_white": "#fbf1c7ff",
"terminal.ansi.dim_white": "#b0a189ff",
"link_text.hover": "#83a598ff",
"version_control_added": "#b7bb26ff",
"version_control_modified": "#f9bd2fff",
"version_control_deleted": "#fb4a35ff",
"version_control.added": "#b7bb26ff",
"version_control.modified": "#f9bd2fff",
"version_control.deleted": "#fb4a35ff",
"conflict": "#f9bd2fff",
"conflict.background": "#572e10ff",
"conflict.border": "#754916ff",
@@ -771,6 +760,11 @@
"font_style": null,
"font_weight": null
},
"variable.special": {
"color": "#83a598ff",
"font_style": null,
"font_weight": null
},
"variant": {
"color": "#83a598ff",
"font_style": null,
@@ -782,15 +776,7 @@
{
"name": "Gruvbox Dark Soft",
"appearance": "dark",
"accents": [
"#cc241dff",
"#98971aff",
"#d79921ff",
"#458588ff",
"#b16286ff",
"#689d6aff",
"#d65d0eff"
],
"accents": ["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"],
"style": {
"border": "#5b534dff",
"border.variant": "#494340ff",
@@ -881,9 +867,9 @@
"terminal.ansi.bright_white": "#fbf1c7ff",
"terminal.ansi.dim_white": "#b0a189ff",
"link_text.hover": "#83a598ff",
"version_control_added": "#b7bb26ff",
"version_control_modified": "#f9bd2fff",
"version_control_deleted": "#fb4a35ff",
"version_control.added": "#b7bb26ff",
"version_control.modified": "#f9bd2fff",
"version_control.deleted": "#fb4a35ff",
"conflict": "#f9bd2fff",
"conflict.background": "#572e10ff",
"conflict.border": "#754916ff",
@@ -1159,6 +1145,11 @@
"font_style": null,
"font_weight": null
},
"variable.special": {
"color": "#83a598ff",
"font_style": null,
"font_weight": null
},
"variant": {
"color": "#83a598ff",
"font_style": null,
@@ -1170,15 +1161,7 @@
{
"name": "Gruvbox Light",
"appearance": "light",
"accents": [
"#cc241dff",
"#98971aff",
"#d79921ff",
"#458588ff",
"#b16286ff",
"#689d6aff",
"#d65d0eff"
],
"accents": ["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"],
"style": {
"border": "#c8b899ff",
"border.variant": "#ddcca7ff",
@@ -1269,9 +1252,9 @@
"terminal.ansi.bright_white": "#282828ff",
"terminal.ansi.dim_white": "#73675eff",
"link_text.hover": "#0b6678ff",
"version_control_added": "#797410ff",
"version_control_modified": "#b57615ff",
"version_control_deleted": "#9d0308ff",
"version_control.added": "#797410ff",
"version_control.modified": "#b57615ff",
"version_control.deleted": "#9d0308ff",
"conflict": "#b57615ff",
"conflict.background": "#f5e2d0ff",
"conflict.border": "#ebccabff",
@@ -1547,6 +1530,11 @@
"font_style": null,
"font_weight": null
},
"variable.special": {
"color": "#066578ff",
"font_style": null,
"font_weight": null
},
"variant": {
"color": "#0b6678ff",
"font_style": null,
@@ -1558,15 +1546,7 @@
{
"name": "Gruvbox Light Hard",
"appearance": "light",
"accents": [
"#cc241dff",
"#98971aff",
"#d79921ff",
"#458588ff",
"#b16286ff",
"#689d6aff",
"#d65d0eff"
],
"accents": ["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"],
"style": {
"border": "#c8b899ff",
"border.variant": "#ddcca7ff",
@@ -1657,9 +1637,9 @@
"terminal.ansi.bright_white": "#282828ff",
"terminal.ansi.dim_white": "#73675eff",
"link_text.hover": "#0b6678ff",
"version_control_added": "#797410ff",
"version_control_modified": "#b57615ff",
"version_control_deleted": "#9d0308ff",
"version_control.added": "#797410ff",
"version_control.modified": "#b57615ff",
"version_control.deleted": "#9d0308ff",
"conflict": "#b57615ff",
"conflict.background": "#f5e2d0ff",
"conflict.border": "#ebccabff",
@@ -1935,6 +1915,11 @@
"font_style": null,
"font_weight": null
},
"variable.special": {
"color": "#066578ff",
"font_style": null,
"font_weight": null
},
"variant": {
"color": "#0b6678ff",
"font_style": null,
@@ -1946,15 +1931,7 @@
{
"name": "Gruvbox Light Soft",
"appearance": "light",
"accents": [
"#cc241dff",
"#98971aff",
"#d79921ff",
"#458588ff",
"#b16286ff",
"#689d6aff",
"#d65d0eff"
],
"accents": ["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"],
"style": {
"border": "#c8b899ff",
"border.variant": "#ddcca7ff",
@@ -2045,9 +2022,9 @@
"terminal.ansi.bright_white": "#282828ff",
"terminal.ansi.dim_white": "#73675eff",
"link_text.hover": "#0b6678ff",
"version_control_added": "#797410ff",
"version_control_modified": "#b57615ff",
"version_control_deleted": "#9d0308ff",
"version_control.added": "#797410ff",
"version_control.modified": "#b57615ff",
"version_control.deleted": "#9d0308ff",
"conflict": "#b57615ff",
"conflict.background": "#f5e2d0ff",
"conflict.border": "#ebccabff",
@@ -2323,6 +2300,11 @@
"font_style": null,
"font_weight": null
},
"variable.special": {
"color": "#066578ff",
"font_style": null,
"font_weight": null
},
"variant": {
"color": "#0b6678ff",
"font_style": null,

View File

@@ -96,9 +96,9 @@
"terminal.ansi.bright_white": "#dce0e5ff",
"terminal.ansi.dim_white": "#575d65ff",
"link_text.hover": "#74ade8ff",
"version_control_added": "#a7c088ff",
"version_control_modified": "#dec184ff",
"version_control_deleted": "#d07277ff",
"version_control.added": "#27a657ff",
"version_control.modified": "#d3b020ff",
"version_control.deleted": "#e06c76ff",
"conflict": "#dec184ff",
"conflict.background": "#dec1841a",
"conflict.border": "#5d4c2fff",
@@ -475,9 +475,9 @@
"terminal.ansi.bright_white": "#242529ff",
"terminal.ansi.dim_white": "#97979aff",
"link_text.hover": "#5c78e2ff",
"version_control_added": "#669f59ff",
"version_control_modified": "#a48819ff",
"version_control_deleted": "#d36151ff",
"version_control.added": "#27a657ff",
"version_control.modified": "#d3b020ff",
"version_control.deleted": "#e06c76ff",
"conflict": "#a48819ff",
"conflict.background": "#faf2e6ff",
"conflict.border": "#f4e7d1ff",

View File

@@ -20,7 +20,6 @@ extension_host.workspace = true
futures.workspace = true
gpui.workspace = true
language.workspace = true
lsp.workspace = true
project.workspace = true
smallvec.workspace = true
ui.workspace = true

View File

@@ -7,8 +7,7 @@ use gpui::{
EventEmitter, InteractiveElement as _, ParentElement as _, Render, SharedString,
StatefulInteractiveElement, Styled, Transformation, Window,
};
use language::{LanguageRegistry, LanguageServerBinaryStatus, LanguageServerId};
use lsp::LanguageServerName;
use language::{BinaryStatus, LanguageRegistry, LanguageServerId};
use project::{
EnvironmentErrorMessage, LanguageServerProgress, LspStoreEvent, Project,
ProjectEnvironmentEvent, WorktreeId,
@@ -23,21 +22,21 @@ actions!(activity_indicator, [ShowErrorMessage]);
pub enum Event {
ShowError {
lsp_name: LanguageServerName,
server_name: SharedString,
error: String,
},
}
pub struct ActivityIndicator {
statuses: Vec<LspStatus>,
statuses: Vec<ServerStatus>,
project: Entity<Project>,
auto_updater: Option<Entity<AutoUpdater>>,
context_menu_handle: PopoverMenuHandle<ContextMenu>,
}
struct LspStatus {
name: LanguageServerName,
status: LanguageServerBinaryStatus,
struct ServerStatus {
name: SharedString,
status: BinaryStatus,
}
struct PendingWork<'a> {
@@ -64,11 +63,24 @@ impl ActivityIndicator {
let auto_updater = AutoUpdater::get(cx);
let this = cx.new(|cx| {
let mut status_events = languages.language_server_binary_statuses();
cx.spawn(|this, mut cx| async move {
cx.spawn(async move |this, cx| {
while let Some((name, status)) = status_events.next().await {
this.update(&mut cx, |this: &mut ActivityIndicator, cx| {
this.update(cx, |this: &mut ActivityIndicator, cx| {
this.statuses.retain(|s| s.name != name);
this.statuses.push(LspStatus { name, status });
this.statuses.push(ServerStatus { name, status });
cx.notify();
})?;
}
anyhow::Ok(())
})
.detach();
let mut status_events = languages.dap_server_binary_statuses();
cx.spawn(async move |this, cx| {
while let Some((name, status)) = status_events.next().await {
this.update(cx, |this, cx| {
this.statuses.retain(|s| s.name != name);
this.statuses.push(ServerStatus { name, status });
cx.notify();
})?;
}
@@ -106,25 +118,25 @@ impl ActivityIndicator {
});
cx.subscribe_in(&this, window, move |_, _, event, window, cx| match event {
Event::ShowError { lsp_name, error } => {
Event::ShowError { server_name, error } => {
let create_buffer = project.update(cx, |project, cx| project.create_buffer(cx));
let project = project.clone();
let error = error.clone();
let lsp_name = lsp_name.clone();
cx.spawn_in(window, |workspace, mut cx| async move {
let server_name = server_name.clone();
cx.spawn_in(window, async move |workspace, cx| {
let buffer = create_buffer.await?;
buffer.update(&mut cx, |buffer, cx| {
buffer.update(cx, |buffer, cx| {
buffer.edit(
[(
0..0,
format!("Language server error: {}\n\n{}", lsp_name, error),
format!("Language server error: {}\n\n{}", server_name, error),
)],
None,
cx,
);
buffer.set_capability(language::Capability::ReadOnly, cx);
})?;
workspace.update_in(&mut cx, |workspace, window, cx| {
workspace.update_in(cx, |workspace, window, cx| {
workspace.add_item_to_active_pane(
Box::new(cx.new(|cx| {
Editor::for_buffer(buffer, Some(project.clone()), window, cx)
@@ -147,9 +159,9 @@ impl ActivityIndicator {
fn show_error_message(&mut self, _: &ShowErrorMessage, _: &mut Window, cx: &mut Context<Self>) {
self.statuses.retain(|status| {
if let LanguageServerBinaryStatus::Failed { error } = &status.status {
if let BinaryStatus::Failed { error } = &status.status {
cx.emit(Event::ShowError {
lsp_name: status.name.clone(),
server_name: status.name.clone(),
error: error.clone(),
});
false
@@ -278,12 +290,10 @@ impl ActivityIndicator {
let mut failed = SmallVec::<[_; 3]>::new();
for status in &self.statuses {
match status.status {
LanguageServerBinaryStatus::CheckingForUpdate => {
checking_for_update.push(status.name.clone())
}
LanguageServerBinaryStatus::Downloading => downloading.push(status.name.clone()),
LanguageServerBinaryStatus::Failed { .. } => failed.push(status.name.clone()),
LanguageServerBinaryStatus::None => {}
BinaryStatus::CheckingForUpdate => checking_for_update.push(status.name.clone()),
BinaryStatus::Downloading => downloading.push(status.name.clone()),
BinaryStatus::Failed { .. } => failed.push(status.name.clone()),
BinaryStatus::None => {}
}
}
@@ -296,7 +306,7 @@ impl ActivityIndicator {
),
message: format!(
"Downloading {}...",
downloading.iter().map(|name| name.0.as_ref()).fold(
downloading.iter().map(|name| name.as_ref()).fold(
String::new(),
|mut acc, s| {
if !acc.is_empty() {
@@ -324,7 +334,7 @@ impl ActivityIndicator {
),
message: format!(
"Checking for updates to {}...",
checking_for_update.iter().map(|name| name.0.as_ref()).fold(
checking_for_update.iter().map(|name| name.as_ref()).fold(
String::new(),
|mut acc, s| {
if !acc.is_empty() {
@@ -354,7 +364,7 @@ impl ActivityIndicator {
"Failed to run {}. Click to show error.",
failed
.iter()
.map(|name| name.0.as_ref())
.map(|name| name.as_ref())
.fold(String::new(), |mut acc, s| {
if !acc.is_empty() {
acc.push_str(", ");

View File

@@ -24,6 +24,16 @@ pub struct AnthropicModelCacheConfiguration {
pub max_cache_anchors: usize,
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub enum AnthropicModelMode {
#[default]
Default,
Thinking {
budget_tokens: Option<u32>,
},
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, EnumIter)]
pub enum Model {
@@ -32,6 +42,11 @@ pub enum Model {
Claude3_5Sonnet,
#[serde(rename = "claude-3-7-sonnet", alias = "claude-3-7-sonnet-latest")]
Claude3_7Sonnet,
#[serde(
rename = "claude-3-7-sonnet-thinking",
alias = "claude-3-7-sonnet-thinking-latest"
)]
Claude3_7SonnetThinking,
#[serde(rename = "claude-3-5-haiku", alias = "claude-3-5-haiku-latest")]
Claude3_5Haiku,
#[serde(rename = "claude-3-opus", alias = "claude-3-opus-latest")]
@@ -54,6 +69,8 @@ pub enum Model {
default_temperature: Option<f32>,
#[serde(default)]
extra_beta_headers: Vec<String>,
#[serde(default)]
mode: AnthropicModelMode,
},
}
@@ -61,6 +78,8 @@ impl Model {
pub fn from_id(id: &str) -> Result<Self> {
if id.starts_with("claude-3-5-sonnet") {
Ok(Self::Claude3_5Sonnet)
} else if id.starts_with("claude-3-7-sonnet-thinking") {
Ok(Self::Claude3_7SonnetThinking)
} else if id.starts_with("claude-3-7-sonnet") {
Ok(Self::Claude3_7Sonnet)
} else if id.starts_with("claude-3-5-haiku") {
@@ -80,6 +99,20 @@ impl Model {
match self {
Model::Claude3_5Sonnet => "claude-3-5-sonnet-latest",
Model::Claude3_7Sonnet => "claude-3-7-sonnet-latest",
Model::Claude3_7SonnetThinking => "claude-3-7-sonnet-thinking-latest",
Model::Claude3_5Haiku => "claude-3-5-haiku-latest",
Model::Claude3Opus => "claude-3-opus-latest",
Model::Claude3Sonnet => "claude-3-sonnet-20240229",
Model::Claude3Haiku => "claude-3-haiku-20240307",
Self::Custom { name, .. } => name,
}
}
/// The id of the model that should be used for making API requests
pub fn request_id(&self) -> &str {
match self {
Model::Claude3_5Sonnet => "claude-3-5-sonnet-latest",
Model::Claude3_7Sonnet | Model::Claude3_7SonnetThinking => "claude-3-7-sonnet-latest",
Model::Claude3_5Haiku => "claude-3-5-haiku-latest",
Model::Claude3Opus => "claude-3-opus-latest",
Model::Claude3Sonnet => "claude-3-sonnet-20240229",
@@ -92,6 +125,7 @@ impl Model {
match self {
Self::Claude3_7Sonnet => "Claude 3.7 Sonnet",
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
Self::Claude3_7SonnetThinking => "Claude 3.7 Sonnet Thinking",
Self::Claude3_5Haiku => "Claude 3.5 Haiku",
Self::Claude3Opus => "Claude 3 Opus",
Self::Claude3Sonnet => "Claude 3 Sonnet",
@@ -107,6 +141,7 @@ impl Model {
Self::Claude3_5Sonnet
| Self::Claude3_5Haiku
| Self::Claude3_7Sonnet
| Self::Claude3_7SonnetThinking
| Self::Claude3Haiku => Some(AnthropicModelCacheConfiguration {
min_total_token: 2_048,
should_speculate: true,
@@ -125,6 +160,7 @@ impl Model {
Self::Claude3_5Sonnet
| Self::Claude3_5Haiku
| Self::Claude3_7Sonnet
| Self::Claude3_7SonnetThinking
| Self::Claude3Opus
| Self::Claude3Sonnet
| Self::Claude3Haiku => 200_000,
@@ -135,7 +171,10 @@ impl Model {
pub fn max_output_tokens(&self) -> u32 {
match self {
Self::Claude3Opus | Self::Claude3Sonnet | Self::Claude3Haiku => 4_096,
Self::Claude3_5Sonnet | Self::Claude3_7Sonnet | Self::Claude3_5Haiku => 8_192,
Self::Claude3_5Sonnet
| Self::Claude3_7Sonnet
| Self::Claude3_7SonnetThinking
| Self::Claude3_5Haiku => 8_192,
Self::Custom {
max_output_tokens, ..
} => max_output_tokens.unwrap_or(4_096),
@@ -146,6 +185,7 @@ impl Model {
match self {
Self::Claude3_5Sonnet
| Self::Claude3_7Sonnet
| Self::Claude3_7SonnetThinking
| Self::Claude3_5Haiku
| Self::Claude3Opus
| Self::Claude3Sonnet
@@ -157,6 +197,21 @@ impl Model {
}
}
pub fn mode(&self) -> AnthropicModelMode {
match self {
Self::Claude3_5Sonnet
| Self::Claude3_7Sonnet
| Self::Claude3_5Haiku
| Self::Claude3Opus
| Self::Claude3Sonnet
| Self::Claude3Haiku => AnthropicModelMode::Default,
Self::Claude3_7SonnetThinking => AnthropicModelMode::Thinking {
budget_tokens: Some(4_096),
},
Self::Custom { mode, .. } => mode.clone(),
}
}
pub const DEFAULT_BETA_HEADERS: &[&str] = &["prompt-caching-2024-07-31"];
pub fn beta_headers(&self) -> String {
@@ -188,7 +243,7 @@ impl Model {
{
tool_override
} else {
self.id()
self.request_id()
}
}
}
@@ -409,6 +464,8 @@ pub async fn extract_tool_args_from_events(
Err(error) => Some(Err(error)),
Ok(Event::ContentBlockDelta { index, delta }) => match delta {
ContentDelta::TextDelta { .. } => None,
ContentDelta::ThinkingDelta { .. } => None,
ContentDelta::SignatureDelta { .. } => None,
ContentDelta::InputJsonDelta { partial_json } => {
if index == tool_use_index {
Some(Ok(partial_json))
@@ -487,6 +544,10 @@ pub enum RequestContent {
pub enum ResponseContent {
#[serde(rename = "text")]
Text { text: String },
#[serde(rename = "thinking")]
Thinking { thinking: String },
#[serde(rename = "redacted_thinking")]
RedactedThinking { data: String },
#[serde(rename = "tool_use")]
ToolUse {
id: String,
@@ -518,6 +579,19 @@ pub enum ToolChoice {
Tool { name: String },
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum Thinking {
Enabled { budget_tokens: Option<u32> },
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum StringOrContents {
String(String),
Content(Vec<RequestContent>),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Request {
pub model: String,
@@ -526,9 +600,11 @@ pub struct Request {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tools: Vec<Tool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub thinking: Option<Thinking>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_choice: Option<ToolChoice>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub system: Option<String>,
pub system: Option<StringOrContents>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
@@ -553,7 +629,7 @@ pub struct Metadata {
pub user_id: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Usage {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input_tokens: Option<u32>,
@@ -609,6 +685,10 @@ pub enum Event {
pub enum ContentDelta {
#[serde(rename = "text_delta")]
TextDelta { text: String },
#[serde(rename = "thinking_delta")]
ThinkingDelta { thinking: String },
#[serde(rename = "signature_delta")]
SignatureDelta { signature: String },
#[serde(rename = "input_json_delta")]
InputJsonDelta { partial_json: String },
}

View File

@@ -34,9 +34,9 @@ impl AskPassDelegate {
password_prompt: impl Fn(String, oneshot::Sender<String>, &mut AsyncApp) + Send + Sync + 'static,
) -> Self {
let (tx, mut rx) = mpsc::unbounded::<(String, oneshot::Sender<String>)>();
let task = cx.spawn(|mut cx| async move {
let task = cx.spawn(async move |cx: &mut AsyncApp| {
while let Some((prompt, channel)) = rx.next().await {
password_prompt(prompt, channel, &mut cx);
password_prompt(prompt, channel, cx);
}
});
Self { tx, _task: task }
@@ -188,7 +188,7 @@ impl AskPassSession {
}
pub async fn run(&mut self) -> AskPassResult {
futures::FutureExt::fuse(smol::Timer::after(Duration::from_secs(10))).await;
futures::FutureExt::fuse(smol::Timer::after(Duration::from_secs(20))).await;
AskPassResult::Timedout
}
}

View File

@@ -48,7 +48,6 @@ lsp.workspace = true
menu.workspace = true
multi_buffer.workspace = true
parking_lot.workspace = true
paths.workspace = true
project.workspace = true
prompt_library.workspace = true
prompt_store.workspace = true
@@ -56,7 +55,6 @@ proto.workspace = true
rope.workspace = true
schemars.workspace = true
search.workspace = true
semantic_index.workspace = true
serde.workspace = true
settings.workspace = true
smol.workspace = true

View File

@@ -10,17 +10,15 @@ use std::sync::Arc;
use assistant_settings::AssistantSettings;
use assistant_slash_command::SlashCommandRegistry;
use assistant_slash_commands::{ProjectSlashCommandFeatureFlag, SearchSlashCommandFeatureFlag};
use client::Client;
use command_palette_hooks::CommandPaletteFilter;
use feature_flags::FeatureFlagAppExt;
use fs::Fs;
use gpui::{actions, App, Global, UpdateGlobal};
use gpui::{actions, App, Global, ReadGlobal, UpdateGlobal};
use language_model::{
LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
};
use prompt_store::PromptBuilder;
use semantic_index::{CloudEmbeddingProvider, SemanticDb};
use serde::Deserialize;
use settings::{Settings, SettingsStore};
@@ -86,6 +84,10 @@ impl Assistant {
filter.show_namespace(Self::NAMESPACE);
});
}
pub fn enabled(cx: &App) -> bool {
Self::global(cx).enabled
}
}
pub fn init(
@@ -98,33 +100,6 @@ pub fn init(
AssistantSettings::register(cx);
SlashCommandSettings::register(cx);
cx.spawn(|mut cx| {
let client = client.clone();
async move {
let is_search_slash_command_enabled = cx
.update(|cx| cx.wait_for_flag::<SearchSlashCommandFeatureFlag>())?
.await;
let is_project_slash_command_enabled = cx
.update(|cx| cx.wait_for_flag::<ProjectSlashCommandFeatureFlag>())?
.await;
if !is_search_slash_command_enabled && !is_project_slash_command_enabled {
return Ok(());
}
let embedding_provider = CloudEmbeddingProvider::new(client.clone());
let semantic_index = SemanticDb::new(
paths::embeddings_dir().join("semantic-index-db.0.mdb"),
Arc::new(embedding_provider),
&mut cx,
)
.await?;
cx.update(|cx| cx.set_global(semantic_index))
}
})
.detach();
assistant_context_editor::init(client.clone(), cx);
prompt_library::init(cx);
init_language_model_settings(cx);
@@ -133,7 +108,7 @@ pub fn init(
assistant_panel::init(cx);
context_server::init(cx);
register_slash_commands(Some(prompt_builder.clone()), cx);
register_slash_commands(cx);
inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
@@ -186,8 +161,12 @@ fn init_language_model_settings(cx: &mut App) {
fn update_active_language_model_from_settings(cx: &mut App) {
let settings = AssistantSettings::get_global(cx);
let provider_name = LanguageModelProviderId::from(settings.default_model.provider.clone());
let model_id = LanguageModelId::from(settings.default_model.model.clone());
let active_model_provider_name =
LanguageModelProviderId::from(settings.default_model.provider.clone());
let active_model_id = LanguageModelId::from(settings.default_model.model.clone());
let editor_provider_name =
LanguageModelProviderId::from(settings.editor_model.provider.clone());
let editor_model_id = LanguageModelId::from(settings.editor_model.model.clone());
let inline_alternatives = settings
.inline_alternatives
.iter()
@@ -199,12 +178,13 @@ fn update_active_language_model_from_settings(cx: &mut App) {
})
.collect::<Vec<_>>();
LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
registry.select_active_model(&provider_name, &model_id, cx);
registry.select_active_model(&active_model_provider_name, &active_model_id, cx);
registry.select_editor_model(&editor_provider_name, &editor_model_id, cx);
registry.select_inline_alternative_models(inline_alternatives, cx);
});
}
fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut App) {
fn register_slash_commands(cx: &mut App) {
let slash_command_registry = SlashCommandRegistry::global(cx);
slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
@@ -222,33 +202,6 @@ fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut
.register_command(assistant_slash_commands::DiagnosticsSlashCommand, true);
slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true);
if let Some(prompt_builder) = prompt_builder {
cx.observe_flag::<assistant_slash_commands::ProjectSlashCommandFeatureFlag, _>({
let slash_command_registry = slash_command_registry.clone();
move |is_enabled, _cx| {
if is_enabled {
slash_command_registry.register_command(
assistant_slash_commands::ProjectSlashCommand::new(prompt_builder.clone()),
true,
);
}
}
})
.detach();
}
cx.observe_flag::<assistant_slash_commands::AutoSlashCommandFeatureFlag, _>({
let slash_command_registry = slash_command_registry.clone();
move |is_enabled, _cx| {
if is_enabled {
// [#auto-staff-ship] TODO remove this when /auto is no longer staff-shipped
slash_command_registry
.register_command(assistant_slash_commands::AutoCommand, true);
}
}
})
.detach();
cx.observe_flag::<assistant_slash_commands::StreamingExampleSlashCommandFeatureFlag, _>({
let slash_command_registry = slash_command_registry.clone();
move |is_enabled, _cx| {
@@ -265,17 +218,6 @@ fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut
update_slash_commands_from_settings(cx);
cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
.detach();
cx.observe_flag::<assistant_slash_commands::SearchSlashCommandFeatureFlag, _>({
let slash_command_registry = slash_command_registry.clone();
move |is_enabled, _cx| {
if is_enabled {
slash_command_registry
.register_command(assistant_slash_commands::SearchSlashCommand, true);
}
}
})
.detach();
}
fn update_slash_commands_from_settings(cx: &mut App) {

View File

@@ -1,4 +1,5 @@
use crate::assistant_configuration::{ConfigurationView, ConfigurationViewEvent};
use crate::Assistant;
use crate::{
terminal_inline_assistant::TerminalInlineAssistant, DeployHistory, InlineAssistant, NewChat,
};
@@ -38,7 +39,7 @@ use workspace::{
dock::{DockPosition, Panel, PanelEvent},
pane, DraggedSelection, Pane, ShowConfiguration, ToggleZoom, Workspace,
};
use zed_actions::assistant::{DeployPromptLibrary, InlineAssist, ToggleFocus};
use zed_actions::assistant::{InlineAssist, OpenPromptLibrary, ToggleFocus};
pub fn init(cx: &mut App) {
workspace::FollowableViewRegistry::register::<ContextEditor>(cx);
@@ -58,8 +59,7 @@ pub fn init(cx: &mut App) {
cx.observe_new(
|terminal_panel: &mut TerminalPanel, _, cx: &mut Context<TerminalPanel>| {
let settings = AssistantSettings::get_global(cx);
terminal_panel.set_assistant_enabled(settings.enabled, cx);
terminal_panel.set_assistant_enabled(Assistant::enabled(cx), cx);
},
)
.detach();
@@ -98,16 +98,16 @@ impl AssistantPanel {
prompt_builder: Arc<PromptBuilder>,
cx: AsyncWindowContext,
) -> Task<Result<Entity<Self>>> {
cx.spawn(|mut cx| async move {
cx.spawn(async move |cx| {
let slash_commands = Arc::new(SlashCommandWorkingSet::default());
let context_store = workspace
.update(&mut cx, |workspace, cx| {
.update(cx, |workspace, cx| {
let project = workspace.project().clone();
ContextStore::new(project, prompt_builder.clone(), slash_commands, cx)
})?
.await?;
workspace.update_in(&mut cx, |workspace, window, cx| {
workspace.update_in(cx, |workspace, window, cx| {
// TODO: deserialize state.
cx.new(|cx| Self::new(workspace, context_store, window, cx))
})
@@ -259,7 +259,7 @@ impl AssistantPanel {
menu.context(focus_handle.clone())
.action("New Chat", Box::new(NewChat))
.action("History", Box::new(DeployHistory))
.action("Prompt Library", Box::new(DeployPromptLibrary))
.action("Prompt Library", Box::new(OpenPromptLibrary))
.action("Configure", Box::new(ShowConfiguration))
.action(zoom_label, Box::new(ToggleZoom))
}))
@@ -297,7 +297,8 @@ impl AssistantPanel {
&LanguageModelRegistry::global(cx),
window,
|this, _, event: &language_model::Event, window, cx| match event {
language_model::Event::ActiveModelChanged => {
language_model::Event::ActiveModelChanged
| language_model::Event::EditorModelChanged => {
this.completion_provider_changed(window, cx);
}
language_model::Event::ProviderStateChanged => {
@@ -341,12 +342,12 @@ impl AssistantPanel {
window: &mut Window,
cx: &mut Context<Workspace>,
) {
let settings = AssistantSettings::get_global(cx);
if !settings.enabled {
return;
if workspace
.panel::<Self>(cx)
.is_some_and(|panel| panel.read(cx).enabled(cx))
{
workspace.toggle_panel_focus::<Self>(window, cx);
}
workspace.toggle_panel_focus::<Self>(window, cx);
}
fn watch_client_status(
@@ -356,9 +357,9 @@ impl AssistantPanel {
) -> Task<()> {
let mut status_rx = client.status();
cx.spawn_in(window, |this, mut cx| async move {
cx.spawn_in(window, async move |this, cx| {
while let Some(status) = status_rx.next().await {
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
if this.client_status.is_none()
|| this
.client_status
@@ -370,7 +371,7 @@ impl AssistantPanel {
})
.log_err();
}
this.update(&mut cx, |this, _cx| this.watch_client_status = None)
this.update(cx, |this, _cx| this.watch_client_status = None)
.log_err();
})
}
@@ -575,11 +576,11 @@ impl AssistantPanel {
if self.authenticate_provider_task.is_none() {
self.authenticate_provider_task = Some((
provider.id(),
cx.spawn_in(window, |this, mut cx| async move {
cx.spawn_in(window, async move |this, cx| {
if let Some(future) = load_credentials {
let _ = future.await;
}
this.update(&mut cx, |this, _cx| {
this.update(cx, |this, _cx| {
this.authenticate_provider_task = None;
})
.log_err();
@@ -594,12 +595,10 @@ impl AssistantPanel {
window: &mut Window,
cx: &mut Context<Workspace>,
) {
let settings = AssistantSettings::get_global(cx);
if !settings.enabled {
return;
}
let Some(assistant_panel) = workspace.panel::<AssistantPanel>(cx) else {
let Some(assistant_panel) = workspace
.panel::<AssistantPanel>(cx)
.filter(|panel| panel.read(cx).enabled(cx))
else {
return;
};
@@ -640,9 +639,9 @@ impl AssistantPanel {
}
} else {
let assistant_panel = assistant_panel.downgrade();
cx.spawn_in(window, |workspace, mut cx| async move {
cx.spawn_in(window, async move |workspace, cx| {
let Some(task) =
assistant_panel.update(&mut cx, |assistant, cx| assistant.authenticate(cx))?
assistant_panel.update(cx, |assistant, cx| assistant.authenticate(cx))?
else {
let answer = cx
.prompt(
@@ -664,7 +663,7 @@ impl AssistantPanel {
return Ok(());
};
task.await?;
if assistant_panel.update(&mut cx, |panel, cx| panel.is_authenticated(cx))? {
if assistant_panel.update(cx, |panel, cx| panel.is_authenticated(cx))? {
cx.update(|window, cx| match inline_assist_target {
InlineAssistTarget::Editor(active_editor, include_context) => {
let assistant_panel = if include_context {
@@ -697,7 +696,7 @@ impl AssistantPanel {
}
})?
} else {
workspace.update_in(&mut cx, |workspace, window, cx| {
workspace.update_in(cx, |workspace, window, cx| {
workspace.focus_panel::<AssistantPanel>(window, cx)
})?;
}
@@ -790,10 +789,10 @@ impl AssistantPanel {
.context_store
.update(cx, |store, cx| store.create_remote_context(cx));
cx.spawn_in(window, |this, mut cx| async move {
cx.spawn_in(window, async move |this, cx| {
let context = task.await?;
this.update_in(&mut cx, |this, window, cx| {
this.update_in(cx, |this, window, cx| {
let workspace = this.workspace.clone();
let project = this.project.clone();
let lsp_adapter_delegate =
@@ -846,9 +845,9 @@ impl AssistantPanel {
self.show_context(editor.clone(), window, cx);
let workspace = self.workspace.clone();
cx.spawn_in(window, move |_, mut cx| async move {
cx.spawn_in(window, async move |_, cx| {
workspace
.update_in(&mut cx, |workspace, window, cx| {
.update_in(cx, |workspace, window, cx| {
workspace.focus_panel::<AssistantPanel>(window, cx);
})
.ok();
@@ -1027,7 +1026,7 @@ impl AssistantPanel {
fn deploy_prompt_library(
&mut self,
_: &DeployPromptLibrary,
_: &OpenPromptLibrary,
_window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -1068,8 +1067,8 @@ impl AssistantPanel {
.filter(|editor| editor.read(cx).context().read(cx).path() == Some(&path))
});
if let Some(existing_context) = existing_context {
return cx.spawn_in(window, |this, mut cx| async move {
this.update_in(&mut cx, |this, window, cx| {
return cx.spawn_in(window, async move |this, cx| {
this.update_in(cx, |this, window, cx| {
this.show_context(existing_context, window, cx)
})
});
@@ -1084,9 +1083,9 @@ impl AssistantPanel {
let lsp_adapter_delegate = make_lsp_adapter_delegate(&project, cx).log_err().flatten();
cx.spawn_in(window, |this, mut cx| async move {
cx.spawn_in(window, async move |this, cx| {
let context = context.await?;
this.update_in(&mut cx, |this, window, cx| {
this.update_in(cx, |this, window, cx| {
let editor = cx.new(|cx| {
ContextEditor::for_context(
context,
@@ -1116,8 +1115,8 @@ impl AssistantPanel {
.filter(|editor| *editor.read(cx).context().read(cx).id() == id)
});
if let Some(existing_context) = existing_context {
return cx.spawn_in(window, |this, mut cx| async move {
this.update_in(&mut cx, |this, window, cx| {
return cx.spawn_in(window, async move |this, cx| {
this.update_in(cx, |this, window, cx| {
this.show_context(existing_context.clone(), window, cx)
})?;
Ok(existing_context)
@@ -1133,9 +1132,9 @@ impl AssistantPanel {
.log_err()
.flatten();
cx.spawn_in(window, |this, mut cx| async move {
cx.spawn_in(window, async move |this, cx| {
let context = context.await?;
this.update_in(&mut cx, |this, window, cx| {
this.update_in(cx, |this, window, cx| {
let editor = cx.new(|cx| {
ContextEditor::for_context(
context,
@@ -1297,12 +1296,8 @@ impl Panel for AssistantPanel {
}
fn icon(&self, _: &Window, cx: &App) -> Option<IconName> {
let settings = AssistantSettings::get_global(cx);
if !settings.enabled || !settings.button {
return None;
}
Some(IconName::ZedAssistant)
(self.enabled(cx) && AssistantSettings::get_global(cx).button)
.then_some(IconName::ZedAssistant)
}
fn icon_tooltip(&self, _: &Window, _: &App) -> Option<&'static str> {
@@ -1316,6 +1311,10 @@ impl Panel for AssistantPanel {
fn activation_priority(&self) -> u32 {
4
}
fn enabled(&self, cx: &App) -> bool {
Assistant::enabled(cx)
}
}
impl EventEmitter<PanelEvent> for AssistantPanel {}

View File

@@ -1,5 +1,6 @@
use crate::{
AssistantPanel, AssistantPanelEvent, CycleNextInlineAssist, CyclePreviousInlineAssist,
Assistant, AssistantPanel, AssistantPanelEvent, CycleNextInlineAssist,
CyclePreviousInlineAssist,
};
use anyhow::{anyhow, Context as _, Result};
use assistant_context_editor::{humanize_token_count, RequestType};
@@ -232,7 +233,7 @@ impl InlineAssistant {
) {
let (snapshot, initial_selections) = editor.update(cx, |editor, cx| {
(
editor.buffer().read(cx).snapshot(cx),
editor.snapshot(window, cx),
editor.selections.all::<Point>(cx),
)
});
@@ -246,7 +247,37 @@ impl InlineAssistant {
if selection.end.column == 0 {
selection.end.row -= 1;
}
selection.end.column = snapshot.line_len(MultiBufferRow(selection.end.row));
selection.end.column = snapshot
.buffer_snapshot
.line_len(MultiBufferRow(selection.end.row));
} else if let Some(fold) =
snapshot.crease_for_buffer_row(MultiBufferRow(selection.end.row))
{
selection.start = fold.range().start;
selection.end = fold.range().end;
if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot.max_row() {
let chars = snapshot
.buffer_snapshot
.chars_at(Point::new(selection.end.row + 1, 0));
for c in chars {
if c == '\n' {
break;
}
if c.is_whitespace() {
continue;
}
if snapshot
.language_at(selection.end)
.is_some_and(|language| language.config().brackets.is_closing_brace(c))
{
selection.end.row += 1;
selection.end.column = snapshot
.buffer_snapshot
.line_len(MultiBufferRow(selection.end.row));
}
}
}
}
if let Some(prev_selection) = selections.last_mut() {
@@ -262,6 +293,7 @@ impl InlineAssistant {
}
selections.push(selection);
}
let snapshot = &snapshot.buffer_snapshot;
let newest_selection = newest_selection.unwrap();
let mut codegen_ranges = Vec::new();
@@ -386,7 +418,6 @@ impl InlineAssistant {
}
}
#[allow(clippy::too_many_arguments)]
pub fn suggest_assist(
&mut self,
editor: &Entity<Editor>,
@@ -1247,7 +1278,7 @@ impl InlineAssistant {
});
enum DeletedLines {}
let mut editor = Editor::for_multibuffer(multi_buffer, None, true, window, cx);
let mut editor = Editor::for_multibuffer(multi_buffer, None, window, cx);
editor.set_soft_wrap_mode(language::language_settings::SoftWrap::None, cx);
editor.set_show_wrap_guides(false, cx);
editor.set_show_gutter(false, cx);
@@ -1312,9 +1343,9 @@ impl EditorInlineAssists {
assist_ids: Vec::new(),
scroll_lock: None,
highlight_updates: highlight_updates_tx,
_update_highlights: cx.spawn(|cx| {
_update_highlights: cx.spawn({
let editor = editor.downgrade();
async move {
async move |cx| {
while let Ok(()) = highlight_updates_rx.changed().await {
let editor = editor.upgrade().context("editor was dropped")?;
cx.update_global(|assistant: &mut InlineAssistant, cx| {
@@ -1674,7 +1705,6 @@ impl Focusable for PromptEditor {
impl PromptEditor {
const MAX_LINES: u8 = 8;
#[allow(clippy::too_many_arguments)]
fn new(
id: InlineAssistId,
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
@@ -1695,7 +1725,6 @@ impl PromptEditor {
},
prompt_buffer,
None,
false,
window,
cx,
);
@@ -1853,7 +1882,7 @@ impl PromptEditor {
fn count_tokens(&mut self, cx: &mut Context<Self>) {
let assist_id = self.id;
self.pending_token_count = cx.spawn(|this, mut cx| async move {
self.pending_token_count = cx.spawn(async move |this, cx| {
cx.background_executor().timer(Duration::from_secs(1)).await;
let token_count = cx
.update_global(|inline_assistant: &mut InlineAssistant, cx| {
@@ -1865,7 +1894,7 @@ impl PromptEditor {
})??
.await?;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
this.token_counts = Some(token_count);
cx.notify();
})
@@ -2333,7 +2362,6 @@ struct InlineAssist {
}
impl InlineAssist {
#[allow(clippy::too_many_arguments)]
fn new(
assist_id: InlineAssistId,
group_id: InlineAssistGroupId,
@@ -2886,7 +2914,7 @@ impl CodegenAlternative {
let request = self.build_request(user_prompt, assistant_panel_context, cx)?;
self.request = Some(request.clone());
cx.spawn(|_, cx| async move { model.stream_completion_text(request, &cx).await })
cx.spawn(async move |_, cx| model.stream_completion_text(request, &cx).await)
.boxed_local()
};
self.handle_stream(telemetry_id, provider_id.to_string(), api_key, stream, cx);
@@ -3003,213 +3031,207 @@ impl CodegenAlternative {
let completion = Arc::new(Mutex::new(String::new()));
let completion_clone = completion.clone();
self.generation = cx.spawn(|codegen, mut cx| {
async move {
let stream = stream.await;
let message_id = stream
.as_ref()
.ok()
.and_then(|stream| stream.message_id.clone());
let generate = async {
let (mut diff_tx, mut diff_rx) = mpsc::channel(1);
let executor = cx.background_executor().clone();
let message_id = message_id.clone();
let line_based_stream_diff: Task<anyhow::Result<()>> =
cx.background_spawn(async move {
let mut response_latency = None;
let request_start = Instant::now();
let diff = async {
let chunks = StripInvalidSpans::new(stream?.stream);
futures::pin_mut!(chunks);
let mut diff = StreamingDiff::new(selected_text.to_string());
let mut line_diff = LineDiff::default();
self.generation = cx.spawn(async move |codegen, cx| {
let stream = stream.await;
let message_id = stream
.as_ref()
.ok()
.and_then(|stream| stream.message_id.clone());
let generate = async {
let (mut diff_tx, mut diff_rx) = mpsc::channel(1);
let executor = cx.background_executor().clone();
let message_id = message_id.clone();
let line_based_stream_diff: Task<anyhow::Result<()>> =
cx.background_spawn(async move {
let mut response_latency = None;
let request_start = Instant::now();
let diff = async {
let chunks = StripInvalidSpans::new(stream?.stream);
futures::pin_mut!(chunks);
let mut diff = StreamingDiff::new(selected_text.to_string());
let mut line_diff = LineDiff::default();
let mut new_text = String::new();
let mut base_indent = None;
let mut line_indent = None;
let mut first_line = true;
let mut new_text = String::new();
let mut base_indent = None;
let mut line_indent = None;
let mut first_line = true;
while let Some(chunk) = chunks.next().await {
if response_latency.is_none() {
response_latency = Some(request_start.elapsed());
}
let chunk = chunk?;
completion_clone.lock().push_str(&chunk);
while let Some(chunk) = chunks.next().await {
if response_latency.is_none() {
response_latency = Some(request_start.elapsed());
}
let chunk = chunk?;
completion_clone.lock().push_str(&chunk);
let mut lines = chunk.split('\n').peekable();
while let Some(line) = lines.next() {
new_text.push_str(line);
if line_indent.is_none() {
if let Some(non_whitespace_ch_ix) =
new_text.find(|ch: char| !ch.is_whitespace())
{
line_indent = Some(non_whitespace_ch_ix);
base_indent = base_indent.or(line_indent);
let mut lines = chunk.split('\n').peekable();
while let Some(line) = lines.next() {
new_text.push_str(line);
if line_indent.is_none() {
if let Some(non_whitespace_ch_ix) =
new_text.find(|ch: char| !ch.is_whitespace())
{
line_indent = Some(non_whitespace_ch_ix);
base_indent = base_indent.or(line_indent);
let line_indent = line_indent.unwrap();
let base_indent = base_indent.unwrap();
let indent_delta =
line_indent as i32 - base_indent as i32;
let mut corrected_indent_len = cmp::max(
0,
suggested_line_indent.len as i32 + indent_delta,
)
as usize;
if first_line {
corrected_indent_len = corrected_indent_len
.saturating_sub(
selection_start.column as usize,
);
}
let indent_char = suggested_line_indent.char();
let mut indent_buffer = [0; 4];
let indent_str =
indent_char.encode_utf8(&mut indent_buffer);
new_text.replace_range(
..line_indent,
&indent_str.repeat(corrected_indent_len),
);
let line_indent = line_indent.unwrap();
let base_indent = base_indent.unwrap();
let indent_delta =
line_indent as i32 - base_indent as i32;
let mut corrected_indent_len = cmp::max(
0,
suggested_line_indent.len as i32 + indent_delta,
)
as usize;
if first_line {
corrected_indent_len = corrected_indent_len
.saturating_sub(
selection_start.column as usize,
);
}
}
if line_indent.is_some() {
let char_ops = diff.push_new(&new_text);
line_diff
.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
let indent_char = suggested_line_indent.char();
let mut indent_buffer = [0; 4];
let indent_str =
indent_char.encode_utf8(&mut indent_buffer);
new_text.replace_range(
..line_indent,
&indent_str.repeat(corrected_indent_len),
);
}
}
if line_indent.is_some() {
let char_ops = diff.push_new(&new_text);
line_diff.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
new_text.clear();
}
if lines.peek().is_some() {
let char_ops = diff.push_new("\n");
line_diff.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
if line_indent.is_none() {
// Don't write out the leading indentation in empty lines on the next line
// This is the case where the above if statement didn't clear the buffer
new_text.clear();
}
if lines.peek().is_some() {
let char_ops = diff.push_new("\n");
line_diff
.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
if line_indent.is_none() {
// Don't write out the leading indentation in empty lines on the next line
// This is the case where the above if statement didn't clear the buffer
new_text.clear();
}
line_indent = None;
first_line = false;
}
line_indent = None;
first_line = false;
}
}
let mut char_ops = diff.push_new(&new_text);
char_ops.extend(diff.finish());
line_diff.push_char_operations(&char_ops, &selected_text);
line_diff.finish(&selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
anyhow::Ok(())
};
let result = diff.await;
let error_message =
result.as_ref().err().map(|error| error.to_string());
report_assistant_event(
AssistantEvent {
conversation_id: None,
message_id,
kind: AssistantKind::Inline,
phase: AssistantPhase::Response,
model: model_telemetry_id,
model_provider: model_provider_id.to_string(),
response_latency,
error_message,
language_name: language_name.map(|name| name.to_proto()),
},
telemetry,
http_client,
model_api_key,
&executor,
);
result?;
Ok(())
});
while let Some((char_ops, line_ops)) = diff_rx.next().await {
codegen.update(&mut cx, |codegen, cx| {
codegen.last_equal_ranges.clear();
let edits = char_ops
.into_iter()
.filter_map(|operation| match operation {
CharOperation::Insert { text } => {
let edit_start = snapshot.anchor_after(edit_start);
Some((edit_start..edit_start, text))
}
CharOperation::Delete { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
Some((edit_range, String::new()))
}
CharOperation::Keep { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
codegen.last_equal_ranges.push(edit_range);
None
}
})
.collect::<Vec<_>>();
if codegen.active {
codegen.apply_edits(edits.iter().cloned(), cx);
codegen.reapply_line_based_diff(line_ops.iter().cloned(), cx);
}
codegen.edits.extend(edits);
codegen.line_operations = line_ops;
codegen.edit_position = Some(snapshot.anchor_after(edit_start));
cx.notify();
})?;
}
let mut char_ops = diff.push_new(&new_text);
char_ops.extend(diff.finish());
line_diff.push_char_operations(&char_ops, &selected_text);
line_diff.finish(&selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
// Streaming stopped and we have the new text in the buffer, and a line-based diff applied for the whole new buffer.
// That diff is not what a regular diff is and might look unexpected, ergo apply a regular diff.
// It's fine to apply even if the rest of the line diffing fails, as no more hunks are coming through `diff_rx`.
let batch_diff_task =
codegen.update(&mut cx, |codegen, cx| codegen.reapply_batch_diff(cx))?;
let (line_based_stream_diff, ()) =
join!(line_based_stream_diff, batch_diff_task);
line_based_stream_diff?;
anyhow::Ok(())
};
anyhow::Ok(())
};
let result = diff.await;
let result = generate.await;
let elapsed_time = start_time.elapsed().as_secs_f64();
let error_message = result.as_ref().err().map(|error| error.to_string());
report_assistant_event(
AssistantEvent {
conversation_id: None,
message_id,
kind: AssistantKind::Inline,
phase: AssistantPhase::Response,
model: model_telemetry_id,
model_provider: model_provider_id.to_string(),
response_latency,
error_message,
language_name: language_name.map(|name| name.to_proto()),
},
telemetry,
http_client,
model_api_key,
&executor,
);
codegen
.update(&mut cx, |this, cx| {
this.message_id = message_id;
this.last_equal_ranges.clear();
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {
this.status = CodegenStatus::Done;
result?;
Ok(())
});
while let Some((char_ops, line_ops)) = diff_rx.next().await {
codegen.update(cx, |codegen, cx| {
codegen.last_equal_ranges.clear();
let edits = char_ops
.into_iter()
.filter_map(|operation| match operation {
CharOperation::Insert { text } => {
let edit_start = snapshot.anchor_after(edit_start);
Some((edit_start..edit_start, text))
}
CharOperation::Delete { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
Some((edit_range, String::new()))
}
CharOperation::Keep { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
codegen.last_equal_ranges.push(edit_range);
None
}
})
.collect::<Vec<_>>();
if codegen.active {
codegen.apply_edits(edits.iter().cloned(), cx);
codegen.reapply_line_based_diff(line_ops.iter().cloned(), cx);
}
this.elapsed_time = Some(elapsed_time);
this.completion = Some(completion.lock().clone());
cx.emit(CodegenEvent::Finished);
codegen.edits.extend(edits);
codegen.line_operations = line_ops;
codegen.edit_position = Some(snapshot.anchor_after(edit_start));
cx.notify();
})
.ok();
}
})?;
}
// Streaming stopped and we have the new text in the buffer, and a line-based diff applied for the whole new buffer.
// That diff is not what a regular diff is and might look unexpected, ergo apply a regular diff.
// It's fine to apply even if the rest of the line diffing fails, as no more hunks are coming through `diff_rx`.
let batch_diff_task =
codegen.update(cx, |codegen, cx| codegen.reapply_batch_diff(cx))?;
let (line_based_stream_diff, ()) = join!(line_based_stream_diff, batch_diff_task);
line_based_stream_diff?;
anyhow::Ok(())
};
let result = generate.await;
let elapsed_time = start_time.elapsed().as_secs_f64();
codegen
.update(cx, |this, cx| {
this.message_id = message_id;
this.last_equal_ranges.clear();
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {
this.status = CodegenStatus::Done;
}
this.elapsed_time = Some(elapsed_time);
this.completion = Some(completion.lock().clone());
cx.emit(CodegenEvent::Finished);
cx.notify();
})
.ok();
});
cx.notify();
}
@@ -3327,7 +3349,7 @@ impl CodegenAlternative {
let new_snapshot = self.buffer.read(cx).snapshot(cx);
let new_range = self.range.to_point(&new_snapshot);
cx.spawn(|codegen, mut cx| async move {
cx.spawn(async move |codegen, cx| {
let (deleted_row_ranges, inserted_row_ranges) = cx
.background_spawn(async move {
let old_text = old_snapshot
@@ -3377,7 +3399,7 @@ impl CodegenAlternative {
.await;
codegen
.update(&mut cx, |codegen, cx| {
.update(cx, |codegen, cx| {
codegen.diff.deleted_row_ranges = deleted_row_ranges;
codegen.diff.inserted_row_ranges = inserted_row_ranges;
cx.notify();
@@ -3534,7 +3556,7 @@ impl CodeActionProvider for AssistantCodeActionProvider {
_: &mut Window,
cx: &mut App,
) -> Task<Result<Vec<CodeAction>>> {
if !AssistantSettings::get_global(cx).enabled {
if !Assistant::enabled(cx) {
return Task::ready(Ok(Vec::new()));
}
@@ -3573,6 +3595,7 @@ impl CodeActionProvider for AssistantCodeActionProvider {
title: "Fix with Assistant".into(),
..Default::default()
})),
resolved: true,
}]))
} else {
Task::ready(Ok(Vec::new()))
@@ -3590,10 +3613,10 @@ impl CodeActionProvider for AssistantCodeActionProvider {
) -> Task<Result<ProjectTransaction>> {
let editor = self.editor.clone();
let workspace = self.workspace.clone();
window.spawn(cx, |mut cx| async move {
window.spawn(cx, async move |cx| {
let editor = editor.upgrade().context("editor was released")?;
let range = editor
.update(&mut cx, |editor, cx| {
.update(cx, |editor, cx| {
editor.buffer().update(cx, |multibuffer, cx| {
let buffer = buffer.read(cx);
let multibuffer_snapshot = multibuffer.read(cx);
@@ -3628,7 +3651,7 @@ impl CodeActionProvider for AssistantCodeActionProvider {
})
})?
.context("invalid range")?;
let assistant_panel = workspace.update(&mut cx, |workspace, cx| {
let assistant_panel = workspace.update(cx, |workspace, cx| {
workspace
.panel::<AssistantPanel>(cx)
.context("assistant panel was released")
@@ -3690,7 +3713,7 @@ mod tests {
language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, LanguageMatcher,
Point,
};
use language_model::LanguageModelRegistry;
use language_model::{LanguageModelRegistry, TokenUsage};
use rand::prelude::*;
use serde::Serialize;
use settings::SettingsStore;
@@ -4069,6 +4092,7 @@ mod tests {
future::ready(Ok(LanguageModelTextStream {
message_id: None,
stream: chunks_rx.map(Ok).boxed(),
last_token_usage: Arc::new(Mutex::new(TokenUsage::default())),
})),
cx,
);

View File

@@ -702,7 +702,6 @@ impl Focusable for PromptEditor {
impl PromptEditor {
const MAX_LINES: u8 = 8;
#[allow(clippy::too_many_arguments)]
fn new(
id: TerminalInlineAssistId,
prompt_history: VecDeque<String>,
@@ -721,7 +720,6 @@ impl PromptEditor {
},
prompt_buffer,
None,
false,
window,
cx,
);
@@ -827,7 +825,7 @@ impl PromptEditor {
let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
return;
};
self.pending_token_count = cx.spawn(|this, mut cx| async move {
self.pending_token_count = cx.spawn(async move |this, cx| {
cx.background_executor().timer(Duration::from_secs(1)).await;
let request =
cx.update_global(|inline_assistant: &mut TerminalInlineAssistant, cx| {
@@ -835,7 +833,7 @@ impl PromptEditor {
})??;
let token_count = cx.update(|cx| model.count_tokens(request, cx))?.await?;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
this.token_count = Some(token_count);
cx.notify();
})
@@ -1142,7 +1140,7 @@ impl Codegen {
let telemetry = self.telemetry.clone();
self.status = CodegenStatus::Pending;
self.transaction = Some(TerminalTransaction::start(self.terminal.clone()));
self.generation = cx.spawn(|this, mut cx| async move {
self.generation = cx.spawn(async move |this, cx| {
let model_telemetry_id = model.telemetry_id();
let model_provider_id = model.provider_id();
let response = model.stream_completion_text(prompt, &cx).await;
@@ -1199,12 +1197,12 @@ impl Codegen {
}
});
this.update(&mut cx, |this, _| {
this.update(cx, |this, _| {
this.message_id = message_id;
})?;
while let Some(hunk) = hunks_rx.next().await {
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
if let Some(transaction) = &mut this.transaction {
transaction.push(hunk, cx);
cx.notify();
@@ -1218,7 +1216,7 @@ impl Codegen {
let result = generate.await;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {

View File

@@ -31,6 +31,7 @@ clock.workspace = true
collections.workspace = true
command_palette_hooks.workspace = true
context_server.workspace = true
convert_case.workspace = true
db.workspace = true
editor.workspace = true
feature_flags.workspace = true
@@ -38,10 +39,13 @@ file_icons.workspace = true
fs.workspace = true
futures.workspace = true
fuzzy.workspace = true
git.workspace = true
git_ui.workspace = true
gpui.workspace = true
heed.workspace = true
html_to_markdown.workspace = true
http_client.workspace = true
indexmap.workspace = true
itertools.workspace = true
language.workspace = true
language_model.workspace = true
@@ -58,12 +62,15 @@ project.workspace = true
prompt_library.workspace = true
prompt_store.workspace = true
proto.workspace = true
release_channel.workspace = true
rope.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
smallvec.workspace = true
smol.workspace = true
streaming_diff.workspace = true
telemetry.workspace = true
telemetry_events.workspace = true
terminal.workspace = true
terminal_view.workspace = true
@@ -81,8 +88,8 @@ zed_actions.workspace = true
[dev-dependencies]
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"] }
project = { workspace = true, features = ["test-support"] }
rand.workspace = true
indoc.workspace = true

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@ mod history_store;
mod inline_assistant;
mod inline_prompt_editor;
mod message_editor;
mod profile_selector;
mod terminal_codegen;
mod terminal_inline_assistant;
mod thread;
@@ -30,8 +31,12 @@ use gpui::{actions, App};
use prompt_store::PromptBuilder;
use settings::Settings as _;
pub use crate::active_thread::ActiveThread;
use crate::assistant_configuration::{AddContextServerModal, ManageProfilesModal};
pub use crate::assistant_panel::{AssistantPanel, ConcreteAssistantPanelDelegate};
pub use crate::inline_assistant::InlineAssistant;
pub use crate::thread::{Message, RequestKind, Thread, ThreadEvent};
pub use crate::thread_store::ThreadStore;
actions!(
assistant2,
@@ -42,6 +47,8 @@ actions!(
RemoveAllContext,
OpenHistory,
OpenConfiguration,
ManageProfiles,
AddContextServer,
RemoveSelectedThread,
Chat,
ChatMode,
@@ -52,7 +59,8 @@ actions!(
FocusLeft,
FocusRight,
RemoveFocusedContext,
AcceptSuggestedContext
AcceptSuggestedContext,
OpenActiveThreadAsMarkdown
]
);
@@ -81,6 +89,8 @@ pub fn init(
client.telemetry().clone(),
cx,
);
cx.observe_new(AddContextServerModal::register).detach();
cx.observe_new(ManageProfilesModal::register).detach();
feature_gate_assistant2_actions(cx);
}

View File

@@ -1,19 +1,40 @@
mod add_context_server_modal;
mod manage_profiles_modal;
mod profile_picker;
mod tool_picker;
use std::sync::Arc;
use assistant_tool::{ToolSource, ToolWorkingSet};
use collections::HashMap;
use gpui::{Action, AnyView, App, EventEmitter, FocusHandle, Focusable, Subscription};
use context_server::manager::ContextServerManager;
use gpui::{Action, AnyView, App, Entity, EventEmitter, FocusHandle, Focusable, Subscription};
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
use ui::{prelude::*, Divider, DividerColor, ElevationIndex};
use zed_actions::assistant::DeployPromptLibrary;
use ui::{prelude::*, Disclosure, Divider, DividerColor, ElevationIndex, Indicator, Switch};
use util::ResultExt as _;
use zed_actions::ExtensionCategoryFilter;
pub(crate) use add_context_server_modal::AddContextServerModal;
pub(crate) use manage_profiles_modal::ManageProfilesModal;
use crate::AddContextServer;
pub struct AssistantConfiguration {
focus_handle: FocusHandle,
configuration_views_by_provider: HashMap<LanguageModelProviderId, AnyView>,
context_server_manager: Entity<ContextServerManager>,
expanded_context_server_tools: HashMap<Arc<str>, bool>,
tools: Arc<ToolWorkingSet>,
_registry_subscription: Subscription,
}
impl AssistantConfiguration {
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
pub fn new(
context_server_manager: Entity<ContextServerManager>,
tools: Arc<ToolWorkingSet>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let focus_handle = cx.focus_handle();
let registry_subscription = cx.subscribe_in(
@@ -36,6 +57,9 @@ impl AssistantConfiguration {
let mut this = Self {
focus_handle,
configuration_views_by_provider: HashMap::default(),
context_server_manager,
expanded_context_server_tools: HashMap::default(),
tools,
_registry_subscription: registry_subscription,
};
this.build_provider_configuration_views(window, cx);
@@ -143,6 +167,186 @@ impl AssistantConfiguration {
}),
)
}
fn render_context_servers_section(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
let context_servers = self.context_server_manager.read(cx).all_servers().clone();
let tools_by_source = self.tools.tools_by_source(cx);
let empty = Vec::new();
const SUBHEADING: &str = "Connect to context servers via the Model Context Protocol either via Zed extensions or directly.";
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.gap_2()
.flex_1()
.child(
v_flex()
.gap_0p5()
.child(Headline::new("Context Servers (MCP)").size(HeadlineSize::Small))
.child(Label::new(SUBHEADING).color(Color::Muted)),
)
.children(context_servers.into_iter().map(|context_server| {
let is_running = context_server.client().is_some();
let are_tools_expanded = self
.expanded_context_server_tools
.get(&context_server.id())
.copied()
.unwrap_or_default();
let tools = tools_by_source
.get(&ToolSource::ContextServer {
id: context_server.id().into(),
})
.unwrap_or_else(|| &empty);
let tool_count = tools.len();
v_flex()
.id(SharedString::from(context_server.id()))
.border_1()
.rounded_sm()
.border_color(cx.theme().colors().border)
.bg(cx.theme().colors().editor_background)
.child(
h_flex()
.justify_between()
.px_2()
.py_1()
.when(are_tools_expanded, |element| {
element
.border_b_1()
.border_color(cx.theme().colors().border)
})
.child(
h_flex()
.gap_2()
.child(
Disclosure::new("tool-list-disclosure", are_tools_expanded)
.on_click(cx.listener({
let context_server_id = context_server.id();
move |this, _event, _window, _cx| {
let is_open = this
.expanded_context_server_tools
.entry(context_server_id.clone())
.or_insert(false);
*is_open = !*is_open;
}
})),
)
.child(Indicator::dot().color(if is_running {
Color::Success
} else {
Color::Error
}))
.child(Label::new(context_server.id()))
.child(
Label::new(format!("{tool_count} tools"))
.color(Color::Muted),
),
)
.child(h_flex().child(
Switch::new("context-server-switch", is_running.into()).on_click({
let context_server_manager =
self.context_server_manager.clone();
let context_server = context_server.clone();
move |state, _window, cx| match state {
ToggleState::Unselected | ToggleState::Indeterminate => {
context_server_manager.update(cx, |this, cx| {
this.stop_server(context_server.clone(), cx)
.log_err();
});
}
ToggleState::Selected => {
cx.spawn({
let context_server_manager =
context_server_manager.clone();
let context_server = context_server.clone();
async move |cx| {
if let Some(start_server_task) =
context_server_manager
.update(cx, |this, cx| {
this.start_server(
context_server,
cx,
)
})
.log_err()
{
start_server_task.await.log_err();
}
}
})
.detach();
}
}
}),
)),
)
.map(|parent| {
if !are_tools_expanded {
return parent;
}
parent.child(v_flex().children(tools.into_iter().enumerate().map(
|(ix, tool)| {
h_flex()
.px_2()
.py_1()
.when(ix < tool_count - 1, |element| {
element
.border_b_1()
.border_color(cx.theme().colors().border)
})
.child(Label::new(tool.name()))
},
)))
})
}))
.child(
h_flex()
.justify_between()
.gap_2()
.child(
h_flex().w_full().child(
Button::new("add-context-server", "Add Context Server")
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.full_width()
.icon(IconName::Plus)
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.on_click(|_event, window, cx| {
window.dispatch_action(AddContextServer.boxed_clone(), cx)
}),
),
)
.child(
h_flex().w_full().child(
Button::new(
"install-context-server-extensions",
"Install Context Server Extensions",
)
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.full_width()
.icon(IconName::DatabaseZap)
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.on_click(|_event, window, cx| {
window.dispatch_action(
zed_actions::Extensions {
category_filter: Some(
ExtensionCategoryFilter::ContextServers,
),
}
.boxed_clone(),
cx,
)
}),
),
),
)
}
}
impl Render for AssistantConfiguration {
@@ -155,32 +359,7 @@ impl Render for AssistantConfiguration {
.bg(cx.theme().colors().panel_background)
.size_full()
.overflow_y_scroll()
.child(
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.gap_2()
.child(
v_flex()
.gap_0p5()
.child(Headline::new("Prompt Library").size(HeadlineSize::Small))
.child(
Label::new("Create reusable prompts and tag which ones you want sent in every LLM interaction.")
.color(Color::Muted),
),
)
.child(
Button::new("open-prompt-library", "Open Prompt Library")
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.full_width()
.icon(IconName::Book)
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.on_click(|_event, window, cx| {
window.dispatch_action(DeployPromptLibrary.boxed_clone(), cx)
}),
),
)
.child(self.render_context_servers_section(cx))
.child(Divider::horizontal().color(DividerColor::Border))
.child(
v_flex()

View File

@@ -0,0 +1,164 @@
use context_server::{ContextServerSettings, ServerCommand, ServerConfig};
use editor::Editor;
use gpui::{prelude::*, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, WeakEntity};
use serde_json::json;
use settings::update_settings_file;
use ui::{prelude::*, Modal, ModalFooter, ModalHeader, Section, Tooltip};
use workspace::{ModalView, Workspace};
use crate::AddContextServer;
pub struct AddContextServerModal {
workspace: WeakEntity<Workspace>,
name_editor: Entity<Editor>,
command_editor: Entity<Editor>,
}
impl AddContextServerModal {
pub fn register(
workspace: &mut Workspace,
_window: Option<&mut Window>,
_cx: &mut Context<Workspace>,
) {
workspace.register_action(|workspace, _: &AddContextServer, window, cx| {
let workspace_handle = cx.entity().downgrade();
workspace.toggle_modal(window, cx, |window, cx| {
Self::new(workspace_handle, window, cx)
})
});
}
pub fn new(
workspace: WeakEntity<Workspace>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let name_editor = cx.new(|cx| Editor::single_line(window, cx));
let command_editor = cx.new(|cx| Editor::single_line(window, cx));
name_editor.update(cx, |editor, cx| {
editor.set_placeholder_text("Context server name", cx);
});
command_editor.update(cx, |editor, cx| {
editor.set_placeholder_text("Command to run the context server", cx);
});
Self {
name_editor,
command_editor,
workspace,
}
}
fn confirm(&mut self, cx: &mut Context<Self>) {
let name = self.name_editor.read(cx).text(cx).trim().to_string();
let command = self.command_editor.read(cx).text(cx).trim().to_string();
if name.is_empty() || command.is_empty() {
return;
}
let mut command_parts = command.split(' ').map(|part| part.trim().to_string());
let Some(path) = command_parts.next() else {
return;
};
let args = command_parts.collect::<Vec<_>>();
if let Some(workspace) = self.workspace.upgrade() {
workspace.update(cx, |workspace, cx| {
let fs = workspace.app_state().fs.clone();
update_settings_file::<ContextServerSettings>(fs.clone(), cx, |settings, _| {
settings.context_servers.insert(
name.into(),
ServerConfig {
command: Some(ServerCommand {
path,
args,
env: None,
}),
settings: Some(json!({})),
},
);
});
});
}
cx.emit(DismissEvent);
}
fn cancel(&mut self, cx: &mut Context<Self>) {
cx.emit(DismissEvent);
}
}
impl ModalView for AddContextServerModal {}
impl Focusable for AddContextServerModal {
fn focus_handle(&self, cx: &App) -> FocusHandle {
self.name_editor.focus_handle(cx).clone()
}
}
impl EventEmitter<DismissEvent> for AddContextServerModal {}
impl Render for AddContextServerModal {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let is_name_empty = self.name_editor.read(cx).text(cx).trim().is_empty();
let is_command_empty = self.command_editor.read(cx).text(cx).trim().is_empty();
div()
.elevation_3(cx)
.w(rems(34.))
.key_context("AddContextServerModal")
.on_action(cx.listener(|this, _: &menu::Cancel, _window, cx| this.cancel(cx)))
.on_action(cx.listener(|this, _: &menu::Confirm, _window, cx| this.confirm(cx)))
.capture_any_mouse_down(cx.listener(|this, _, window, cx| {
this.focus_handle(cx).focus(window);
}))
.on_mouse_down_out(cx.listener(|_this, _, _, cx| cx.emit(DismissEvent)))
.child(
Modal::new("add-context-server", None)
.header(ModalHeader::new().headline("Add Context Server"))
.section(
Section::new()
.child(
v_flex()
.gap_1()
.child(Label::new("Name"))
.child(self.name_editor.clone()),
)
.child(
v_flex()
.gap_1()
.child(Label::new("Command"))
.child(self.command_editor.clone()),
),
)
.footer(
ModalFooter::new()
.start_slot(
Button::new("cancel", "Cancel").on_click(
cx.listener(|this, _event, _window, cx| this.cancel(cx)),
),
)
.end_slot(
Button::new("add-server", "Add Server")
.disabled(is_name_empty || is_command_empty)
.map(|button| {
if is_name_empty {
button.tooltip(Tooltip::text("Name is required"))
} else if is_command_empty {
button.tooltip(Tooltip::text("Command is required"))
} else {
button
}
})
.on_click(
cx.listener(|this, _event, _window, cx| this.confirm(cx)),
),
),
),
)
}
}

View File

@@ -0,0 +1,464 @@
mod profile_modal_header;
use std::sync::Arc;
use assistant_settings::{
AgentProfile, AgentProfileContent, AssistantSettings, AssistantSettingsContent,
ContextServerPresetContent, VersionedAssistantSettingsContent,
};
use assistant_tool::ToolWorkingSet;
use convert_case::{Case, Casing as _};
use editor::Editor;
use fs::Fs;
use gpui::{
prelude::*, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Subscription,
WeakEntity,
};
use settings::{update_settings_file, Settings as _};
use ui::{prelude::*, ListItem, ListItemSpacing, ListSeparator, Navigable, NavigableEntry};
use workspace::{ModalView, Workspace};
use crate::assistant_configuration::manage_profiles_modal::profile_modal_header::ProfileModalHeader;
use crate::assistant_configuration::profile_picker::{ProfilePicker, ProfilePickerDelegate};
use crate::assistant_configuration::tool_picker::{ToolPicker, ToolPickerDelegate};
use crate::{AssistantPanel, ManageProfiles, ThreadStore};
enum Mode {
ChooseProfile {
profile_picker: Entity<ProfilePicker>,
_subscription: Subscription,
},
NewProfile(NewProfileMode),
ViewProfile(ViewProfileMode),
ConfigureTools {
profile_id: Arc<str>,
tool_picker: Entity<ToolPicker>,
_subscription: Subscription,
},
}
impl Mode {
pub fn choose_profile(window: &mut Window, cx: &mut Context<ManageProfilesModal>) -> Self {
let this = cx.entity();
let profile_picker = cx.new(|cx| {
let delegate = ProfilePickerDelegate::new(
move |profile_id, window, cx| {
this.update(cx, |this, cx| {
this.view_profile(profile_id.clone(), window, cx);
})
},
cx,
);
ProfilePicker::new(delegate, window, cx)
});
let dismiss_subscription = cx.subscribe_in(
&profile_picker,
window,
|_this, _profile_picker, _: &DismissEvent, _window, cx| {
cx.emit(DismissEvent);
},
);
Self::ChooseProfile {
profile_picker,
_subscription: dismiss_subscription,
}
}
}
#[derive(Clone)]
pub struct ViewProfileMode {
profile_id: Arc<str>,
fork_profile: NavigableEntry,
configure_tools: NavigableEntry,
}
#[derive(Clone)]
pub struct NewProfileMode {
name_editor: Entity<Editor>,
base_profile_id: Option<Arc<str>>,
}
pub struct ManageProfilesModal {
fs: Arc<dyn Fs>,
tools: Arc<ToolWorkingSet>,
thread_store: WeakEntity<ThreadStore>,
focus_handle: FocusHandle,
mode: Mode,
}
impl ManageProfilesModal {
pub fn register(
workspace: &mut Workspace,
_window: Option<&mut Window>,
_cx: &mut Context<Workspace>,
) {
workspace.register_action(|workspace, _: &ManageProfiles, window, cx| {
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
let fs = workspace.app_state().fs.clone();
let thread_store = panel.read(cx).thread_store();
let tools = thread_store.read(cx).tools();
let thread_store = thread_store.downgrade();
workspace.toggle_modal(window, cx, |window, cx| {
Self::new(fs, tools, thread_store, window, cx)
})
}
});
}
pub fn new(
fs: Arc<dyn Fs>,
tools: Arc<ToolWorkingSet>,
thread_store: WeakEntity<ThreadStore>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let focus_handle = cx.focus_handle();
Self {
fs,
tools,
thread_store,
focus_handle,
mode: Mode::choose_profile(window, cx),
}
}
fn choose_profile(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.mode = Mode::choose_profile(window, cx);
self.focus_handle(cx).focus(window);
}
fn new_profile(
&mut self,
base_profile_id: Option<Arc<str>>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let name_editor = cx.new(|cx| Editor::single_line(window, cx));
name_editor.update(cx, |editor, cx| {
editor.set_placeholder_text("Profile name", cx);
});
self.mode = Mode::NewProfile(NewProfileMode {
name_editor,
base_profile_id,
});
self.focus_handle(cx).focus(window);
}
pub fn view_profile(
&mut self,
profile_id: Arc<str>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.mode = Mode::ViewProfile(ViewProfileMode {
profile_id,
fork_profile: NavigableEntry::focusable(cx),
configure_tools: NavigableEntry::focusable(cx),
});
self.focus_handle(cx).focus(window);
}
fn configure_tools(
&mut self,
profile_id: Arc<str>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let settings = AssistantSettings::get_global(cx);
let Some(profile) = settings.profiles.get(&profile_id).cloned() else {
return;
};
let tool_picker = cx.new(|cx| {
let delegate = ToolPickerDelegate::new(
self.fs.clone(),
self.tools.clone(),
self.thread_store.clone(),
profile_id.clone(),
profile,
cx,
);
ToolPicker::new(delegate, window, cx)
});
let dismiss_subscription = cx.subscribe_in(&tool_picker, window, {
let profile_id = profile_id.clone();
move |this, _tool_picker, _: &DismissEvent, window, cx| {
this.view_profile(profile_id.clone(), window, cx);
}
});
self.mode = Mode::ConfigureTools {
profile_id,
tool_picker,
_subscription: dismiss_subscription,
};
self.focus_handle(cx).focus(window);
}
fn confirm(&mut self, window: &mut Window, cx: &mut Context<Self>) {
match &self.mode {
Mode::ChooseProfile { .. } => {}
Mode::NewProfile(mode) => {
let settings = AssistantSettings::get_global(cx);
let base_profile = mode
.base_profile_id
.as_ref()
.and_then(|profile_id| settings.profiles.get(profile_id).cloned());
let name = mode.name_editor.read(cx).text(cx);
let profile_id: Arc<str> = name.to_case(Case::Kebab).into();
let profile = AgentProfile {
name: name.into(),
tools: base_profile
.as_ref()
.map(|profile| profile.tools.clone())
.unwrap_or_default(),
context_servers: base_profile
.map(|profile| profile.context_servers)
.unwrap_or_default(),
};
self.create_profile(profile_id.clone(), profile, cx);
self.view_profile(profile_id, window, cx);
}
Mode::ViewProfile(_) => {}
Mode::ConfigureTools { .. } => {}
}
}
fn cancel(&mut self, window: &mut Window, cx: &mut Context<Self>) {
match &self.mode {
Mode::ChooseProfile { .. } => {}
Mode::NewProfile(mode) => {
if let Some(profile_id) = mode.base_profile_id.clone() {
self.view_profile(profile_id, window, cx);
} else {
self.choose_profile(window, cx);
}
}
Mode::ViewProfile(_) => self.choose_profile(window, cx),
Mode::ConfigureTools { .. } => {}
}
}
fn create_profile(&self, profile_id: Arc<str>, profile: AgentProfile, cx: &mut Context<Self>) {
update_settings_file::<AssistantSettings>(self.fs.clone(), cx, {
move |settings, _cx| match settings {
AssistantSettingsContent::Versioned(VersionedAssistantSettingsContent::V2(
settings,
)) => {
let profiles = settings.profiles.get_or_insert_default();
if profiles.contains_key(&profile_id) {
log::error!("profile with ID '{profile_id}' already exists");
return;
}
profiles.insert(
profile_id,
AgentProfileContent {
name: profile.name.into(),
tools: profile.tools,
context_servers: profile
.context_servers
.into_iter()
.map(|(server_id, preset)| {
(
server_id,
ContextServerPresetContent {
tools: preset.tools,
},
)
})
.collect(),
},
);
}
_ => {}
}
});
}
}
impl ModalView for ManageProfilesModal {}
impl Focusable for ManageProfilesModal {
fn focus_handle(&self, cx: &App) -> FocusHandle {
match &self.mode {
Mode::ChooseProfile { profile_picker, .. } => profile_picker.focus_handle(cx),
Mode::NewProfile(mode) => mode.name_editor.focus_handle(cx),
Mode::ViewProfile(_) => self.focus_handle.clone(),
Mode::ConfigureTools { tool_picker, .. } => tool_picker.focus_handle(cx),
}
}
}
impl EventEmitter<DismissEvent> for ManageProfilesModal {}
impl ManageProfilesModal {
fn render_new_profile(
&mut self,
mode: NewProfileMode,
_window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
v_flex()
.id("new-profile")
.track_focus(&self.focus_handle(cx))
.child(h_flex().p_2().child(mode.name_editor.clone()))
}
fn render_view_profile(
&mut self,
mode: ViewProfileMode,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl IntoElement {
let settings = AssistantSettings::get_global(cx);
let profile_name = settings
.profiles
.get(&mode.profile_id)
.map(|profile| profile.name.clone())
.unwrap_or_else(|| "Unknown".into());
Navigable::new(
div()
.track_focus(&self.focus_handle(cx))
.size_full()
.child(ProfileModalHeader::new(
profile_name,
IconName::ZedAssistant,
))
.child(
v_flex()
.pb_1()
.child(ListSeparator)
.child(
div()
.id("fork-profile")
.track_focus(&mode.fork_profile.focus_handle)
.on_action({
let profile_id = mode.profile_id.clone();
cx.listener(move |this, _: &menu::Confirm, window, cx| {
this.new_profile(Some(profile_id.clone()), window, cx);
})
})
.child(
ListItem::new("fork-profile")
.toggle_state(
mode.fork_profile
.focus_handle
.contains_focused(window, cx),
)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.start_slot(Icon::new(IconName::GitBranch))
.child(Label::new("Fork Profile"))
.on_click({
let profile_id = mode.profile_id.clone();
cx.listener(move |this, _, window, cx| {
this.new_profile(
Some(profile_id.clone()),
window,
cx,
);
})
}),
),
)
.child(
div()
.id("configure-tools")
.track_focus(&mode.configure_tools.focus_handle)
.on_action({
let profile_id = mode.profile_id.clone();
cx.listener(move |this, _: &menu::Confirm, window, cx| {
this.configure_tools(profile_id.clone(), window, cx);
})
})
.child(
ListItem::new("configure-tools")
.toggle_state(
mode.configure_tools
.focus_handle
.contains_focused(window, cx),
)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.start_slot(Icon::new(IconName::Cog))
.child(Label::new("Configure Tools"))
.on_click({
let profile_id = mode.profile_id.clone();
cx.listener(move |this, _, window, cx| {
this.configure_tools(
profile_id.clone(),
window,
cx,
);
})
}),
),
),
)
.into_any_element(),
)
.entry(mode.fork_profile)
.entry(mode.configure_tools)
}
}
impl Render for ManageProfilesModal {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let settings = AssistantSettings::get_global(cx);
div()
.elevation_3(cx)
.w(rems(34.))
.key_context("ManageProfilesModal")
.on_action(cx.listener(|this, _: &menu::Cancel, window, cx| this.cancel(window, cx)))
.on_action(cx.listener(|this, _: &menu::Confirm, window, cx| this.confirm(window, cx)))
.capture_any_mouse_down(cx.listener(|this, _, window, cx| {
this.focus_handle(cx).focus(window);
}))
.on_mouse_down_out(cx.listener(|_this, _, _, cx| cx.emit(DismissEvent)))
.child(match &self.mode {
Mode::ChooseProfile { profile_picker, .. } => div()
.child(ProfileModalHeader::new("Profiles", IconName::ZedAssistant))
.child(ListSeparator)
.child(profile_picker.clone())
.into_any_element(),
Mode::NewProfile(mode) => self
.render_new_profile(mode.clone(), window, cx)
.into_any_element(),
Mode::ViewProfile(mode) => self
.render_view_profile(mode.clone(), window, cx)
.into_any_element(),
Mode::ConfigureTools {
profile_id,
tool_picker,
..
} => {
let profile_name = settings
.profiles
.get(profile_id)
.map(|profile| profile.name.clone())
.unwrap_or_else(|| "Unknown".into());
div()
.child(ProfileModalHeader::new(
format!("{profile_name}: Configure Tools"),
IconName::Cog,
))
.child(ListSeparator)
.child(tool_picker.clone())
.into_any_element()
}
})
}
}

View File

@@ -0,0 +1,38 @@
use ui::prelude::*;
#[derive(IntoElement)]
pub struct ProfileModalHeader {
label: SharedString,
icon: IconName,
}
impl ProfileModalHeader {
pub fn new(label: impl Into<SharedString>, icon: IconName) -> Self {
Self {
label: label.into(),
icon,
}
}
}
impl RenderOnce for ProfileModalHeader {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
h_flex()
.w_full()
.px(DynamicSpacing::Base12.rems(cx))
.pt(DynamicSpacing::Base08.rems(cx))
.pb(DynamicSpacing::Base04.rems(cx))
.rounded_t_sm()
.gap_1p5()
.child(Icon::new(self.icon).size(IconSize::XSmall))
.child(
h_flex().gap_1().overflow_x_hidden().child(
div()
.max_w_96()
.overflow_x_hidden()
.text_ellipsis()
.child(Headline::new(self.label).size(HeadlineSize::XSmall)),
),
)
}
}

View File

@@ -0,0 +1,194 @@
use std::sync::Arc;
use assistant_settings::AssistantSettings;
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
use gpui::{
App, Context, DismissEvent, Entity, EventEmitter, Focusable, SharedString, Task, WeakEntity,
Window,
};
use picker::{Picker, PickerDelegate};
use settings::Settings;
use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing};
use util::ResultExt as _;
pub struct ProfilePicker {
picker: Entity<Picker<ProfilePickerDelegate>>,
}
impl ProfilePicker {
pub fn new(
delegate: ProfilePickerDelegate,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(false));
Self { picker }
}
}
impl EventEmitter<DismissEvent> for ProfilePicker {}
impl Focusable for ProfilePicker {
fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
self.picker.focus_handle(cx)
}
}
impl Render for ProfilePicker {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
v_flex().w(rems(34.)).child(self.picker.clone())
}
}
#[derive(Debug)]
pub struct ProfileEntry {
pub id: Arc<str>,
pub name: SharedString,
}
pub struct ProfilePickerDelegate {
profile_picker: WeakEntity<ProfilePicker>,
profiles: Vec<ProfileEntry>,
matches: Vec<StringMatch>,
selected_index: usize,
on_confirm: Arc<dyn Fn(&Arc<str>, &mut Window, &mut App) + 'static>,
}
impl ProfilePickerDelegate {
pub fn new(
on_confirm: impl Fn(&Arc<str>, &mut Window, &mut App) + 'static,
cx: &mut Context<ProfilePicker>,
) -> Self {
let settings = AssistantSettings::get_global(cx);
let profiles = settings
.profiles
.iter()
.map(|(id, profile)| ProfileEntry {
id: id.clone(),
name: profile.name.clone(),
})
.collect::<Vec<_>>();
Self {
profile_picker: cx.entity().downgrade(),
profiles,
matches: Vec::new(),
selected_index: 0,
on_confirm: Arc::new(on_confirm),
}
}
}
impl PickerDelegate for ProfilePickerDelegate {
type ListItem = ListItem;
fn match_count(&self) -> usize {
self.matches.len()
}
fn selected_index(&self) -> usize {
self.selected_index
}
fn set_selected_index(
&mut self,
ix: usize,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) {
self.selected_index = ix;
}
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
"Search profiles…".into()
}
fn update_matches(
&mut self,
query: String,
window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Task<()> {
let background = cx.background_executor().clone();
let candidates = self
.profiles
.iter()
.enumerate()
.map(|(id, profile)| StringMatchCandidate::new(id, profile.name.as_ref()))
.collect::<Vec<_>>();
cx.spawn_in(window, async move |this, cx| {
let matches = if query.is_empty() {
candidates
.into_iter()
.enumerate()
.map(|(index, candidate)| StringMatch {
candidate_id: index,
string: candidate.string,
positions: Vec::new(),
score: 0.,
})
.collect()
} else {
match_strings(
&candidates,
&query,
false,
100,
&Default::default(),
background,
)
.await
};
this.update(cx, |this, _cx| {
this.delegate.matches = matches;
this.delegate.selected_index = this
.delegate
.selected_index
.min(this.delegate.matches.len().saturating_sub(1));
})
.log_err();
})
}
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
if self.matches.is_empty() {
self.dismissed(window, cx);
return;
}
let candidate_id = self.matches[self.selected_index].candidate_id;
let profile = &self.profiles[candidate_id];
(self.on_confirm)(&profile.id, window, cx);
}
fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
self.profile_picker
.update(cx, |_this, cx| cx.emit(DismissEvent))
.log_err();
}
fn render_match(
&self,
ix: usize,
selected: bool,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) -> Option<Self::ListItem> {
let profile_match = &self.matches[ix];
Some(
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.toggle_state(selected)
.child(HighlightedLabel::new(
profile_match.string.clone(),
profile_match.positions.clone(),
)),
)
}
}

View File

@@ -0,0 +1,299 @@
use std::sync::Arc;
use assistant_settings::{
AgentProfile, AgentProfileContent, AssistantSettings, AssistantSettingsContent,
ContextServerPresetContent, VersionedAssistantSettingsContent,
};
use assistant_tool::{ToolSource, ToolWorkingSet};
use fs::Fs;
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
use gpui::{App, Context, DismissEvent, Entity, EventEmitter, Focusable, Task, WeakEntity, Window};
use picker::{Picker, PickerDelegate};
use settings::{update_settings_file, Settings as _};
use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing};
use util::ResultExt as _;
use crate::ThreadStore;
pub struct ToolPicker {
picker: Entity<Picker<ToolPickerDelegate>>,
}
impl ToolPicker {
pub fn new(delegate: ToolPickerDelegate, window: &mut Window, cx: &mut Context<Self>) -> Self {
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx).modal(false));
Self { picker }
}
}
impl EventEmitter<DismissEvent> for ToolPicker {}
impl Focusable for ToolPicker {
fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
self.picker.focus_handle(cx)
}
}
impl Render for ToolPicker {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
v_flex().w(rems(34.)).child(self.picker.clone())
}
}
#[derive(Debug, Clone)]
pub struct ToolEntry {
pub name: Arc<str>,
pub source: ToolSource,
}
pub struct ToolPickerDelegate {
tool_picker: WeakEntity<ToolPicker>,
thread_store: WeakEntity<ThreadStore>,
fs: Arc<dyn Fs>,
tools: Vec<ToolEntry>,
profile_id: Arc<str>,
profile: AgentProfile,
matches: Vec<StringMatch>,
selected_index: usize,
}
impl ToolPickerDelegate {
pub fn new(
fs: Arc<dyn Fs>,
tool_set: Arc<ToolWorkingSet>,
thread_store: WeakEntity<ThreadStore>,
profile_id: Arc<str>,
profile: AgentProfile,
cx: &mut Context<ToolPicker>,
) -> Self {
let mut tool_entries = Vec::new();
for (source, tools) in tool_set.tools_by_source(cx) {
tool_entries.extend(tools.into_iter().map(|tool| ToolEntry {
name: tool.name().into(),
source: source.clone(),
}));
}
Self {
tool_picker: cx.entity().downgrade(),
thread_store,
fs,
tools: tool_entries,
profile_id,
profile,
matches: Vec::new(),
selected_index: 0,
}
}
}
impl PickerDelegate for ToolPickerDelegate {
type ListItem = ListItem;
fn match_count(&self) -> usize {
self.matches.len()
}
fn selected_index(&self) -> usize {
self.selected_index
}
fn set_selected_index(
&mut self,
ix: usize,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) {
self.selected_index = ix;
}
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
"Search tools…".into()
}
fn update_matches(
&mut self,
query: String,
window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Task<()> {
let background = cx.background_executor().clone();
let candidates = self
.tools
.iter()
.enumerate()
.map(|(id, profile)| StringMatchCandidate::new(id, profile.name.as_ref()))
.collect::<Vec<_>>();
cx.spawn_in(window, async move |this, cx| {
let matches = if query.is_empty() {
candidates
.into_iter()
.enumerate()
.map(|(index, candidate)| StringMatch {
candidate_id: index,
string: candidate.string,
positions: Vec::new(),
score: 0.,
})
.collect()
} else {
match_strings(
&candidates,
&query,
false,
100,
&Default::default(),
background,
)
.await
};
this.update(cx, |this, _cx| {
this.delegate.matches = matches;
this.delegate.selected_index = this
.delegate
.selected_index
.min(this.delegate.matches.len().saturating_sub(1));
})
.log_err();
})
}
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
if self.matches.is_empty() {
self.dismissed(window, cx);
return;
}
let candidate_id = self.matches[self.selected_index].candidate_id;
let tool = &self.tools[candidate_id];
let is_enabled = match &tool.source {
ToolSource::Native => {
let is_enabled = self.profile.tools.entry(tool.name.clone()).or_default();
*is_enabled = !*is_enabled;
*is_enabled
}
ToolSource::ContextServer { id } => {
let preset = self
.profile
.context_servers
.entry(id.clone().into())
.or_default();
let is_enabled = preset.tools.entry(tool.name.clone()).or_default();
*is_enabled = !*is_enabled;
*is_enabled
}
};
let active_profile_id = &AssistantSettings::get_global(cx).default_profile;
if active_profile_id == &self.profile_id {
self.thread_store
.update(cx, |this, _cx| {
this.load_profile(&self.profile);
})
.log_err();
}
update_settings_file::<AssistantSettings>(self.fs.clone(), cx, {
let profile_id = self.profile_id.clone();
let default_profile = self.profile.clone();
let tool = tool.clone();
move |settings, _cx| match settings {
AssistantSettingsContent::Versioned(VersionedAssistantSettingsContent::V2(
settings,
)) => {
let profiles = settings.profiles.get_or_insert_default();
let profile =
profiles
.entry(profile_id)
.or_insert_with(|| AgentProfileContent {
name: default_profile.name.into(),
tools: default_profile.tools,
context_servers: default_profile
.context_servers
.into_iter()
.map(|(server_id, preset)| {
(
server_id,
ContextServerPresetContent {
tools: preset.tools,
},
)
})
.collect(),
});
match tool.source {
ToolSource::Native => {
*profile.tools.entry(tool.name).or_default() = is_enabled;
}
ToolSource::ContextServer { id } => {
let preset = profile
.context_servers
.entry(id.clone().into())
.or_default();
*preset.tools.entry(tool.name.clone()).or_default() = is_enabled;
}
}
}
_ => {}
}
});
}
fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
self.tool_picker
.update(cx, |_this, cx| cx.emit(DismissEvent))
.log_err();
}
fn render_match(
&self,
ix: usize,
selected: bool,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) -> Option<Self::ListItem> {
let tool_match = &self.matches[ix];
let tool = &self.tools[tool_match.candidate_id];
let is_enabled = match &tool.source {
ToolSource::Native => self.profile.tools.get(&tool.name).copied().unwrap_or(false),
ToolSource::ContextServer { id } => self
.profile
.context_servers
.get(id.as_ref())
.and_then(|preset| preset.tools.get(&tool.name))
.copied()
.unwrap_or(false),
};
Some(
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.toggle_state(selected)
.child(
h_flex()
.gap_2()
.child(HighlightedLabel::new(
tool_match.string.clone(),
tool_match.positions.clone(),
))
.map(|parent| match &tool.source {
ToolSource::Native => parent,
ToolSource::ContextServer { id } => parent
.child(Label::new(id).size(LabelSize::XSmall).color(Color::Muted)),
}),
)
.end_slot::<Icon>(is_enabled.then(|| {
Icon::new(IconName::Check)
.size(IconSize::Small)
.color(Color::Success)
})),
)
}
}

View File

@@ -11,12 +11,12 @@ use assistant_slash_command::SlashCommandWorkingSet;
use assistant_tool::ToolWorkingSet;
use client::zed_urls;
use editor::Editor;
use editor::{Editor, MultiBuffer};
use fs::Fs;
use gpui::{
prelude::*, Action, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter,
FocusHandle, Focusable, FontWeight, KeyContext, Pixels, Subscription, Task, UpdateGlobal,
WeakEntity,
action_with_deprecated_aliases, prelude::*, Action, AnyElement, App, AsyncWindowContext,
Corner, Entity, EventEmitter, FocusHandle, Focusable, FontWeight, KeyContext, Pixels,
Subscription, Task, UpdateGlobal, WeakEntity,
};
use language::LanguageRegistry;
use language_model::{LanguageModelProviderTosView, LanguageModelRegistry};
@@ -29,7 +29,7 @@ use ui::{prelude::*, ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle, Ta
use util::ResultExt as _;
use workspace::dock::{DockPosition, Panel, PanelEvent};
use workspace::Workspace;
use zed_actions::assistant::{DeployPromptLibrary, ToggleFocus};
use zed_actions::assistant::ToggleFocus;
use crate::active_thread::ActiveThread;
use crate::assistant_configuration::{AssistantConfiguration, AssistantConfigurationEvent};
@@ -38,7 +38,16 @@ use crate::message_editor::MessageEditor;
use crate::thread::{Thread, ThreadError, ThreadId};
use crate::thread_history::{PastContext, PastThread, ThreadHistory};
use crate::thread_store::ThreadStore;
use crate::{InlineAssistant, NewPromptEditor, NewThread, OpenConfiguration, OpenHistory};
use crate::{
InlineAssistant, NewPromptEditor, NewThread, OpenActiveThreadAsMarkdown, OpenConfiguration,
OpenHistory,
};
action_with_deprecated_aliases!(
assistant,
OpenPromptLibrary,
["assistant::DeployPromptLibrary"]
);
pub fn init(cx: &mut App) {
cx.observe_new(
@@ -62,6 +71,14 @@ pub fn init(cx: &mut App) {
panel.update(cx, |panel, cx| panel.new_prompt_editor(window, cx));
}
})
.register_action(|workspace, _: &OpenPromptLibrary, window, cx| {
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
workspace.focus_panel::<AssistantPanel>(window, cx);
panel.update(cx, |panel, cx| {
panel.deploy_prompt_library(&OpenPromptLibrary, window, cx)
});
}
})
.register_action(|workspace, _: &OpenConfiguration, window, cx| {
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
workspace.focus_panel::<AssistantPanel>(window, cx);
@@ -107,19 +124,16 @@ impl AssistantPanel {
prompt_builder: Arc<PromptBuilder>,
cx: AsyncWindowContext,
) -> Task<Result<Entity<Self>>> {
cx.spawn(|mut cx| async move {
cx.spawn(async move |cx| {
let tools = Arc::new(ToolWorkingSet::default());
log::info!("[assistant2-debug] initializing ThreadStore");
let thread_store = workspace.update(&mut cx, |workspace, cx| {
let thread_store = workspace.update(cx, |workspace, cx| {
let project = workspace.project().clone();
ThreadStore::new(project, tools.clone(), cx)
ThreadStore::new(project, tools.clone(), prompt_builder.clone(), cx)
})??;
log::info!("[assistant2-debug] finished initializing ThreadStore");
let slash_commands = Arc::new(SlashCommandWorkingSet::default());
log::info!("[assistant2-debug] initializing ContextStore");
let context_store = workspace
.update(&mut cx, |workspace, cx| {
.update(cx, |workspace, cx| {
let project = workspace.project().clone();
assistant_context_editor::ContextStore::new(
project,
@@ -129,9 +143,8 @@ impl AssistantPanel {
)
})?
.await?;
log::info!("[assistant2-debug] finished initializing ContextStore");
workspace.update_in(&mut cx, |workspace, window, cx| {
workspace.update_in(cx, |workspace, window, cx| {
cx.new(|cx| Self::new(workspace, thread_store, context_store, window, cx))
})
})
@@ -144,7 +157,6 @@ impl AssistantPanel {
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
log::info!("[assistant2-debug] AssistantPanel::new");
let thread = thread_store.update(cx, |this, cx| this.create_thread(cx));
let fs = workspace.app_state().fs.clone();
let project = workspace.project().clone();
@@ -152,10 +164,14 @@ impl AssistantPanel {
let workspace = workspace.weak_handle();
let weak_self = cx.entity().downgrade();
let message_editor_context_store =
cx.new(|_cx| crate::context_store::ContextStore::new(workspace.clone()));
let message_editor = cx.new(|cx| {
MessageEditor::new(
fs.clone(),
workspace.clone(),
message_editor_context_store.clone(),
thread_store.downgrade(),
thread.clone(),
window,
@@ -166,22 +182,26 @@ impl AssistantPanel {
let history_store =
cx.new(|cx| HistoryStore::new(thread_store.clone(), context_store.clone(), cx));
let thread = cx.new(|cx| {
ActiveThread::new(
thread.clone(),
thread_store.clone(),
language_registry.clone(),
message_editor_context_store.clone(),
workspace.clone(),
window,
cx,
)
});
Self {
active_view: ActiveView::Thread,
workspace,
project: project.clone(),
fs: fs.clone(),
language_registry: language_registry.clone(),
language_registry,
thread_store: thread_store.clone(),
thread: cx.new(|cx| {
ActiveThread::new(
thread.clone(),
thread_store.clone(),
language_registry,
window,
cx,
)
}),
thread,
message_editor,
context_store,
context_editor: None,
@@ -205,12 +225,12 @@ impl AssistantPanel {
window: &mut Window,
cx: &mut Context<Workspace>,
) {
let settings = AssistantSettings::get_global(cx);
if !settings.enabled {
return;
if workspace
.panel::<Self>(cx)
.is_some_and(|panel| panel.read(cx).enabled(cx))
{
workspace.toggle_panel_focus::<Self>(window, cx);
}
workspace.toggle_panel_focus::<Self>(window, cx);
}
pub(crate) fn local_timezone(&self) -> UtcOffset {
@@ -237,11 +257,17 @@ impl AssistantPanel {
.update(cx, |this, cx| this.create_thread(cx));
self.active_view = ActiveView::Thread;
let message_editor_context_store =
cx.new(|_cx| crate::context_store::ContextStore::new(self.workspace.clone()));
self.thread = cx.new(|cx| {
ActiveThread::new(
thread.clone(),
self.thread_store.clone(),
self.language_registry.clone(),
message_editor_context_store.clone(),
self.workspace.clone(),
window,
cx,
)
@@ -250,6 +276,7 @@ impl AssistantPanel {
MessageEditor::new(
self.fs.clone(),
self.workspace.clone(),
message_editor_context_store,
self.thread_store.downgrade(),
thread,
window,
@@ -290,7 +317,7 @@ impl AssistantPanel {
fn deploy_prompt_library(
&mut self,
_: &DeployPromptLibrary,
_: &OpenPromptLibrary,
_window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -333,9 +360,9 @@ impl AssistantPanel {
let lsp_adapter_delegate = make_lsp_adapter_delegate(&project, cx).log_err().flatten();
cx.spawn_in(window, |this, mut cx| async move {
cx.spawn_in(window, async move |this, cx| {
let context = context.await?;
this.update_in(&mut cx, |this, window, cx| {
this.update_in(cx, |this, window, cx| {
let editor = cx.new(|cx| {
ContextEditor::for_context(
context,
@@ -366,15 +393,19 @@ impl AssistantPanel {
.thread_store
.update(cx, |this, cx| this.open_thread(thread_id, cx));
cx.spawn_in(window, |this, mut cx| async move {
cx.spawn_in(window, async move |this, cx| {
let thread = open_thread_task.await?;
this.update_in(&mut cx, |this, window, cx| {
this.update_in(cx, |this, window, cx| {
this.active_view = ActiveView::Thread;
let message_editor_context_store =
cx.new(|_cx| crate::context_store::ContextStore::new(this.workspace.clone()));
this.thread = cx.new(|cx| {
ActiveThread::new(
thread.clone(),
this.thread_store.clone(),
this.language_registry.clone(),
message_editor_context_store.clone(),
this.workspace.clone(),
window,
cx,
)
@@ -383,6 +414,7 @@ impl AssistantPanel {
MessageEditor::new(
this.fs.clone(),
this.workspace.clone(),
message_editor_context_store,
this.thread_store.downgrade(),
thread,
window,
@@ -395,8 +427,13 @@ impl AssistantPanel {
}
pub(crate) fn open_configuration(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let context_server_manager = self.thread_store.read(cx).context_server_manager();
let tools = self.thread_store.read(cx).tools();
self.active_view = ActiveView::Configuration;
self.configuration = Some(cx.new(|cx| AssistantConfiguration::new(window, cx)));
self.configuration = Some(
cx.new(|cx| AssistantConfiguration::new(context_server_manager, tools, window, cx)),
);
if let Some(configuration) = self.configuration.as_ref() {
self.configuration_subscription = Some(cx.subscribe_in(
@@ -409,6 +446,65 @@ impl AssistantPanel {
}
}
pub(crate) fn open_active_thread_as_markdown(
&mut self,
_: &OpenActiveThreadAsMarkdown,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(workspace) = self
.workspace
.upgrade()
.ok_or_else(|| anyhow!("workspace dropped"))
.log_err()
else {
return;
};
let markdown_language_task = workspace
.read(cx)
.app_state()
.languages
.language_for_name("Markdown");
let thread = self.active_thread(cx);
cx.spawn_in(window, async move |_this, cx| {
let markdown_language = markdown_language_task.await?;
workspace.update_in(cx, |workspace, window, cx| {
let thread = thread.read(cx);
let markdown = thread.to_markdown(cx)?;
let thread_summary = thread
.summary()
.map(|summary| summary.to_string())
.unwrap_or_else(|| "Thread".to_string());
let project = workspace.project().clone();
let buffer = project.update(cx, |project, cx| {
project.create_local_buffer(&markdown, Some(markdown_language), cx)
});
let buffer = cx.new(|cx| {
MultiBuffer::singleton(buffer, cx).with_title(thread_summary.clone())
});
workspace.add_item_to_active_pane(
Box::new(cx.new(|cx| {
let mut editor =
Editor::for_multibuffer(buffer, Some(project.clone()), window, cx);
editor.set_breadcrumb_header(thread_summary);
editor
})),
None,
true,
window,
cx,
);
anyhow::Ok(())
})
})
.detach_and_log_err(cx);
}
fn handle_assistant_configuration_event(
&mut self,
_entity: &Entity<AssistantConfiguration>,
@@ -541,12 +637,8 @@ impl Panel for AssistantPanel {
}
fn icon(&self, _window: &Window, cx: &App) -> Option<IconName> {
let settings = AssistantSettings::get_global(cx);
if !settings.enabled || !settings.button {
return None;
}
Some(IconName::ZedAssistant)
(self.enabled(cx) && AssistantSettings::get_global(cx).button)
.then_some(IconName::ZedAssistant)
}
fn icon_tooltip(&self, _window: &Window, _cx: &App) -> Option<&'static str> {
@@ -560,6 +652,10 @@ impl Panel for AssistantPanel {
fn activation_priority(&self) -> u32 {
3
}
fn enabled(&self, cx: &App) -> bool {
AssistantSettings::get_global(cx).enabled
}
}
impl AssistantPanel {
@@ -843,8 +939,8 @@ impl AssistantPanel {
ThreadError::MaxMonthlySpendReached => {
self.render_max_monthly_spend_reached_error(cx)
}
ThreadError::Message(error_message) => {
self.render_error_message(&error_message, cx)
ThreadError::Message { header, message } => {
self.render_error_message(header, message, cx)
}
})
.into_any(),
@@ -947,7 +1043,8 @@ impl AssistantPanel {
fn render_error_message(
&self,
error_message: &SharedString,
header: SharedString,
message: SharedString,
cx: &mut Context<Self>,
) -> AnyElement {
v_flex()
@@ -957,17 +1054,14 @@ impl AssistantPanel {
.gap_1p5()
.items_center()
.child(Icon::new(IconName::XCircle).color(Color::Error))
.child(
Label::new("Error interacting with language model")
.weight(FontWeight::MEDIUM),
),
.child(Label::new(header).weight(FontWeight::MEDIUM)),
)
.child(
div()
.id("error-message")
.max_h_32()
.overflow_y_scroll()
.child(Label::new(error_message.clone())),
.child(Label::new(message)),
)
.child(
h_flex()
@@ -1009,6 +1103,7 @@ impl Render for AssistantPanel {
.on_action(cx.listener(|this, _: &OpenHistory, window, cx| {
this.open_history(window, cx);
}))
.on_action(cx.listener(Self::open_active_thread_as_markdown))
.on_action(cx.listener(Self::deploy_prompt_library))
.child(self.render_toolbar(cx))
.map(|parent| match self.active_view {

View File

@@ -367,7 +367,7 @@ impl CodegenAlternative {
let request = self.build_request(user_prompt, cx)?;
self.request = Some(request.clone());
cx.spawn(|_, cx| async move { model.stream_completion_text(request, &cx).await })
cx.spawn(async move |_, cx| model.stream_completion_text(request, &cx).await)
.boxed_local()
};
self.handle_stream(telemetry_id, provider_id.to_string(), api_key, stream, cx);
@@ -480,213 +480,223 @@ impl CodegenAlternative {
let completion = Arc::new(Mutex::new(String::new()));
let completion_clone = completion.clone();
self.generation = cx.spawn(|codegen, mut cx| {
async move {
let stream = stream.await;
let message_id = stream
.as_ref()
.ok()
.and_then(|stream| stream.message_id.clone());
let generate = async {
let (mut diff_tx, mut diff_rx) = mpsc::channel(1);
let executor = cx.background_executor().clone();
let message_id = message_id.clone();
let line_based_stream_diff: Task<anyhow::Result<()>> =
cx.background_spawn(async move {
let mut response_latency = None;
let request_start = Instant::now();
let diff = async {
let chunks = StripInvalidSpans::new(stream?.stream);
futures::pin_mut!(chunks);
let mut diff = StreamingDiff::new(selected_text.to_string());
let mut line_diff = LineDiff::default();
self.generation = cx.spawn(async move |codegen, cx| {
let stream = stream.await;
let token_usage = stream
.as_ref()
.ok()
.map(|stream| stream.last_token_usage.clone());
let message_id = stream
.as_ref()
.ok()
.and_then(|stream| stream.message_id.clone());
let generate = async {
let model_telemetry_id = model_telemetry_id.clone();
let model_provider_id = model_provider_id.clone();
let (mut diff_tx, mut diff_rx) = mpsc::channel(1);
let executor = cx.background_executor().clone();
let message_id = message_id.clone();
let line_based_stream_diff: Task<anyhow::Result<()>> =
cx.background_spawn(async move {
let mut response_latency = None;
let request_start = Instant::now();
let diff = async {
let chunks = StripInvalidSpans::new(stream?.stream);
futures::pin_mut!(chunks);
let mut diff = StreamingDiff::new(selected_text.to_string());
let mut line_diff = LineDiff::default();
let mut new_text = String::new();
let mut base_indent = None;
let mut line_indent = None;
let mut first_line = true;
let mut new_text = String::new();
let mut base_indent = None;
let mut line_indent = None;
let mut first_line = true;
while let Some(chunk) = chunks.next().await {
if response_latency.is_none() {
response_latency = Some(request_start.elapsed());
}
let chunk = chunk?;
completion_clone.lock().push_str(&chunk);
while let Some(chunk) = chunks.next().await {
if response_latency.is_none() {
response_latency = Some(request_start.elapsed());
}
let chunk = chunk?;
completion_clone.lock().push_str(&chunk);
let mut lines = chunk.split('\n').peekable();
while let Some(line) = lines.next() {
new_text.push_str(line);
if line_indent.is_none() {
if let Some(non_whitespace_ch_ix) =
new_text.find(|ch: char| !ch.is_whitespace())
{
line_indent = Some(non_whitespace_ch_ix);
base_indent = base_indent.or(line_indent);
let mut lines = chunk.split('\n').peekable();
while let Some(line) = lines.next() {
new_text.push_str(line);
if line_indent.is_none() {
if let Some(non_whitespace_ch_ix) =
new_text.find(|ch: char| !ch.is_whitespace())
{
line_indent = Some(non_whitespace_ch_ix);
base_indent = base_indent.or(line_indent);
let line_indent = line_indent.unwrap();
let base_indent = base_indent.unwrap();
let indent_delta =
line_indent as i32 - base_indent as i32;
let mut corrected_indent_len = cmp::max(
0,
suggested_line_indent.len as i32 + indent_delta,
)
as usize;
if first_line {
corrected_indent_len = corrected_indent_len
.saturating_sub(
selection_start.column as usize,
);
}
let indent_char = suggested_line_indent.char();
let mut indent_buffer = [0; 4];
let indent_str =
indent_char.encode_utf8(&mut indent_buffer);
new_text.replace_range(
..line_indent,
&indent_str.repeat(corrected_indent_len),
);
let line_indent = line_indent.unwrap();
let base_indent = base_indent.unwrap();
let indent_delta =
line_indent as i32 - base_indent as i32;
let mut corrected_indent_len = cmp::max(
0,
suggested_line_indent.len as i32 + indent_delta,
)
as usize;
if first_line {
corrected_indent_len = corrected_indent_len
.saturating_sub(
selection_start.column as usize,
);
}
}
if line_indent.is_some() {
let char_ops = diff.push_new(&new_text);
line_diff
.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
let indent_char = suggested_line_indent.char();
let mut indent_buffer = [0; 4];
let indent_str =
indent_char.encode_utf8(&mut indent_buffer);
new_text.replace_range(
..line_indent,
&indent_str.repeat(corrected_indent_len),
);
}
}
if line_indent.is_some() {
let char_ops = diff.push_new(&new_text);
line_diff.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
new_text.clear();
}
if lines.peek().is_some() {
let char_ops = diff.push_new("\n");
line_diff.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
if line_indent.is_none() {
// Don't write out the leading indentation in empty lines on the next line
// This is the case where the above if statement didn't clear the buffer
new_text.clear();
}
if lines.peek().is_some() {
let char_ops = diff.push_new("\n");
line_diff
.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
if line_indent.is_none() {
// Don't write out the leading indentation in empty lines on the next line
// This is the case where the above if statement didn't clear the buffer
new_text.clear();
}
line_indent = None;
first_line = false;
}
line_indent = None;
first_line = false;
}
}
let mut char_ops = diff.push_new(&new_text);
char_ops.extend(diff.finish());
line_diff.push_char_operations(&char_ops, &selected_text);
line_diff.finish(&selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
anyhow::Ok(())
};
let result = diff.await;
let error_message =
result.as_ref().err().map(|error| error.to_string());
report_assistant_event(
AssistantEvent {
conversation_id: None,
message_id,
kind: AssistantKind::Inline,
phase: AssistantPhase::Response,
model: model_telemetry_id,
model_provider: model_provider_id.to_string(),
response_latency,
error_message,
language_name: language_name.map(|name| name.to_proto()),
},
telemetry,
http_client,
model_api_key,
&executor,
);
result?;
Ok(())
});
while let Some((char_ops, line_ops)) = diff_rx.next().await {
codegen.update(&mut cx, |codegen, cx| {
codegen.last_equal_ranges.clear();
let edits = char_ops
.into_iter()
.filter_map(|operation| match operation {
CharOperation::Insert { text } => {
let edit_start = snapshot.anchor_after(edit_start);
Some((edit_start..edit_start, text))
}
CharOperation::Delete { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
Some((edit_range, String::new()))
}
CharOperation::Keep { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
codegen.last_equal_ranges.push(edit_range);
None
}
})
.collect::<Vec<_>>();
if codegen.active {
codegen.apply_edits(edits.iter().cloned(), cx);
codegen.reapply_line_based_diff(line_ops.iter().cloned(), cx);
}
codegen.edits.extend(edits);
codegen.line_operations = line_ops;
codegen.edit_position = Some(snapshot.anchor_after(edit_start));
cx.notify();
})?;
}
let mut char_ops = diff.push_new(&new_text);
char_ops.extend(diff.finish());
line_diff.push_char_operations(&char_ops, &selected_text);
line_diff.finish(&selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
.await?;
// Streaming stopped and we have the new text in the buffer, and a line-based diff applied for the whole new buffer.
// That diff is not what a regular diff is and might look unexpected, ergo apply a regular diff.
// It's fine to apply even if the rest of the line diffing fails, as no more hunks are coming through `diff_rx`.
let batch_diff_task =
codegen.update(&mut cx, |codegen, cx| codegen.reapply_batch_diff(cx))?;
let (line_based_stream_diff, ()) =
join!(line_based_stream_diff, batch_diff_task);
line_based_stream_diff?;
anyhow::Ok(())
};
anyhow::Ok(())
};
let result = diff.await;
let result = generate.await;
let elapsed_time = start_time.elapsed().as_secs_f64();
let error_message = result.as_ref().err().map(|error| error.to_string());
report_assistant_event(
AssistantEvent {
conversation_id: None,
message_id,
kind: AssistantKind::Inline,
phase: AssistantPhase::Response,
model: model_telemetry_id,
model_provider: model_provider_id,
response_latency,
error_message,
language_name: language_name.map(|name| name.to_proto()),
},
telemetry,
http_client,
model_api_key,
&executor,
);
codegen
.update(&mut cx, |this, cx| {
this.message_id = message_id;
this.last_equal_ranges.clear();
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {
this.status = CodegenStatus::Done;
result?;
Ok(())
});
while let Some((char_ops, line_ops)) = diff_rx.next().await {
codegen.update(cx, |codegen, cx| {
codegen.last_equal_ranges.clear();
let edits = char_ops
.into_iter()
.filter_map(|operation| match operation {
CharOperation::Insert { text } => {
let edit_start = snapshot.anchor_after(edit_start);
Some((edit_start..edit_start, text))
}
CharOperation::Delete { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
Some((edit_range, String::new()))
}
CharOperation::Keep { bytes } => {
let edit_end = edit_start + bytes;
let edit_range = snapshot.anchor_after(edit_start)
..snapshot.anchor_before(edit_end);
edit_start = edit_end;
codegen.last_equal_ranges.push(edit_range);
None
}
})
.collect::<Vec<_>>();
if codegen.active {
codegen.apply_edits(edits.iter().cloned(), cx);
codegen.reapply_line_based_diff(line_ops.iter().cloned(), cx);
}
this.elapsed_time = Some(elapsed_time);
this.completion = Some(completion.lock().clone());
cx.emit(CodegenEvent::Finished);
codegen.edits.extend(edits);
codegen.line_operations = line_ops;
codegen.edit_position = Some(snapshot.anchor_after(edit_start));
cx.notify();
})
.ok();
}
})?;
}
// Streaming stopped and we have the new text in the buffer, and a line-based diff applied for the whole new buffer.
// That diff is not what a regular diff is and might look unexpected, ergo apply a regular diff.
// It's fine to apply even if the rest of the line diffing fails, as no more hunks are coming through `diff_rx`.
let batch_diff_task =
codegen.update(cx, |codegen, cx| codegen.reapply_batch_diff(cx))?;
let (line_based_stream_diff, ()) = join!(line_based_stream_diff, batch_diff_task);
line_based_stream_diff?;
anyhow::Ok(())
};
let result = generate.await;
let elapsed_time = start_time.elapsed().as_secs_f64();
codegen
.update(cx, |this, cx| {
this.message_id = message_id;
this.last_equal_ranges.clear();
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {
this.status = CodegenStatus::Done;
}
this.elapsed_time = Some(elapsed_time);
this.completion = Some(completion.lock().clone());
if let Some(usage) = token_usage {
let usage = usage.lock();
telemetry::event!(
"Inline Assistant Completion",
model = model_telemetry_id,
model_provider = model_provider_id,
input_tokens = usage.input_tokens,
output_tokens = usage.output_tokens,
)
}
cx.emit(CodegenEvent::Finished);
cx.notify();
})
.ok();
});
cx.notify();
}
@@ -804,7 +814,7 @@ impl CodegenAlternative {
let new_snapshot = self.buffer.read(cx).snapshot(cx);
let new_range = self.range.to_point(&new_snapshot);
cx.spawn(|codegen, mut cx| async move {
cx.spawn(async move |codegen, cx| {
let (deleted_row_ranges, inserted_row_ranges) = cx
.background_spawn(async move {
let old_text = old_snapshot
@@ -854,7 +864,7 @@ impl CodegenAlternative {
.await;
codegen
.update(&mut cx, |codegen, cx| {
.update(cx, |codegen, cx| {
codegen.diff.deleted_row_ranges = deleted_row_ranges;
codegen.diff.inserted_row_ranges = inserted_row_ranges;
cx.notify();
@@ -1027,7 +1037,7 @@ mod tests {
language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, LanguageMatcher,
Point,
};
use language_model::LanguageModelRegistry;
use language_model::{LanguageModelRegistry, TokenUsage};
use rand::prelude::*;
use serde::Serialize;
use settings::SettingsStore;
@@ -1411,6 +1421,7 @@ mod tests {
future::ready(Ok(LanguageModelTextStream {
message_id: None,
stream: chunks_rx.map(Ok).boxed(),
last_token_usage: Arc::new(Mutex::new(TokenUsage::default())),
})),
cx,
);

View File

@@ -43,15 +43,6 @@ pub enum ContextKind {
}
impl ContextKind {
pub fn label(&self) -> &'static str {
match self {
ContextKind::File => "File",
ContextKind::Directory => "Folder",
ContextKind::FetchedUrl => "Fetch",
ContextKind::Thread => "Thread",
}
}
pub fn icon(&self) -> IconName {
match self {
ContextKind::File => IconName::File,

View File

@@ -1,22 +1,28 @@
mod directory_context_picker;
mod completion_provider;
mod fetch_context_picker;
mod file_context_picker;
mod thread_context_picker;
use std::ops::Range;
use std::path::PathBuf;
use std::sync::Arc;
use anyhow::{anyhow, Result};
use editor::Editor;
use editor::display_map::{Crease, FoldId};
use editor::{Anchor, AnchorRangeExt as _, Editor, ExcerptId, FoldPlaceholder, ToOffset};
use file_context_picker::render_file_context_entry;
use gpui::{App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity};
use gpui::{
App, DismissEvent, Empty, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity,
};
use multi_buffer::MultiBufferRow;
use project::ProjectPath;
use thread_context_picker::{render_thread_context_entry, ThreadContextEntry};
use ui::{prelude::*, ContextMenu, ContextMenuEntry, ContextMenuItem};
use ui::{
prelude::*, ButtonLike, ContextMenu, ContextMenuEntry, ContextMenuItem, Disclosure, TintColor,
};
use workspace::{notifications::NotifyResultExt, Workspace};
use crate::context::ContextKind;
use crate::context_picker::directory_context_picker::DirectoryContextPicker;
pub use crate::context_picker::completion_provider::ContextPickerCompletionProvider;
use crate::context_picker::fetch_context_picker::FetchContextPicker;
use crate::context_picker::file_context_picker::FileContextPicker;
use crate::context_picker::thread_context_picker::ThreadContextPicker;
@@ -30,19 +36,63 @@ pub enum ConfirmBehavior {
Close,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ContextPickerMode {
File,
Fetch,
Thread,
}
impl TryFrom<&str> for ContextPickerMode {
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"file" => Ok(Self::File),
"fetch" => Ok(Self::Fetch),
"thread" => Ok(Self::Thread),
_ => Err(format!("Invalid context picker mode: {}", value)),
}
}
}
impl ContextPickerMode {
pub fn mention_prefix(&self) -> &'static str {
match self {
Self::File => "file",
Self::Fetch => "fetch",
Self::Thread => "thread",
}
}
pub fn label(&self) -> &'static str {
match self {
Self::File => "Files & Directories",
Self::Fetch => "Fetch",
Self::Thread => "Thread",
}
}
pub fn icon(&self) -> IconName {
match self {
Self::File => IconName::File,
Self::Fetch => IconName::Globe,
Self::Thread => IconName::MessageCircle,
}
}
}
#[derive(Debug, Clone)]
enum ContextPickerState {
Default(Entity<ContextMenu>),
File(Entity<FileContextPicker>),
Directory(Entity<DirectoryContextPicker>),
Fetch(Entity<FetchContextPicker>),
Thread(Entity<ThreadContextPicker>),
}
pub(super) struct ContextPicker {
mode: ContextPickerMode,
mode: ContextPickerState,
workspace: WeakEntity<Workspace>,
editor: WeakEntity<Editor>,
context_store: WeakEntity<ContextStore>,
thread_store: Option<WeakEntity<ThreadStore>>,
confirm_behavior: ConfirmBehavior,
@@ -53,13 +103,12 @@ impl ContextPicker {
workspace: WeakEntity<Workspace>,
thread_store: Option<WeakEntity<ThreadStore>>,
context_store: WeakEntity<ContextStore>,
editor: WeakEntity<Editor>,
confirm_behavior: ConfirmBehavior,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
ContextPicker {
mode: ContextPickerMode::Default(ContextMenu::build(
mode: ContextPickerState::Default(ContextMenu::build(
window,
cx,
|menu, _window, _cx| menu,
@@ -67,13 +116,12 @@ impl ContextPicker {
workspace,
context_store,
thread_store,
editor,
confirm_behavior,
}
}
pub fn init(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.mode = ContextPickerMode::Default(self.build_menu(window, cx));
self.mode = ContextPickerState::Default(self.build_menu(window, cx));
cx.notify();
}
@@ -88,14 +136,7 @@ impl ContextPicker {
.enumerate()
.map(|(ix, entry)| self.recent_menu_item(context_picker.clone(), ix, entry));
let mut context_kinds = vec![
ContextKind::File,
ContextKind::Directory,
ContextKind::FetchedUrl,
];
if self.allow_threads() {
context_kinds.push(ContextKind::Thread);
}
let modes = supported_context_picker_modes(&self.thread_store);
let menu = menu
.when(has_recent, |menu| {
@@ -112,15 +153,15 @@ impl ContextPicker {
})
.extend(recent_entries)
.when(has_recent, |menu| menu.separator())
.extend(context_kinds.into_iter().map(|kind| {
.extend(modes.into_iter().map(|mode| {
let context_picker = context_picker.clone();
ContextMenuEntry::new(kind.label())
.icon(kind.icon())
ContextMenuEntry::new(mode.label())
.icon(mode.icon())
.icon_size(IconSize::XSmall)
.icon_color(Color::Muted)
.handler(move |window, cx| {
context_picker.update(cx, |this, cx| this.select_kind(kind, window, cx))
context_picker.update(cx, |this, cx| this.select_mode(mode, window, cx))
})
}));
@@ -143,16 +184,20 @@ impl ContextPicker {
self.thread_store.is_some()
}
fn select_kind(&mut self, kind: ContextKind, window: &mut Window, cx: &mut Context<Self>) {
fn select_mode(
&mut self,
mode: ContextPickerMode,
window: &mut Window,
cx: &mut Context<Self>,
) {
let context_picker = cx.entity().downgrade();
match kind {
ContextKind::File => {
self.mode = ContextPickerMode::File(cx.new(|cx| {
match mode {
ContextPickerMode::File => {
self.mode = ContextPickerState::File(cx.new(|cx| {
FileContextPicker::new(
context_picker.clone(),
self.workspace.clone(),
self.editor.clone(),
self.context_store.clone(),
self.confirm_behavior,
window,
@@ -160,20 +205,8 @@ impl ContextPicker {
)
}));
}
ContextKind::Directory => {
self.mode = ContextPickerMode::Directory(cx.new(|cx| {
DirectoryContextPicker::new(
context_picker.clone(),
self.workspace.clone(),
self.context_store.clone(),
self.confirm_behavior,
window,
cx,
)
}));
}
ContextKind::FetchedUrl => {
self.mode = ContextPickerMode::Fetch(cx.new(|cx| {
ContextPickerMode::Fetch => {
self.mode = ContextPickerState::Fetch(cx.new(|cx| {
FetchContextPicker::new(
context_picker.clone(),
self.workspace.clone(),
@@ -184,9 +217,9 @@ impl ContextPicker {
)
}));
}
ContextKind::Thread => {
ContextPickerMode::Thread => {
if let Some(thread_store) = self.thread_store.as_ref() {
self.mode = ContextPickerMode::Thread(cx.new(|cx| {
self.mode = ContextPickerState::Thread(cx.new(|cx| {
ThreadContextPicker::new(
thread_store.clone(),
context_picker.clone(),
@@ -224,6 +257,7 @@ impl ContextPicker {
ElementId::NamedInteger("ctx-recent".into(), ix),
&path,
&path_prefix,
false,
context_store.clone(),
cx,
)
@@ -267,13 +301,11 @@ impl ContextPicker {
};
let task = context_store.update(cx, |context_store, cx| {
context_store.add_file_from_path(project_path.clone(), cx)
context_store.add_file_from_path(project_path.clone(), true, cx)
});
cx.spawn_in(window, |_, mut cx| async move {
task.await.notify_async_err(&mut cx)
})
.detach();
cx.spawn_in(window, async move |_, cx| task.await.notify_async_err(cx))
.detach();
cx.notify();
}
@@ -296,13 +328,13 @@ impl ContextPicker {
};
let open_thread_task = thread_store.update(cx, |this, cx| this.open_thread(&thread.id, cx));
cx.spawn(|this, mut cx| async move {
cx.spawn(async move |this, cx| {
let thread = open_thread_task.await?;
context_store.update(&mut cx, |context_store, cx| {
context_store.add_thread(thread, cx);
context_store.update(cx, |context_store, cx| {
context_store.add_thread(thread, true, cx);
})?;
this.update(&mut cx, |_this, cx| cx.notify())
this.update(cx, |_this, cx| cx.notify())
})
}
@@ -319,7 +351,7 @@ impl ContextPicker {
let mut current_files = context_store.file_paths(cx);
if let Some(active_path) = Self::active_singleton_buffer_path(&workspace, cx) {
if let Some(active_path) = active_singleton_buffer_path(&workspace, cx) {
current_files.insert(active_path);
}
@@ -375,16 +407,6 @@ impl ContextPicker {
recent
}
fn active_singleton_buffer_path(workspace: &Workspace, cx: &App) -> Option<PathBuf> {
let active_item = workspace.active_item(cx)?;
let editor = active_item.to_any().downcast::<Editor>().ok()?.read(cx);
let buffer = editor.buffer().read(cx).as_singleton()?;
let path = buffer.read(cx).file()?.path().to_path_buf();
Some(path)
}
}
impl EventEmitter<DismissEvent> for ContextPicker {}
@@ -392,11 +414,10 @@ impl EventEmitter<DismissEvent> for ContextPicker {}
impl Focusable for ContextPicker {
fn focus_handle(&self, cx: &App) -> FocusHandle {
match &self.mode {
ContextPickerMode::Default(menu) => menu.focus_handle(cx),
ContextPickerMode::File(file_picker) => file_picker.focus_handle(cx),
ContextPickerMode::Directory(directory_picker) => directory_picker.focus_handle(cx),
ContextPickerMode::Fetch(fetch_picker) => fetch_picker.focus_handle(cx),
ContextPickerMode::Thread(thread_picker) => thread_picker.focus_handle(cx),
ContextPickerState::Default(menu) => menu.focus_handle(cx),
ContextPickerState::File(file_picker) => file_picker.focus_handle(cx),
ContextPickerState::Fetch(fetch_picker) => fetch_picker.focus_handle(cx),
ContextPickerState::Thread(thread_picker) => thread_picker.focus_handle(cx),
}
}
}
@@ -407,13 +428,10 @@ impl Render for ContextPicker {
.w(px(400.))
.min_w(px(400.))
.map(|parent| match &self.mode {
ContextPickerMode::Default(menu) => parent.child(menu.clone()),
ContextPickerMode::File(file_picker) => parent.child(file_picker.clone()),
ContextPickerMode::Directory(directory_picker) => {
parent.child(directory_picker.clone())
}
ContextPickerMode::Fetch(fetch_picker) => parent.child(fetch_picker.clone()),
ContextPickerMode::Thread(thread_picker) => parent.child(thread_picker.clone()),
ContextPickerState::Default(menu) => parent.child(menu.clone()),
ContextPickerState::File(file_picker) => parent.child(file_picker.clone()),
ContextPickerState::Fetch(fetch_picker) => parent.child(fetch_picker.clone()),
ContextPickerState::Thread(thread_picker) => parent.child(thread_picker.clone()),
})
}
}
@@ -424,3 +442,212 @@ enum RecentEntry {
},
Thread(ThreadContextEntry),
}
fn supported_context_picker_modes(
thread_store: &Option<WeakEntity<ThreadStore>>,
) -> Vec<ContextPickerMode> {
let mut modes = vec![ContextPickerMode::File, ContextPickerMode::Fetch];
if thread_store.is_some() {
modes.push(ContextPickerMode::Thread);
}
modes
}
fn active_singleton_buffer_path(workspace: &Workspace, cx: &App) -> Option<PathBuf> {
let active_item = workspace.active_item(cx)?;
let editor = active_item.to_any().downcast::<Editor>().ok()?.read(cx);
let buffer = editor.buffer().read(cx).as_singleton()?;
let path = buffer.read(cx).file()?.path().to_path_buf();
Some(path)
}
fn recent_context_picker_entries(
context_store: Entity<ContextStore>,
thread_store: Option<WeakEntity<ThreadStore>>,
workspace: Entity<Workspace>,
cx: &App,
) -> Vec<RecentEntry> {
let mut recent = Vec::with_capacity(6);
let mut current_files = context_store.read(cx).file_paths(cx);
let workspace = workspace.read(cx);
if let Some(active_path) = active_singleton_buffer_path(workspace, cx) {
current_files.insert(active_path);
}
let project = workspace.project().read(cx);
recent.extend(
workspace
.recent_navigation_history_iter(cx)
.filter(|(path, _)| !current_files.contains(&path.path.to_path_buf()))
.take(4)
.filter_map(|(project_path, _)| {
project
.worktree_for_id(project_path.worktree_id, cx)
.map(|worktree| RecentEntry::File {
project_path,
path_prefix: worktree.read(cx).root_name().into(),
})
}),
);
let mut current_threads = context_store.read(cx).thread_ids();
if let Some(active_thread) = workspace
.panel::<AssistantPanel>(cx)
.map(|panel| panel.read(cx).active_thread(cx))
{
current_threads.insert(active_thread.read(cx).id().clone());
}
if let Some(thread_store) = thread_store.and_then(|thread_store| thread_store.upgrade()) {
recent.extend(
thread_store
.read(cx)
.threads()
.into_iter()
.filter(|thread| !current_threads.contains(&thread.id))
.take(2)
.map(|thread| {
RecentEntry::Thread(ThreadContextEntry {
id: thread.id,
summary: thread.summary,
})
}),
);
}
recent
}
pub(crate) fn insert_crease_for_mention(
excerpt_id: ExcerptId,
crease_start: text::Anchor,
content_len: usize,
crease_label: SharedString,
crease_icon_path: SharedString,
editor_entity: Entity<Editor>,
window: &mut Window,
cx: &mut App,
) {
editor_entity.update(cx, |editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
let Some(start) = snapshot.anchor_in_excerpt(excerpt_id, crease_start) else {
return;
};
let end = snapshot.anchor_before(start.to_offset(&snapshot) + content_len);
let placeholder = FoldPlaceholder {
render: render_fold_icon_button(
crease_icon_path,
crease_label,
editor_entity.downgrade(),
),
..Default::default()
};
let render_trailer =
move |_row, _unfold, _window: &mut Window, _cx: &mut App| Empty.into_any();
let crease = Crease::inline(
start..end,
placeholder.clone(),
fold_toggle("mention"),
render_trailer,
);
editor.insert_creases(vec![crease.clone()], cx);
editor.fold_creases(vec![crease], false, window, cx);
});
}
fn render_fold_icon_button(
icon_path: SharedString,
label: SharedString,
editor: WeakEntity<Editor>,
) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut App) -> AnyElement> {
Arc::new({
move |fold_id, fold_range, cx| {
let is_in_text_selection = editor.upgrade().is_some_and(|editor| {
editor.update(cx, |editor, cx| {
let snapshot = editor
.buffer()
.update(cx, |multi_buffer, cx| multi_buffer.snapshot(cx));
let is_in_pending_selection = || {
editor
.selections
.pending
.as_ref()
.is_some_and(|pending_selection| {
pending_selection
.selection
.range()
.includes(&fold_range, &snapshot)
})
};
let mut is_in_complete_selection = || {
editor
.selections
.disjoint_in_range::<usize>(fold_range.clone(), cx)
.into_iter()
.any(|selection| {
// This is needed to cover a corner case, if we just check for an existing
// selection in the fold range, having a cursor at the start of the fold
// marks it as selected. Non-empty selections don't cause this.
let length = selection.end - selection.start;
length > 0
})
};
is_in_pending_selection() || is_in_complete_selection()
})
});
ButtonLike::new(fold_id)
.style(ButtonStyle::Filled)
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
.toggle_state(is_in_text_selection)
.child(
h_flex()
.gap_1()
.child(
Icon::from_path(icon_path.clone())
.size(IconSize::Small)
.color(Color::Muted),
)
.child(
Label::new(label.clone())
.size(LabelSize::Small)
.single_line(),
),
)
.into_any_element()
}
})
}
fn fold_toggle(
name: &'static str,
) -> impl Fn(
MultiBufferRow,
bool,
Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
&mut Window,
&mut App,
) -> AnyElement {
move |row, is_folded, fold, _window, _cx| {
Disclosure::new((name, row.0 as u64), !is_folded)
.toggle_state(is_folded)
.on_click(move |_e, window, cx| fold(!is_folded, window, cx))
.into_any_element()
}
}

View File

@@ -0,0 +1,966 @@
use std::cell::RefCell;
use std::ops::Range;
use std::path::Path;
use std::rc::Rc;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use anyhow::Result;
use editor::{CompletionProvider, Editor, ExcerptId};
use file_icons::FileIcons;
use gpui::{App, Entity, Task, WeakEntity};
use http_client::HttpClientWithUrl;
use language::{Buffer, CodeLabel, HighlightId};
use lsp::CompletionContext;
use project::{Completion, CompletionIntent, ProjectPath, WorktreeId};
use rope::Point;
use text::{Anchor, ToPoint};
use ui::prelude::*;
use workspace::Workspace;
use crate::context::AssistantContext;
use crate::context_store::ContextStore;
use crate::thread_store::ThreadStore;
use super::fetch_context_picker::fetch_url_content;
use super::thread_context_picker::ThreadContextEntry;
use super::{recent_context_picker_entries, supported_context_picker_modes, ContextPickerMode};
pub struct ContextPickerCompletionProvider {
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
thread_store: Option<WeakEntity<ThreadStore>>,
editor: WeakEntity<Editor>,
}
impl ContextPickerCompletionProvider {
pub fn new(
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
thread_store: Option<WeakEntity<ThreadStore>>,
editor: WeakEntity<Editor>,
) -> Self {
Self {
workspace,
context_store,
thread_store,
editor,
}
}
fn default_completions(
excerpt_id: ExcerptId,
source_range: Range<Anchor>,
context_store: Entity<ContextStore>,
thread_store: Option<WeakEntity<ThreadStore>>,
editor: Entity<Editor>,
workspace: Entity<Workspace>,
cx: &App,
) -> Vec<Completion> {
let mut completions = Vec::new();
completions.extend(
recent_context_picker_entries(
context_store.clone(),
thread_store.clone(),
workspace.clone(),
cx,
)
.iter()
.filter_map(|entry| match entry {
super::RecentEntry::File {
project_path,
path_prefix,
} => Some(Self::completion_for_path(
project_path.clone(),
path_prefix,
true,
false,
excerpt_id,
source_range.clone(),
editor.clone(),
context_store.clone(),
cx,
)),
super::RecentEntry::Thread(thread_context_entry) => {
let thread_store = thread_store
.as_ref()
.and_then(|thread_store| thread_store.upgrade())?;
Some(Self::completion_for_thread(
thread_context_entry.clone(),
excerpt_id,
source_range.clone(),
true,
editor.clone(),
context_store.clone(),
thread_store,
))
}
}),
);
completions.extend(
supported_context_picker_modes(&thread_store)
.iter()
.map(|mode| {
Completion {
old_range: source_range.clone(),
new_text: format!("@{} ", mode.mention_prefix()),
label: CodeLabel::plain(mode.label().to_string(), None),
icon_path: Some(mode.icon().path().into()),
documentation: None,
source: project::CompletionSource::Custom,
// This ensures that when a user accepts this completion, the
// completion menu will still be shown after "@category " is
// inserted
confirm: Some(Arc::new(|_, _, _| true)),
}
}),
);
completions
}
fn build_code_label_for_full_path(
file_name: &str,
directory: Option<&str>,
cx: &App,
) -> CodeLabel {
let comment_id = cx.theme().syntax().highlight_id("comment").map(HighlightId);
let mut label = CodeLabel::default();
label.push_str(&file_name, None);
label.push_str(" ", None);
if let Some(directory) = directory {
label.push_str(&directory, comment_id);
}
label.filter_range = 0..label.text().len();
label
}
fn completion_for_thread(
thread_entry: ThreadContextEntry,
excerpt_id: ExcerptId,
source_range: Range<Anchor>,
recent: bool,
editor: Entity<Editor>,
context_store: Entity<ContextStore>,
thread_store: Entity<ThreadStore>,
) -> Completion {
let icon_for_completion = if recent {
IconName::HistoryRerun
} else {
IconName::MessageCircle
};
let new_text = format!("@thread {}", thread_entry.summary);
let new_text_len = new_text.len();
Completion {
old_range: source_range.clone(),
new_text,
label: CodeLabel::plain(thread_entry.summary.to_string(), None),
documentation: None,
source: project::CompletionSource::Custom,
icon_path: Some(icon_for_completion.path().into()),
confirm: Some(confirm_completion_callback(
IconName::MessageCircle.path().into(),
thread_entry.summary.clone(),
excerpt_id,
source_range.start,
new_text_len,
editor.clone(),
move |cx| {
let thread_id = thread_entry.id.clone();
let context_store = context_store.clone();
let thread_store = thread_store.clone();
cx.spawn(async move |cx| {
let thread = thread_store
.update(cx, |thread_store, cx| {
thread_store.open_thread(&thread_id, cx)
})?
.await?;
context_store.update(cx, |context_store, cx| {
context_store.add_thread(thread, false, cx)
})
})
.detach_and_log_err(cx);
},
)),
}
}
fn completion_for_fetch(
source_range: Range<Anchor>,
url_to_fetch: SharedString,
excerpt_id: ExcerptId,
editor: Entity<Editor>,
context_store: Entity<ContextStore>,
http_client: Arc<HttpClientWithUrl>,
) -> Completion {
let new_text = format!("@fetch {}", url_to_fetch);
let new_text_len = new_text.len();
Completion {
old_range: source_range.clone(),
new_text,
label: CodeLabel::plain(url_to_fetch.to_string(), None),
documentation: None,
source: project::CompletionSource::Custom,
icon_path: Some(IconName::Globe.path().into()),
confirm: Some(confirm_completion_callback(
IconName::Globe.path().into(),
url_to_fetch.clone(),
excerpt_id,
source_range.start,
new_text_len,
editor.clone(),
move |cx| {
let context_store = context_store.clone();
let http_client = http_client.clone();
let url_to_fetch = url_to_fetch.clone();
cx.spawn(async move |cx| {
if context_store.update(cx, |context_store, _| {
context_store.includes_url(&url_to_fetch).is_some()
})? {
return Ok(());
}
let content = cx
.background_spawn(fetch_url_content(
http_client,
url_to_fetch.to_string(),
))
.await?;
context_store.update(cx, |context_store, _| {
context_store.add_fetched_url(url_to_fetch.to_string(), content)
})
})
.detach_and_log_err(cx);
},
)),
}
}
fn completion_for_path(
project_path: ProjectPath,
path_prefix: &str,
is_recent: bool,
is_directory: bool,
excerpt_id: ExcerptId,
source_range: Range<Anchor>,
editor: Entity<Editor>,
context_store: Entity<ContextStore>,
cx: &App,
) -> Completion {
let (file_name, directory) = super::file_context_picker::extract_file_name_and_directory(
&project_path.path,
path_prefix,
);
let label = Self::build_code_label_for_full_path(
&file_name,
directory.as_ref().map(|s| s.as_ref()),
cx,
);
let full_path = if let Some(directory) = directory {
format!("{}{}", directory, file_name)
} else {
file_name.to_string()
};
let crease_icon_path = if is_directory {
FileIcons::get_folder_icon(false, cx).unwrap_or_else(|| IconName::Folder.path().into())
} else {
FileIcons::get_icon(Path::new(&full_path), cx)
.unwrap_or_else(|| IconName::File.path().into())
};
let completion_icon_path = if is_recent {
IconName::HistoryRerun.path().into()
} else {
crease_icon_path.clone()
};
let new_text = format!("@file {}", full_path);
let new_text_len = new_text.len();
Completion {
old_range: source_range.clone(),
new_text,
label,
documentation: None,
source: project::CompletionSource::Custom,
icon_path: Some(completion_icon_path),
confirm: Some(confirm_completion_callback(
crease_icon_path,
file_name,
excerpt_id,
source_range.start,
new_text_len,
editor,
move |cx| {
context_store.update(cx, |context_store, cx| {
let task = if is_directory {
context_store.add_directory(project_path.clone(), false, cx)
} else {
context_store.add_file_from_path(project_path.clone(), false, cx)
};
task.detach_and_log_err(cx);
})
},
)),
}
}
}
impl CompletionProvider for ContextPickerCompletionProvider {
fn completions(
&self,
excerpt_id: ExcerptId,
buffer: &Entity<Buffer>,
buffer_position: Anchor,
_trigger: CompletionContext,
_window: &mut Window,
cx: &mut Context<Editor>,
) -> Task<Result<Option<Vec<Completion>>>> {
let state = buffer.update(cx, |buffer, _cx| {
let position = buffer_position.to_point(buffer);
let line_start = Point::new(position.row, 0);
let offset_to_line = buffer.point_to_offset(line_start);
let mut lines = buffer.text_for_range(line_start..position).lines();
let line = lines.next()?;
MentionCompletion::try_parse(line, offset_to_line)
});
let Some(state) = state else {
return Task::ready(Ok(None));
};
let Some(workspace) = self.workspace.upgrade() else {
return Task::ready(Ok(None));
};
let Some(context_store) = self.context_store.upgrade() else {
return Task::ready(Ok(None));
};
let snapshot = buffer.read(cx).snapshot();
let source_range = snapshot.anchor_after(state.source_range.start)
..snapshot.anchor_before(state.source_range.end);
let thread_store = self.thread_store.clone();
let editor = self.editor.clone();
let http_client = workspace.read(cx).client().http_client().clone();
cx.spawn(async move |_, cx| {
let mut completions = Vec::new();
let MentionCompletion {
mode: category,
argument,
..
} = state;
let query = argument.unwrap_or_else(|| "".to_string());
match category {
Some(ContextPickerMode::File) => {
let path_matches = cx
.update(|cx| {
super::file_context_picker::search_paths(
query,
Arc::<AtomicBool>::default(),
&workspace,
cx,
)
})?
.await;
if let Some(editor) = editor.upgrade() {
completions.reserve(path_matches.len());
cx.update(|cx| {
completions.extend(path_matches.iter().map(|mat| {
Self::completion_for_path(
ProjectPath {
worktree_id: WorktreeId::from_usize(mat.worktree_id),
path: mat.path.clone(),
},
&mat.path_prefix,
false,
mat.is_dir,
excerpt_id,
source_range.clone(),
editor.clone(),
context_store.clone(),
cx,
)
}));
})?;
}
}
Some(ContextPickerMode::Fetch) => {
if let Some(editor) = editor.upgrade() {
if !query.is_empty() {
completions.push(Self::completion_for_fetch(
source_range.clone(),
query.into(),
excerpt_id,
editor.clone(),
context_store.clone(),
http_client.clone(),
));
}
context_store.update(cx, |store, _| {
let urls = store.context().iter().filter_map(|context| {
if let AssistantContext::FetchedUrl(context) = context {
Some(context.url.clone())
} else {
None
}
});
for url in urls {
completions.push(Self::completion_for_fetch(
source_range.clone(),
url,
excerpt_id,
editor.clone(),
context_store.clone(),
http_client.clone(),
));
}
})?;
}
}
Some(ContextPickerMode::Thread) => {
if let Some((thread_store, editor)) = thread_store
.and_then(|thread_store| thread_store.upgrade())
.zip(editor.upgrade())
{
let threads = cx
.update(|cx| {
super::thread_context_picker::search_threads(
query,
thread_store.clone(),
cx,
)
})?
.await;
for thread in threads {
completions.push(Self::completion_for_thread(
thread.clone(),
excerpt_id,
source_range.clone(),
false,
editor.clone(),
context_store.clone(),
thread_store.clone(),
));
}
}
}
None => {
cx.update(|cx| {
if let Some(editor) = editor.upgrade() {
completions.extend(Self::default_completions(
excerpt_id,
source_range.clone(),
context_store.clone(),
thread_store.clone(),
editor,
workspace.clone(),
cx,
));
}
})?;
}
}
Ok(Some(completions))
})
}
fn resolve_completions(
&self,
_buffer: Entity<Buffer>,
_completion_indices: Vec<usize>,
_completions: Rc<RefCell<Box<[Completion]>>>,
_cx: &mut Context<Editor>,
) -> Task<Result<bool>> {
Task::ready(Ok(true))
}
fn is_completion_trigger(
&self,
buffer: &Entity<language::Buffer>,
position: language::Anchor,
_: &str,
_: bool,
cx: &mut Context<Editor>,
) -> bool {
let buffer = buffer.read(cx);
let position = position.to_point(buffer);
let line_start = Point::new(position.row, 0);
let offset_to_line = buffer.point_to_offset(line_start);
let mut lines = buffer.text_for_range(line_start..position).lines();
if let Some(line) = lines.next() {
MentionCompletion::try_parse(line, offset_to_line)
.map(|completion| {
completion.source_range.start <= offset_to_line + position.column as usize
&& completion.source_range.end >= offset_to_line + position.column as usize
})
.unwrap_or(false)
} else {
false
}
}
fn sort_completions(&self) -> bool {
false
}
fn filter_completions(&self) -> bool {
false
}
}
fn confirm_completion_callback(
crease_icon_path: SharedString,
crease_text: SharedString,
excerpt_id: ExcerptId,
start: Anchor,
content_len: usize,
editor: Entity<Editor>,
add_context_fn: impl Fn(&mut App) -> () + Send + Sync + 'static,
) -> Arc<dyn Fn(CompletionIntent, &mut Window, &mut App) -> bool + Send + Sync> {
Arc::new(move |_, window, cx| {
add_context_fn(cx);
let crease_text = crease_text.clone();
let crease_icon_path = crease_icon_path.clone();
let editor = editor.clone();
window.defer(cx, move |window, cx| {
crate::context_picker::insert_crease_for_mention(
excerpt_id,
start,
content_len,
crease_text,
crease_icon_path,
editor,
window,
cx,
);
});
false
})
}
#[derive(Debug, Default, PartialEq)]
struct MentionCompletion {
source_range: Range<usize>,
mode: Option<ContextPickerMode>,
argument: Option<String>,
}
impl MentionCompletion {
fn try_parse(line: &str, offset_to_line: usize) -> Option<Self> {
let last_mention_start = line.rfind('@')?;
if last_mention_start >= line.len() {
return Some(Self::default());
}
let rest_of_line = &line[last_mention_start + 1..];
let mut mode = None;
let mut argument = None;
let mut parts = rest_of_line.split_whitespace();
let mut end = last_mention_start + 1;
if let Some(mode_text) = parts.next() {
end += mode_text.len();
mode = ContextPickerMode::try_from(mode_text).ok();
match rest_of_line[mode_text.len()..].find(|c: char| !c.is_whitespace()) {
Some(whitespace_count) => {
if let Some(argument_text) = parts.next() {
argument = Some(argument_text.to_string());
end += whitespace_count + argument_text.len();
}
}
None => {
// Rest of line is entirely whitespace
end += rest_of_line.len() - mode_text.len();
}
}
}
Some(Self {
source_range: last_mention_start + offset_to_line..end + offset_to_line,
mode,
argument,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use gpui::{Focusable, TestAppContext, VisualTestContext};
use project::{Project, ProjectPath};
use serde_json::json;
use settings::SettingsStore;
use std::{ops::Deref, path::PathBuf};
use util::{path, separator};
use workspace::AppState;
#[test]
fn test_mention_completion_parse() {
assert_eq!(MentionCompletion::try_parse("Lorem Ipsum", 0), None);
assert_eq!(
MentionCompletion::try_parse("Lorem @", 0),
Some(MentionCompletion {
source_range: 6..7,
mode: None,
argument: None,
})
);
assert_eq!(
MentionCompletion::try_parse("Lorem @file", 0),
Some(MentionCompletion {
source_range: 6..11,
mode: Some(ContextPickerMode::File),
argument: None,
})
);
assert_eq!(
MentionCompletion::try_parse("Lorem @file ", 0),
Some(MentionCompletion {
source_range: 6..12,
mode: Some(ContextPickerMode::File),
argument: None,
})
);
assert_eq!(
MentionCompletion::try_parse("Lorem @file main.rs", 0),
Some(MentionCompletion {
source_range: 6..19,
mode: Some(ContextPickerMode::File),
argument: Some("main.rs".to_string()),
})
);
assert_eq!(
MentionCompletion::try_parse("Lorem @file main.rs ", 0),
Some(MentionCompletion {
source_range: 6..19,
mode: Some(ContextPickerMode::File),
argument: Some("main.rs".to_string()),
})
);
assert_eq!(
MentionCompletion::try_parse("Lorem @file main.rs Ipsum", 0),
Some(MentionCompletion {
source_range: 6..19,
mode: Some(ContextPickerMode::File),
argument: Some("main.rs".to_string()),
})
);
}
#[gpui::test]
async fn test_context_completion_provider(cx: &mut TestAppContext) {
init_test(cx);
let app_state = cx.update(AppState::test);
cx.update(|cx| {
language::init(cx);
editor::init(cx);
workspace::init(app_state.clone(), cx);
Project::init_settings(cx);
});
app_state
.fs
.as_fake()
.insert_tree(
path!("/dir"),
json!({
"editor": "",
"a": {
"one.txt": "",
"two.txt": "",
"three.txt": "",
"four.txt": ""
},
"b": {
"five.txt": "",
"six.txt": "",
"seven.txt": "",
}
}),
)
.await;
let project = Project::test(app_state.fs.clone(), [path!("/dir").as_ref()], cx).await;
let window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
let workspace = window.root(cx).unwrap();
let worktree = project.update(cx, |project, cx| {
let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 1);
worktrees.pop().unwrap()
});
let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
let mut cx = VisualTestContext::from_window(*window.deref(), cx);
let paths = vec![
separator!("a/one.txt"),
separator!("a/two.txt"),
separator!("a/three.txt"),
separator!("a/four.txt"),
separator!("b/five.txt"),
separator!("b/six.txt"),
separator!("b/seven.txt"),
];
for path in paths {
workspace
.update_in(&mut cx, |workspace, window, cx| {
workspace.open_path(
ProjectPath {
worktree_id,
path: Path::new(path).into(),
},
None,
false,
window,
cx,
)
})
.await
.unwrap();
}
let item = workspace
.update_in(&mut cx, |workspace, window, cx| {
workspace.open_path(
ProjectPath {
worktree_id,
path: PathBuf::from("editor").into(),
},
None,
true,
window,
cx,
)
})
.await
.expect("Could not open test file");
let editor = cx.update(|_, cx| {
item.act_as::<Editor>(cx)
.expect("Opened test file wasn't an editor")
});
let context_store = cx.new(|_| ContextStore::new(workspace.downgrade()));
let editor_entity = editor.downgrade();
editor.update_in(&mut cx, |editor, window, cx| {
window.focus(&editor.focus_handle(cx));
editor.set_completion_provider(Some(Box::new(ContextPickerCompletionProvider::new(
workspace.downgrade(),
context_store.downgrade(),
None,
editor_entity,
))));
});
cx.simulate_input("Lorem ");
editor.update(&mut cx, |editor, cx| {
assert_eq!(editor.text(cx), "Lorem ");
assert!(!editor.has_visible_completions_menu());
});
cx.simulate_input("@");
editor.update(&mut cx, |editor, cx| {
assert_eq!(editor.text(cx), "Lorem @");
assert!(editor.has_visible_completions_menu());
assert_eq!(
current_completion_labels(editor),
&[
"seven.txt dir/b/",
"six.txt dir/b/",
"five.txt dir/b/",
"four.txt dir/a/",
"Files & Directories",
"Fetch"
]
);
});
// Select and confirm "File"
editor.update_in(&mut cx, |editor, window, cx| {
assert!(editor.has_visible_completions_menu());
editor.context_menu_next(&editor::actions::ContextMenuNext, window, cx);
editor.context_menu_next(&editor::actions::ContextMenuNext, window, cx);
editor.context_menu_next(&editor::actions::ContextMenuNext, window, cx);
editor.context_menu_next(&editor::actions::ContextMenuNext, window, cx);
editor.confirm_completion(&editor::actions::ConfirmCompletion::default(), window, cx);
});
cx.run_until_parked();
editor.update(&mut cx, |editor, cx| {
assert_eq!(editor.text(cx), "Lorem @file ");
assert!(editor.has_visible_completions_menu());
});
cx.simulate_input("one");
editor.update(&mut cx, |editor, cx| {
assert_eq!(editor.text(cx), "Lorem @file one");
assert!(editor.has_visible_completions_menu());
assert_eq!(current_completion_labels(editor), vec!["one.txt dir/a/"]);
});
editor.update_in(&mut cx, |editor, window, cx| {
assert!(editor.has_visible_completions_menu());
editor.confirm_completion(&editor::actions::ConfirmCompletion::default(), window, cx);
});
editor.update(&mut cx, |editor, cx| {
assert_eq!(editor.text(cx), "Lorem @file dir/a/one.txt",);
assert!(!editor.has_visible_completions_menu());
assert_eq!(
crease_ranges(editor, cx),
vec![Point::new(0, 6)..Point::new(0, 25)]
);
});
cx.simulate_input(" ");
editor.update(&mut cx, |editor, cx| {
assert_eq!(editor.text(cx), "Lorem @file dir/a/one.txt ",);
assert!(!editor.has_visible_completions_menu());
assert_eq!(
crease_ranges(editor, cx),
vec![Point::new(0, 6)..Point::new(0, 25)]
);
});
cx.simulate_input("Ipsum ");
editor.update(&mut cx, |editor, cx| {
assert_eq!(editor.text(cx), "Lorem @file dir/a/one.txt Ipsum ",);
assert!(!editor.has_visible_completions_menu());
assert_eq!(
crease_ranges(editor, cx),
vec![Point::new(0, 6)..Point::new(0, 25)]
);
});
cx.simulate_input("@file ");
editor.update(&mut cx, |editor, cx| {
assert_eq!(editor.text(cx), "Lorem @file dir/a/one.txt Ipsum @file ",);
assert!(editor.has_visible_completions_menu());
assert_eq!(
crease_ranges(editor, cx),
vec![Point::new(0, 6)..Point::new(0, 25)]
);
});
editor.update_in(&mut cx, |editor, window, cx| {
editor.confirm_completion(&editor::actions::ConfirmCompletion::default(), window, cx);
});
cx.run_until_parked();
editor.update(&mut cx, |editor, cx| {
assert_eq!(
editor.text(cx),
"Lorem @file dir/a/one.txt Ipsum @file dir/b/seven.txt"
);
assert!(!editor.has_visible_completions_menu());
assert_eq!(
crease_ranges(editor, cx),
vec![
Point::new(0, 6)..Point::new(0, 25),
Point::new(0, 32)..Point::new(0, 53)
]
);
});
cx.simulate_input("\n@");
editor.update(&mut cx, |editor, cx| {
assert_eq!(
editor.text(cx),
"Lorem @file dir/a/one.txt Ipsum @file dir/b/seven.txt\n@"
);
assert!(editor.has_visible_completions_menu());
assert_eq!(
crease_ranges(editor, cx),
vec![
Point::new(0, 6)..Point::new(0, 25),
Point::new(0, 32)..Point::new(0, 53)
]
);
});
editor.update_in(&mut cx, |editor, window, cx| {
editor.confirm_completion(&editor::actions::ConfirmCompletion::default(), window, cx);
});
cx.run_until_parked();
editor.update(&mut cx, |editor, cx| {
assert_eq!(
editor.text(cx),
"Lorem @file dir/a/one.txt Ipsum @file dir/b/seven.txt\n@file dir/b/six.txt"
);
assert!(!editor.has_visible_completions_menu());
assert_eq!(
crease_ranges(editor, cx),
vec![
Point::new(0, 6)..Point::new(0, 25),
Point::new(0, 32)..Point::new(0, 53),
Point::new(1, 0)..Point::new(1, 19)
]
);
});
}
fn crease_ranges(editor: &Editor, cx: &mut App) -> Vec<Range<Point>> {
let snapshot = editor.buffer().read(cx).snapshot(cx);
editor.display_map.update(cx, |display_map, cx| {
display_map
.snapshot(cx)
.crease_snapshot
.crease_items_with_offsets(&snapshot)
.into_iter()
.map(|(_, range)| range)
.collect()
})
}
fn current_completion_labels(editor: &Editor) -> Vec<String> {
let completions = editor.current_completions().expect("Missing completions");
completions
.into_iter()
.map(|completion| completion.label.text.to_string())
.collect::<Vec<_>>()
}
pub(crate) fn init_test(cx: &mut TestAppContext) {
cx.update(|cx| {
let store = SettingsStore::test(cx);
cx.set_global(store);
theme::init(theme::LoadThemes::JustBase, cx);
client::init_settings(cx);
language::init(cx);
Project::init_settings(cx);
workspace::init_settings(cx);
editor::init_settings(cx);
});
}
}

View File

@@ -1,269 +0,0 @@
use std::path::Path;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use fuzzy::PathMatch;
use gpui::{App, DismissEvent, Entity, FocusHandle, Focusable, Task, WeakEntity};
use picker::{Picker, PickerDelegate};
use project::{PathMatchCandidateSet, ProjectPath, WorktreeId};
use ui::{prelude::*, ListItem};
use util::ResultExt as _;
use workspace::{notifications::NotifyResultExt, Workspace};
use crate::context_picker::{ConfirmBehavior, ContextPicker};
use crate::context_store::ContextStore;
pub struct DirectoryContextPicker {
picker: Entity<Picker<DirectoryContextPickerDelegate>>,
}
impl DirectoryContextPicker {
pub fn new(
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let delegate = DirectoryContextPickerDelegate::new(
context_picker,
workspace,
context_store,
confirm_behavior,
);
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
Self { picker }
}
}
impl Focusable for DirectoryContextPicker {
fn focus_handle(&self, cx: &App) -> FocusHandle {
self.picker.focus_handle(cx)
}
}
impl Render for DirectoryContextPicker {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
self.picker.clone()
}
}
pub struct DirectoryContextPickerDelegate {
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
matches: Vec<PathMatch>,
selected_index: usize,
}
impl DirectoryContextPickerDelegate {
pub fn new(
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
) -> Self {
Self {
context_picker,
workspace,
context_store,
confirm_behavior,
matches: Vec::new(),
selected_index: 0,
}
}
fn search(
&mut self,
query: String,
cancellation_flag: Arc<AtomicBool>,
workspace: &Entity<Workspace>,
cx: &mut Context<Picker<Self>>,
) -> Task<Vec<PathMatch>> {
if query.is_empty() {
let workspace = workspace.read(cx);
let project = workspace.project().read(cx);
let directory_matches = project.worktrees(cx).flat_map(|worktree| {
let worktree = worktree.read(cx);
let path_prefix: Arc<str> = worktree.root_name().into();
worktree.directories(false, 0).map(move |entry| PathMatch {
score: 0.,
positions: Vec::new(),
worktree_id: worktree.id().to_usize(),
path: entry.path.clone(),
path_prefix: path_prefix.clone(),
distance_to_relative_ancestor: 0,
is_dir: true,
})
});
Task::ready(directory_matches.collect())
} else {
let worktrees = workspace.read(cx).visible_worktrees(cx).collect::<Vec<_>>();
let candidate_sets = worktrees
.into_iter()
.map(|worktree| {
let worktree = worktree.read(cx);
PathMatchCandidateSet {
snapshot: worktree.snapshot(),
include_ignored: worktree
.root_entry()
.map_or(false, |entry| entry.is_ignored),
include_root_name: true,
candidates: project::Candidates::Directories,
}
})
.collect::<Vec<_>>();
let executor = cx.background_executor().clone();
cx.foreground_executor().spawn(async move {
fuzzy::match_path_sets(
candidate_sets.as_slice(),
query.as_str(),
None,
false,
100,
&cancellation_flag,
executor,
)
.await
})
}
}
}
impl PickerDelegate for DirectoryContextPickerDelegate {
type ListItem = ListItem;
fn match_count(&self) -> usize {
self.matches.len()
}
fn selected_index(&self) -> usize {
self.selected_index
}
fn set_selected_index(
&mut self,
ix: usize,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) {
self.selected_index = ix;
}
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
"Search folders…".into()
}
fn update_matches(
&mut self,
query: String,
_window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Task<()> {
let Some(workspace) = self.workspace.upgrade() else {
return Task::ready(());
};
let search_task = self.search(query, Arc::<AtomicBool>::default(), &workspace, cx);
cx.spawn(|this, mut cx| async move {
let mut paths = search_task.await;
let empty_path = Path::new("");
paths.retain(|path_match| path_match.path.as_ref() != empty_path);
this.update(&mut cx, |this, _cx| {
this.delegate.matches = paths;
})
.log_err();
})
}
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
let Some(mat) = self.matches.get(self.selected_index) else {
return;
};
let project_path = ProjectPath {
worktree_id: WorktreeId::from_usize(mat.worktree_id),
path: mat.path.clone(),
};
let Some(task) = self
.context_store
.update(cx, |context_store, cx| {
context_store.add_directory(project_path, cx)
})
.ok()
else {
return;
};
let confirm_behavior = self.confirm_behavior;
cx.spawn_in(window, |this, mut cx| async move {
match task.await.notify_async_err(&mut cx) {
None => anyhow::Ok(()),
Some(()) => this.update_in(&mut cx, |this, window, cx| match confirm_behavior {
ConfirmBehavior::KeepOpen => {}
ConfirmBehavior::Close => this.delegate.dismissed(window, cx),
}),
}
})
.detach_and_log_err(cx);
}
fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
self.context_picker
.update(cx, |_, cx| {
cx.emit(DismissEvent);
})
.ok();
}
fn render_match(
&self,
ix: usize,
selected: bool,
_window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Option<Self::ListItem> {
let path_match = &self.matches[ix];
let directory_name = path_match.path.to_string_lossy().to_string();
let added = self.context_store.upgrade().map_or(false, |context_store| {
context_store
.read(cx)
.includes_directory(&path_match.path)
.is_some()
});
Some(
ListItem::new(ix)
.inset(true)
.toggle_state(selected)
.start_slot(
Icon::new(IconName::Folder)
.size(IconSize::XSmall)
.color(Color::Muted),
)
.child(Label::new(directory_name))
.when(added, |el| {
el.end_slot(
h_flex()
.gap_1()
.child(
Icon::new(IconName::Check)
.size(IconSize::Small)
.color(Color::Success),
)
.child(Label::new("Added").size(LabelSize::Small)),
)
}),
)
}
}

View File

@@ -81,77 +81,80 @@ impl FetchContextPickerDelegate {
url: String::new(),
}
}
}
async fn build_message(http_client: Arc<HttpClientWithUrl>, url: String) -> Result<String> {
let url = if !url.starts_with("https://") && !url.starts_with("http://") {
format!("https://{url}")
} else {
url
};
pub(crate) async fn fetch_url_content(
http_client: Arc<HttpClientWithUrl>,
url: String,
) -> Result<String> {
let url = if !url.starts_with("https://") && !url.starts_with("http://") {
format!("https://{url}")
} else {
url
};
let mut response = http_client.get(&url, AsyncBody::default(), true).await?;
let mut response = http_client.get(&url, AsyncBody::default(), true).await?;
let mut body = Vec::new();
response
.body_mut()
.read_to_end(&mut body)
.await
.context("error reading response body")?;
let mut body = Vec::new();
response
.body_mut()
.read_to_end(&mut body)
.await
.context("error reading response body")?;
if response.status().is_client_error() {
let text = String::from_utf8_lossy(body.as_slice());
bail!(
"status error {}, response: {text:?}",
response.status().as_u16()
);
if response.status().is_client_error() {
let text = String::from_utf8_lossy(body.as_slice());
bail!(
"status error {}, response: {text:?}",
response.status().as_u16()
);
}
let Some(content_type) = response.headers().get("content-type") else {
bail!("missing Content-Type header");
};
let content_type = content_type
.to_str()
.context("invalid Content-Type header")?;
let content_type = match content_type {
"text/html" => ContentType::Html,
"text/plain" => ContentType::Plaintext,
"application/json" => ContentType::Json,
_ => ContentType::Html,
};
match content_type {
ContentType::Html => {
let mut handlers: Vec<TagHandler> = vec![
Rc::new(RefCell::new(markdown::WebpageChromeRemover)),
Rc::new(RefCell::new(markdown::ParagraphHandler)),
Rc::new(RefCell::new(markdown::HeadingHandler)),
Rc::new(RefCell::new(markdown::ListHandler)),
Rc::new(RefCell::new(markdown::TableHandler::new())),
Rc::new(RefCell::new(markdown::StyledTextHandler)),
];
if url.contains("wikipedia.org") {
use html_to_markdown::structure::wikipedia;
handlers.push(Rc::new(RefCell::new(wikipedia::WikipediaChromeRemover)));
handlers.push(Rc::new(RefCell::new(wikipedia::WikipediaInfoboxHandler)));
handlers.push(Rc::new(
RefCell::new(wikipedia::WikipediaCodeHandler::new()),
));
} else {
handlers.push(Rc::new(RefCell::new(markdown::CodeHandler)));
}
convert_html_to_markdown(&body[..], &mut handlers)
}
ContentType::Plaintext => Ok(std::str::from_utf8(&body)?.to_owned()),
ContentType::Json => {
let json: serde_json::Value = serde_json::from_slice(&body)?;
let Some(content_type) = response.headers().get("content-type") else {
bail!("missing Content-Type header");
};
let content_type = content_type
.to_str()
.context("invalid Content-Type header")?;
let content_type = match content_type {
"text/html" => ContentType::Html,
"text/plain" => ContentType::Plaintext,
"application/json" => ContentType::Json,
_ => ContentType::Html,
};
match content_type {
ContentType::Html => {
let mut handlers: Vec<TagHandler> = vec![
Rc::new(RefCell::new(markdown::WebpageChromeRemover)),
Rc::new(RefCell::new(markdown::ParagraphHandler)),
Rc::new(RefCell::new(markdown::HeadingHandler)),
Rc::new(RefCell::new(markdown::ListHandler)),
Rc::new(RefCell::new(markdown::TableHandler::new())),
Rc::new(RefCell::new(markdown::StyledTextHandler)),
];
if url.contains("wikipedia.org") {
use html_to_markdown::structure::wikipedia;
handlers.push(Rc::new(RefCell::new(wikipedia::WikipediaChromeRemover)));
handlers.push(Rc::new(RefCell::new(wikipedia::WikipediaInfoboxHandler)));
handlers.push(Rc::new(
RefCell::new(wikipedia::WikipediaCodeHandler::new()),
));
} else {
handlers.push(Rc::new(RefCell::new(markdown::CodeHandler)));
}
convert_html_to_markdown(&body[..], &mut handlers)
}
ContentType::Plaintext => Ok(std::str::from_utf8(&body)?.to_owned()),
ContentType::Json => {
let json: serde_json::Value = serde_json::from_slice(&body)?;
Ok(format!(
"```json\n{}\n```",
serde_json::to_string_pretty(&json)?
))
}
Ok(format!(
"```json\n{}\n```",
serde_json::to_string_pretty(&json)?
))
}
}
}
@@ -167,8 +170,8 @@ impl PickerDelegate for FetchContextPickerDelegate {
}
}
fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> SharedString {
"Enter the URL that you would like to fetch".into()
fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> Option<SharedString> {
Some("Enter the URL that you would like to fetch".into())
}
fn selected_index(&self) -> usize {
@@ -206,12 +209,12 @@ impl PickerDelegate for FetchContextPickerDelegate {
let http_client = workspace.read(cx).client().http_client().clone();
let url = self.url.clone();
let confirm_behavior = self.confirm_behavior;
cx.spawn_in(window, |this, mut cx| async move {
cx.spawn_in(window, async move |this, cx| {
let text = cx
.background_spawn(Self::build_message(http_client, url.clone()))
.background_spawn(fetch_url_content(http_client, url.clone()))
.await?;
this.update_in(&mut cx, |this, window, cx| {
this.update_in(cx, |this, window, cx| {
this.delegate
.context_store
.update(cx, |context_store, _cx| {

View File

@@ -1,25 +1,15 @@
use std::collections::BTreeSet;
use std::ops::Range;
use std::path::Path;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use editor::actions::FoldAt;
use editor::display_map::{Crease, FoldId};
use editor::scroll::Autoscroll;
use editor::{Anchor, AnchorRangeExt, Editor, FoldPlaceholder, ToPoint};
use file_icons::FileIcons;
use fuzzy::PathMatch;
use gpui::{
AnyElement, App, AppContext, DismissEvent, Empty, Entity, FocusHandle, Focusable, Stateful,
Task, WeakEntity,
App, AppContext, DismissEvent, Entity, FocusHandle, Focusable, Stateful, Task, WeakEntity,
};
use multi_buffer::{MultiBufferPoint, MultiBufferRow};
use picker::{Picker, PickerDelegate};
use project::{PathMatchCandidateSet, ProjectPath, WorktreeId};
use rope::Point;
use text::SelectionGoal;
use ui::{prelude::*, ButtonLike, Disclosure, ListItem, TintColor, Tooltip};
use ui::{prelude::*, ListItem, Tooltip};
use util::ResultExt as _;
use workspace::{notifications::NotifyResultExt, Workspace};
@@ -34,7 +24,6 @@ impl FileContextPicker {
pub fn new(
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
editor: WeakEntity<Editor>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
window: &mut Window,
@@ -43,7 +32,6 @@ impl FileContextPicker {
let delegate = FileContextPickerDelegate::new(
context_picker,
workspace,
editor,
context_store,
confirm_behavior,
);
@@ -68,7 +56,6 @@ impl Render for FileContextPicker {
pub struct FileContextPickerDelegate {
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
editor: WeakEntity<Editor>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
matches: Vec<PathMatch>,
@@ -79,96 +66,18 @@ impl FileContextPickerDelegate {
pub fn new(
context_picker: WeakEntity<ContextPicker>,
workspace: WeakEntity<Workspace>,
editor: WeakEntity<Editor>,
context_store: WeakEntity<ContextStore>,
confirm_behavior: ConfirmBehavior,
) -> Self {
Self {
context_picker,
workspace,
editor,
context_store,
confirm_behavior,
matches: Vec::new(),
selected_index: 0,
}
}
fn search(
&mut self,
query: String,
cancellation_flag: Arc<AtomicBool>,
workspace: &Entity<Workspace>,
cx: &mut Context<Picker<Self>>,
) -> Task<Vec<PathMatch>> {
if query.is_empty() {
let workspace = workspace.read(cx);
let project = workspace.project().read(cx);
let recent_matches = workspace
.recent_navigation_history(Some(10), cx)
.into_iter()
.filter_map(|(project_path, _)| {
let worktree = project.worktree_for_id(project_path.worktree_id, cx)?;
Some(PathMatch {
score: 0.,
positions: Vec::new(),
worktree_id: project_path.worktree_id.to_usize(),
path: project_path.path,
path_prefix: worktree.read(cx).root_name().into(),
distance_to_relative_ancestor: 0,
is_dir: false,
})
});
let file_matches = project.worktrees(cx).flat_map(|worktree| {
let worktree = worktree.read(cx);
let path_prefix: Arc<str> = worktree.root_name().into();
worktree.files(false, 0).map(move |entry| PathMatch {
score: 0.,
positions: Vec::new(),
worktree_id: worktree.id().to_usize(),
path: entry.path.clone(),
path_prefix: path_prefix.clone(),
distance_to_relative_ancestor: 0,
is_dir: false,
})
});
Task::ready(recent_matches.chain(file_matches).collect())
} else {
let worktrees = workspace.read(cx).visible_worktrees(cx).collect::<Vec<_>>();
let candidate_sets = worktrees
.into_iter()
.map(|worktree| {
let worktree = worktree.read(cx);
PathMatchCandidateSet {
snapshot: worktree.snapshot(),
include_ignored: worktree
.root_entry()
.map_or(false, |entry| entry.is_ignored),
include_root_name: true,
candidates: project::Candidates::Files,
}
})
.collect::<Vec<_>>();
let executor = cx.background_executor().clone();
cx.foreground_executor().spawn(async move {
fuzzy::match_path_sets(
candidate_sets.as_slice(),
query.as_str(),
None,
false,
100,
&cancellation_flag,
executor,
)
.await
})
}
}
}
impl PickerDelegate for FileContextPickerDelegate {
@@ -192,7 +101,7 @@ impl PickerDelegate for FileContextPickerDelegate {
}
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
"Search files…".into()
"Search files & directories".into()
}
fn update_matches(
@@ -205,13 +114,13 @@ impl PickerDelegate for FileContextPickerDelegate {
return Task::ready(());
};
let search_task = self.search(query, Arc::<AtomicBool>::default(), &workspace, cx);
let search_task = search_paths(query, Arc::<AtomicBool>::default(), &workspace, cx);
cx.spawn_in(window, |this, mut cx| async move {
cx.spawn_in(window, async move |this, cx| {
// TODO: This should be probably be run in the background.
let paths = search_task.await;
this.update(&mut cx, |this, _cx| {
this.update(cx, |this, _cx| {
this.delegate.matches = paths;
})
.log_err();
@@ -223,114 +132,21 @@ impl PickerDelegate for FileContextPickerDelegate {
return;
};
let Some(file_name) = mat
.path
.file_name()
.map(|os_str| os_str.to_string_lossy().into_owned())
else {
return;
};
let full_path = mat.path.display().to_string();
let project_path = ProjectPath {
worktree_id: WorktreeId::from_usize(mat.worktree_id),
path: mat.path.clone(),
};
let Some(editor_entity) = self.editor.upgrade() else {
return;
};
editor_entity.update(cx, |editor, cx| {
editor.transact(window, cx, |editor, window, cx| {
// Move empty selections left by 1 column to select the `@`s, so they get overwritten when we insert.
{
let mut selections = editor.selections.all::<MultiBufferPoint>(cx);
for selection in selections.iter_mut() {
if selection.is_empty() {
let old_head = selection.head();
let new_head = MultiBufferPoint::new(
old_head.row,
old_head.column.saturating_sub(1),
);
selection.set_head(new_head, SelectionGoal::None);
}
}
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.select(selections)
});
}
let start_anchors = {
let snapshot = editor.buffer().read(cx).snapshot(cx);
editor
.selections
.all::<Point>(cx)
.into_iter()
.map(|selection| snapshot.anchor_before(selection.start))
.collect::<Vec<_>>()
};
editor.insert(&full_path, window, cx);
let end_anchors = {
let snapshot = editor.buffer().read(cx).snapshot(cx);
editor
.selections
.all::<Point>(cx)
.into_iter()
.map(|selection| snapshot.anchor_after(selection.end))
.collect::<Vec<_>>()
};
editor.insert("\n", window, cx); // Needed to end the fold
let file_icon = FileIcons::get_icon(&Path::new(&full_path), cx)
.unwrap_or_else(|| SharedString::new(""));
let placeholder = FoldPlaceholder {
render: render_fold_icon_button(
file_icon,
file_name.into(),
editor_entity.downgrade(),
),
..Default::default()
};
let render_trailer =
move |_row, _unfold, _window: &mut Window, _cx: &mut App| Empty.into_any();
let buffer = editor.buffer().read(cx).snapshot(cx);
let mut rows_to_fold = BTreeSet::new();
let crease_iter = start_anchors
.into_iter()
.zip(end_anchors)
.map(|(start, end)| {
rows_to_fold.insert(MultiBufferRow(start.to_point(&buffer).row));
Crease::inline(
start..end,
placeholder.clone(),
fold_toggle("tool-use"),
render_trailer,
)
});
editor.insert_creases(crease_iter, cx);
for buffer_row in rows_to_fold {
editor.fold_at(&FoldAt { buffer_row }, window, cx);
}
});
});
let is_directory = mat.is_dir;
let Some(task) = self
.context_store
.update(cx, |context_store, cx| {
context_store.add_file_from_path(project_path, cx)
if is_directory {
context_store.add_directory(project_path, true, cx)
} else {
context_store.add_file_from_path(project_path, true, cx)
}
})
.ok()
else {
@@ -338,10 +154,10 @@ impl PickerDelegate for FileContextPickerDelegate {
};
let confirm_behavior = self.confirm_behavior;
cx.spawn_in(window, |this, mut cx| async move {
match task.await.notify_async_err(&mut cx) {
cx.spawn_in(window, async move |this, cx| {
match task.await.notify_async_err(cx) {
None => anyhow::Ok(()),
Some(()) => this.update_in(&mut cx, |this, window, cx| match confirm_behavior {
Some(()) => this.update_in(cx, |this, window, cx| match confirm_behavior {
ConfirmBehavior::KeepOpen => {}
ConfirmBehavior::Close => this.delegate.dismissed(window, cx),
}),
@@ -375,6 +191,7 @@ impl PickerDelegate for FileContextPickerDelegate {
ElementId::NamedInteger("file-ctx-picker".into(), ix),
&path_match.path,
&path_match.path_prefix,
path_match.is_dir,
self.context_store.clone(),
cx,
)),
@@ -382,15 +199,93 @@ impl PickerDelegate for FileContextPickerDelegate {
}
}
pub fn render_file_context_entry(
id: ElementId,
path: &Path,
path_prefix: &Arc<str>,
context_store: WeakEntity<ContextStore>,
pub(crate) fn search_paths(
query: String,
cancellation_flag: Arc<AtomicBool>,
workspace: &Entity<Workspace>,
cx: &App,
) -> Stateful<Div> {
let (file_name, directory) = if path == Path::new("") {
(SharedString::from(path_prefix.clone()), None)
) -> Task<Vec<PathMatch>> {
if query.is_empty() {
let workspace = workspace.read(cx);
let project = workspace.project().read(cx);
let recent_matches = workspace
.recent_navigation_history(Some(10), cx)
.into_iter()
.filter_map(|(project_path, _)| {
let worktree = project.worktree_for_id(project_path.worktree_id, cx)?;
Some(PathMatch {
score: 0.,
positions: Vec::new(),
worktree_id: project_path.worktree_id.to_usize(),
path: project_path.path,
path_prefix: worktree.read(cx).root_name().into(),
distance_to_relative_ancestor: 0,
is_dir: false,
})
});
let file_matches = project.worktrees(cx).flat_map(|worktree| {
let worktree = worktree.read(cx);
let path_prefix: Arc<str> = worktree.root_name().into();
worktree.entries(false, 0).map(move |entry| PathMatch {
score: 0.,
positions: Vec::new(),
worktree_id: worktree.id().to_usize(),
path: entry.path.clone(),
path_prefix: path_prefix.clone(),
distance_to_relative_ancestor: 0,
is_dir: entry.is_dir(),
})
});
Task::ready(recent_matches.chain(file_matches).collect())
} else {
let worktrees = workspace.read(cx).visible_worktrees(cx).collect::<Vec<_>>();
let candidate_sets = worktrees
.into_iter()
.map(|worktree| {
let worktree = worktree.read(cx);
PathMatchCandidateSet {
snapshot: worktree.snapshot(),
include_ignored: worktree
.root_entry()
.map_or(false, |entry| entry.is_ignored),
include_root_name: true,
candidates: project::Candidates::Entries,
}
})
.collect::<Vec<_>>();
let executor = cx.background_executor().clone();
cx.foreground_executor().spawn(async move {
fuzzy::match_path_sets(
candidate_sets.as_slice(),
query.as_str(),
None,
false,
100,
&cancellation_flag,
executor,
)
.await
})
}
}
pub fn extract_file_name_and_directory(
path: &Path,
path_prefix: &str,
) -> (SharedString, Option<SharedString>) {
if path == Path::new("") {
(
SharedString::from(
path_prefix
.trim_end_matches(std::path::MAIN_SEPARATOR)
.to_string(),
),
None,
)
} else {
let file_name = path
.file_name()
@@ -399,23 +294,49 @@ pub fn render_file_context_entry(
.to_string()
.into();
let mut directory = format!("{}/", path_prefix);
let mut directory = path_prefix
.trim_end_matches(std::path::MAIN_SEPARATOR)
.to_string();
if !directory.ends_with('/') {
directory.push('/');
}
if let Some(parent) = path.parent().filter(|parent| parent != &Path::new("")) {
directory.push_str(&parent.to_string_lossy());
directory.push('/');
}
(file_name, Some(directory))
};
(file_name, Some(directory.into()))
}
}
let added = context_store
.upgrade()
.and_then(|context_store| context_store.read(cx).will_include_file_path(path, cx));
pub fn render_file_context_entry(
id: ElementId,
path: &Path,
path_prefix: &Arc<str>,
is_directory: bool,
context_store: WeakEntity<ContextStore>,
cx: &App,
) -> Stateful<Div> {
let (file_name, directory) = extract_file_name_and_directory(path, path_prefix);
let file_icon = FileIcons::get_icon(&path, cx)
.map(Icon::from_path)
.unwrap_or_else(|| Icon::new(IconName::File));
let added = context_store.upgrade().and_then(|context_store| {
if is_directory {
context_store
.read(cx)
.includes_directory(path)
.map(FileInclusion::Direct)
} else {
context_store.read(cx).will_include_file_path(path, cx)
}
});
let file_icon = if is_directory {
FileIcons::get_folder_icon(false, cx)
} else {
FileIcons::get_icon(&path, cx)
}
.map(Icon::from_path)
.unwrap_or_else(|| Icon::new(IconName::File));
h_flex()
.id(id)
@@ -464,85 +385,3 @@ pub fn render_file_context_entry(
}
})
}
fn render_fold_icon_button(
icon: SharedString,
label: SharedString,
editor: WeakEntity<Editor>,
) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut App) -> AnyElement> {
Arc::new(move |fold_id, fold_range, cx| {
let is_in_text_selection = editor.upgrade().is_some_and(|editor| {
editor.update(cx, |editor, cx| {
let snapshot = editor
.buffer()
.update(cx, |multi_buffer, cx| multi_buffer.snapshot(cx));
let is_in_pending_selection = || {
editor
.selections
.pending
.as_ref()
.is_some_and(|pending_selection| {
pending_selection
.selection
.range()
.includes(&fold_range, &snapshot)
})
};
let mut is_in_complete_selection = || {
editor
.selections
.disjoint_in_range::<usize>(fold_range.clone(), cx)
.into_iter()
.any(|selection| {
// This is needed to cover a corner case, if we just check for an existing
// selection in the fold range, having a cursor at the start of the fold
// marks it as selected. Non-empty selections don't cause this.
let length = selection.end - selection.start;
length > 0
})
};
is_in_pending_selection() || is_in_complete_selection()
})
});
ButtonLike::new(fold_id)
.style(ButtonStyle::Filled)
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
.toggle_state(is_in_text_selection)
.child(
h_flex()
.gap_1()
.child(
Icon::from_path(icon.clone())
.size(IconSize::Small)
.color(Color::Muted),
)
.child(
Label::new(label.clone())
.size(LabelSize::Small)
.single_line(),
),
)
.into_any_element()
})
}
fn fold_toggle(
name: &'static str,
) -> impl Fn(
MultiBufferRow,
bool,
Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
&mut Window,
&mut App,
) -> AnyElement {
move |row, is_folded, fold, _window, _cx| {
Disclosure::new((name, row.0 as u64), !is_folded)
.toggle_state(is_folded)
.on_click(move |_e, window, cx| fold(!is_folded, window, cx))
.into_any_element()
}
}

View File

@@ -110,48 +110,14 @@ impl PickerDelegate for ThreadContextPickerDelegate {
window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Task<()> {
let Ok(threads) = self.thread_store.update(cx, |this, _cx| {
this.threads()
.into_iter()
.map(|thread| ThreadContextEntry {
id: thread.id,
summary: thread.summary,
})
.collect::<Vec<_>>()
}) else {
let Some(threads) = self.thread_store.upgrade() else {
return Task::ready(());
};
let executor = cx.background_executor().clone();
let search_task = cx.background_spawn(async move {
if query.is_empty() {
threads
} else {
let candidates = threads
.iter()
.enumerate()
.map(|(id, thread)| StringMatchCandidate::new(id, &thread.summary))
.collect::<Vec<_>>();
let matches = fuzzy::match_strings(
&candidates,
&query,
false,
100,
&Default::default(),
executor,
)
.await;
matches
.into_iter()
.map(|mat| threads[mat.candidate_id].clone())
.collect()
}
});
cx.spawn_in(window, |this, mut cx| async move {
let search_task = search_threads(query, threads, cx);
cx.spawn_in(window, async move |this, cx| {
let matches = search_task.await;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
this.delegate.matches = matches;
this.delegate.selected_index = 0;
cx.notify();
@@ -171,12 +137,14 @@ impl PickerDelegate for ThreadContextPickerDelegate {
let open_thread_task = thread_store.update(cx, |this, cx| this.open_thread(&entry.id, cx));
cx.spawn_in(window, |this, mut cx| async move {
cx.spawn_in(window, async move |this, cx| {
let thread = open_thread_task.await?;
this.update_in(&mut cx, |this, window, cx| {
this.update_in(cx, |this, window, cx| {
this.delegate
.context_store
.update(cx, |context_store, cx| context_store.add_thread(thread, cx))
.update(cx, |context_store, cx| {
context_store.add_thread(thread, true, cx)
})
.ok();
match this.delegate.confirm_behavior {
@@ -223,13 +191,18 @@ pub fn render_thread_context_entry(
h_flex()
.gap_1p5()
.w_full()
.justify_between()
.child(
Icon::new(IconName::MessageCircle)
.size(IconSize::XSmall)
.color(Color::Muted),
h_flex()
.gap_1p5()
.max_w_72()
.child(
Icon::new(IconName::MessageCircle)
.size(IconSize::XSmall)
.color(Color::Muted),
)
.child(Label::new(thread.summary.clone()).truncate()),
)
.child(Label::new(thread.summary.clone()))
.child(div().w_full())
.when(added, |el| {
el.child(
h_flex()
@@ -243,3 +216,46 @@ pub fn render_thread_context_entry(
)
})
}
pub(crate) fn search_threads(
query: String,
thread_store: Entity<ThreadStore>,
cx: &mut App,
) -> Task<Vec<ThreadContextEntry>> {
let threads = thread_store.update(cx, |this, _cx| {
this.threads()
.into_iter()
.map(|thread| ThreadContextEntry {
id: thread.id,
summary: thread.summary,
})
.collect::<Vec<_>>()
});
let executor = cx.background_executor().clone();
cx.background_spawn(async move {
if query.is_empty() {
threads
} else {
let candidates = threads
.iter()
.enumerate()
.map(|(id, thread)| StringMatchCandidate::new(id, &thread.summary))
.collect::<Vec<_>>();
let matches = fuzzy::match_strings(
&candidates,
&query,
false,
100,
&Default::default(),
executor,
)
.await;
matches
.into_iter()
.map(|mat| threads[mat.candidate_id].clone())
.collect()
}
})
}

View File

@@ -9,6 +9,7 @@ use language::Buffer;
use project::{ProjectPath, Worktree};
use rope::Rope;
use text::BufferId;
use util::maybe;
use workspace::Workspace;
use crate::context::{
@@ -63,6 +64,7 @@ impl ContextStore {
pub fn add_file_from_path(
&mut self,
project_path: ProjectPath,
remove_if_exists: bool,
cx: &mut Context<Self>,
) -> Task<Result<()>> {
let workspace = self.workspace.clone();
@@ -74,18 +76,20 @@ impl ContextStore {
return Task::ready(Err(anyhow!("failed to read project")));
};
cx.spawn(|this, mut cx| async move {
let open_buffer_task = project.update(&mut cx, |project, cx| {
cx.spawn(async move |this, cx| {
let open_buffer_task = project.update(cx, |project, cx| {
project.open_buffer(project_path.clone(), cx)
})?;
let buffer_entity = open_buffer_task.await?;
let buffer_id = this.update(&mut cx, |_, cx| buffer_entity.read(cx).remote_id())?;
let buffer_id = this.update(cx, |_, cx| buffer_entity.read(cx).remote_id())?;
let already_included = this.update(&mut cx, |this, _cx| {
let already_included = this.update(cx, |this, _cx| {
match this.will_include_buffer(buffer_id, &project_path.path) {
Some(FileInclusion::Direct(context_id)) => {
this.remove_context(context_id);
if remove_if_exists {
this.remove_context(context_id);
}
true
}
Some(FileInclusion::InDirectory(_)) => true,
@@ -97,7 +101,7 @@ impl ContextStore {
return anyhow::Ok(());
}
let (buffer_info, text_task) = this.update(&mut cx, |_, cx| {
let (buffer_info, text_task) = this.update(cx, |_, cx| {
let buffer = buffer_entity.read(cx);
collect_buffer_info_and_text(
project_path.path.clone(),
@@ -109,7 +113,7 @@ impl ContextStore {
let text = text_task.await;
this.update(&mut cx, |this, _cx| {
this.update(cx, |this, _cx| {
this.insert_file(make_context_buffer(buffer_info, text));
})?;
@@ -122,8 +126,8 @@ impl ContextStore {
buffer_entity: Entity<Buffer>,
cx: &mut Context<Self>,
) -> Task<Result<()>> {
cx.spawn(|this, mut cx| async move {
let (buffer_info, text_task) = this.update(&mut cx, |_, cx| {
cx.spawn(async move |this, cx| {
let (buffer_info, text_task) = this.update(cx, |_, cx| {
let buffer = buffer_entity.read(cx);
let Some(file) = buffer.file() else {
return Err(anyhow!("Buffer has no path."));
@@ -138,7 +142,7 @@ impl ContextStore {
let text = text_task.await;
this.update(&mut cx, |this, _cx| {
this.update(cx, |this, _cx| {
this.insert_file(make_context_buffer(buffer_info, text))
})?;
@@ -156,6 +160,7 @@ impl ContextStore {
pub fn add_directory(
&mut self,
project_path: ProjectPath,
remove_if_exists: bool,
cx: &mut Context<Self>,
) -> Task<Result<()>> {
let workspace = self.workspace.clone();
@@ -168,7 +173,9 @@ impl ContextStore {
let already_included = if let Some(context_id) = self.includes_directory(&project_path.path)
{
self.remove_context(context_id);
if remove_if_exists {
self.remove_context(context_id);
}
true
} else {
false
@@ -178,18 +185,18 @@ impl ContextStore {
}
let worktree_id = project_path.worktree_id;
cx.spawn(|this, mut cx| async move {
let worktree = project.update(&mut cx, |project, cx| {
cx.spawn(async move |this, cx| {
let worktree = project.update(cx, |project, cx| {
project
.worktree_for_id(worktree_id, cx)
.ok_or_else(|| anyhow!("no worktree found for {worktree_id:?}"))
})??;
let files = worktree.update(&mut cx, |worktree, _cx| {
let files = worktree.update(cx, |worktree, _cx| {
collect_files_in_path(worktree, &project_path.path)
})?;
let open_buffers_task = project.update(&mut cx, |project, cx| {
let open_buffers_task = project.update(cx, |project, cx| {
let tasks = files.iter().map(|file_path| {
project.open_buffer(
ProjectPath {
@@ -206,7 +213,7 @@ impl ContextStore {
let mut buffer_infos = Vec::new();
let mut text_tasks = Vec::new();
this.update(&mut cx, |_, cx| {
this.update(cx, |_, cx| {
for (path, buffer_entity) in files.into_iter().zip(buffers) {
// Skip all binary files and other non-UTF8 files
if let Ok(buffer_entity) = buffer_entity {
@@ -235,7 +242,7 @@ impl ContextStore {
bail!("No text files found in {}", &project_path.path.display());
}
this.update(&mut cx, |this, _| {
this.update(cx, |this, _| {
this.insert_directory(&project_path.path, context_buffers);
})?;
@@ -255,9 +262,16 @@ impl ContextStore {
)));
}
pub fn add_thread(&mut self, thread: Entity<Thread>, cx: &mut Context<Self>) {
pub fn add_thread(
&mut self,
thread: Entity<Thread>,
remove_if_exists: bool,
cx: &mut Context<Self>,
) {
if let Some(context_id) = self.includes_thread(&thread.read(cx).id()) {
self.remove_context(context_id);
if remove_if_exists {
self.remove_context(context_id);
}
} else {
self.insert_thread(thread, cx);
}
@@ -531,35 +545,59 @@ fn collect_files_in_path(worktree: &Worktree, path: &Path) -> Vec<Arc<Path>> {
pub fn refresh_context_store_text(
context_store: Entity<ContextStore>,
changed_buffers: &HashSet<Entity<Buffer>>,
cx: &App,
) -> impl Future<Output = ()> {
) -> impl Future<Output = Vec<ContextId>> {
let mut tasks = Vec::new();
for context in &context_store.read(cx).context {
match context {
AssistantContext::File(file_context) => {
let context_store = context_store.clone();
if let Some(task) = refresh_file_text(context_store, file_context, cx) {
tasks.push(task);
let id = context.id();
let task = maybe!({
match context {
AssistantContext::File(file_context) => {
if changed_buffers.is_empty()
|| changed_buffers.contains(&file_context.context_buffer.buffer)
{
let context_store = context_store.clone();
return refresh_file_text(context_store, file_context, cx);
}
}
}
AssistantContext::Directory(directory_context) => {
let context_store = context_store.clone();
if let Some(task) = refresh_directory_text(context_store, directory_context, cx) {
tasks.push(task);
AssistantContext::Directory(directory_context) => {
let should_refresh = changed_buffers.is_empty()
|| changed_buffers.iter().any(|buffer| {
let buffer = buffer.read(cx);
buffer_path_log_err(&buffer)
.map_or(false, |path| path.starts_with(&directory_context.path))
});
if should_refresh {
let context_store = context_store.clone();
return refresh_directory_text(context_store, directory_context, cx);
}
}
AssistantContext::Thread(thread_context) => {
if changed_buffers.is_empty() {
let context_store = context_store.clone();
return Some(refresh_thread_text(context_store, thread_context, cx));
}
}
// Intentionally omit refreshing fetched URLs as it doesn't seem all that useful,
// and doing the caching properly could be tricky (unless it's already handled by
// the HttpClient?).
AssistantContext::FetchedUrl(_) => {}
}
AssistantContext::Thread(thread_context) => {
let context_store = context_store.clone();
tasks.push(refresh_thread_text(context_store, thread_context, cx));
}
// Intentionally omit refreshing fetched URLs as it doesn't seem all that useful,
// and doing the caching properly could be tricky (unless it's already handled by
// the HttpClient?).
AssistantContext::FetchedUrl(_) => {}
None
});
if let Some(task) = task {
tasks.push(task.map(move |_| id));
}
}
future::join_all(tasks).map(|_| ())
future::join_all(tasks)
}
fn refresh_file_text(
@@ -570,10 +608,10 @@ fn refresh_file_text(
let id = file_context.id;
let task = refresh_context_buffer(&file_context.context_buffer, cx);
if let Some(task) = task {
Some(cx.spawn(|mut cx| async move {
Some(cx.spawn(async move |cx| {
let context_buffer = task.await;
context_store
.update(&mut cx, |context_store, _| {
.update(cx, |context_store, _| {
let new_file_context = FileContext { id, context_buffer };
context_store.replace_context(AssistantContext::File(new_file_context));
})
@@ -611,10 +649,10 @@ fn refresh_directory_text(
let id = directory_context.snapshot.id;
let path = directory_context.path.clone();
Some(cx.spawn(|mut cx| async move {
Some(cx.spawn(async move |cx| {
let context_buffers = context_buffers.await;
context_store
.update(&mut cx, |context_store, _| {
.update(cx, |context_store, _| {
let new_directory_context = DirectoryContext::new(id, &path, context_buffers);
context_store.replace_context(AssistantContext::Directory(new_directory_context));
})
@@ -629,9 +667,9 @@ fn refresh_thread_text(
) -> Task<()> {
let id = thread_context.id;
let thread = thread_context.thread.clone();
cx.spawn(move |mut cx| async move {
cx.spawn(async move |cx| {
context_store
.update(&mut cx, |context_store, cx| {
.update(cx, |context_store, cx| {
let text = thread.read(cx).text().into();
context_store.replace_context(AssistantContext::Thread(ThreadContext {
id,

View File

@@ -25,7 +25,7 @@ use crate::{
pub struct ContextStrip {
context_store: Entity<ContextStore>,
pub context_picker: Entity<ContextPicker>,
context_picker: Entity<ContextPicker>,
context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
focus_handle: FocusHandle,
suggest_context_kind: SuggestContextKind,
@@ -36,11 +36,9 @@ pub struct ContextStrip {
}
impl ContextStrip {
#[allow(clippy::too_many_arguments)]
pub fn new(
context_store: Entity<ContextStore>,
workspace: WeakEntity<Workspace>,
editor: WeakEntity<Editor>,
thread_store: Option<WeakEntity<ThreadStore>>,
context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
suggest_context_kind: SuggestContextKind,
@@ -52,7 +50,6 @@ impl ContextStrip {
workspace.clone(),
thread_store.clone(),
context_store.downgrade(),
editor.clone(),
ConfirmBehavior::KeepOpen,
window,
cx,
@@ -336,12 +333,12 @@ impl ContextStrip {
context_store.accept_suggested_context(&suggested, cx)
});
cx.spawn_in(window, |this, mut cx| async move {
match task.await.notify_async_err(&mut cx) {
cx.spawn_in(window, async move |this, cx| {
match task.await.notify_async_err(cx) {
None => {}
Some(()) => {
if let Some(this) = this.upgrade() {
this.update(&mut cx, |_, cx| cx.notify())?;
this.update(cx, |_, cx| cx.notify())?;
}
}
}

View File

@@ -2,10 +2,10 @@ use assistant_context_editor::SavedContextMetadata;
use chrono::{DateTime, Utc};
use gpui::{prelude::*, Entity};
use crate::thread_store::{SavedThreadMetadata, ThreadStore};
use crate::thread_store::{SerializedThreadMetadata, ThreadStore};
pub enum HistoryEntry {
Thread(SavedThreadMetadata),
Thread(SerializedThreadMetadata),
Context(SavedContextMetadata),
}

View File

@@ -276,7 +276,7 @@ impl InlineAssistant {
if is_authenticated() {
handle_assist(window, cx);
} else {
cx.spawn_in(window, |_workspace, mut cx| async move {
cx.spawn_in(window, async move |_workspace, cx| {
let Some(task) = cx.update(|_, cx| {
LanguageModelRegistry::read_global(cx)
.active_provider()
@@ -324,7 +324,7 @@ impl InlineAssistant {
) {
let (snapshot, initial_selections) = editor.update(cx, |editor, cx| {
(
editor.buffer().read(cx).snapshot(cx),
editor.snapshot(window, cx),
editor.selections.all::<Point>(cx),
)
});
@@ -338,7 +338,37 @@ impl InlineAssistant {
if selection.end.column == 0 {
selection.end.row -= 1;
}
selection.end.column = snapshot.line_len(MultiBufferRow(selection.end.row));
selection.end.column = snapshot
.buffer_snapshot
.line_len(MultiBufferRow(selection.end.row));
} else if let Some(fold) =
snapshot.crease_for_buffer_row(MultiBufferRow(selection.end.row))
{
selection.start = fold.range().start;
selection.end = fold.range().end;
if MultiBufferRow(selection.end.row) < snapshot.buffer_snapshot.max_row() {
let chars = snapshot
.buffer_snapshot
.chars_at(Point::new(selection.end.row + 1, 0));
for c in chars {
if c == '\n' {
break;
}
if c.is_whitespace() {
continue;
}
if snapshot
.language_at(selection.end)
.is_some_and(|language| language.config().brackets.is_closing_brace(c))
{
selection.end.row += 1;
selection.end.column = snapshot
.buffer_snapshot
.line_len(MultiBufferRow(selection.end.row));
}
}
}
}
if let Some(prev_selection) = selections.last_mut() {
@@ -354,6 +384,7 @@ impl InlineAssistant {
}
selections.push(selection);
}
let snapshot = &snapshot.buffer_snapshot;
let newest_selection = newest_selection.unwrap();
let mut codegen_ranges = Vec::new();
@@ -480,7 +511,6 @@ impl InlineAssistant {
}
}
#[allow(clippy::too_many_arguments)]
pub fn suggest_assist(
&mut self,
editor: &Entity<Editor>,
@@ -1342,7 +1372,7 @@ impl InlineAssistant {
});
enum DeletedLines {}
let mut editor = Editor::for_multibuffer(multi_buffer, None, true, window, cx);
let mut editor = Editor::for_multibuffer(multi_buffer, None, window, cx);
editor.set_soft_wrap_mode(language::language_settings::SoftWrap::None, cx);
editor.set_show_wrap_guides(false, cx);
editor.set_show_gutter(false, cx);
@@ -1451,16 +1481,15 @@ struct InlineAssistScrollLock {
}
impl EditorInlineAssists {
#[allow(clippy::too_many_arguments)]
fn new(editor: &Entity<Editor>, window: &mut Window, cx: &mut App) -> Self {
let (highlight_updates_tx, mut highlight_updates_rx) = async_watch::channel(());
Self {
assist_ids: Vec::new(),
scroll_lock: None,
highlight_updates: highlight_updates_tx,
_update_highlights: cx.spawn(|cx| {
_update_highlights: cx.spawn({
let editor = editor.downgrade();
async move {
async move |cx| {
while let Ok(()) = highlight_updates_rx.changed().await {
let editor = editor.upgrade().context("editor was dropped")?;
cx.update_global(|assistant: &mut InlineAssistant, cx| {
@@ -1563,7 +1592,6 @@ pub struct InlineAssist {
}
impl InlineAssist {
#[allow(clippy::too_many_arguments)]
fn new(
assist_id: InlineAssistId,
group_id: InlineAssistGroupId,
@@ -1732,6 +1760,7 @@ impl CodeActionProvider for AssistantCodeActionProvider {
title: "Fix with Assistant".into(),
..Default::default()
})),
resolved: true,
}]))
} else {
Task::ready(Ok(Vec::new()))
@@ -1750,10 +1779,10 @@ impl CodeActionProvider for AssistantCodeActionProvider {
let editor = self.editor.clone();
let workspace = self.workspace.clone();
let thread_store = self.thread_store.clone();
window.spawn(cx, |mut cx| async move {
window.spawn(cx, async move |cx| {
let editor = editor.upgrade().context("editor was released")?;
let range = editor
.update(&mut cx, |editor, cx| {
.update(cx, |editor, cx| {
editor.buffer().update(cx, |multibuffer, cx| {
let buffer = buffer.read(cx);
let multibuffer_snapshot = multibuffer.read(cx);

View File

@@ -816,7 +816,6 @@ impl InlineAssistId {
}
impl PromptEditor<BufferCodegen> {
#[allow(clippy::too_many_arguments)]
pub fn new_buffer(
id: InlineAssistId,
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
@@ -844,7 +843,6 @@ impl PromptEditor<BufferCodegen> {
},
prompt_buffer,
None,
false,
window,
cx,
);
@@ -863,7 +861,6 @@ impl PromptEditor<BufferCodegen> {
ContextStrip::new(
context_store.clone(),
workspace.clone(),
prompt_editor.downgrade(),
thread_store.clone(),
context_picker_menu_handle.clone(),
SuggestContextKind::Thread,
@@ -976,7 +973,6 @@ impl TerminalInlineAssistId {
}
impl PromptEditor<TerminalCodegen> {
#[allow(clippy::too_many_arguments)]
pub fn new_terminal(
id: TerminalInlineAssistId,
prompt_history: VecDeque<String>,
@@ -1003,7 +999,6 @@ impl PromptEditor<TerminalCodegen> {
},
prompt_buffer,
None,
false,
window,
cx,
);
@@ -1018,7 +1013,6 @@ impl PromptEditor<TerminalCodegen> {
ContextStrip::new(
context_store.clone(),
workspace.clone(),
prompt_editor.downgrade(),
thread_store.clone(),
context_picker_menu_handle.clone(),
SuggestContextKind::Thread,

View File

@@ -1,44 +1,49 @@
use std::sync::Arc;
use collections::HashSet;
use editor::actions::MoveUp;
use editor::{Editor, EditorElement, EditorEvent, EditorStyle};
use editor::{ContextMenuOptions, ContextMenuPlacement, Editor, EditorElement, EditorStyle};
use fs::Fs;
use git::ExpandCommitEditor;
use git_ui::git_panel;
use gpui::{
Animation, AnimationExt, App, DismissEvent, Entity, Focusable, Subscription, TextStyle,
point, Animation, AnimationExt, App, DismissEvent, Entity, Focusable, Subscription, TextStyle,
WeakEntity,
};
use language_model::LanguageModelRegistry;
use language_model_selector::ToggleModelSelector;
use rope::Point;
use project::Project;
use settings::Settings;
use std::time::Duration;
use text::Bias;
use theme::ThemeSettings;
use ui::{
prelude::*, ButtonLike, KeyBinding, PlatformStyle, PopoverMenu, PopoverMenuHandle, Switch,
Tooltip,
prelude::*, ButtonLike, KeyBinding, PlatformStyle, PopoverMenu, PopoverMenuHandle, Tooltip,
};
use vim_mode_setting::VimModeSetting;
use workspace::Workspace;
use crate::assistant_model_selector::AssistantModelSelector;
use crate::context_picker::{ConfirmBehavior, ContextPicker};
use crate::context_picker::{ConfirmBehavior, ContextPicker, ContextPickerCompletionProvider};
use crate::context_store::{refresh_context_store_text, ContextStore};
use crate::context_strip::{ContextStrip, ContextStripEvent, SuggestContextKind};
use crate::profile_selector::ProfileSelector;
use crate::thread::{RequestKind, Thread};
use crate::thread_store::ThreadStore;
use crate::{Chat, ChatMode, RemoveAllContext, ToggleContextPicker};
use crate::{Chat, ChatMode, RemoveAllContext, ThreadEvent, ToggleContextPicker};
pub struct MessageEditor {
thread: Entity<Thread>,
editor: Entity<Editor>,
#[allow(dead_code)]
workspace: WeakEntity<Workspace>,
project: Entity<Project>,
context_store: Entity<ContextStore>,
context_strip: Entity<ContextStrip>,
context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
inline_context_picker: Entity<ContextPicker>,
inline_context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
model_selector: Entity<AssistantModelSelector>,
use_tools: bool,
profile_selector: Entity<ProfileSelector>,
_subscriptions: Vec<Subscription>,
}
@@ -46,12 +51,12 @@ impl MessageEditor {
pub fn new(
fs: Arc<dyn Fs>,
workspace: WeakEntity<Workspace>,
context_store: Entity<ContextStore>,
thread_store: WeakEntity<ThreadStore>,
thread: Entity<Thread>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
let context_picker_menu_handle = PopoverMenuHandle::default();
let inline_context_picker_menu_handle = PopoverMenuHandle::default();
let model_selector_menu_handle = PopoverMenuHandle::default();
@@ -60,16 +65,30 @@ impl MessageEditor {
let mut editor = Editor::auto_height(10, window, cx);
editor.set_placeholder_text("Ask anything, @ to mention, ↑ to select", cx);
editor.set_show_indent_guides(false, cx);
editor.set_context_menu_options(ContextMenuOptions {
min_entries_visible: 12,
max_entries_visible: 12,
placement: Some(ContextMenuPlacement::Above),
});
editor
});
let editor_entity = editor.downgrade();
editor.update(cx, |editor, _| {
editor.set_completion_provider(Some(Box::new(ContextPickerCompletionProvider::new(
workspace.clone(),
context_store.downgrade(),
Some(thread_store.clone()),
editor_entity,
))));
});
let inline_context_picker = cx.new(|cx| {
ContextPicker::new(
workspace.clone(),
Some(thread_store.clone()),
context_store.downgrade(),
editor.downgrade(),
ConfirmBehavior::Close,
window,
cx,
@@ -80,7 +99,6 @@ impl MessageEditor {
ContextStrip::new(
context_store.clone(),
workspace.clone(),
editor.downgrade(),
Some(thread_store.clone()),
context_picker_menu_handle.clone(),
SuggestContextKind::File,
@@ -90,7 +108,6 @@ impl MessageEditor {
});
let subscriptions = vec![
cx.subscribe_in(&editor, window, Self::handle_editor_event),
cx.subscribe_in(
&inline_context_picker,
window,
@@ -100,8 +117,10 @@ impl MessageEditor {
];
Self {
thread,
editor: editor.clone(),
project: thread.read(cx).project().clone(),
thread,
workspace,
context_store,
context_strip,
context_picker_menu_handle,
@@ -109,20 +128,19 @@ impl MessageEditor {
inline_context_picker_menu_handle,
model_selector: cx.new(|cx| {
AssistantModelSelector::new(
fs,
fs.clone(),
model_selector_menu_handle,
editor.focus_handle(cx),
window,
cx,
)
}),
use_tools: false,
profile_selector: cx.new(|cx| ProfileSelector::new(fs, thread_store, cx)),
_subscriptions: subscriptions,
}
}
fn toggle_chat_mode(&mut self, _: &ChatMode, _window: &mut Window, cx: &mut Context<Self>) {
self.use_tools = !self.use_tools;
cx.notify();
}
@@ -134,7 +152,6 @@ impl MessageEditor {
) {
self.context_picker_menu_handle.toggle(window, cx);
}
pub fn remove_all_context(
&mut self,
_: &RemoveAllContext,
@@ -146,6 +163,14 @@ impl MessageEditor {
}
fn chat(&mut self, _: &Chat, window: &mut Window, cx: &mut Context<Self>) {
if self.is_editor_empty(cx) {
return;
}
if self.thread.read(cx).is_generating() {
return;
}
self.send_to_model(RequestKind::Chat, window, cx);
}
@@ -185,52 +210,37 @@ impl MessageEditor {
text
});
let refresh_task = refresh_context_store_text(self.context_store.clone(), cx);
let refresh_task =
refresh_context_store_text(self.context_store.clone(), &HashSet::default(), cx);
let system_prompt_context_task = self.thread.read(cx).load_system_prompt_context(cx);
let thread = self.thread.clone();
let context_store = self.context_store.clone();
let use_tools = self.use_tools;
cx.spawn(move |_, mut cx| async move {
let checkpoint = self.project.read(cx).git_store().read(cx).checkpoint(cx);
cx.spawn(async move |_, cx| {
let checkpoint = checkpoint.await.ok();
refresh_task.await;
let (system_prompt_context, load_error) = system_prompt_context_task.await;
thread
.update(&mut cx, |thread, cx| {
.update(cx, |thread, cx| {
thread.set_system_prompt_context(system_prompt_context);
if let Some(load_error) = load_error {
cx.emit(ThreadEvent::ShowError(load_error));
}
})
.ok();
thread
.update(cx, |thread, cx| {
let context = context_store.read(cx).snapshot(cx).collect::<Vec<_>>();
thread.insert_user_message(user_message, context, cx);
thread.send_to_model(model, request_kind, use_tools, cx);
thread.insert_user_message(user_message, context, checkpoint, cx);
thread.send_to_model(model, request_kind, cx);
})
.ok();
})
.detach();
}
fn handle_editor_event(
&mut self,
editor: &Entity<Editor>,
event: &EditorEvent,
window: &mut Window,
cx: &mut Context<Self>,
) {
match event {
EditorEvent::SelectionsChanged { .. } => {
editor.update(cx, |editor, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
let newest_cursor = editor.selections.newest::<Point>(cx).head();
if newest_cursor.column > 0 {
let behind_cursor = snapshot.clip_point(
Point::new(newest_cursor.row, newest_cursor.column - 1),
Bias::Left,
);
let char_behind_cursor = snapshot.chars_at(behind_cursor).next();
if char_behind_cursor == Some('@') {
self.inline_context_picker_menu_handle.show(window, cx);
}
}
});
}
_ => {}
}
}
fn handle_inline_context_picker_event(
&mut self,
_inline_context_picker: &Entity<ContextPicker>,
@@ -281,10 +291,12 @@ impl Render for MessageEditor {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let font_size = TextSize::Default.rems(cx);
let line_height = font_size.to_pixels(window.rem_size()) * 1.5;
let focus_handle = self.editor.focus_handle(cx);
let inline_context_picker = self.inline_context_picker.clone();
let bg_color = cx.theme().colors().editor_background;
let is_streaming_completion = self.thread.read(cx).is_streaming();
let empty_thread = self.thread.read(cx).is_empty();
let is_generating = self.thread.read(cx).is_generating();
let is_model_selected = self.is_model_selected(cx);
let is_editor_empty = self.is_editor_empty(cx);
let submit_label_color = if is_editor_empty {
@@ -303,9 +315,34 @@ impl Render for MessageEditor {
px(64.)
};
let project = self.thread.read(cx).project();
let changed_files = if let Some(repository) = project.read(cx).active_repository(cx) {
repository.read(cx).cached_status().count()
} else {
0
};
let border_color = cx.theme().colors().border;
let active_color = cx.theme().colors().element_selected;
let editor_bg_color = cx.theme().colors().editor_background;
let bg_edit_files_disclosure = editor_bg_color.blend(active_color.opacity(0.3));
let edit_files_container = || {
h_flex()
.mx_2()
.py_1()
.pl_2p5()
.pr_1()
.bg(bg_edit_files_disclosure)
.border_1()
.border_color(border_color)
.justify_between()
.flex_wrap()
};
v_flex()
.size_full()
.when(is_streaming_completion, |parent| {
.when(is_generating, |parent| {
let focus_handle = self.editor.focus_handle(cx).clone();
parent.child(
h_flex().py_3().w_full().justify_center().child(
@@ -314,7 +351,7 @@ impl Render for MessageEditor {
.pl_2()
.pr_1()
.py_1()
.bg(cx.theme().colors().editor_background)
.bg(editor_bg_color)
.border_1()
.border_color(cx.theme().colors().border_variant)
.rounded_lg()
@@ -363,6 +400,163 @@ impl Render for MessageEditor {
),
)
})
.when(
changed_files > 0 && !is_generating && !empty_thread,
|parent| {
parent.child(
edit_files_container()
.border_b_0()
.rounded_t_md()
.shadow(smallvec::smallvec![gpui::BoxShadow {
color: gpui::black().opacity(0.15),
offset: point(px(1.), px(-1.)),
blur_radius: px(3.),
spread_radius: px(0.),
}])
.child(
h_flex()
.gap_2()
.child(Label::new("Edits").size(LabelSize::XSmall))
.child(div().size_1().rounded_full().bg(border_color))
.child(
Label::new(format!(
"{} {}",
changed_files,
if changed_files == 1 { "file" } else { "files" }
))
.size(LabelSize::XSmall),
),
)
.child(
h_flex()
.gap_1()
.child(
Button::new("panel", "Open Git Panel")
.label_size(LabelSize::XSmall)
.key_binding({
let focus_handle = focus_handle.clone();
KeyBinding::for_action_in(
&git_panel::ToggleFocus,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(10.)))
})
.on_click(|_ev, _window, cx| {
cx.defer(|cx| {
cx.dispatch_action(&git_panel::ToggleFocus)
});
}),
)
.child(
Button::new("review", "Review Diff")
.label_size(LabelSize::XSmall)
.key_binding({
let focus_handle = focus_handle.clone();
KeyBinding::for_action_in(
&git_ui::project_diff::Diff,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(10.)))
})
.on_click(|_event, _window, cx| {
cx.defer(|cx| {
cx.dispatch_action(&git_ui::project_diff::Diff)
});
}),
)
.child(
Button::new("commit", "Commit Changes")
.label_size(LabelSize::XSmall)
.key_binding({
let focus_handle = focus_handle.clone();
KeyBinding::for_action_in(
&ExpandCommitEditor,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(10.)))
})
.on_click(|_event, _window, cx| {
cx.defer(|cx| {
cx.dispatch_action(&ExpandCommitEditor)
});
}),
),
),
)
},
)
.when(
changed_files > 0 && !is_generating && empty_thread,
|parent| {
parent.child(
edit_files_container()
.mb_2()
.rounded_md()
.child(
h_flex()
.gap_2()
.child(Label::new("Consider committing your changes before starting a fresh thread").size(LabelSize::XSmall))
.child(div().size_1().rounded_full().bg(border_color))
.child(
Label::new(format!(
"{} {}",
changed_files,
if changed_files == 1 { "file" } else { "files" }
))
.size(LabelSize::XSmall),
),
)
.child(
h_flex()
.gap_1()
.child(
Button::new("review", "Review Diff")
.label_size(LabelSize::XSmall)
.key_binding({
let focus_handle = focus_handle.clone();
KeyBinding::for_action_in(
&git_ui::project_diff::Diff,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(10.)))
})
.on_click(|_event, _window, cx| {
cx.defer(|cx| {
cx.dispatch_action(&git_ui::project_diff::Diff)
});
}),
)
.child(
Button::new("commit", "Commit Changes")
.label_size(LabelSize::XSmall)
.key_binding({
let focus_handle = focus_handle.clone();
KeyBinding::for_action_in(
&ExpandCommitEditor,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(10.)))
})
.on_click(|_event, _window, cx| {
cx.defer(|cx| {
cx.dispatch_action(&ExpandCommitEditor)
});
}),
),
),
)
},
)
.child(
v_flex()
.key_context("MessageEditor")
@@ -377,10 +571,10 @@ impl Render for MessageEditor {
.on_action(cx.listener(Self::toggle_chat_mode))
.gap_2()
.p_2()
.bg(bg_color)
.bg(editor_bg_color)
.border_t_1()
.border_color(cx.theme().colors().border)
.child(self.context_strip.clone())
.child(h_flex().justify_between().child(self.context_strip.clone()))
.child(
v_flex()
.gap_5()
@@ -400,9 +594,10 @@ impl Render for MessageEditor {
EditorElement::new(
&self.editor,
EditorStyle {
background: bg_color,
background: editor_bg_color,
local_player: cx.theme().players().local(),
text: text_style,
syntax: cx.theme().syntax().clone(),
..Default::default()
},
)
@@ -428,25 +623,7 @@ impl Render for MessageEditor {
.child(
h_flex()
.justify_between()
.child(
Switch::new("use-tools", self.use_tools.into())
.label("Tools")
.on_click(cx.listener(
|this, selection, _window, _cx| {
this.use_tools = match selection {
ToggleState::Selected => true,
ToggleState::Unselected
| ToggleState::Indeterminate => false,
};
},
))
.key_binding(KeyBinding::for_action_in(
&ChatMode,
&focus_handle,
window,
cx,
)),
)
.child(h_flex().gap_2().child(self.profile_selector.clone()))
.child(
h_flex().gap_1().child(self.model_selector.clone()).child(
ButtonLike::new("submit-message")
@@ -455,7 +632,7 @@ impl Render for MessageEditor {
.disabled(
is_editor_empty
|| !is_model_selected
|| is_streaming_completion,
|| is_generating,
)
.child(
h_flex()
@@ -490,7 +667,7 @@ impl Render for MessageEditor {
"Type a message to submit",
))
})
.when(is_streaming_completion, |button| {
.when(is_generating, |button| {
button.tooltip(Tooltip::text(
"Cancel to submit a new message",
))

View File

@@ -0,0 +1,121 @@
use std::sync::Arc;
use assistant_settings::{AgentProfile, AssistantSettings};
use fs::Fs;
use gpui::{prelude::*, Action, Entity, Subscription, WeakEntity};
use indexmap::IndexMap;
use settings::{update_settings_file, Settings as _, SettingsStore};
use ui::{prelude::*, ContextMenu, ContextMenuEntry, PopoverMenu, Tooltip};
use util::ResultExt as _;
use crate::{ManageProfiles, ThreadStore};
pub struct ProfileSelector {
profiles: IndexMap<Arc<str>, AgentProfile>,
fs: Arc<dyn Fs>,
thread_store: WeakEntity<ThreadStore>,
_subscriptions: Vec<Subscription>,
}
impl ProfileSelector {
pub fn new(
fs: Arc<dyn Fs>,
thread_store: WeakEntity<ThreadStore>,
cx: &mut Context<Self>,
) -> Self {
let settings_subscription = cx.observe_global::<SettingsStore>(move |this, cx| {
this.refresh_profiles(cx);
});
let mut this = Self {
profiles: IndexMap::default(),
fs,
thread_store,
_subscriptions: vec![settings_subscription],
};
this.refresh_profiles(cx);
this
}
fn refresh_profiles(&mut self, cx: &mut Context<Self>) {
let settings = AssistantSettings::get_global(cx);
self.profiles = settings.profiles.clone();
}
fn build_context_menu(
&self,
window: &mut Window,
cx: &mut Context<Self>,
) -> Entity<ContextMenu> {
ContextMenu::build(window, cx, |mut menu, _window, cx| {
let settings = AssistantSettings::get_global(cx);
let icon_position = IconPosition::Start;
menu = menu.header("Profiles");
for (profile_id, profile) in self.profiles.clone() {
menu = menu.toggleable_entry(
profile.name.clone(),
profile_id == settings.default_profile,
icon_position,
None,
{
let fs = self.fs.clone();
let thread_store = self.thread_store.clone();
move |_window, cx| {
update_settings_file::<AssistantSettings>(fs.clone(), cx, {
let profile_id = profile_id.clone();
move |settings, _cx| {
settings.set_profile(profile_id.clone());
}
});
thread_store
.update(cx, |this, cx| {
this.load_profile_by_id(&profile_id, cx);
})
.log_err();
}
},
);
}
menu = menu.separator();
menu = menu.item(
ContextMenuEntry::new("Configure Profiles")
.icon(IconName::Pencil)
.icon_color(Color::Muted)
.handler(move |window, cx| {
window.dispatch_action(ManageProfiles.boxed_clone(), cx);
}),
);
menu
})
}
}
impl Render for ProfileSelector {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let settings = AssistantSettings::get_global(cx);
let profile = settings
.profiles
.get(&settings.default_profile)
.map(|profile| profile.name.clone())
.unwrap_or_else(|| "Unknown".into());
let this = cx.entity().clone();
PopoverMenu::new("tool-selector")
.menu(move |window, cx| {
Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
})
.trigger_with_tooltip(
Button::new("profile-selector-button", profile)
.style(ButtonStyle::Filled)
.label_size(LabelSize::Small),
Tooltip::text("Change Profile"),
)
.anchor(gpui::Corner::BottomLeft)
}
}

View File

@@ -40,7 +40,7 @@ impl TerminalCodegen {
let telemetry = self.telemetry.clone();
self.status = CodegenStatus::Pending;
self.transaction = Some(TerminalTransaction::start(self.terminal.clone()));
self.generation = cx.spawn(|this, mut cx| async move {
self.generation = cx.spawn(async move |this, cx| {
let model_telemetry_id = model.telemetry_id();
let model_provider_id = model.provider_id();
let response = model.stream_completion_text(prompt, &cx).await;
@@ -97,12 +97,12 @@ impl TerminalCodegen {
}
});
this.update(&mut cx, |this, _| {
this.update(cx, |this, _| {
this.message_id = message_id;
})?;
while let Some(hunk) = hunks_rx.next().await {
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
if let Some(transaction) = &mut this.transaction {
transaction.push(hunk, cx);
cx.notify();
@@ -116,7 +116,7 @@ impl TerminalCodegen {
let result = generate.await;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ use time::{OffsetDateTime, UtcOffset};
use ui::{prelude::*, IconButtonShape, ListItem, ListItemSpacing, Tooltip};
use crate::history_store::{HistoryEntry, HistoryStore};
use crate::thread_store::SavedThreadMetadata;
use crate::thread_store::SerializedThreadMetadata;
use crate::{AssistantPanel, RemoveSelectedThread};
pub struct ThreadHistory {
@@ -221,14 +221,14 @@ impl Render for ThreadHistory {
#[derive(IntoElement)]
pub struct PastThread {
thread: SavedThreadMetadata,
thread: SerializedThreadMetadata,
assistant_panel: WeakEntity<AssistantPanel>,
selected: bool,
}
impl PastThread {
pub fn new(
thread: SavedThreadMetadata,
thread: SerializedThreadMetadata,
assistant_panel: WeakEntity<AssistantPanel>,
selected: bool,
) -> Self {

View File

@@ -1,8 +1,10 @@
use std::borrow::Cow;
use std::path::PathBuf;
use std::sync::Arc;
use anyhow::{anyhow, Result};
use assistant_tool::{ToolId, ToolWorkingSet};
use assistant_settings::{AgentProfile, AssistantSettings};
use assistant_tool::{ToolId, ToolSource, ToolWorkingSet};
use chrono::{DateTime, Utc};
use collections::HashMap;
use context_server::manager::ContextServerManager;
@@ -12,14 +14,16 @@ use futures::FutureExt as _;
use gpui::{
prelude::*, App, BackgroundExecutor, Context, Entity, Global, ReadGlobal, SharedString, Task,
};
use heed::types::{SerdeBincode, SerdeJson};
use heed::types::SerdeBincode;
use heed::Database;
use language_model::{LanguageModelToolUseId, Role};
use language_model::{LanguageModelToolUseId, Role, TokenUsage};
use project::Project;
use prompt_store::PromptBuilder;
use serde::{Deserialize, Serialize};
use settings::Settings as _;
use util::ResultExt as _;
use crate::thread::{MessageId, Thread, ThreadId};
use crate::thread::{MessageId, ProjectSnapshot, Thread, ThreadEvent, ThreadId};
pub fn init(cx: &mut App) {
ThreadsDatabase::init(cx);
@@ -28,15 +32,17 @@ pub fn init(cx: &mut App) {
pub struct ThreadStore {
project: Entity<Project>,
tools: Arc<ToolWorkingSet>,
prompt_builder: Arc<PromptBuilder>,
context_server_manager: Entity<ContextServerManager>,
context_server_tool_ids: HashMap<Arc<str>, Vec<ToolId>>,
threads: Vec<SavedThreadMetadata>,
threads: Vec<SerializedThreadMetadata>,
}
impl ThreadStore {
pub fn new(
project: Entity<Project>,
tools: Arc<ToolWorkingSet>,
prompt_builder: Arc<PromptBuilder>,
cx: &mut App,
) -> Result<Entity<Self>> {
let this = cx.new(|cx| {
@@ -48,10 +54,12 @@ impl ThreadStore {
let this = Self {
project,
tools,
prompt_builder,
context_server_manager,
context_server_tool_ids: HashMap::default(),
threads: Vec::new(),
};
this.load_default_profile(cx);
this.register_context_server_handlers(cx);
this.reload(cx).detach_and_log_err(cx);
@@ -61,23 +69,38 @@ impl ThreadStore {
Ok(this)
}
pub fn context_server_manager(&self) -> Entity<ContextServerManager> {
self.context_server_manager.clone()
}
pub fn tools(&self) -> Arc<ToolWorkingSet> {
self.tools.clone()
}
/// Returns the number of threads.
pub fn thread_count(&self) -> usize {
self.threads.len()
}
pub fn threads(&self) -> Vec<SavedThreadMetadata> {
pub fn threads(&self) -> Vec<SerializedThreadMetadata> {
let mut threads = self.threads.iter().cloned().collect::<Vec<_>>();
threads.sort_unstable_by_key(|thread| std::cmp::Reverse(thread.updated_at));
threads
}
pub fn recent_threads(&self, limit: usize) -> Vec<SavedThreadMetadata> {
pub fn recent_threads(&self, limit: usize) -> Vec<SerializedThreadMetadata> {
self.threads().into_iter().take(limit).collect()
}
pub fn create_thread(&mut self, cx: &mut Context<Self>) -> Entity<Thread> {
cx.new(|cx| Thread::new(self.project.clone(), self.tools.clone(), cx))
cx.new(|cx| {
Thread::new(
self.project.clone(),
self.tools.clone(),
self.prompt_builder.clone(),
cx,
)
})
}
pub fn open_thread(
@@ -87,81 +110,62 @@ impl ThreadStore {
) -> Task<Result<Entity<Thread>>> {
let id = id.clone();
let database_future = ThreadsDatabase::global_future(cx);
cx.spawn(|this, mut cx| async move {
cx.spawn(async move |this, cx| {
let database = database_future.await.map_err(|err| anyhow!(err))?;
let thread = database
.try_find_thread(id.clone())
.await?
.ok_or_else(|| anyhow!("no thread found with ID: {id:?}"))?;
this.update(&mut cx, |this, cx| {
let thread = this.update(cx, |this, cx| {
cx.new(|cx| {
Thread::from_saved(
Thread::deserialize(
id.clone(),
thread,
this.project.clone(),
this.tools.clone(),
this.prompt_builder.clone(),
cx,
)
})
})
})?;
let (system_prompt_context, load_error) = thread
.update(cx, |thread, cx| thread.load_system_prompt_context(cx))?
.await;
thread.update(cx, |thread, cx| {
thread.set_system_prompt_context(system_prompt_context);
if let Some(load_error) = load_error {
cx.emit(ThreadEvent::ShowError(load_error));
}
})?;
Ok(thread)
})
}
pub fn save_thread(&self, thread: &Entity<Thread>, cx: &mut Context<Self>) -> Task<Result<()>> {
let (metadata, thread) = thread.update(cx, |thread, _cx| {
let id = thread.id().clone();
let thread = SavedThread {
summary: thread.summary_or_default(),
updated_at: thread.updated_at(),
messages: thread
.messages()
.map(|message| SavedMessage {
id: message.id,
role: message.role,
text: message.text.clone(),
tool_uses: thread
.tool_uses_for_message(message.id)
.into_iter()
.map(|tool_use| SavedToolUse {
id: tool_use.id,
name: tool_use.name,
input: tool_use.input,
})
.collect(),
tool_results: thread
.tool_results_for_message(message.id)
.into_iter()
.map(|tool_result| SavedToolResult {
tool_use_id: tool_result.tool_use_id.clone(),
is_error: tool_result.is_error,
content: tool_result.content.clone(),
})
.collect(),
})
.collect(),
};
(id, thread)
});
let (metadata, serialized_thread) =
thread.update(cx, |thread, cx| (thread.id().clone(), thread.serialize(cx)));
let database_future = ThreadsDatabase::global_future(cx);
cx.spawn(|this, mut cx| async move {
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, thread).await?;
database.save_thread(metadata, serialized_thread).await?;
this.update(&mut 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(|this, mut cx| async move {
cx.spawn(async move |this, cx| {
let database = database_future.await.map_err(|err| anyhow!(err))?;
database.delete_thread(id.clone()).await?;
this.update(&mut cx, |this, _cx| {
this.update(cx, |this, _cx| {
this.threads.retain(|thread| thread.id != id)
})
})
@@ -169,20 +173,59 @@ impl ThreadStore {
pub fn reload(&self, cx: &mut Context<Self>) -> Task<Result<()>> {
let database_future = ThreadsDatabase::global_future(cx);
cx.spawn(|this, mut cx| async move {
cx.spawn(async move |this, cx| {
let threads = database_future
.await
.map_err(|err| anyhow!(err))?
.list_threads()
.await?;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
this.threads = threads;
cx.notify();
})
})
}
fn load_default_profile(&self, cx: &Context<Self>) {
let assistant_settings = AssistantSettings::get_global(cx);
self.load_profile_by_id(&assistant_settings.default_profile, cx);
}
pub fn load_profile_by_id(&self, profile_id: &Arc<str>, cx: &Context<Self>) {
let assistant_settings = AssistantSettings::get_global(cx);
if let Some(profile) = assistant_settings.profiles.get(profile_id) {
self.load_profile(profile);
}
}
pub fn load_profile(&self, profile: &AgentProfile) {
self.tools.disable_all_tools();
self.tools.enable(
ToolSource::Native,
&profile
.tools
.iter()
.filter_map(|(tool, enabled)| enabled.then(|| tool.clone()))
.collect::<Vec<_>>(),
);
for (context_server_id, preset) in &profile.context_servers {
self.tools.enable(
ToolSource::ContextServer {
id: context_server_id.clone().into(),
},
&preset
.tools
.iter()
.filter_map(|(tool, enabled)| enabled.then(|| tool.clone()))
.collect::<Vec<_>>(),
)
}
}
fn register_context_server_handlers(&self, cx: &mut Context<Self>) {
cx.subscribe(
&self.context_server_manager.clone(),
@@ -205,7 +248,7 @@ impl ThreadStore {
cx.spawn({
let server = server.clone();
let server_id = server_id.clone();
|this, mut cx| async move {
async move |this, cx| {
let Some(protocol) = server.client() else {
return;
};
@@ -230,7 +273,7 @@ impl ThreadStore {
})
.collect::<Vec<_>>();
this.update(&mut cx, |this, _cx| {
this.update(cx, |this, _cx| {
this.context_server_tool_ids.insert(server_id, tool_ids);
})
.log_err();
@@ -251,44 +294,132 @@ impl ThreadStore {
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SavedThreadMetadata {
pub struct SerializedThreadMetadata {
pub id: ThreadId,
pub summary: SharedString,
pub updated_at: DateTime<Utc>,
}
#[derive(Serialize, Deserialize)]
pub struct SavedThread {
pub struct SerializedThread {
pub version: String,
pub summary: SharedString,
pub updated_at: DateTime<Utc>,
pub messages: Vec<SavedMessage>,
pub messages: Vec<SerializedMessage>,
#[serde(default)]
pub initial_project_snapshot: Option<Arc<ProjectSnapshot>>,
#[serde(default)]
pub cumulative_token_usage: TokenUsage,
}
impl SerializedThread {
pub const VERSION: &'static str = "0.1.0";
pub fn from_json(json: &[u8]) -> Result<Self> {
let saved_thread_json = serde_json::from_slice::<serde_json::Value>(json)?;
match saved_thread_json.get("version") {
Some(serde_json::Value::String(version)) => match version.as_str() {
SerializedThread::VERSION => Ok(serde_json::from_value::<SerializedThread>(
saved_thread_json,
)?),
_ => Err(anyhow!(
"unrecognized serialized thread version: {}",
version
)),
},
None => {
let saved_thread =
serde_json::from_value::<LegacySerializedThread>(saved_thread_json)?;
Ok(saved_thread.upgrade())
}
version => Err(anyhow!(
"unrecognized serialized thread version: {:?}",
version
)),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SavedMessage {
pub struct SerializedMessage {
pub id: MessageId,
pub role: Role,
pub text: String,
#[serde(default)]
pub tool_uses: Vec<SavedToolUse>,
pub segments: Vec<SerializedMessageSegment>,
#[serde(default)]
pub tool_results: Vec<SavedToolResult>,
pub tool_uses: Vec<SerializedToolUse>,
#[serde(default)]
pub tool_results: Vec<SerializedToolResult>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SavedToolUse {
#[serde(tag = "type")]
pub enum SerializedMessageSegment {
#[serde(rename = "text")]
Text { text: String },
#[serde(rename = "thinking")]
Thinking { text: String },
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SerializedToolUse {
pub id: LanguageModelToolUseId,
pub name: SharedString,
pub input: serde_json::Value,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SavedToolResult {
pub struct SerializedToolResult {
pub tool_use_id: LanguageModelToolUseId,
pub is_error: bool,
pub content: Arc<str>,
}
#[derive(Serialize, Deserialize)]
struct LegacySerializedThread {
pub summary: SharedString,
pub updated_at: DateTime<Utc>,
pub messages: Vec<LegacySerializedMessage>,
#[serde(default)]
pub initial_project_snapshot: Option<Arc<ProjectSnapshot>>,
}
impl LegacySerializedThread {
pub fn upgrade(self) -> SerializedThread {
SerializedThread {
version: SerializedThread::VERSION.to_string(),
summary: self.summary,
updated_at: self.updated_at,
messages: self.messages.into_iter().map(|msg| msg.upgrade()).collect(),
initial_project_snapshot: self.initial_project_snapshot,
cumulative_token_usage: TokenUsage::default(),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
struct LegacySerializedMessage {
pub id: MessageId,
pub role: Role,
pub text: String,
#[serde(default)]
pub tool_uses: Vec<SerializedToolUse>,
#[serde(default)]
pub tool_results: Vec<SerializedToolResult>,
}
impl LegacySerializedMessage {
fn upgrade(self) -> SerializedMessage {
SerializedMessage {
id: self.id,
role: self.role,
segments: vec![SerializedMessageSegment::Text { text: self.text }],
tool_uses: self.tool_uses,
tool_results: self.tool_results,
}
}
}
struct GlobalThreadsDatabase(
Shared<BoxFuture<'static, Result<Arc<ThreadsDatabase>, Arc<anyhow::Error>>>>,
);
@@ -298,7 +429,25 @@ impl Global for GlobalThreadsDatabase {}
pub(crate) struct ThreadsDatabase {
executor: BackgroundExecutor,
env: heed::Env,
threads: Database<SerdeBincode<ThreadId>, SerdeJson<SavedThread>>,
threads: Database<SerdeBincode<ThreadId>, SerializedThread>,
}
impl heed::BytesEncode<'_> for SerializedThread {
type EItem = SerializedThread;
fn bytes_encode(item: &Self::EItem) -> Result<Cow<[u8]>, heed::BoxedError> {
serde_json::to_vec(item).map(Cow::Owned).map_err(Into::into)
}
}
impl<'a> heed::BytesDecode<'a> for SerializedThread {
type DItem = SerializedThread;
fn bytes_decode(bytes: &'a [u8]) -> Result<Self::DItem, heed::BoxedError> {
// We implement this type manually because we want to call `SerializedThread::from_json`,
// instead of the Deserialize trait implementation for `SerializedThread`.
SerializedThread::from_json(bytes).map_err(Into::into)
}
}
impl ThreadsDatabase {
@@ -345,7 +494,7 @@ impl ThreadsDatabase {
})
}
pub fn list_threads(&self) -> Task<Result<Vec<SavedThreadMetadata>>> {
pub fn list_threads(&self) -> Task<Result<Vec<SerializedThreadMetadata>>> {
let env = self.env.clone();
let threads = self.threads;
@@ -354,7 +503,7 @@ impl ThreadsDatabase {
let mut iter = threads.iter(&txn)?;
let mut threads = Vec::new();
while let Some((key, value)) = iter.next().transpose()? {
threads.push(SavedThreadMetadata {
threads.push(SerializedThreadMetadata {
id: key,
summary: value.summary,
updated_at: value.updated_at,
@@ -365,7 +514,7 @@ impl ThreadsDatabase {
})
}
pub fn try_find_thread(&self, id: ThreadId) -> Task<Result<Option<SavedThread>>> {
pub fn try_find_thread(&self, id: ThreadId) -> Task<Result<Option<SerializedThread>>> {
let env = self.env.clone();
let threads = self.threads;
@@ -376,7 +525,7 @@ impl ThreadsDatabase {
})
}
pub fn save_thread(&self, id: ThreadId, thread: SavedThread) -> Task<Result<()>> {
pub fn save_thread(&self, id: ThreadId, thread: SerializedThread) -> Task<Result<()>> {
let env = self.env.clone();
let threads = self.threads;

View File

@@ -1,28 +1,33 @@
use std::sync::Arc;
use anyhow::Result;
use assistant_tool::{Tool, ToolWorkingSet};
use collections::HashMap;
use futures::future::Shared;
use futures::FutureExt as _;
use gpui::{SharedString, Task};
use gpui::{App, SharedString, Task};
use language_model::{
LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse,
LanguageModelToolUseId, MessageContent, Role,
};
use ui::IconName;
use crate::thread::MessageId;
use crate::thread_store::SavedMessage;
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,
}
#[derive(Debug, Clone)]
pub enum ToolUseStatus {
NeedsConfirmation,
Pending,
Running,
Finished(SharedString),
@@ -30,6 +35,7 @@ pub enum ToolUseStatus {
}
pub struct ToolUseState {
tools: Arc<ToolWorkingSet>,
tool_uses_by_assistant_message: HashMap<MessageId, Vec<LanguageModelToolUse>>,
tool_uses_by_user_message: HashMap<MessageId, Vec<LanguageModelToolUseId>>,
tool_results: HashMap<LanguageModelToolUseId, LanguageModelToolResult>,
@@ -37,8 +43,9 @@ pub struct ToolUseState {
}
impl ToolUseState {
pub fn new() -> Self {
pub fn new(tools: Arc<ToolWorkingSet>) -> Self {
Self {
tools,
tool_uses_by_assistant_message: HashMap::default(),
tool_uses_by_user_message: HashMap::default(),
tool_results: HashMap::default(),
@@ -46,25 +53,40 @@ impl ToolUseState {
}
}
pub fn from_saved_messages(messages: &[SavedMessage]) -> Self {
let mut this = Self::new();
/// 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.
pub fn from_serialized_messages(
tools: Arc<ToolWorkingSet>,
messages: &[SerializedMessage],
mut filter_by_tool_name: impl FnMut(&str) -> bool,
) -> Self {
let mut this = Self::new(tools);
let mut tool_names_by_id = HashMap::default();
for message in messages {
match message.role {
Role::Assistant => {
if !message.tool_uses.is_empty() {
this.tool_uses_by_assistant_message.insert(
message.id,
message
.tool_uses
let tool_uses = message
.tool_uses
.iter()
.filter(|tool_use| (filter_by_tool_name)(tool_use.name.as_ref()))
.map(|tool_use| LanguageModelToolUse {
id: tool_use.id.clone(),
name: tool_use.name.clone().into(),
input: tool_use.input.clone(),
})
.collect::<Vec<_>>();
tool_names_by_id.extend(
tool_uses
.iter()
.map(|tool_use| LanguageModelToolUse {
id: tool_use.id.clone(),
name: tool_use.name.clone().into(),
input: tool_use.input.clone(),
})
.collect(),
.map(|tool_use| (tool_use.id.clone(), tool_use.name.clone())),
);
this.tool_uses_by_assistant_message
.insert(message.id, tool_uses);
}
}
Role::User => {
@@ -76,6 +98,14 @@ impl ToolUseState {
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;
};
if !(filter_by_tool_name)(tool_use.as_ref()) {
continue;
}
tool_uses_by_user_message.push(tool_use_id.clone());
this.tool_results.insert(
@@ -96,11 +126,27 @@ impl ToolUseState {
this
}
pub fn cancel_pending(&mut self) -> Vec<PendingToolUse> {
let mut pending_tools = Vec::new();
for (tool_use_id, tool_use) in self.pending_tool_uses_by_id.drain() {
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id,
content: "Tool canceled by user".into(),
is_error: true,
},
);
pending_tools.push(tool_use.clone());
}
pending_tools
}
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) -> Vec<ToolUse> {
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();
};
@@ -120,29 +166,53 @@ impl ToolUseState {
}
if let Some(pending_tool_use) = self.pending_tool_uses_by_id.get(&tool_use.id) {
return match pending_tool_use.status {
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())
}
};
}
} else {
ToolUseStatus::Pending
}
ToolUseStatus::Pending
})();
let icon = if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
tool.icon()
} else {
IconName::Cog
};
tool_uses.push(ToolUse {
id: tool_use.id.clone(),
name: tool_use.name.clone().into(),
ui_text: self.tool_ui_label(&tool_use.name, &tool_use.input, cx),
input: tool_use.input.clone(),
status,
icon,
})
}
tool_uses
}
pub fn tool_ui_label(
&self,
tool_name: &str,
input: &serde_json::Value,
cx: &App,
) -> SharedString {
if let Some(tool) = self.tools.tool(tool_name, cx) {
tool.ui_text(input).into()
} else {
"Unknown tool".into()
}
}
pub fn tool_results_for_message(&self, message_id: MessageId) -> Vec<&LanguageModelToolResult> {
let empty = Vec::new();
@@ -160,10 +230,18 @@ impl ToolUseState {
.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 request_tool_use(
&mut self,
assistant_message_id: MessageId,
tool_use: LanguageModelToolUse,
cx: &App,
) {
self.tool_uses_by_assistant_message
.entry(assistant_message_id)
@@ -183,37 +261,68 @@ impl ToolUseState {
PendingToolUse {
assistant_message_id,
id: tool_use.id,
name: tool_use.name,
name: tool_use.name.clone(),
ui_text: self
.tool_ui_label(&tool_use.name, &tool_use.input, cx)
.into(),
input: tool_use.input,
status: PendingToolUseStatus::Idle,
},
);
}
pub fn run_pending_tool(&mut self, tool_use_id: LanguageModelToolUseId, task: Task<()>) {
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,
messages: Arc<Vec<LanguageModelRequestMessage>>,
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,
messages,
tool,
ui_text,
};
tool_use.status = PendingToolUseStatus::NeedsConfirmation(Arc::new(confirmation));
}
}
pub fn insert_tool_output(
&mut self,
tool_use_id: LanguageModelToolUseId,
output: Result<String>,
) {
) -> Option<PendingToolUse> {
match output {
Ok(output) => {
Ok(tool_result) => {
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
content: output.into(),
content: tool_result.into(),
is_error: false,
},
);
self.pending_tool_uses_by_id.remove(&tool_use_id);
self.pending_tool_uses_by_id.remove(&tool_use_id)
}
Err(err) => {
self.tool_results.insert(
@@ -228,6 +337,8 @@ impl ToolUseState {
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()
}
}
}
@@ -239,9 +350,17 @@ impl ToolUseState {
) {
if let Some(tool_uses) = self.tool_uses_by_assistant_message.get(&message_id) {
for tool_use in tool_uses {
request_message
.content
.push(MessageContent::ToolUse(tool_use.clone()));
if self.tool_results.contains_key(&tool_use.id) {
// Do not send tool uses until they are completed
request_message
.content
.push(MessageContent::ToolUse(tool_use.clone()));
} else {
log::debug!(
"skipped tool use {:?} because it is still pending",
tool_use
);
}
}
}
}
@@ -254,9 +373,19 @@ impl ToolUseState {
if let Some(tool_uses) = self.tool_uses_by_user_message.get(&message_id) {
for tool_use_id in tool_uses {
if let Some(tool_result) = self.tool_results.get(tool_use_id) {
request_message
.content
.push(MessageContent::ToolResult(tool_result.clone()));
request_message.content.push(MessageContent::ToolResult(
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
is_error: tool_result.is_error,
content: if tool_result.content.is_empty() {
// Surprisingly, the API fails if we return an empty string here.
// It thinks we are sending a tool use without a tool result.
"<Tool returned an empty string>".into()
} else {
tool_result.content.clone()
},
},
));
}
}
}
@@ -267,15 +396,27 @@ impl ToolUseState {
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,
}
#[derive(Debug, Clone)]
pub struct Confirmation {
pub tool_use_id: LanguageModelToolUseId,
pub input: serde_json::Value,
pub ui_text: Arc<str>,
pub messages: Arc<Vec<LanguageModelRequestMessage>>,
pub tool: Arc<dyn Tool>,
}
#[derive(Debug, Clone)]
pub enum PendingToolUseStatus {
Idle,
NeedsConfirmation(Arc<Confirmation>),
Running { _task: Shared<Task<()>> },
Error(#[allow(unused)] Arc<str>),
}
@@ -288,4 +429,8 @@ impl PendingToolUseStatus {
pub fn is_error(&self) -> bool {
matches!(self, PendingToolUseStatus::Error(_))
}
pub fn needs_confirmation(&self) -> bool {
matches!(self, PendingToolUseStatus::NeedsConfirmation { .. })
}
}

View File

@@ -1,3 +1,5 @@
mod context_pill;
mod tool_ready_pop_up;
pub use context_pill::*;
pub use tool_ready_pop_up::*;

View File

@@ -126,7 +126,13 @@ impl RenderOnce for ContextPill {
h_flex()
.id("context-data")
.gap_1()
.child(Label::new(context.name.clone()).size(LabelSize::Small))
.child(
div().max_w_64().child(
Label::new(context.name.clone())
.size(LabelSize::Small)
.truncate(),
),
)
.when_some(context.parent.as_ref(), |element, parent_name| {
if *dupe_name {
element.child(
@@ -174,21 +180,22 @@ impl RenderOnce for ContextPill {
})
.hover(|style| style.bg(color.element_hover.opacity(0.5)))
.child(
Label::new(name.clone())
.size(LabelSize::Small)
.color(Color::Muted),
div().px_0p5().max_w_64().child(
Label::new(name.clone())
.size(LabelSize::Small)
.color(Color::Muted)
.truncate(),
),
)
.child(
div().px_0p5().child(
Label::new(match kind {
ContextKind::File => "Active Tab",
ContextKind::Thread
| ContextKind::Directory
| ContextKind::FetchedUrl => "Active",
})
.size(LabelSize::XSmall)
.color(Color::Muted),
),
Label::new(match kind {
ContextKind::File => "Active Tab",
ContextKind::Thread | ContextKind::Directory | ContextKind::FetchedUrl => {
"Active"
}
})
.size(LabelSize::XSmall)
.color(Color::Muted),
)
.child(
Icon::new(IconName::Plus)

View File

@@ -0,0 +1,129 @@
use gpui::{
point, App, Context, EventEmitter, IntoElement, PlatformDisplay, Size, Window,
WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind, WindowOptions,
};
use release_channel::ReleaseChannel;
use std::rc::Rc;
use theme;
use ui::{prelude::*, Render};
pub struct ToolReadyPopUp {
caption: SharedString,
icon: IconName,
icon_color: Color,
}
impl ToolReadyPopUp {
pub fn new(caption: impl Into<SharedString>, icon: IconName, icon_color: Color) -> Self {
Self {
caption: caption.into(),
icon,
icon_color,
}
}
pub fn window_options(screen: Rc<dyn PlatformDisplay>, cx: &App) -> WindowOptions {
let size = Size {
width: px(450.),
height: px(72.),
};
let notification_margin_width = px(16.);
let notification_margin_height = px(-48.);
let bounds = gpui::Bounds::<Pixels> {
origin: screen.bounds().top_right()
- point(
size.width + notification_margin_width,
notification_margin_height,
),
size,
};
let app_id = ReleaseChannel::global(cx).app_id();
WindowOptions {
window_bounds: Some(WindowBounds::Windowed(bounds)),
titlebar: None,
focus: false,
show: true,
kind: WindowKind::PopUp,
is_movable: false,
display_id: Some(screen.id()),
window_background: WindowBackgroundAppearance::Transparent,
app_id: Some(app_id.to_owned()),
window_min_size: None,
window_decorations: Some(WindowDecorations::Client),
}
}
}
pub enum ToolReadyPopupEvent {
Accepted,
Dismissed,
}
impl EventEmitter<ToolReadyPopupEvent> for ToolReadyPopUp {}
impl Render for ToolReadyPopUp {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let ui_font = theme::setup_ui_font(window, cx);
let line_height = window.line_height();
h_flex()
.size_full()
.p_3()
.gap_4()
.justify_between()
.elevation_3(cx)
.text_ui(cx)
.font(ui_font)
.border_color(cx.theme().colors().border)
.rounded_xl()
.child(
h_flex()
.items_start()
.gap_2()
.child(
h_flex().h(line_height).justify_center().child(
Icon::new(self.icon)
.color(self.icon_color)
.size(IconSize::Small),
),
)
.child(
v_flex()
.child(
div()
.text_size(px(16.))
.text_color(cx.theme().colors().text)
.child("Agent Panel"),
)
.child(
div()
.text_size(px(14.))
.text_color(cx.theme().colors().text_muted)
.child(self.caption.clone()),
),
),
)
.child(
h_flex()
.gap_0p5()
.child(
Button::new("open", "View Panel")
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
.on_click({
cx.listener(move |_this, _event, _, cx| {
cx.emit(ToolReadyPopupEvent::Accepted);
})
}),
)
.child(Button::new("dismiss", "Dismiss").on_click({
cx.listener(move |_, _event, _, cx| {
cx.emit(ToolReadyPopupEvent::Dismissed);
})
})),
)
}
}

View File

@@ -162,6 +162,11 @@ pub enum ContextOperation {
section: SlashCommandOutputSection<language::Anchor>,
version: clock::Global,
},
ThoughtProcessOutputSectionAdded {
timestamp: clock::Lamport,
section: ThoughtProcessOutputSection<language::Anchor>,
version: clock::Global,
},
BufferOperation(language::Operation),
}
@@ -259,6 +264,20 @@ impl ContextOperation {
version: language::proto::deserialize_version(&message.version),
})
}
proto::context_operation::Variant::ThoughtProcessOutputSectionAdded(message) => {
let section = message.section.context("missing section")?;
Ok(Self::ThoughtProcessOutputSectionAdded {
timestamp: language::proto::deserialize_timestamp(
message.timestamp.context("missing timestamp")?,
),
section: ThoughtProcessOutputSection {
range: language::proto::deserialize_anchor_range(
section.range.context("invalid range")?,
)?,
},
version: language::proto::deserialize_version(&message.version),
})
}
proto::context_operation::Variant::BufferOperation(op) => Ok(Self::BufferOperation(
language::proto::deserialize_operation(
op.operation.context("invalid buffer operation")?,
@@ -370,6 +389,27 @@ impl ContextOperation {
},
)),
},
Self::ThoughtProcessOutputSectionAdded {
timestamp,
section,
version,
} => proto::ContextOperation {
variant: Some(
proto::context_operation::Variant::ThoughtProcessOutputSectionAdded(
proto::context_operation::ThoughtProcessOutputSectionAdded {
timestamp: Some(language::proto::serialize_timestamp(*timestamp)),
section: Some({
proto::ThoughtProcessOutputSection {
range: Some(language::proto::serialize_anchor_range(
section.range.clone(),
)),
}
}),
version: language::proto::serialize_version(version),
},
),
),
},
Self::BufferOperation(operation) => proto::ContextOperation {
variant: Some(proto::context_operation::Variant::BufferOperation(
proto::context_operation::BufferOperation {
@@ -387,7 +427,8 @@ impl ContextOperation {
Self::UpdateSummary { summary, .. } => summary.timestamp,
Self::SlashCommandStarted { id, .. } => id.0,
Self::SlashCommandOutputSectionAdded { timestamp, .. }
| Self::SlashCommandFinished { timestamp, .. } => *timestamp,
| Self::SlashCommandFinished { timestamp, .. }
| Self::ThoughtProcessOutputSectionAdded { timestamp, .. } => *timestamp,
Self::BufferOperation(_) => {
panic!("reading the timestamp of a buffer operation is not supported")
}
@@ -402,7 +443,8 @@ impl ContextOperation {
| Self::UpdateSummary { version, .. }
| Self::SlashCommandStarted { version, .. }
| Self::SlashCommandOutputSectionAdded { version, .. }
| Self::SlashCommandFinished { version, .. } => version,
| Self::SlashCommandFinished { version, .. }
| Self::ThoughtProcessOutputSectionAdded { version, .. } => version,
Self::BufferOperation(_) => {
panic!("reading the version of a buffer operation is not supported")
}
@@ -418,6 +460,8 @@ pub enum ContextEvent {
MessagesEdited,
SummaryChanged,
StreamedCompletion,
StartedThoughtProcess(Range<language::Anchor>),
EndedThoughtProcess(language::Anchor),
PatchesUpdated {
removed: Vec<Range<language::Anchor>>,
updated: Vec<Range<language::Anchor>>,
@@ -498,6 +542,17 @@ impl MessageMetadata {
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ThoughtProcessOutputSection<T> {
pub range: Range<T>,
}
impl ThoughtProcessOutputSection<language::Anchor> {
pub fn is_valid(&self, buffer: &language::TextBuffer) -> bool {
self.range.start.is_valid(buffer) && !self.range.to_offset(buffer).is_empty()
}
}
#[derive(Clone, Debug)]
pub struct Message {
pub offset_range: Range<usize>,
@@ -580,6 +635,7 @@ pub struct AssistantContext {
edits_since_last_parse: language::Subscription,
slash_commands: Arc<SlashCommandWorkingSet>,
slash_command_output_sections: Vec<SlashCommandOutputSection<language::Anchor>>,
thought_process_output_sections: Vec<ThoughtProcessOutputSection<language::Anchor>>,
message_anchors: Vec<MessageAnchor>,
contents: Vec<Content>,
messages_metadata: HashMap<MessageId, MessageMetadata>,
@@ -647,7 +703,6 @@ impl AssistantContext {
)
}
#[allow(clippy::too_many_arguments)]
pub fn new(
id: ContextId,
replica_id: ReplicaId,
@@ -683,6 +738,7 @@ impl AssistantContext {
parsed_slash_commands: Vec::new(),
invoked_slash_commands: HashMap::default(),
slash_command_output_sections: Vec::new(),
thought_process_output_sections: Vec::new(),
edits_since_last_parse: edits_since_last_slash_command_parse,
summary: None,
pending_summary: Task::ready(None),
@@ -765,10 +821,21 @@ impl AssistantContext {
}
})
.collect(),
thought_process_output_sections: self
.thought_process_output_sections
.iter()
.filter_map(|section| {
if section.is_valid(buffer) {
let range = section.range.to_offset(buffer);
Some(ThoughtProcessOutputSection { range })
} else {
None
}
})
.collect(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn deserialize(
saved_context: SavedContext,
path: PathBuf,
@@ -959,6 +1026,16 @@ impl AssistantContext {
cx.emit(ContextEvent::SlashCommandOutputSectionAdded { section });
}
}
ContextOperation::ThoughtProcessOutputSectionAdded { section, .. } => {
let buffer = self.buffer.read(cx);
if let Err(ix) = self
.thought_process_output_sections
.binary_search_by(|probe| probe.range.cmp(&section.range, buffer))
{
self.thought_process_output_sections
.insert(ix, section.clone());
}
}
ContextOperation::SlashCommandFinished {
id,
error_message,
@@ -1022,6 +1099,9 @@ impl AssistantContext {
ContextOperation::SlashCommandOutputSectionAdded { section, .. } => {
self.has_received_operations_for_anchor_range(section.range.clone(), cx)
}
ContextOperation::ThoughtProcessOutputSectionAdded { section, .. } => {
self.has_received_operations_for_anchor_range(section.range.clone(), cx)
}
ContextOperation::SlashCommandFinished { .. } => true,
ContextOperation::BufferOperation(_) => {
panic!("buffer operations should always be applied")
@@ -1130,6 +1210,12 @@ impl AssistantContext {
&self.slash_command_output_sections
}
pub fn thought_process_output_sections(
&self,
) -> &[ThoughtProcessOutputSection<language::Anchor>] {
&self.thought_process_output_sections
}
pub fn contains_files(&self, cx: &App) -> bool {
let buffer = self.buffer.read(cx);
self.slash_command_output_sections.iter().any(|section| {
@@ -1146,9 +1232,9 @@ impl AssistantContext {
fn set_language(&mut self, cx: &mut Context<Self>) {
let markdown = self.language_registry.language_for_name("Markdown");
cx.spawn(|this, mut cx| async move {
cx.spawn(async move |this, cx| {
let markdown = markdown.await?;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
this.buffer
.update(cx, |buffer, cx| buffer.set_language(Some(markdown), cx));
})
@@ -1190,7 +1276,7 @@ impl AssistantContext {
return;
};
let debounce = self.token_count.is_some();
self.pending_token_count = cx.spawn(|this, mut cx| {
self.pending_token_count = cx.spawn(async move |this, cx| {
async move {
if debounce {
cx.background_executor()
@@ -1199,13 +1285,14 @@ impl AssistantContext {
}
let token_count = cx.update(|cx| model.count_tokens(request, cx))?.await?;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
this.token_count = Some(token_count);
this.start_cache_warming(&model, cx);
cx.notify()
})
}
.log_err()
.await
});
}
@@ -1344,7 +1431,7 @@ impl AssistantContext {
};
let model = Arc::clone(model);
self.pending_cache_warming_task = cx.spawn(|this, mut cx| {
self.pending_cache_warming_task = cx.spawn(async move |this, cx| {
async move {
match model.stream_completion(request, &cx).await {
Ok(mut stream) => {
@@ -1355,13 +1442,14 @@ impl AssistantContext {
log::warn!("Cache warming failed: {}", e);
}
};
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
this.update_cache_status_for_completion(cx);
})
.ok();
anyhow::Ok(())
}
.log_err()
.await
});
}
@@ -1918,7 +2006,7 @@ impl AssistantContext {
});
self.reparse(cx);
let insert_output_task = cx.spawn(|this, mut cx| async move {
let insert_output_task = cx.spawn(async move |this, cx| {
let run_command = async {
let mut stream = output.await?;
@@ -1935,7 +2023,7 @@ impl AssistantContext {
while let Some(event) = stream.next().await {
let event = event?;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
this.buffer.update(cx, |buffer, _cx| {
buffer.finalize_last_transaction();
buffer.start_transaction()
@@ -2036,7 +2124,7 @@ impl AssistantContext {
})?;
}
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
this.buffer.update(cx, |buffer, cx| {
buffer.finalize_last_transaction();
buffer.start_transaction();
@@ -2082,7 +2170,7 @@ impl AssistantContext {
let command_result = run_command.await;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
let version = this.version.clone();
let timestamp = this.next_timestamp();
let Some(invoked_slash_command) = this.invoked_slash_commands.get_mut(&command_id)
@@ -2168,6 +2256,35 @@ impl AssistantContext {
);
}
fn insert_thought_process_output_section(
&mut self,
section: ThoughtProcessOutputSection<language::Anchor>,
cx: &mut Context<Self>,
) {
let buffer = self.buffer.read(cx);
let insertion_ix = match self
.thought_process_output_sections
.binary_search_by(|probe| probe.range.cmp(&section.range, buffer))
{
Ok(ix) | Err(ix) => ix,
};
self.thought_process_output_sections
.insert(insertion_ix, section.clone());
// cx.emit(ContextEvent::ThoughtProcessOutputSectionAdded {
// section: section.clone(),
// });
let version = self.version.clone();
let timestamp = self.next_timestamp();
self.push_op(
ContextOperation::ThoughtProcessOutputSectionAdded {
timestamp,
section,
version,
},
cx,
);
}
pub fn completion_provider_changed(&mut self, cx: &mut Context<Self>) {
self.count_remaining_tokens(cx);
}
@@ -2212,7 +2329,7 @@ impl AssistantContext {
let pending_completion_id = post_inc(&mut self.completion_count);
let task = cx.spawn({
|this, mut cx| async move {
async move |this, cx| {
let stream = model.stream_completion(request, &cx);
let assistant_message_id = assistant_message.id;
let mut response_latency = None;
@@ -2220,6 +2337,10 @@ impl AssistantContext {
let request_start = Instant::now();
let mut events = stream.await?;
let mut stop_reason = StopReason::EndTurn;
let mut thought_process_stack = Vec::new();
const THOUGHT_PROCESS_START_MARKER: &str = "<think>\n";
const THOUGHT_PROCESS_END_MARKER: &str = "\n</think>";
while let Some(event) = events.next().await {
if response_latency.is_none() {
@@ -2227,7 +2348,10 @@ impl AssistantContext {
}
let event = event?;
this.update(&mut cx, |this, cx| {
let mut context_event = None;
let mut thought_process_output_section = None;
this.update(cx, |this, cx| {
let message_ix = this
.message_anchors
.iter()
@@ -2245,7 +2369,50 @@ impl AssistantContext {
LanguageModelCompletionEvent::Stop(reason) => {
stop_reason = reason;
}
LanguageModelCompletionEvent::Text(chunk) => {
LanguageModelCompletionEvent::Thinking(chunk) => {
if thought_process_stack.is_empty() {
let start =
buffer.anchor_before(message_old_end_offset);
thought_process_stack.push(start);
let chunk =
format!("{THOUGHT_PROCESS_START_MARKER}{chunk}{THOUGHT_PROCESS_END_MARKER}");
let chunk_len = chunk.len();
buffer.edit(
[(
message_old_end_offset..message_old_end_offset,
chunk,
)],
None,
cx,
);
let end = buffer
.anchor_before(message_old_end_offset + chunk_len);
context_event = Some(
ContextEvent::StartedThoughtProcess(start..end),
);
} else {
// This ensures that all the thinking chunks are inserted inside the thinking tag
let insertion_position =
message_old_end_offset - THOUGHT_PROCESS_END_MARKER.len();
buffer.edit(
[(insertion_position..insertion_position, chunk)],
None,
cx,
);
}
}
LanguageModelCompletionEvent::Text(mut chunk) => {
if let Some(start) = thought_process_stack.pop() {
let end = buffer.anchor_before(message_old_end_offset);
context_event =
Some(ContextEvent::EndedThoughtProcess(end));
thought_process_output_section =
Some(ThoughtProcessOutputSection {
range: start..end,
});
chunk.insert_str(0, "\n\n");
}
buffer.edit(
[(
message_old_end_offset..message_old_end_offset,
@@ -2256,16 +2423,24 @@ impl AssistantContext {
);
}
LanguageModelCompletionEvent::ToolUse(_) => {}
LanguageModelCompletionEvent::UsageUpdate(_) => {}
}
});
if let Some(section) = thought_process_output_section.take() {
this.insert_thought_process_output_section(section, cx);
}
if let Some(context_event) = context_event.take() {
cx.emit(context_event);
}
cx.emit(ContextEvent::StreamedCompletion);
Some(())
})?;
smol::future::yield_now().await;
}
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
this.pending_completions
.retain(|completion| completion.id != pending_completion_id);
this.summarize(false, cx);
@@ -2277,7 +2452,7 @@ impl AssistantContext {
let result = stream_completion.await;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
let error_message = if let Some(error) = result.as_ref().err() {
if error.is::<PaymentRequiredError>() {
cx.emit(ContextEvent::ShowPaymentRequiredError);
@@ -2787,7 +2962,7 @@ impl AssistantContext {
cache: false,
});
self.pending_summary = cx.spawn(|this, mut cx| {
self.pending_summary = cx.spawn(async move |this, cx| {
async move {
let stream = model.stream_completion_text(request, &cx);
let mut messages = stream.await?;
@@ -2796,7 +2971,7 @@ impl AssistantContext {
while let Some(message) = messages.stream.next().await {
let text = message?;
let mut lines = text.lines();
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
let version = this.version.clone();
let timestamp = this.next_timestamp();
let summary = this.summary.get_or_insert(ContextSummary::default());
@@ -2820,7 +2995,7 @@ impl AssistantContext {
}
}
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
let version = this.version.clone();
let timestamp = this.next_timestamp();
if let Some(summary) = this.summary.as_mut() {
@@ -2838,6 +3013,7 @@ impl AssistantContext {
anyhow::Ok(())
}
.log_err()
.await
});
}
}
@@ -2944,12 +3120,12 @@ impl AssistantContext {
return;
}
self.pending_save = cx.spawn(|this, mut cx| async move {
self.pending_save = cx.spawn(async move |this, cx| {
if let Some(debounce) = debounce {
cx.background_executor().timer(debounce).await;
}
let (old_path, summary) = this.read_with(&cx, |this, _| {
let (old_path, summary) = this.read_with(cx, |this, _| {
let path = this.path.clone();
let summary = if let Some(summary) = this.summary.as_ref() {
if summary.done {
@@ -2964,7 +3140,7 @@ impl AssistantContext {
})?;
if let Some(summary) = summary {
let context = this.read_with(&cx, |this, cx| this.serialize(cx))?;
let context = this.read_with(cx, |this, cx| this.serialize(cx))?;
let mut discriminant = 1;
let mut new_path;
loop {
@@ -2996,7 +3172,7 @@ impl AssistantContext {
}
}
this.update(&mut cx, |this, _| this.path = Some(new_path))?;
this.update(cx, |this, _| this.path = Some(new_path))?;
}
Ok(())
@@ -3125,6 +3301,8 @@ pub struct SavedContext {
pub summary: String,
pub slash_command_output_sections:
Vec<assistant_slash_command::SlashCommandOutputSection<usize>>,
#[serde(default)]
pub thought_process_output_sections: Vec<ThoughtProcessOutputSection<usize>>,
}
impl SavedContext {
@@ -3226,6 +3404,20 @@ impl SavedContext {
version.observe(timestamp);
}
for section in self.thought_process_output_sections {
let timestamp = next_timestamp.tick();
operations.push(ContextOperation::ThoughtProcessOutputSectionAdded {
timestamp,
section: ThoughtProcessOutputSection {
range: buffer.anchor_after(section.range.start)
..buffer.anchor_before(section.range.end),
},
version: version.clone(),
});
version.observe(timestamp);
}
let timestamp = next_timestamp.tick();
operations.push(ContextOperation::UpdateSummary {
summary: ContextSummary {
@@ -3300,6 +3492,7 @@ impl SavedContextV0_3_0 {
.collect(),
summary: self.summary,
slash_command_output_sections: self.slash_command_output_sections,
thought_process_output_sections: Vec::new(),
}
}
}

View File

@@ -13,7 +13,7 @@ use editor::{
BlockContext, BlockId, BlockPlacement, BlockProperties, BlockStyle, Crease, CreaseMetadata,
CustomBlockId, FoldId, RenderBlock, ToDisplayPoint,
},
scroll::{Autoscroll, AutoscrollStrategy},
scroll::Autoscroll,
Anchor, Editor, EditorEvent, MenuInlineCompletionsPolicy, ProposedChangeLocation,
ProposedChangesEditor, RowExt, ToOffset as _, ToPoint,
};
@@ -64,7 +64,10 @@ use workspace::{
Workspace,
};
use crate::{slash_command::SlashCommandCompletionProvider, slash_command_picker};
use crate::{
slash_command::SlashCommandCompletionProvider, slash_command_picker,
ThoughtProcessOutputSection,
};
use crate::{
AssistantContext, AssistantPatch, AssistantPatchStatus, CacheStatus, Content, ContextEvent,
ContextId, InvokedSlashCommandId, InvokedSlashCommandStatus, Message, MessageId,
@@ -120,6 +123,11 @@ enum AssistError {
Message(SharedString),
}
pub enum ThoughtProcessStatus {
Pending,
Completed,
}
pub trait AssistantPanelDelegate {
fn active_context_editor(
&self,
@@ -178,6 +186,7 @@ pub struct ContextEditor {
project: Entity<Project>,
lsp_adapter_delegate: Option<Arc<dyn LspAdapterDelegate>>,
editor: Entity<Editor>,
pending_thought_process: Option<(CreaseId, language::Anchor)>,
blocks: HashMap<MessageId, (MessageHeader, CustomBlockId)>,
image_blocks: HashSet<CustomBlockId>,
scroll_position: Option<ScrollPosition>,
@@ -229,6 +238,7 @@ impl ContextEditor {
editor.set_show_git_diff_gutter(false, cx);
editor.set_show_code_actions(false, cx);
editor.set_show_runnables(false, cx);
editor.set_show_breakpoints(false, cx);
editor.set_show_wrap_guides(false, cx);
editor.set_show_indent_guides(false, cx);
editor.set_completion_provider(Some(Box::new(completion_provider)));
@@ -252,7 +262,8 @@ impl ContextEditor {
cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
];
let sections = context.read(cx).slash_command_output_sections().to_vec();
let slash_command_sections = context.read(cx).slash_command_output_sections().to_vec();
let thought_process_sections = context.read(cx).thought_process_output_sections().to_vec();
let patch_ranges = context.read(cx).patch_ranges().collect::<Vec<_>>();
let slash_commands = context.read(cx).slash_commands().clone();
let mut this = Self {
@@ -264,6 +275,7 @@ impl ContextEditor {
image_blocks: Default::default(),
scroll_position: None,
remote_id: None,
pending_thought_process: None,
fs: fs.clone(),
workspace,
project,
@@ -293,7 +305,14 @@ impl ContextEditor {
};
this.update_message_headers(cx);
this.update_image_blocks(cx);
this.insert_slash_command_output_sections(sections, false, window, cx);
this.insert_slash_command_output_sections(slash_command_sections, false, window, cx);
this.insert_thought_process_output_sections(
thought_process_sections
.into_iter()
.map(|section| (section, ThoughtProcessStatus::Completed)),
window,
cx,
);
this.patches_updated(&Vec::new(), &patch_ranges, window, cx);
this
}
@@ -395,12 +414,9 @@ impl ContextEditor {
cursor..cursor
};
self.editor.update(cx, |editor, cx| {
editor.change_selections(
Some(Autoscroll::Strategy(AutoscrollStrategy::Fit)),
window,
cx,
|selections| selections.select_ranges([new_selection]),
);
editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
selections.select_ranges([new_selection])
});
});
// Avoid scrolling to the new cursor position so the assistant's output is stable.
cx.defer_in(window, |this, _, _| this.scroll_position = None);
@@ -535,7 +551,6 @@ impl ContextEditor {
}
}
#[allow(clippy::too_many_arguments)]
pub fn run_command(
&mut self,
command_range: Range<language::Anchor>,
@@ -599,6 +614,47 @@ impl ContextEditor {
context.save(Some(Duration::from_millis(500)), self.fs.clone(), cx);
});
}
ContextEvent::StartedThoughtProcess(range) => {
let creases = self.insert_thought_process_output_sections(
[(
ThoughtProcessOutputSection {
range: range.clone(),
},
ThoughtProcessStatus::Pending,
)],
window,
cx,
);
self.pending_thought_process = Some((creases[0], range.start));
}
ContextEvent::EndedThoughtProcess(end) => {
if let Some((crease_id, start)) = self.pending_thought_process.take() {
self.editor.update(cx, |editor, cx| {
let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
let (excerpt_id, _, _) = multi_buffer_snapshot.as_singleton().unwrap();
let start_anchor = multi_buffer_snapshot
.anchor_in_excerpt(*excerpt_id, start)
.unwrap();
editor.display_map.update(cx, |display_map, cx| {
display_map.unfold_intersecting(
vec![start_anchor..start_anchor],
true,
cx,
);
});
editor.remove_creases(vec![crease_id], cx);
});
self.insert_thought_process_output_sections(
[(
ThoughtProcessOutputSection { range: start..*end },
ThoughtProcessStatus::Completed,
)],
window,
cx,
);
}
}
ContextEvent::StreamedCompletion => {
self.editor.update(cx, |editor, cx| {
if let Some(scroll_position) = self.scroll_position {
@@ -907,7 +963,7 @@ impl ContextEditor {
if editor_state.opened_patch != patch {
state.update_task = Some({
let this = this.clone();
cx.spawn_in(window, |_, cx| async move {
cx.spawn_in(window, async move |_, cx| {
Self::update_patch_editor(this.clone(), patch, cx)
.await
.log_err();
@@ -946,6 +1002,62 @@ impl ContextEditor {
self.update_active_patch(window, cx);
}
fn insert_thought_process_output_sections(
&mut self,
sections: impl IntoIterator<
Item = (
ThoughtProcessOutputSection<language::Anchor>,
ThoughtProcessStatus,
),
>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Vec<CreaseId> {
self.editor.update(cx, |editor, cx| {
let buffer = editor.buffer().read(cx).snapshot(cx);
let excerpt_id = *buffer.as_singleton().unwrap().0;
let mut buffer_rows_to_fold = BTreeSet::new();
let mut creases = Vec::new();
for (section, status) in sections {
let start = buffer
.anchor_in_excerpt(excerpt_id, section.range.start)
.unwrap();
let end = buffer
.anchor_in_excerpt(excerpt_id, section.range.end)
.unwrap();
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
buffer_rows_to_fold.insert(buffer_row);
creases.push(
Crease::inline(
start..end,
FoldPlaceholder {
render: render_thought_process_fold_icon_button(
cx.entity().downgrade(),
status,
),
merge_adjacent: false,
..Default::default()
},
render_slash_command_output_toggle,
|_, _, _, _| Empty.into_any_element(),
)
.with_metadata(CreaseMetadata {
icon: IconName::Ai,
label: "Thinking Process".into(),
}),
);
}
let creases = editor.insert_creases(creases, cx);
for buffer_row in buffer_rows_to_fold.into_iter().rev() {
editor.fold_at(&FoldAt { buffer_row }, window, cx);
}
creases
})
}
fn insert_slash_command_output_sections(
&mut self,
sections: impl IntoIterator<Item = SlashCommandOutputSection<language::Anchor>>,
@@ -1070,10 +1182,9 @@ impl ContextEditor {
})
.ok();
} else {
patch_state.update_task =
Some(cx.spawn_in(window, move |this, cx| async move {
Self::open_patch_editor(this, new_patch, cx).await.log_err();
}));
patch_state.update_task = Some(cx.spawn_in(window, async move |this, cx| {
Self::open_patch_editor(this, new_patch, cx).await.log_err();
}));
}
}
}
@@ -1103,10 +1214,10 @@ impl ContextEditor {
async fn open_patch_editor(
this: WeakEntity<Self>,
patch: AssistantPatch,
mut cx: AsyncWindowContext,
cx: &mut AsyncWindowContext,
) -> Result<()> {
let project = this.read_with(&cx, |this, _| this.project.clone())?;
let resolved_patch = patch.resolve(project.clone(), &mut cx).await;
let project = this.read_with(cx, |this, _| this.project.clone())?;
let resolved_patch = patch.resolve(project.clone(), cx).await;
let editor = cx.new_window_entity(|window, cx| {
let editor = ProposedChangesEditor::new(
@@ -1130,7 +1241,7 @@ impl ContextEditor {
editor
})?;
this.update(&mut cx, |this, _| {
this.update(cx, |this, _| {
if let Some(patch_state) = this.patches.get_mut(&patch.range) {
patch_state.editor = Some(PatchEditorState {
editor: editor.downgrade(),
@@ -1139,8 +1250,8 @@ impl ContextEditor {
patch_state.update_task.take();
}
})?;
this.read_with(&cx, |this, _| this.workspace.clone())?
.update_in(&mut cx, |workspace, window, cx| {
this.read_with(cx, |this, _| this.workspace.clone())?
.update_in(cx, |workspace, window, cx| {
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, false, window, cx)
})
.log_err();
@@ -1151,11 +1262,11 @@ impl ContextEditor {
async fn update_patch_editor(
this: WeakEntity<Self>,
patch: AssistantPatch,
mut cx: AsyncWindowContext,
cx: &mut AsyncWindowContext,
) -> Result<()> {
let project = this.update(&mut cx, |this, _| this.project.clone())?;
let resolved_patch = patch.resolve(project.clone(), &mut cx).await;
this.update_in(&mut cx, |this, window, cx| {
let project = this.update(cx, |this, _| this.project.clone())?;
let resolved_patch = patch.resolve(project.clone(), cx).await;
this.update_in(cx, |this, window, cx| {
let patch_state = this.patches.get_mut(&patch.range)?;
let locations = resolved_patch
@@ -1625,14 +1736,14 @@ impl ContextEditor {
.map(|path| Workspace::project_path_for_path(project.clone(), &path, false, cx))
.collect::<Vec<_>>();
cx.spawn(move |_, cx| async move {
cx.spawn(async move |_, cx| {
let mut paths = vec![];
let mut worktrees = vec![];
let opened_paths = futures::future::join_all(tasks).await;
for (worktree, project_path) in opened_paths.into_iter().flatten() {
let Ok(worktree_root_name) =
worktree.read_with(&cx, |worktree, _| worktree.root_name().to_string())
worktree.read_with(cx, |worktree, _| worktree.root_name().to_string())
else {
continue;
};
@@ -1649,12 +1760,12 @@ impl ContextEditor {
};
window
.spawn(cx, |mut cx| async move {
.spawn(cx, async move |cx| {
let (paths, dragged_file_worktrees) = paths.await;
let cmd_name = FileSlashCommand.name();
context_editor_view
.update_in(&mut cx, |context_editor, window, cx| {
.update_in(cx, |context_editor, window, cx| {
let file_argument = paths
.into_iter()
.map(|path| path.to_string_lossy().to_string())
@@ -2057,7 +2168,6 @@ impl ContextEditor {
.unwrap_or_else(|| Cow::Borrowed(DEFAULT_TAB_TITLE))
}
#[allow(clippy::too_many_arguments)]
fn render_patch_block(
&mut self,
range: Range<text::Anchor>,
@@ -2201,9 +2311,9 @@ impl ContextEditor {
.log_err();
if let Some(client) = client {
cx.spawn(|this, mut cx| async move {
client.authenticate_and_connect(true, &mut cx).await?;
this.update(&mut cx, |_, cx| cx.notify())
cx.spawn(async move |this, cx| {
client.authenticate_and_connect(true, cx).await?;
this.update(cx, |_, cx| cx.notify())
})
.detach_and_log_err(cx)
}
@@ -2654,6 +2764,52 @@ fn find_surrounding_code_block(snapshot: &BufferSnapshot, offset: usize) -> Opti
None
}
fn render_thought_process_fold_icon_button(
editor: WeakEntity<Editor>,
status: ThoughtProcessStatus,
) -> Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut App) -> AnyElement> {
Arc::new(move |fold_id, fold_range, _cx| {
let editor = editor.clone();
let button = ButtonLike::new(fold_id).layer(ElevationIndex::ElevatedSurface);
let button = match status {
ThoughtProcessStatus::Pending => button
.child(
Icon::new(IconName::Brain)
.size(IconSize::Small)
.color(Color::Muted),
)
.child(
Label::new("Thinking…").color(Color::Muted).with_animation(
"pulsating-label",
Animation::new(Duration::from_secs(2))
.repeat()
.with_easing(pulsating_between(0.4, 0.8)),
|label, delta| label.alpha(delta),
),
),
ThoughtProcessStatus::Completed => button
.style(ButtonStyle::Filled)
.child(Icon::new(IconName::Brain).size(IconSize::Small))
.child(Label::new("Thought Process").single_line()),
};
button
.on_click(move |_, window, cx| {
editor
.update(cx, |editor, cx| {
let buffer_start = fold_range
.start
.to_point(&editor.buffer().read(cx).read(cx));
let buffer_row = MultiBufferRow(buffer_start.row);
editor.unfold_at(&UnfoldAt { buffer_row }, window, cx);
})
.ok();
})
.into_any_element()
})
}
fn render_fold_icon_button(
editor: WeakEntity<Editor>,
icon: IconName,
@@ -3162,10 +3318,10 @@ impl FollowableItem for ContextEditor {
assistant_panel_delegate.open_remote_context(workspace, context_id, window, cx)
});
Some(window.spawn(cx, |mut cx| async move {
Some(window.spawn(cx, async move |cx| {
let context_editor = context_editor_task.await?;
context_editor
.update_in(&mut cx, |context_editor, window, cx| {
.update_in(cx, |context_editor, window, cx| {
context_editor.remote_id = Some(id);
context_editor.editor.update(cx, |editor, cx| {
editor.apply_update_proto(

View File

@@ -164,9 +164,9 @@ impl PickerDelegate for SavedContextPickerDelegate {
cx: &mut Context<Picker<Self>>,
) -> Task<()> {
let search = self.store.read(cx).search(query, cx);
cx.spawn(|this, mut cx| async move {
cx.spawn(async move |this, cx| {
let matches = search.await;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
let host_contexts = this.delegate.store.read(cx).host_contexts();
this.delegate.matches = host_contexts
.iter()

View File

@@ -100,7 +100,7 @@ impl ContextStore {
let fs = project.read(cx).fs().clone();
let languages = project.read(cx).languages().clone();
let telemetry = project.read(cx).client().telemetry().clone();
cx.spawn(|mut cx| async move {
cx.spawn(async move |cx| {
const CONTEXT_WATCH_DURATION: Duration = Duration::from_millis(100);
let (mut events, _) = fs.watch(contexts_dir(), CONTEXT_WATCH_DURATION).await;
@@ -125,16 +125,15 @@ impl ContextStore {
languages,
slash_commands,
telemetry,
_watch_updates: cx.spawn(|this, mut cx| {
_watch_updates: cx.spawn(async move |this, cx| {
async move {
while events.next().await.is_some() {
this.update(&mut cx, |this, cx| this.reload(cx))?
.await
.log_err();
this.update(cx, |this, cx| this.reload(cx))?.await.log_err();
}
anyhow::Ok(())
}
.log_err()
.await
}),
client_subscription: None,
_project_subscriptions: vec![
@@ -395,7 +394,7 @@ impl ContextStore {
let prompt_builder = self.prompt_builder.clone();
let slash_commands = self.slash_commands.clone();
let request = self.client.request(proto::CreateContext { project_id });
cx.spawn(|this, mut cx| async move {
cx.spawn(async move |this, cx| {
let response = request.await?;
let context_id = ContextId::from_proto(response.context_id);
let context_proto = response.context.context("invalid context")?;
@@ -421,8 +420,8 @@ impl ContextStore {
.collect::<Result<Vec<_>>>()
})
.await?;
context.update(&mut cx, |context, cx| context.apply_ops(operations, cx))?;
this.update(&mut cx, |this, cx| {
context.update(cx, |context, cx| context.apply_ops(operations, cx))?;
this.update(cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) {
existing_context
} else {
@@ -457,7 +456,7 @@ impl ContextStore {
let prompt_builder = self.prompt_builder.clone();
let slash_commands = self.slash_commands.clone();
cx.spawn(|this, mut cx| async move {
cx.spawn(async move |this, cx| {
let saved_context = load.await?;
let context = cx.new(|cx| {
AssistantContext::deserialize(
@@ -471,7 +470,7 @@ impl ContextStore {
cx,
)
})?;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_path(&path, cx) {
existing_context
} else {
@@ -489,7 +488,7 @@ impl ContextStore {
) -> Task<Result<()>> {
let fs = self.fs.clone();
cx.spawn(|this, mut cx| async move {
cx.spawn(async move |this, cx| {
fs.remove_file(
&path,
RemoveOptions {
@@ -499,7 +498,7 @@ impl ContextStore {
)
.await?;
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
this.contexts.retain(|context| {
context
.upgrade()
@@ -565,7 +564,7 @@ impl ContextStore {
});
let prompt_builder = self.prompt_builder.clone();
let slash_commands = self.slash_commands.clone();
cx.spawn(|this, mut cx| async move {
cx.spawn(async move |this, cx| {
let response = request.await?;
let context_proto = response.context.context("invalid context")?;
let context = cx.new(|cx| {
@@ -590,8 +589,8 @@ impl ContextStore {
.collect::<Result<Vec<_>>>()
})
.await?;
context.update(&mut cx, |context, cx| context.apply_ops(operations, cx))?;
this.update(&mut cx, |this, cx| {
context.update(cx, |context, cx| context.apply_ops(operations, cx))?;
this.update(cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) {
existing_context
} else {
@@ -700,12 +699,12 @@ impl ContextStore {
project_id,
contexts,
});
cx.spawn(|this, cx| async move {
cx.spawn(async move |this, cx| {
let response = request.await?;
let mut context_ids = Vec::new();
let mut operations = Vec::new();
this.read_with(&cx, |this, cx| {
this.read_with(cx, |this, cx| {
for context_version_proto in response.contexts {
let context_version = ContextVersion::from_proto(&context_version_proto);
let context_id = ContextId::from_proto(context_version_proto.context_id);
@@ -768,7 +767,7 @@ impl ContextStore {
fn reload(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
let fs = self.fs.clone();
cx.spawn(|this, mut cx| async move {
cx.spawn(async move |this, cx| {
fs.create_dir(contexts_dir()).await?;
let mut paths = fs.read_dir(contexts_dir()).await?;
@@ -808,7 +807,7 @@ impl ContextStore {
}
contexts.sort_unstable_by_key(|context| Reverse(context.mtime));
this.update(&mut cx, |this, cx| {
this.update(cx, |this, cx| {
this.contexts_metadata = contexts;
cx.notify();
})
@@ -819,7 +818,7 @@ impl ContextStore {
cx.update_entity(
&self.context_server_manager,
|context_server_manager, cx| {
for server in context_server_manager.servers() {
for server in context_server_manager.running_servers() {
context_server_manager
.restart_server(&server.id(), cx)
.detach_and_log_err(cx);
@@ -850,7 +849,7 @@ impl ContextStore {
cx.spawn({
let server = server.clone();
let server_id = server_id.clone();
|this, mut cx| async move {
async move |this, cx| {
let Some(protocol) = server.client() else {
return;
};
@@ -875,7 +874,7 @@ impl ContextStore {
})
.collect::<Vec<_>>();
this.update(&mut cx, |this, _cx| {
this.update( cx, |this, _cx| {
this.context_server_slash_command_ids
.insert(server_id.clone(), slash_command_ids);
})

View File

@@ -2,7 +2,7 @@ use crate::context_editor::ContextEditor;
use anyhow::Result;
pub use assistant_slash_command::SlashCommand;
use assistant_slash_command::{AfterCompletion, SlashCommandLine, SlashCommandWorkingSet};
use editor::{CompletionProvider, Editor};
use editor::{CompletionProvider, Editor, ExcerptId};
use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{App, AppContext as _, Context, Entity, Task, WeakEntity, Window};
use language::{Anchor, Buffer, ToPoint};
@@ -48,7 +48,7 @@ impl SlashCommandCompletionProvider {
name_range: Range<Anchor>,
window: &mut Window,
cx: &mut App,
) -> Task<Result<Vec<project::Completion>>> {
) -> Task<Result<Option<Vec<project::Completion>>>> {
let slash_commands = self.slash_commands.clone();
let candidates = slash_commands
.command_names(cx)
@@ -59,7 +59,7 @@ impl SlashCommandCompletionProvider {
let command_name = command_name.to_string();
let editor = self.editor.clone();
let workspace = self.workspace.clone();
window.spawn(cx, |mut cx| async move {
window.spawn(cx, async move |cx| {
let matches = match_strings(
&candidates,
&command_name,
@@ -71,70 +71,72 @@ impl SlashCommandCompletionProvider {
.await;
cx.update(|_, cx| {
matches
.into_iter()
.filter_map(|mat| {
let command = slash_commands.command(&mat.string, cx)?;
let mut new_text = mat.string.clone();
let requires_argument = command.requires_argument();
let accepts_arguments = command.accepts_arguments();
if requires_argument || accepts_arguments {
new_text.push(' ');
}
Some(
matches
.into_iter()
.filter_map(|mat| {
let command = slash_commands.command(&mat.string, cx)?;
let mut new_text = mat.string.clone();
let requires_argument = command.requires_argument();
let accepts_arguments = command.accepts_arguments();
if requires_argument || accepts_arguments {
new_text.push(' ');
}
let confirm =
editor
.clone()
.zip(workspace.clone())
.map(|(editor, workspace)| {
let command_name = mat.string.clone();
let command_range = command_range.clone();
let editor = editor.clone();
let workspace = workspace.clone();
Arc::new(
move |intent: CompletionIntent,
window: &mut Window,
cx: &mut App| {
if !requires_argument
&& (!accepts_arguments || intent.is_complete())
{
editor
.update(cx, |editor, cx| {
editor.run_command(
command_range.clone(),
&command_name,
&[],
true,
workspace.clone(),
window,
cx,
);
})
.ok();
false
} else {
requires_argument || accepts_arguments
}
},
) as Arc<_>
});
Some(project::Completion {
old_range: name_range.clone(),
documentation: Some(CompletionDocumentation::SingleLine(
command.description().into(),
)),
new_text,
label: command.label(cx),
confirm,
source: CompletionSource::Custom,
let confirm =
editor
.clone()
.zip(workspace.clone())
.map(|(editor, workspace)| {
let command_name = mat.string.clone();
let command_range = command_range.clone();
let editor = editor.clone();
let workspace = workspace.clone();
Arc::new(
move |intent: CompletionIntent,
window: &mut Window,
cx: &mut App| {
if !requires_argument
&& (!accepts_arguments || intent.is_complete())
{
editor
.update(cx, |editor, cx| {
editor.run_command(
command_range.clone(),
&command_name,
&[],
true,
workspace.clone(),
window,
cx,
);
})
.ok();
false
} else {
requires_argument || accepts_arguments
}
},
) as Arc<_>
});
Some(project::Completion {
old_range: name_range.clone(),
documentation: Some(CompletionDocumentation::SingleLine(
command.description().into(),
)),
new_text,
label: command.label(cx),
icon_path: None,
confirm,
source: CompletionSource::Custom,
})
})
})
.collect()
.collect(),
)
})
})
}
#[allow(clippy::too_many_arguments)]
fn complete_command_argument(
&self,
command_name: &str,
@@ -144,7 +146,7 @@ impl SlashCommandCompletionProvider {
last_argument_range: Range<Anchor>,
window: &mut Window,
cx: &mut App,
) -> Task<Result<Vec<project::Completion>>> {
) -> Task<Result<Option<Vec<project::Completion>>>> {
let new_cancel_flag = Arc::new(AtomicBool::new(false));
let mut flag = self.cancel_flag.lock();
flag.store(true, SeqCst);
@@ -162,27 +164,28 @@ impl SlashCommandCompletionProvider {
let workspace = self.workspace.clone();
let arguments = arguments.to_vec();
cx.background_spawn(async move {
Ok(completions
.await?
.into_iter()
.map(|new_argument| {
let confirm =
editor
.clone()
.zip(workspace.clone())
.map(|(editor, workspace)| {
Arc::new({
let mut completed_arguments = arguments.clone();
if new_argument.replace_previous_arguments {
completed_arguments.clear();
} else {
completed_arguments.pop();
}
completed_arguments.push(new_argument.new_text.clone());
Ok(Some(
completions
.await?
.into_iter()
.map(|new_argument| {
let confirm =
editor
.clone()
.zip(workspace.clone())
.map(|(editor, workspace)| {
Arc::new({
let mut completed_arguments = arguments.clone();
if new_argument.replace_previous_arguments {
completed_arguments.clear();
} else {
completed_arguments.pop();
}
completed_arguments.push(new_argument.new_text.clone());
let command_range = command_range.clone();
let command_name = command_name.clone();
move |intent: CompletionIntent,
let command_range = command_range.clone();
let command_name = command_name.clone();
move |intent: CompletionIntent,
window: &mut Window,
cx: &mut App| {
if new_argument.after_completion.run()
@@ -206,31 +209,33 @@ impl SlashCommandCompletionProvider {
!new_argument.after_completion.run()
}
}
}) as Arc<_>
});
}) as Arc<_>
});
let mut new_text = new_argument.new_text.clone();
if new_argument.after_completion == AfterCompletion::Continue {
new_text.push(' ');
}
let mut new_text = new_argument.new_text.clone();
if new_argument.after_completion == AfterCompletion::Continue {
new_text.push(' ');
}
project::Completion {
old_range: if new_argument.replace_previous_arguments {
argument_range.clone()
} else {
last_argument_range.clone()
},
label: new_argument.label,
new_text,
documentation: None,
confirm,
source: CompletionSource::Custom,
}
})
.collect())
project::Completion {
old_range: if new_argument.replace_previous_arguments {
argument_range.clone()
} else {
last_argument_range.clone()
},
label: new_argument.label,
icon_path: None,
new_text,
documentation: None,
confirm,
source: CompletionSource::Custom,
}
})
.collect(),
))
})
} else {
Task::ready(Ok(Vec::new()))
Task::ready(Ok(Some(Vec::new())))
}
}
}
@@ -238,12 +243,13 @@ impl SlashCommandCompletionProvider {
impl CompletionProvider for SlashCommandCompletionProvider {
fn completions(
&self,
_excerpt_id: ExcerptId,
buffer: &Entity<Buffer>,
buffer_position: Anchor,
_: editor::CompletionContext,
window: &mut Window,
cx: &mut Context<Editor>,
) -> Task<Result<Vec<project::Completion>>> {
) -> Task<Result<Option<Vec<project::Completion>>>> {
let Some((name, arguments, command_range, last_argument_range)) =
buffer.update(cx, |buffer, _cx| {
let position = buffer_position.to_point(buffer);
@@ -287,7 +293,7 @@ impl CompletionProvider for SlashCommandCompletionProvider {
Some((name, arguments, command_range, last_argument_range))
})
else {
return Task::ready(Ok(Vec::new()));
return Task::ready(Ok(Some(Vec::new())));
};
if let Some((arguments, argument_range)) = arguments {

View File

@@ -100,7 +100,7 @@ impl PickerDelegate for SlashCommandDelegate {
cx: &mut Context<Picker<Self>>,
) -> Task<()> {
let all_commands = self.all_commands.clone();
cx.spawn_in(window, |this, mut cx| async move {
cx.spawn_in(window, async move |this, cx| {
let filtered_commands = cx
.background_spawn(async move {
if query.is_empty() {
@@ -119,7 +119,7 @@ impl PickerDelegate for SlashCommandDelegate {
})
.await;
this.update_in(&mut cx, |this, window, cx| {
this.update_in(cx, |this, window, cx| {
this.delegate.filtered_commands = filtered_commands;
this.delegate.set_selected_index(0, window, cx);
cx.notify();

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