Compare commits

..

316 Commits

Author SHA1 Message Date
Richard Feldman
44501581ee Start on streaming JSON 2025-03-07 10:14:16 -05:00
Richard Feldman
ae95142cc8 Got basic chunk streaming working 2025-03-07 00:23:37 -05:00
Richard Feldman
b1b8d596b9 Use full_moon for lexing 2025-03-06 21:53:18 -05:00
Mikayla Maki
6c025507b6 Restore co-author hiding (#26257)
Release Notes:

- N/A
2025-03-07 01:40:17 +00:00
Marshall Bowers
8f4b7aa5db Improve the generate commit message design (#26233)
[WIP]

Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-03-07 01:21:20 +00:00
Cole Miller
3345666557 Fix paths on Windows in new test (#26255)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-03-06 20:08:13 -05:00
Max Brunsfeld
40c62cda5f Fix early return when reaching end excerpt in lift_buffer_metadata (#26253)
Release Notes:

- Fixed a bug causing slowness when viewing multi buffers with lots of
excerpts
2025-03-06 16:25:27 -08:00
Marshall Bowers
349a48d937 lua: Extract to zed-extensions/lua repository (#26250)
This PR extracts the Lua extension to the
[zed-extensions/lua](https://github.com/zed-extensions/lua) repository.

Release Notes:

- N/A
2025-03-06 23:17:34 +00:00
Cole Miller
a88af7351a Disable restore hunk control for created files (#25841)
Release Notes:

- Git Beta: disable hunk restore action and button for created files
2025-03-06 23:06:03 +00:00
Finn Evers
efaf358876 lua: Update keyword operator highlighting (#26091)
Resolves #26032 

This PR changes the highlighting for `and`, `not` and `or` in Lua from
`operator` to `keyword.operator`. [VS Code also highlights these as
keyword
operators](1483add845/Syntaxes/Lua.plist (L277-L279))
and Zed does this for other languages as well, like
[Python](813e207514/crates/languages/src/python/highlights.scm (L221-L229))
or
[Zig](813e207514/extensions/zig/languages/zig/highlights.scm (L145-L149)).

Additionally, in 813e207514 I removed
duplicate matches for existing keywords to improve readability and align
them to how keywords are generally matched across languages (see
[Rust](813e207514/crates/languages/src/rust/highlights.scm (L79-L119))
and
[Typescript](813e207514/crates/languages/src/typescript/highlights.scm (L210-L269))
for example).
Whilst contributing to the majority of the diff, this does not change
any existing highlights.

| Before | After | 
| --- | --- |
| <img width="309" alt="old"
src="https://github.com/user-attachments/assets/7790817e-4a0d-442b-b176-9a84bcc6f3c4"
/> | <img width="309" alt="PR"
src="https://github.com/user-attachments/assets/34a57962-938a-4465-9406-288f5c456aa3"
/> |


Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-03-06 18:01:13 -05:00
Marshall Bowers
06a226dc32 editor: Remove some blank lines (#26249)
This PR removes some blank lines in `blink_manager.rs`.

Release Notes:

- N/A
2025-03-06 22:58:46 +00:00
Cole Miller
1763dd714b Worktree paths in git panel, take 2 (#26047)
Modified version of #25950. We still use worktree paths, but repo paths
with a status that lie outside the worktree are not excluded; instead,
we relativize them by adding `..`. This makes the list in the git panel
match what you'd get from running `git status` (with the repo's worktree
root as the working directory).

- [x] Implement + test new unrelativization logic
- [x] ~~When collecting repositories, dedup by .git abs path, so
worktrees can share a repo at the project level~~ dedup repos at the
repository selector layer, with repos coming from larger worktrees being
preferred
- [x] Open single-file worktree with diff when activating a path not in
the worktree

Release Notes:

- N/A
2025-03-06 22:55:28 +00:00
Marshall Bowers
330e799293 erlang: Extract to zed-extensions/erlang repository (#26248)
This PR extracts the Erlang extension to the
[zed-extensions/erlang](https://github.com/zed-extensions/erlang)
repository.

Release Notes:

- N/A
2025-03-06 22:53:13 +00:00
Max Brunsfeld
51c900366d Enable soft-wrap by default in markdown (#26247)
Release Notes:

- Enabled soft-wrap by default in markdown
2025-03-06 22:30:26 +00:00
Max Brunsfeld
be75f17429 Fix auto-indent when pasting multi-line content that was copied start… (#26246)
Closes https://github.com/zed-industries/zed/issues/24914 (again)

Release Notes:

- Fixed an issue where multi-line pasted content was auto-indented
incorrectly if copied from the middle of an existing line.
2025-03-06 22:13:34 +00:00
Conrad Irwin
f373383fc1 Track dirtyness per item (#26237)
This reduces the number of multibuffer syncs when starting the editor
with 80
files open in the Zed repo from 10,000,000 to 100,000 by avoiding
O(n**2)
dirtyness checks.

Release Notes:

- Fixed a beachball when restarting in a large repo with a large number
open files
2025-03-06 15:12:56 -07:00
Joseph T. Lyons
263d9ff755 Add event to track LLM-generated commit messages (#26245)
Release Notes:

- N/A
2025-03-06 21:46:57 +00:00
Naim A.
829ecda370 lsp: Add support for clangd's inactiveRegions extension (#26146)
Closes #13089 

Here we use `experimental` to advertise our support for
`inactiveRegions`. Note that clangd does not currently have a stable
release that reads the `experimental` object (PR
https://github.com/llvm/llvm-project/pull/116531), this can be tested
with one of clangd's recent "unstable snapshots" in their
[releases](https://github.com/clangd/clangd/releases).

Release Notes:

- Added support for clangd's `inactiveRegions` extension.

![Screen Recording 2025-03-05 at 22 39
58](https://github.com/user-attachments/assets/ceade8bd-4d8e-43c3-9520-ad44efa50d2f)

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-03-06 21:30:05 +00:00
Kirill Bulatov
af5af9d7c5 Support workspace/executeCommand for actions' data (#26239)
Closes https://github.com/zed-industries/zed/issues/16746
Part of https://github.com/zed-extensions/deno/issues/2

Changes the action-related code so, that

* `lsp::Command` as actions are supported, if server replies with them
* actions with commands are filtered out based on servers'
`executeCommandOptions`
(https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#executeCommandOptions)
— commands that are not listed won't be executed and the corresponding
actions will be hidden in Zed

Release Notes:

- Added support of `workspace/executeCommand` for actions' data

---------

Co-authored-by: Peter Tripp <petertripp@gmail.com>
2025-03-06 23:26:46 +02:00
Marshall Bowers
97c0a0a86e language_models: Remove .unwraps in Bedrock provider (#26238)
This PR removes a number of `.unwrap`s in the Bedrock provider.

We must not `.unwrap` in situations where it is not provably safe to do
so, which it was not in any of these cases.

Release Notes:

- Fixed some potential panics in the AWS Bedrock model provider.
2025-03-06 21:02:37 +00:00
Nate Butler
7e964290bf Add StatusToast & the ToastLayer (#26232)
https://github.com/user-attachments/assets/b16e32e6-46c6-41dc-ab68-1824d288c8c2

This PR adds the first part of our planned extended notification system:
StatusToasts.

It also makes various updates to ComponentPreview and adds a `Styled`
extension in `ui::style::animation` to make it easier to animate styled
elements.

_**Note**: We will be very, very selective with what elements are
allowed to be animated in Zed. Assume PRs adding animation to elements
will all need to be manually signed off on by a designer._

## Status Toast

![CleanShot 2025-03-06 at 14 15
52@2x](https://github.com/user-attachments/assets/b65d4661-f8d1-4e98-b9be-2c05cba1409f)

These are designed to be used for notifying about things that don't
require an action to be taken or don't need to be triaged. They are
designed to be ignorable, and dismiss themselves automatically after a
set time.

They can optionally include a single action. 

Example: When the user enables Vim Mode, that action might let them undo
enabling it.

![CleanShot 2025-03-06 at 14 18
34@2x](https://github.com/user-attachments/assets/eb6cb20e-c968-4f03-88a5-ecb6a8809150)

Status Toasts should _not_ be used when an action is required, or for
any binary choice.

If the user must provide some input, this isn't the right component!

### Out of scope

- Toasts should fade over a short time (like AnimationDuration::Fast or
Instant) when dismissed
- We should visually show when the toast will dismiss. We'll need to
pipe the `duration_remaining` from the toast layer -> ActiveToast to do
this.
- Dismiss any active toast if another notification kind is created, like
a Notification or Alert.

Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
2025-03-06 20:37:54 +00:00
Marshall Bowers
b8a8b9c699 git_ui: Add support for generating commit messages with an LLM (#26227)
This PR finishes up the support for generating commit messages using an
LLM.

We're shelling out to `git diff` to get the diff text, as it seemed more
efficient than attempting to reconstruct the diff ourselves from our
internal Git state.


https://github.com/user-attachments/assets/9bcf30a7-7a08-4f49-a753-72a5d954bddd

Release Notes:

- Git Beta: Added support for generating commit messages using a
language model.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-03-06 19:47:52 +00:00
Danilo Leal
d1cec209d4 gpui: Add rounded_md token (#26179)
This PR adds a new rounded/corner border token: `rounded_md` with a
value of 6px.

I feel like I was wanting to use 6px border radius a lot but avoiding
due to it being an arbitrary value... so, not anymore! It's also cool to
have this be consistent with Tailwind v4.

Follow on to the prior renames:

- `rounded_sm` -> `rounded_xs`:
https://github.com/zed-industries/zed/pull/26221
- `rounded_md` -> `rounded_sm`:
https://github.com/zed-industries/zed/pull/26228

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-03-06 13:41:21 -05:00
Marshall Bowers
aceab76ae4 gpui: Rename rounded_md to rounded_sm (#26228)
This PR renames the `rounded_md` style method to `rounded_sm`.

Follow up to https://github.com/zed-industries/zed/pull/26221, which
freed up the `rounded_sm` name.

Release Notes:

- N/A
2025-03-06 17:57:31 +00:00
Conrad Irwin
9c054f207e Git telemetry (#26222)
Release Notes:

- git: Adds telemetry to git actions
2025-03-06 10:56:28 -07:00
smit
219d36f589 migrator: Add versioned migrations (#26215)
There is a drawback to how we currently write our migrations:

For example:

1. Suppose we change one of our actions from a string to an array and
rename it, then roll out the preview build:

      Before: `"ctrl-x": "editor::GoToPrevHunk"`
Latest: `"ctrl-x": ["editor::GoToPreviousHunk", { "center_cursor": true
}]`
      
To handle this, we wrote migration `A` to convert the string to an
array.

2. Now, suppose we decide to change it back to a string:
- User who hasn't migrated yet on Preview: `"ctrl-x":
"editor::GoToPrevHunk"`
- User who has migrated on Preview: `"ctrl-x":
["editor::GoToPreviousHunk", { "center_cursor": true }]`
    - Latest: `"ctrl-x": "editor::GoToPreviousHunk"`

To handle this, we would need to remove migration `A` and add two more
migrations:
- **Migration B**: `"ctrl-x": "editor::GoToPrevHunk"` -> `"ctrl-x":
"editor::GoToPreviousHunk"`
- **Migration C**: `"ctrl-x": ["editor::GoToPreviousHunk", {
"center_cursor": true }]` -> `"ctrl-x": "editor::GoToPreviousHunk"`

Nice. But over time, this keeps increasing, making it impossible to
track outdated versions and handle all cases. Missing a case means users
stuck on `"ctrl-x": "editor::GoToPrevHunk"` will remain there and won't
be automatically migrated to the latest state.

---

To fix this, we introduce versioned migrations. Instead of removing
migration `A`, we simply write a new migration that takes the user to
the latest version—i.e., in this case, migration `C`.

- A user who hasn't migrated before will go through both migrations `A`
and `C` in order.
- A user who has already migrated will only go through `C`, since `A`
wouldn't change anything for them.

With incremental migrations, we only need to write migrations on top of
the latest state (big win!), as know internally they all would be on
latest state. You *must not* modify previous migrations. Always create
new ones instead.

This also serves as base for only prompting user to migrate, when
feature reaches stable. That way, preview and stable keymap and settings
are in sync.

cc: @mgsloan @ConradIrwin @probably-neb 

Release Notes:

- N/A
2025-03-06 23:04:48 +05:30
Marshall Bowers
6fd9708eee extension: Add capabilities for the process API (#26224)
This PR adds support for capabilities for the extension process API.

In order to use the process API, an extension must declare which
commands it wants to use, with arguments:

```toml
[[capabilities]]
kind = "process:exec"
command = "echo"
args = ["hello!"]
```

A `*` can be used to denote a single wildcard in the argument list:

```toml
[[capabilities]]
kind = "process:exec"
command = "echo"
args = ["*"]
```

And `**` can be used to denote a wildcard for the remaining arguments:

```toml
[[capabilities]]
kind = "process:exec"
command = "ls"
args = ["-a", "**"]
```

Release Notes:

- N/A

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-03-06 11:55:00 -05:00
Marshall Bowers
99216acdec gpui: Rename rounded_sm to rounded_xs (#26221)
This PR renames the `rounded_sm` style method to `rounded_xs`.

This will allow us to add an additional step in the scale.

Release Notes:

- N/A
2025-03-06 16:08:19 +00:00
Chris Boette
aef25a3bc3 slash_commands_example: Improve setup instructions in README (#26217)
This PR improves the setup instructions for the slash-commands-example
extension by:

1. Replacing the `sed` command with a more reliable approach that
completely replaces the Cargo.toml file.

2. Explicitly showing how to create a standalone extension with a
properly configured Cargo.toml file that:
   - Uses `edition = "2021"` instead of `edition.workspace = true`
   - Doesn't include `publish.workspace = true`
   - Doesn't include the `[lints]` section

This change addresses an issue where the extension wouldn't work when
copied as a standalone project due to workspace references that are only
valid when the extension is built as part of the main Zed repository.

The updated instructions provide a clear, reliable path for developers
to create their own Zed extensions based on the slash commands example.

Release Notes:

- N/A
2025-03-06 10:56:17 -05:00
greathongtu
9b07f36199 gpui: Fix Cut action in input example (#26203)
Zed fan trying to learn GPUI here. Notice one problem in input example
which cause cmd-x function not work.
Let me know if any adjustments are needed!

Release Notes:

- N/A
2025-03-06 10:02:52 -05:00
Ben Kunkle
ff25fa24e7 Add support for auto-closing of JSX tags (#25681)
Closes #4271

Implemented by kicking of a task on the main thread at the end of
`Editor::handle_input` which waits for the buffer to be re-parsed before
checking if JSX tag completion possible based on the recent edits, and
if it is then it spawns a task on the background thread to generate the
edits to be auto-applied to the buffer

Release Notes:

- Added support for auto-closing of JSX tags

---------

Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Max Brunsfeld <max@zed.dev>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Peter Tripp <peter@zed.dev>
2025-03-06 08:36:10 -06:00
张小白
05df3d1bd6 windows: Dock menu impl 2 (#26010)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-03-06 12:40:34 +00:00
Piotr Osiewicz
84f4d2630f node_runtime: Use user/global configuration when using system node installation (#26209)
This partially reverts https://github.com/zed-industries/zed/pull/3324
We will still blank out user/global config when running managed NPM, to
keep to the spirit of #3324 (which was made at the time we did not allow
user-provided NPM builds - the intent of the change was to make the
behavior of NPM as consistent as possible).

I tested this change by:
1. Setting up a custom NPM registry via Versaccio
2. Adding this new registry to my .npmrc
3. Mirroring `vscode-langservers-extracted` to it 
4. Blocking access to `registry.npmjs.org`
5. Opening up settings.json file in Zed Nightly
- Verifying that language server update fails for it
6. Opening up Zed Dev build of this branch
- Confirming that language server update check goes through for it

Closes #19806
Closes #20749
Closes #9422

Release Notes:

- User and global .npmrc configuration is now respected when running
user-provided NPM binary (which also happens automatically when `npm`
from PATH is newer than 18.0.0)
2025-03-06 12:50:42 +01:00
renovate[bot]
b42930f5be Update Rust crate embed-resource to v3.0.2 (#26171)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[embed-resource](https://redirect.github.com/nabijaczleweli/rust-embed-resource)
| build-dependencies | patch | `3.0.1` -> `3.0.2` |

---

### Release Notes

<details>
<summary>nabijaczleweli/rust-embed-resource (embed-resource)</summary>

###
[`v3.0.2`](https://redirect.github.com/nabijaczleweli/rust-embed-resource/compare/v3.0.1...v3.0.2)

[Compare
Source](https://redirect.github.com/nabijaczleweli/rust-embed-resource/compare/v3.0.1...v3.0.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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 12:31:11 +02:00
renovate[bot]
cb2eef6fb6 Update Rust crate chrono to v0.4.40 (#26170)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [chrono](https://redirect.github.com/chronotope/chrono) |
workspace.dependencies | patch | `0.4.39` -> `0.4.40` |

---

### Release Notes

<details>
<summary>chronotope/chrono (chrono)</summary>

###
[`v0.4.40`](https://redirect.github.com/chronotope/chrono/releases/tag/v0.4.40):
0.4.40

[Compare
Source](https://redirect.github.com/chronotope/chrono/compare/v0.4.39...v0.4.40)

#### What's Changed

- Add Month::num_days() by
[@&#8203;djc](https://redirect.github.com/djc) in
[https://github.com/chronotope/chrono/pull/1645](https://redirect.github.com/chronotope/chrono/pull/1645)
- Update Windows dependencies by
[@&#8203;kennykerr](https://redirect.github.com/kennykerr) in
[https://github.com/chronotope/chrono/pull/1646](https://redirect.github.com/chronotope/chrono/pull/1646)
- Feature/round_up method on DurationRound trait by
[@&#8203;MagnumTrader](https://redirect.github.com/MagnumTrader) in
[https://github.com/chronotope/chrono/pull/1651](https://redirect.github.com/chronotope/chrono/pull/1651)
- Expose `write_to` for `DelayedFormat` by
[@&#8203;tugtugtug](https://redirect.github.com/tugtugtug) in
[https://github.com/chronotope/chrono/pull/1654](https://redirect.github.com/chronotope/chrono/pull/1654)
- Update LICENSE.txt by
[@&#8203;maximevtush](https://redirect.github.com/maximevtush) in
[https://github.com/chronotope/chrono/pull/1656](https://redirect.github.com/chronotope/chrono/pull/1656)
- docs: fix minor typo by
[@&#8203;samfolo](https://redirect.github.com/samfolo) in
[https://github.com/chronotope/chrono/pull/1659](https://redirect.github.com/chronotope/chrono/pull/1659)
- Use NaiveDateTime for internal tz_info methods. by
[@&#8203;AVee](https://redirect.github.com/AVee) in
[https://github.com/chronotope/chrono/pull/1658](https://redirect.github.com/chronotope/chrono/pull/1658)
- Upgrade to windows-bindgen 0.60 by
[@&#8203;djc](https://redirect.github.com/djc) in
[https://github.com/chronotope/chrono/pull/1665](https://redirect.github.com/chronotope/chrono/pull/1665)
- Add quarter (%q) date string specifier by
[@&#8203;drinkcat](https://redirect.github.com/drinkcat) in
[https://github.com/chronotope/chrono/pull/1666](https://redirect.github.com/chronotope/chrono/pull/1666)

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 12:30:56 +02:00
renovate[bot]
73668c2c4b Update Rust crate async-compression to v0.4.20 (#26154)
This PR contains the following updates:

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

---

### Release Notes

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

###
[`v0.4.20`](https://redirect.github.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#0420---2025-02-28)

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

##### Added

-   Add support for `wasm32-wasip1-*` targets.

###
[`v0.4.19`](https://redirect.github.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#0419---2025-02-27)

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

##### Changed

-   Update `bzip2` dependency to `0.5`.

##### Fixed

-   Ensure that flush finishes before continuing.

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 12:30:38 +02:00
renovate[bot]
07f555ca3b Update Rust crate cargo_metadata to v0.19.2 (#26165)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [cargo_metadata](https://redirect.github.com/oli-obk/cargo_metadata) |
workspace.dependencies | patch | `0.19.1` -> `0.19.2` |

---

### Release Notes

<details>
<summary>oli-obk/cargo_metadata (cargo_metadata)</summary>

###
[`v0.19.2`](https://redirect.github.com/oli-obk/cargo_metadata/compare/0.19.1...0.19.2)

[Compare
Source](https://redirect.github.com/oli-obk/cargo_metadata/compare/0.19.1...0.19.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.

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 09:24:48 +00:00
renovate[bot]
b0e2b57462 Update Rust crate linkme to v0.3.32 (#26191)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [linkme](https://redirect.github.com/dtolnay/linkme) |
workspace.dependencies | patch | `0.3.31` -> `0.3.32` |

---

### Release Notes

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

###
[`v0.3.32`](https://redirect.github.com/dtolnay/linkme/releases/tag/0.3.32)

[Compare
Source](https://redirect.github.com/dtolnay/linkme/compare/0.3.31...0.3.32)

-   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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 11:06:55 +02:00
renovate[bot]
d404d7964e Update Rust crate blake3 to v1.6.1 (#26159)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

version 1.6.1

Changes since 1.6.0:

-   Remove `mmap` from the default features list. It was added
    accidentally in v1.6.0, last week. This is technically a
backwards-incompatible change, but I would rather not tag v2.0.0 for a
    build-time bugfix with a simple workaround.

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 11:06:06 +02:00
renovate[bot]
69af9bedaf Update Rust crate oo7 to v0.4.1 (#26192)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [oo7](https://redirect.github.com/bilelmoussaoui/oo7) | 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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 11:05:44 +02:00
Conrad Irwin
2ebbcf15ea Don't deleete non-extant files (#26187)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-03-05 22:45:25 -07:00
renovate[bot]
631cab5e40 Update Rust crate indoc to v2.0.6 (#26177)
This PR contains the following updates:

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

---

### Release Notes

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

###
[`v2.0.6`](https://redirect.github.com/dtolnay/indoc/releases/tag/2.0.6)

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

-   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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 05:39:10 +00:00
renovate[bot]
ba39a47c52 Update Rust crate libc to v0.2.170 (#26181)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

##### Added

- Android: Declare `setdomainname` and `getdomainname`
[#&#8203;4212](https://redirect.github.com/rust-lang/libc/pull/4212)
- FreeBSD: Add `evdev` structures
[#&#8203;3756](https://redirect.github.com/rust-lang/libc/pull/3756)
- FreeBSD: Add the new `st_filerev` field to `stat32`
([#&#8203;4254](https://redirect.github.com/rust-lang/libc/pull/4254))
- Linux: Add ` SI_*`` and `TRAP_\*\`\` signal codes
[#&#8203;4225](https://redirect.github.com/rust-lang/libc/pull/4225)
- Linux: Add experimental configuration to enable 64-bit time in kernel
APIs, set by `RUST_LIBC_UNSTABLE_LINUX_TIME_BITS64`.
[#&#8203;4148](https://redirect.github.com/rust-lang/libc/pull/4148)
- Linux: Add recent socket timestamping flags
[#&#8203;4273](https://redirect.github.com/rust-lang/libc/pull/4273)
- Linux: Added new CANFD_FDF flag for the flags field of canfd_frame
[#&#8203;4223](https://redirect.github.com/rust-lang/libc/pull/4223)
- Musl: add CLONE_NEWTIME
[#&#8203;4226](https://redirect.github.com/rust-lang/libc/pull/4226)
- Solarish: add the posix_spawn family of functions
[#&#8203;4259](https://redirect.github.com/rust-lang/libc/pull/4259)

##### Deprecated

- Linux: deprecate kernel modules syscalls
[#&#8203;4228](https://redirect.github.com/rust-lang/libc/pull/4228)

##### Changed

- Emscripten: Assume version is at least 3.1.42
[#&#8203;4243](https://redirect.github.com/rust-lang/libc/pull/4243)

##### Fixed

- BSD: Correct the definition of `WEXITSTATUS`
[#&#8203;4213](https://redirect.github.com/rust-lang/libc/pull/4213)
- Hurd: Fix CMSG_DATA on 64bit systems
([#&#8203;4240](https://redirect.github.com/rust-lang/libc/pull/424))
- NetBSD: fix `getmntinfo`
([#&#8203;4265](https://redirect.github.com/rust-lang/libc/pull/4265)
- VxWorks: Fix the size of `time_t`
[#&#8203;426](https://redirect.github.com/rust-lang/libc/pull/426)

##### Other

- Add labels to FIXMEs
[#&#8203;4230](https://redirect.github.com/rust-lang/libc/pull/4230),
[#&#8203;4229](https://redirect.github.com/rust-lang/libc/pull/4229),
[#&#8203;4237](https://redirect.github.com/rust-lang/libc/pull/4237)
- CI: Bump FreeBSD CI to 13.4 and 14.2
[#&#8203;4260](https://redirect.github.com/rust-lang/libc/pull/4260)
- Copy definitions from core::ffi and centralize them
[#&#8203;4256](https://redirect.github.com/rust-lang/libc/pull/4256)
- Define c_char at top-level and remove per-target c_char definitions
[#&#8203;4202](https://redirect.github.com/rust-lang/libc/pull/4202)
- Port style.rs to syn and add tests for the style checker
[#&#8203;4220](https://redirect.github.com/rust-lang/libc/pull/4220)

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 05:27:23 +00:00
Conrad Irwin
c34357e2ab Git askpass (#25953)
Supersedes #25848

Release Notes:

- git: Supporting push/pull/fetch when remote requires auth

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-03-06 05:20:06 +00:00
renovate[bot]
6fdb666bb7 Update Rust crate inventory to v0.3.20 (#26180)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [inventory](https://redirect.github.com/dtolnay/inventory) |
workspace.dependencies | patch | `0.3.19` -> `0.3.20` |

---

### Release Notes

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

###
[`v0.3.20`](https://redirect.github.com/dtolnay/inventory/releases/tag/0.3.20)

[Compare
Source](https://redirect.github.com/dtolnay/inventory/compare/0.3.19...0.3.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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 00:15:57 -05:00
Peter Tripp
7a9e0b37ed Improve schema_generator CLI (#25898)
Release Notes:

- N/A
2025-03-06 04:59:57 +00:00
renovate[bot]
d44ba92363 Update Rust crate globset to v0.4.16 (#26176)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[globset](https://redirect.github.com/BurntSushi/ripgrep/tree/master/crates/globset)
([source](https://redirect.github.com/BurntSushi/ripgrep/tree/HEAD/crates/globset))
| workspace.dependencies | patch | `0.4.15` -> `0.4.16` |

---

### 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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 04:45:26 +00:00
renovate[bot]
ca4d8fc900 Update Rust crate bytes to v1.10.1 (#26164)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [bytes](https://redirect.github.com/tokio-rs/bytes) |
workspace.dependencies | patch | `1.10.0` -> `1.10.1` |

---

### Release Notes

<details>
<summary>tokio-rs/bytes (bytes)</summary>

###
[`v1.10.1`](https://redirect.github.com/tokio-rs/bytes/blob/HEAD/CHANGELOG.md#1101-March-5th-2025)

[Compare
Source](https://redirect.github.com/tokio-rs/bytes/compare/v1.10.0...v1.10.1)

##### Fixed

- Fix memory leak when using `to_vec` with `Bytes::from_owner`
([#&#8203;773](https://redirect.github.com/tokio-rs/bytes/issues/773))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 04:35:04 +00:00
Agus Zubiaga
352882af77 docs: Improve edit prediction tab conflict section (#25493)
To be merged when https://github.com/zed-industries/zed/pull/25491 is released

Release Notes:

- N/A

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-03-05 23:23:06 -05:00
Cole Miller
c5c4a6201b ci: Upload remote server assets to workflow run as well (#26153)
Closes #ISSUE

Release Notes:

- N/A
2025-03-06 04:20:38 +00:00
Devzeth
6327a5d665 docs: Improve documentation of ensure final new line on save (#25960)
The function ensure_final_newline in buffer.rs has this explanation:
Ensures that the buffer ends with a single newline character, no other
whitespace.

The documentation wasn't explaining well that we actually remove any
lines containing only whitespace and keep only 1 line at the end of a
buffer.

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
Co-authored-by: Danilo Leal <danilo@zed.dev>
2025-03-05 23:14:20 -05:00
Devzeth
57438d30e8 docs: Add documentation for lsp_highlight_debounce (#25974)
Adds documentation for `lsp_highlight_debounce`. 

Release Notes:

- N/A
2025-03-05 23:09:04 -05:00
0x2CA
5c81dd7d39 git: Fix git commit font fallbacks (#26184)
Closes #ISSUE

Release Notes:

- Fixed git commit font_fallbacks
2025-03-06 04:06:00 +00:00
Cole Miller
aec4d5cb26 Fix panic in commit editor selections syncing (#26186)
Closes #26183 

Release Notes:

- Git Beta: Fixed a panic when selecting text in one of the commit
message editors
2025-03-06 03:21:40 +00:00
Peter Tripp
4c0750bd2f ci: Less Windows CI for PRs (#26155)
Split Windows GHA CI job into `windows_clippy` and `windows_tests`
(`cargo test` and `cargo build`). `windows_clippy` will continue to run
on every PR commit, but `windows_tests` will only be run on main. Tag a
PR `windows` if you would like to run windows tests.

Added a call to the Azure metadata service to detect the Azure hardware
used by the GitHub hosted Windows runners. This is temporary and I'll
remove once I've gathered some data (adds 5-15secs to Windows CI times)

Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2025-03-05 21:59:58 -05:00
brian tan
22b1a02e23 vim: Implement <count>% motion (#25839)
Closes https://github.com/zed-industries/zed/discussions/25665

> Currently Zed is missing quite an useful Vim motion: <count>% (go to
{count} percentage in the file).
Description:
{count}% - Go to {count} percentage in the file, on the first non-blank
in the line linewise. To compute the new line number this formula is
used: ({count} * number-of-lines + 99) / 100 .
> [Link](https://neovim.io/doc/user/motion.html#N%25).

Release Notes:

- vim: Added `<count>%` motion

---------

Co-authored-by: Conrad Irwin <conrad@zed.dev>
2025-03-05 19:59:18 -07:00
Max Brunsfeld
314ad5dd5f Clear pending staged/unstaged diff hunks hunks when writing to the git index fails (#26173)
Release Notes:

- Git Beta: Fixed a bug where discarding a hunk in the project diff view
performed two concurrent saves of the buffer.
- Git Beta: Fixed an issue where diff hunks appeared in the wrong state
after failing to write to the git index.
2025-03-05 18:45:09 -08:00
Kirill Bulatov
d3c68650c0 Improve cmd-click in terminal to find more paths (#26174)
Closes https://github.com/zed-industries/zed/issues/25701

Reworks the way cmd-click is handled:

* first, all worktree entries are checked for existence

This allows more fine-grained lookup of entries that are in the
worktree, but their path in the terminal is not "full": in case neither
`cwd` no worktree's root + that temrinal paths form a valid path
(https://github.com/zed-industries/zed/issues/25701)

The worktrees are sorted by "the most close to cwd first" so such files
are attempted to resolved in the most specific worktree.

This also fixes no cmd-click working in the remote ssh.

* second, only if the client is local, do the FS checks to find
non-indexed files

Release Notes:

- Improved cmd-click in terminal to find more paths
2025-03-06 00:41:13 +00:00
Danilo Leal
43339c6869 assistant2: Improve clarity of loading state (#26178)
Follow up to https://github.com/zed-industries/zed/pull/23299.

Having the loading state on the button makes sense, but it's also too
subtle. If you're waiting on an LLM response that takes a while, like a
"thinking state", not having anything more clearly visible communicating
that the model is still in-progress can make you think something is
wrong.

<img
src="https://github.com/user-attachments/assets/da64516e-5540-4294-97a2-e4542ce704f3"
width="700px" />

Release Notes:

- N/A
2025-03-05 21:39:29 -03:00
Julia Ryan
e505d6bf5b Git uncommit warning (#25977)
Adds a prompt when clicking the uncommit button when the current commit
is already present on a remote branch:

![screenshot showing
prompt](https://github.com/user-attachments/assets/d6421875-588e-4db0-aee0-a92f36bce94b)

Release Notes:

- N/A

---------

Co-authored-by: Conrad <conrad@zed.dev>
2025-03-05 15:56:51 -08:00
Julia Ryan
0200dda83d Disable uncommit button for parentless commits (#25983)
Closes #25976

There's a couple states that this covers:
- upon `git init`, no footer is shown at all
- after 1 commit (or when on any parentless commit), the uncommit button
is ~disabled~ hidden
- otherwise commit button is shown

Also updated the button with "meta" tooltip showing human readable
description and git command.

Release Notes:

- N/A

---------

Co-authored-by: Nate Butler <iamnbutler@gmail.com>
2025-03-05 23:23:05 +00:00
Marshall Bowers
4db9ab15a7 elixir: Extract to zed-extensions/elixir repository (#26167)
This PR extracts the Elixir extension to the
[zed-extensions/elixir](https://github.com/zed-extensions/elixir)
repository.

Release Notes:

- N/A
2025-03-05 22:50:35 +00:00
Cole Miller
5daadc0d30 git: Add CHERRY_PICK_HEAD to the list of merge heads (#26145)
Attempt to fix an issue where conflicts from a cherry-pick don't get
cleared out of the git panel after being resolved.

Release Notes:

- Git Beta: Fixed resolution of conflicts from cherry-picks not being
reflected in the git panel
2025-03-05 22:31:45 +00:00
Marshall Bowers
431727fdd7 csharp: Extract to zed-extensions/csharp repository (#26166)
This PR extracts the C# extension to the
[zed-extensions/csharp](https://github.com/zed-extensions/csharp)
repository.

Release Notes:

- N/A
2025-03-05 22:23:49 +00:00
Marshall Bowers
cee98f872a git_ui: Fix typo in comment (#26162)
This PR fixes a typo in a comment.

Release Notes:

- N/A
2025-03-05 21:48:23 +00:00
Marshall Bowers
e99d68a66f git_ui: Scaffold out support for generating commit messages with an LLM (#26161)
This PR adds the rough structure needed to support generating commit
messages using an LLM.

This functionality is not yet surfaced to the user.

This is the current state, if you tweak the source to show the button:


https://github.com/user-attachments/assets/66d1fbc4-09f3-4277-84f4-e9c9ebab274c

Release Notes:

- N/A
2025-03-05 21:42:48 +00:00
renovate[bot]
6a3e8044b1 Update Rust crate anyhow to v1.0.97 (#26152)
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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 16:32:07 -05:00
renovate[bot]
c2375a4164 Update Rust crate async-trait to v0.1.87 (#26158)
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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 16:31:40 -05:00
Mikayla Maki
9d54e63a11 Fix git branches in non-active repository (#26148)
Release Notes:

- Git Beta: Fixed a bug where the branch selector would only show for
the first repository opened.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
2025-03-05 21:16:46 +00:00
Conrad Irwin
2a919ad1d0 git: Make repo selector wider (#26149)
…m_item()

Closes #ISSUE

Release Notes:

- git: Fixed repository selector being too narrow
2025-03-05 13:02:29 -07:00
Julia Ryan
f13b2fd811 Fix left clicking the close button in the switcher (#25979)
The close button on each tab previously only worked when you right
clicked it, presumably because on macos people were using `ctrl+tab` to
open the picker, and clicking with `ctrl` held registers as a right
click. Now it should work with either mouse button.

Release Notes:

- N/A

Co-authored-by: Conrad <conrad@zed.dev>
2025-03-05 11:50:39 -08:00
Marshall Bowers
7c39153160 assistant_tools: Add list-worktrees and read-file tools (#26147)
This PR adds two new tools to Assistant 2:

- `list-worktrees` - Lists the worktrees in a project
- `read-file` - Reads a file at the given path in the project

I don't see `list-worktrees` sticking around long-term, as when we have
tools for listing files those will include the worktree IDs along with
the path, but making this tool available allows the model to utilize
`read-file` when it otherwise wouldn't be able to.

Release Notes:

- N/A
2025-03-05 19:41:42 +00:00
Marshall Bowers
d0c2bef8c3 anthropic: Use an empty object if no tool input is provided (#26144)
This PR changes the default value when no input is provided with a tool
use from `null` to `{}`.

This fixes an issue I was seeing where tools that didn't accept input
were not being called correctly.

Release Notes:

- N/A
2025-03-05 19:17:44 +00:00
Cole Miller
87b3fefdd1 Fix panic when expanding a deletion hunk with blame open (#26130)
Closes #26118

Release Notes:

- Fixed a panic when expanding diff hunks while git blame is open
2025-03-05 13:07:36 -05:00
Marshall Bowers
66784c0b3f Fix language model selector (#26138)
This PR fixes the language model selector.

I tried to piece together the state prior to #25697 (the state it was in
at 11838cf89e) while retaining unrelated
changes that happened since then.

Release Notes:

- Fixed an issue where language models would not be authenticated until
after the model selector was opened (Preview only).
2025-03-05 12:48:10 -05:00
Cole Miller
ad9c508a72 Fix performance regression in multibuffer diff syncing (#26137)
This fixes a performance problem introduced in #25906 and caused by
calling `BufferDiff::snapshot` too frequently.

Release Notes:

- Fixed a performance regression related to buffer diffs

Co-authored-by: Conrad <conrad@zed.dev>
2025-03-05 12:25:51 -05:00
Max Brunsfeld
aaa506c061 Bump Tree-sitter to 0.25.3 for error recovery fixes (#26092)
For https://github.com/tree-sitter/tree-sitter/pull/4257

Release Notes:

- Fixed a hang that could occur when editing certain Zig files.
2025-03-05 08:50:19 -08:00
Bennet Bo Fenner
a602c50a6c assistant2: Allow adding directories as context that contain non-UTF8 files (#26135)
We would previously return an error if there was at least one non-UTF8
file. Now we just ignore them and only add text files. If no text files
are found we show an error.

Release Notes:

- N/A
2025-03-05 16:47:43 +00:00
Marshall Bowers
728c161e8d Clean up language model selector (#26134)
This PR does some cleanup for the language model selector after
https://github.com/zed-industries/zed/pull/26090.

Release Notes:

- N/A
2025-03-05 16:18:01 +00:00
Asqar Arslanov
3975d8ea93 vim: Rename wrapping keybindings + document cursor wrapping (#25694)
https://github.com/zed-industries/zed/pull/25663#issuecomment-2686095807

Renamed the `vim::Backspace` and `vim::Space` actions to
`vim::WrappingLeft` and `vim::WrappingRight` respectively. The old names
are still available, but they are marked as deprecated and users are
advised to use the new names.

Also added a paragraph to the docs describing how to enable wrapping
cursor navigation.
2025-03-05 08:54:30 -07:00
Peter Tripp
2d050a8130 Fix SSH remotes running Nushell (#25613)
- Closes: https://github.com/zed-industries/zed/issues/21005

Nushell does not support `uname -sm`
So invoke `sh -c "uname -sm"` instead which will also work under nushell.
See https://github.com/nushell/nushell/issues/12570 for the choice quote: "being posix/bash compliant is a non-goal"

Release Notes:

- Fixed ssh remotes running Nushell
2025-03-05 15:50:32 +00:00
Dino
e600e71c1c vim: Fix tab title when using !! and disable rerun button for terminal tasks (#26122)
These changes tackle two issues with running terminal commands via vim
mode:

- When using `!!` the tab's title was set to `!!` instead of the
previous command that was run and these changes fix that in order to
always display the previous command in the tab's title when re-running
the command with `!!`
- For a terminal command, pressing the rerun button would actually bring
up the task palette, so this has been updated in order to disable the
rerun button when the terminal tab was spawned via a vim command

Closes #25800 

Release Notes:

- Fixed the terminal tab title when using `!!` to rerun the last command
- Improved the terminal tab for when command is run via vim mode, in
order to disable the rerun button, seeing as Zed does not support it
2025-03-05 08:47:49 -07:00
Marshall Bowers
82d85fd2ed deno: Extract to zed-extensions/deno repository (#26129)
This PR extracts the Deno extension to the
[zed-extensions/deno](https://github.com/zed-extensions/deno)
repository.

Release Notes:

- N/A
2025-03-05 15:31:21 +00:00
smit
e061ebb46c editor: Fix cmd + click on a URL not working sometimes (#26128)
Closes #25647

This PR fixes two issues related to cmd + click on URL:

1. Normally cmd + click on URL, it opens browser. Now, alt + tab back to
Zed. If you cmd + click on link again it won't work, until you normal
click some where else in buffer. It won't even show underline.

2. Again, cmd + click on URL, it opens browser. Now, alt + tab back to
Zed. If you cmd + click, some where else in buffer like just normal
text, and now try to hover on URL it won't show up underline and cmd +
click on it won't work. Unless again, if you plain click somewhere else.

Problem:

Issue is when clicking we set pending anchor (for selection), and when
we mouse up we clear those. This works for normal case without pressing
any modifier.

But, in case of cmd modifier, we set pending anchor (set when
`SelectPhase::Begin`), but we don't clear it once we use that data.

Fix: 

Once we end up using selection, anchor, etc data to figure out where to
navigate either URL/defination etc, we clear selection just like how we
do it in normal click. This doesn't require to happen after navigate
task, so we do it right after our usage of it.

Before:


https://github.com/user-attachments/assets/b33d93fc-f490-4fa4-ae22-1da1fd6b77a9

After:


https://github.com/user-attachments/assets/028f039a-cd13-4651-b461-3ba52f2526de


Release Notes:

- Fixed an issue where cmd + click on a URL was not working sometimes.
2025-03-05 20:58:18 +05:30
smit
387ee46c46 project: Fix issue where Cmd+Click on an import opens the wrong file (#26120)
Closes #21974

`resolve_path_in_worktrees` function looks for provided path in each
worktree until valid file is found.

In this PR we priortize current buffer worktree before other worktrees,
because of edge case where, file with same name might exists in other
worktrees.

Updated tests to handle this case.

Release Notes:

- Fixed an issue where the wrong file from a different worktree would
open when using `Cmd + Click` on a file import.
2025-03-05 16:49:39 +05:30
Maksim Bondarenkov
89d89b8b2d docs: Update MSYS2 section to add information about CLI (#25882)
MSYS2 now provides CLI along with editor in Zed package:
https://packages.msys2.org/packages/mingw-w64-ucrt-x86_64-zed

Closes #ISSUE

Release Notes:

- N/A
2025-03-05 13:08:54 +08:00
AidanV
f07ae541ad vim: Add registers view (#25945)
Closes #18157

Release Notes:

- vim: Added `:reg[isters]` to show the current values of registers

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-03-04 21:59:19 -07:00
brian tan
ff0bb1f389 vim: Fix insert before in visual modes (#25603)
Closes #22536

Changes:
- Visual and visual block: Cursor at start of selection.
- Visual line: Cursor at start on line.
- Uses different handling since the selection does not actually change
in vline.

Release Notes:

- vim: Fixed insert before (`shift-i`) in visual modes.
2025-03-04 21:58:01 -07:00
0x2CA
9c7eee24bc vim: Fix ignoring cursor_shape settings (#25439)
Closes #ISSUE

[Block cursor in insert mode
#25322](https://github.com/zed-industries/zed/discussions/25322)

Respect the `cursor_shape` setting in insert mode

Release Notes:

- Fixed vim ignoring `cursor_shape` settings
2025-03-04 21:48:43 -07:00
Conrad Irwin
ec4719146a Fix . repeat for remapping surrounds/exchange actions (#26101)
Closes #ISSUE

cc @thomasheartman

Release Notes:

- vim: Fixes `.` repeat for remapped surrounds/exchange actions
2025-03-04 21:47:12 -07:00
0x2CA
47f8f891c8 vim: Fix "seed_search_query_from_cursor" : "selection" (#26107)
Closes #9311
Closes #14843

Release Notes:

- Fixed vim `"seed_search_query_from_cursor" : "selection"`
2025-03-04 21:46:58 -07:00
maan2003
d9d3b8847b nix: Bump flake to get Rust 1.85 (#26076)
old nixpkgs versions didn't have rust 1.85 and nix develop failed (1.85
is specified in rust-toolchain.toml).

ran `nix flake update` to bump the flake dependencies. it now works

Release Notes:

- N/A
2025-03-04 19:18:40 -08:00
Conrad Irwin
d7b90f4204 Fix diff_hunk_before in a multibuffer (#26059)
Also simplify it to avoid doing a bunch of unnecessary work.

Co-Authored-By: Cole <cole@zed.dev>

Closes #ISSUE

Release Notes:

- git: Fix jumping to the previous diff hunk

---------

Co-authored-by: Cole <cole@zed.dev>
2025-03-04 20:07:19 -07:00
brian tan
3e64f38ba0 vim: Add support for toggling boolean values (#25997)
Closes #10400
Closes https://github.com/zed-industries/zed/issues/17947

Changes:
- Let vim::increment find boolean values in the line and toggle them. 

Release Notes:

- vim: Added support for toggling boolean values with `ctrl-a`/`ctrl-x`
2025-03-05 03:00:44 +00:00
Thomas Heartman
82338e2c47 vim: Fix clear exchange not working (#25804)
Fixes two issues with the Vim exchange implementation:

1. The clear exchange implementation **didn't** clear the exchange. This
was due to us asking the editor to clear normal highlights instead of
background highlights.
2. Calling clear exchange also wouldn't cause the operator to be
cleared, so you would be left in operator = "cx".

I've added tests for both of these cases.

Partially closes #25750. It doesn't address the problem with dot repeat
not working for my custom bindings, but I don't know what would cause
that. I'd love to hear some thoughts on why that is. That might be a
problem on my part or it might be something with the code. Input would
be appreciated.

Release Notes:

- Fixed: Vim exchange's "clear exchange" function didn't clear the
exchange and kept you in operator pending mode.
2025-03-04 19:34:52 -07:00
Nico Lehmann
229e853874 Make buffer search aware of search direction (#24974)
This solves a couple of issues with Vim search by making the search
buffer and `SearchableItem` aware of the direction of the search. If
`SearchOptions::BACKWARDS` is set, all operations will be reversed. By
making `SearchableItem` aware of the direction, the correct active match
can be selected when searching backward.

Fixes #22506. This PR does not fix the last problem in that issue, but
that one is also tracked in #8049.

Release Notes:

- Fixes incorrect behavior of backward search in Vim mode
2025-03-04 19:27:37 -07:00
Ben Kunkle
ed13e05855 project search: Fix text cutoff in options help text (#26098)
Closes #25495

Release Notes:

- N/A
2025-03-05 01:15:11 +00:00
Conrad Irwin
674fb7621f Fix focus handle leak (#26090)
This fixes a major performance issue in the current git beta.
This PR also removes the PopoverButton component, which was easy to
misuse.

Release Notes:

- Git Beta: Fix frame drops caused by opening the git panel

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-03-05 00:50:26 +00:00
Max Brunsfeld
fe18c73a07 Fix lag when large diff hunk intersects the viewport (#26088)
We were iterating over the row range of a hunk, and inserting into a
hash map for every row.

Release Notes:

- Fixed a performance problem when a large diff hunk was displayed in an
editor.
2025-03-04 16:25:58 -08:00
Marshall Bowers
befacfe8c9 assistant2: Prevent concurrent thread saving tasks (#26089)
This PR makes it so only one thread-saving task will be in flight at a
time.

Release Notes:

- N/A
2025-03-05 00:18:13 +00:00
Danilo Leal
54f0a729c2 assistant2: Adjust edit message actions (#26081)
Fine-tuning the visuals (namely, reducing font and keybinding size) and
passing `on_click` handlers to the Cancel & Regenerate actions.

Release Notes:

- N/A
2025-03-04 20:48:29 -03:00
Marshall Bowers
67f9b2b87f markdown: Only change the copy code icon to a check temporarily (#26079)
This PR makes it so the copy code icon only changes to a check
temporarily.

It will now revert to the "copy" icon after 2 seconds.


https://github.com/user-attachments/assets/e8983268-9710-4519-97a0-b28dc237b109

Release Notes:

- N/A
2025-03-04 23:02:43 +00:00
Richard Feldman
a4ec0af681 Add initial scripting_tool (#26066)
Just a basic implementation so we can start trying it out.

Release Notes:

- N/A

---------

Co-authored-by: Marshall <marshall@zed.dev>
Co-authored-by: Michael <michael@zed.dev>
2025-03-04 17:59:19 -05:00
Marshall Bowers
886d8c1cab markdown: Ensure code block copy button stays in the right spot (#26074)
This PR makes it so the copy button on Markdown code blocks stays
absolutely positioned even when scrolled:

<img width="1297" alt="Screenshot 2025-03-04 at 5 28 48 PM"
src="https://github.com/user-attachments/assets/b0d0fae9-ccd6-43c1-bef3-44d8d3c3e669"
/>

We achieve this by inserting a new parent element around both the copy
button and the code block itself so we can position the copy button
absolutely within that element.

Release Notes:

- N/A
2025-03-04 22:44:29 +00:00
Mikayla Maki
ebc5c213a2 Synchronize modal commit editor with panel editor (#26068)
Release Notes:

- Git Beta: Synchronized selections between the modal editor and the
panel editor
- Git Beta: Allow opening the commit modal even if we're unable to
commit.
2025-03-04 21:58:26 +00:00
Joseph T. Lyons
0a2d938ac5 Do not include recent issues in issue response script (#26064)
Do not report issues that were created yesterday or today.

Release Notes:

- N/A
2025-03-04 16:13:53 -05:00
Kirill Bulatov
fc01f496a9 Fix font sizes not reacting on settings change (#26060)
Proper version of https://github.com/zed-industries/zed/pull/25425
When https://github.com/zed-industries/zed/pull/24857 returned font
updates on settings changes, settings values, not in-memory ones should
be compared.

This PR returns back the logic finally, and changes it to explicitly
track the settings values, not the in-memory ones.
Also adds the same tracking for UI font changes, which had never been
tracked before.

Release Notes:

- Fixed font sizes not reacting on settings change
2025-03-04 20:57:37 +00:00
Isac Ljung
db28b9bbde Add typescript-language-server and vtsls to list of available language servers (#26046)
Add the typescript language severs as lsp adapters.
This would allow language extensions to use them.
For example using on vue files to be able to run the vue-language-server
in
[hybridMode](https://github.com/vuejs/language-tools?tab=readme-ov-file#hybrid-mode-configuration-requires-vuelanguage-server-version-200).

Release Notes:

- Added `vtsls` and `typescript-language-server` to the list of
available language servers.
2025-03-04 15:49:27 -05:00
Cole Miller
0453cb2b06 git: Improvements to fetch/push/pull (#26041)
- Add global handlers so these actions can be invoked from the command
palette, etc.
- Tweak spinner to not show itself until a remote has been selected

Release Notes:

- N/A
2025-03-04 12:37:11 -05:00
Conrad Irwin
85211889e5 git: Fix project diff shortcuts (#26045)
Release Notes:

- git: Fix keyboard shortcut display in project diff view
2025-03-04 10:32:20 -07:00
Marshall Bowers
ad94642e83 markdown: Fix code block wrapping when horizontal scrolling is disabled (#26048)
This PR fixes an issue where code block wrapping was broken when not
using horizontal scrolling after
https://github.com/zed-industries/zed/pull/25956.

Release Notes:

- N/A
2025-03-04 12:24:08 -05:00
Bennet Bo Fenner
f4899d92a4 assistant2: Add support for editing the last message sent by the user (#26037)
https://github.com/user-attachments/assets/df46632b-dfeb-4991-ab2e-86829b72be9b

Closes #ISSUE

Release Notes:

- N/A
2025-03-04 17:57:42 +01:00
0x2CA
6685d85f49 vim: Fix increment step error (#26023)
Closes #12887

Release Notes:

- Fixed `x g ctrl-a` step
2025-03-04 09:53:35 -07:00
Felix Packard
161f8a1dd2 Fix "Open a file or project to get started" placeholder text not always shown (#26044)
Check that there are no `visible_worktrees` rather than checking
`worktrees` when deciding whether to display the "Open a file or project
to get started" text

Closes #25395

Release Notes:

- Fixed the "Open a file or project to get started" message not always
showing after all buffers have been closed
2025-03-04 09:21:16 -07:00
Nate Butler
6cdd7b7390 git: Add hunk_style setting (#26038)
This PR adds the `git.hunk_style` setting, allowing setting an alternate
style for hunks – specifically the rendering of unstaged hunks.

It has 2 options:

- `transparent` (unstaged hunks are more transparent/less opaque than
staged hunks)
- `pattern (unstaged hunks are indicated by a visual pattern)

We'll possibly explore a VSCode-style "don't show staged hunks", but the
complexity it adds is a bit out of scope for now.

Transparent:

![CleanShot 2025-03-04 at 09 07
09@2x](https://github.com/user-attachments/assets/a74c4286-8264-48a2-bd58-0c582efb4e22)

Pattern:

![CleanShot 2025-03-04 at 09 10
12@2x](https://github.com/user-attachments/assets/4dd3040e-fb36-4670-9279-fcc7a4f12ced)

Release Notes:

- Git Beta: Added `git.hunk_style` setting to allow toggling between git
hunk visual styles.
2025-03-04 11:10:39 -05:00
Alex Ozer
0ec15d6b02 Fix soft_wrap setting not applying to buffers starting with a different language (#25880)
Closes #22999 
# Problem

Currently, the default soft wrap mode of an editor is determined by
reading the language-specific settings of the language _at offset zero_
in the editor's (multi)buffer. While this provides a way to pick a
single soft wrap mode for a multi-language multibuffer, it's a bad
choice for a single-buffer multibuffer that begins with a different
embedded language. For example, Markdown with frontmatter:

```markdown
---
my_front_matter
---

# Hello World
```

Setting this in config:

```json
  "languages": {
    "Markdown": { "soft_wrap": "bounded" }
  },
```

Will not soft wrap the Markdown file as the language at offset zero is
YAML.

# Solution

Instead of using the language at offset zero, use the language of the
first buffer in the multibuffer (the buffer at offset zero). This gives
better behavior for single-buffer editors, and a similar default for
multi-language multibuffers as before.

# Testing

All existing `editor` crate tests pass, but I would appreciate any
guidance for where best to add additional testing.

Release Notes:

- Fixed soft_wrap setting not applying to buffers starting with a
different language

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-03-04 15:55:27 +00:00
Bennet Bo Fenner
909de2ca6f assistant2: Use cmd-n to create a new prompt editor when already in a prompt editor (#25935)
This flips the keybindings that are used to create a new thread/prompt
editor (only when you're already in a prompt editor)

Release Notes:

- N/A
2025-03-04 16:44:32 +01:00
Agus Zubiaga
f31749c81b edit predictions: Improve UX when there's no keybinding for accepting predictions (#25815)
If the user already binds `tab`/`alt-tab`/`alt-l` to a different action
in a conflicting context and hasn't assigned a different keybinding for
`editor::AcceptEditPrediction`, we would show broken popovers with no
bindings:

![CleanShot 2025-02-28 at 12 46
13@2x](https://github.com/user-attachments/assets/a2c6a8ad-5e11-46ef-8031-62e1e6900244)

Instead, they will now see an error-variant of every popover which
includes a tooltip with a short description and buttons to open the
keymap, and open a new docs section explaining the issue in detail and
how to fix it.

![CleanShot 2025-02-28 at 12 48
11@2x](https://github.com/user-attachments/assets/36329b1f-6374-4735-9fbc-8fccab70e881)

Note: I included the docs change in this PR because it's ok to deploy
before the release, as it also applies to existing versions.

Release Notes:

- edit predictions: Improve UX when there's no keybinding for accepting
predictions

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Danilo <danilo@zed.dev>
2025-03-04 11:28:36 -03:00
Joseph T. Lyons
76a81607de Reuse existing logic used to generate commit messages to disable commit buttons (#26034)
Also
- Recomputes `suggested_commit_message` and no longer stores it, to
ensure things are always up to date
- Reduces indentation in `render_footer`

Release Notes:

- N/A
2025-03-04 13:50:53 +00:00
smit
7d22059a2f migration: Add for editor::GoToHunk and editor::GoToPrevHunk actions (#26017)
We modified few actions in
https://github.com/zed-industries/zed/pull/25846, which are:

`"editor::GoToHunk" -> ["editor::GoToHunk", { "center_cursor": true }]`
`"editor::GoToPrevHunk" -> ["editor::GoToPrevHunk", { "center_cursor":
true }]`

Also, recently we changed and added migration for:

`["editor::GoToPrevHunk", { "center_cursor": true }] ->
["editor::GoToPreviousHunk", { "center_cursor": true }] `

This means:

1. User that might still have `editor::GoToHunk` won't be automatically
migrated to `["editor::GoToHunk", { "center_cursor": true }]`. Note
value of `center_cursor` is false, in first case (default), and true in
second case.

2. User that might still have `editor::GoToPrevHunk` won't be
automatically migrated to `["editor::GoToPreviousHunk", {
"center_cursor": true }]`. Note, `editor::GoToPrevHunk` is renamed
since, it is now invalid action.

This PR adds those migrations.

cc: @marcospb19 

Release Notes:

- N/A
2025-03-04 18:39:29 +05:30
feeiyu
6b16a5555e Fix lost focus when navigating back in project search result (#22483)
Closes #22447

When navigate forward/back, the focus moves from the ProjectSearchView's
result editor to the Pane, and then move to the ProjectSearchView, but
the event `on_focus_in` not triggered for ProjectSearchView, causing the
result editor to lose focus eventually.


f6dabadaf7/crates/workspace/src/workspace.rs (L1372)


f6dabadaf7/crates/workspace/src/workspace.rs (L1385)

Considering that the navigation might be triggered again in the next
frame, so use `on_next_frame` in `on_focus` event to move focus to
result editor.

Next frame:
- the blur event triggered for result editor.
- focus move from ProjectSearchView to result editor in `on_focus` event
for ProjectSearchView
- navigate again, focus moves from result editor to Pane then move back
to ProjectSearchView
- the focus not change during this frame, so no focus event happened for
ProjectSearchView.

![fix lost
focus1229](https://github.com/user-attachments/assets/bfaac839-7bcf-40e7-b3b4-1423d0510594)

Release Notes:

- Fix lost focus when navigate back in project search result

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-03-04 13:06:44 +00:00
Kirill Bulatov
7ba2b258de Fix a panic on Linux theme appearance change (#26019)
Closes https://github.com/zed-industries/zed/issues/26009


21484a2e9d/crates/gpui/src/platform/linux/platform.rs (L517-L519)

`with_common` panicked at `borrow_mut` which is the way it's implemented
for X11, Wayland and Headless Linux counterparts.


21484a2e9d/crates/gpui/src/platform/linux/wayland/client.rs (L722-L724)

By accessing the appearance global instead of a `RefCell` with it, the
panic goes away with one notable side-effect, on Linux only: the first
global's value on `Dark` appearance would be `Light`: it becomes normal
instantly, thanks to


21484a2e9d/crates/workspace/src/workspace.rs (L1083-L1090)

Things work without flickering:


[linux_theme_toggle.webm](https://github.com/user-attachments/assets/0e39ddc0-b4ff-4475-93ff-7b2bd7233628)


Release Notes:

- Fixed a panic on Linux theme appearance change
2025-03-04 14:47:27 +02:00
Boris Vassilev
cbb535f5eb Fix completion details on new clangd versions (#25405)
Fixes #16057

In newer versions of clangd, the switch labelDetailsSupport in the json
passed to the language server modifies the format of the returned json.
Zed handles well the old format, but misses the function parameters in
the new one. For example:
The old format looks like this:
```json
...
"label": " Window(int width, int height, const char *name, bool vsync, bool resizable)",
...
```
and with labelDetailsSupport = true:
```json
...
 "label": " Window",
 "labelDetails": {
     "detail": "(int width, int height, const char *name, bool vsync, bool resizable)"
 },
...
```
A simple solution is to just to not tell the language server that label
details are supported and force it to use the old format. This is a
dirty fix, but makes the completions behave like in the old versions of
clangd.

I do not know if this will break another language server. From what I've
found out most lsp-s do not depend on that setting and provide all
completion data either way. If not, this switch will need to be exposed
in a config or be at least lsp-dependant.

Lastly, I do not know Rust, maybe will need help to make a better fix
for the issue.

Release Notes:

- Fixed broken C++ completion suggestions
2025-03-04 14:30:03 +02:00
Finn Evers
20fc753f2b editor: Ensure correct tab icon is shown for files outside of the current project (#25933)
Closes #25885 

This PR improves the matching for file icons to tabs. 
Previously, the tab icon would be resolved based upon the relative path
in the current project. However, this caused the default file icon being
assigned to all files outside of the project, as the relative path for
these files would be empty.

Instead, `path_for_buffer` is now used which always returns a proper
file name even for paths outside the current project (as also stated [in
this
comment](fee9c67707/crates/editor/src/items.rs (L1689))).
As the file name is sufficient for matching icons to files, this fixes
the linked issue whilst not changing anything for previously properly
matched icons.

| `main` | This PR |
| --- | --- | 
| <img width="296" alt="main"
src="https://github.com/user-attachments/assets/e72b8b5d-aa1c-4a8e-903f-14239f5b8764"
/> | <img width="296" alt="PR"
src="https://github.com/user-attachments/assets/a736974a-ce41-4861-be3f-95448cc7ffd0"
/> |

Release Notes:

- Fixed wrong file icons being shown for files outside of the current
project.
2025-03-04 13:57:26 +02:00
Finn Evers
042fc82e99 python: Fix and improve highlighting (#25813)
Closes #25803
Closes #25707 

This PR fixes the highlighting regression described in #25803 by fixing
the priorities of highlights as described in the [comment within the
file
itself](5b66ea1563/crates/languages/src/python/highlights.scm (L1)).
A nice side-effect of this is that scoped constants or other identifiers
are now also more accurately highlighted, as seen in the screenshots
below.

While I was at it, I also adressed the highlighting issue for default
typed idenfiers.

| This PR | <img width="575" alt="PR"
src="https://github.com/user-attachments/assets/aed5cdd0-c31a-4794-8128-376944fddd2d"
/> |
| --- | --- |
| Preview | <img width="575" alt="preview"
src="https://github.com/user-attachments/assets/ae3fad35-d436-472c-aff0-16508304ccf7"
/> |
| Stable | <img width="575" alt="stable"
src="https://github.com/user-attachments/assets/3836427c-f1cc-42ea-b1a7-8f5bbbadf210"
/> |

Release Notes:

- Fixed constants not being highlighted in Python-files.
- Improved Python-highlighting for default function arguments and scoped
identifiers.
2025-03-04 08:41:10 +01:00
Finn Evers
27781a8a60 html: Add injections for style attributes and event handler attributes (#23659)
Closes #23653 

Before:
<img width="921" alt="before"
src="https://github.com/user-attachments/assets/e993df15-77a7-4b5a-b6fb-3415047914c0"
/>

After:
<img width="922" alt="after"
src="https://github.com/user-attachments/assets/1b4bd695-2985-46e2-8b55-576d32af0583"
/>

Release Notes:

- N/A
2025-03-04 09:12:25 +02:00
Joseph T. Lyons
33af6bce55 Make suggested commits placeholders and allow them to be committed (#26006)
This does not fix the bug where, when the commit editor modal is open,
changing the staged file does not update the suggested message in the
commit editor. Conrad mentioned he thought we shouldn't be allowed to
change those when the modal is open, so I'm not attempting to fix that.

Release Notes:

- Made suggested commits placeholders and allow them to be committed.
2025-03-04 02:01:52 -05:00
Max Brunsfeld
563baf682e Disable diff hunks for untracked files, even w/ no newline at eof (#25980)
This fixes an issue where diff hunks were shown for untracked files, but
only if the files did not end with a newline.

Release Notes:

- N/A
2025-03-03 22:18:27 -08:00
张小白
11b79d0ab9 workspace: Add trailing / to directories on completion when using OpenPathPrompt (#25430)
Closes #25045

With the setting `"use_system_path_prompts": false`, previously, if the
completion target was a directory, no separator would be added after it,
requiring us to manually append a `/` or `\`. Now, if the completion
target is a directory, a `/` or `\` will be automatically added. On
Windows, both `/` and `\` are considered valid path separators.



https://github.com/user-attachments/assets/0594ce27-9693-4a49-ae0e-3ed29f62526a



Release Notes:

- N/A
2025-03-04 14:01:08 +08:00
Joseph T. Lyons
8c4da9fba0 Disable Git panel button to open commit editor in certain cases (#26000)
Also:

- Internally renames a bit of code to make it easy to identify between
when we are disabling the buttons that open and close the modal editor
(in Git Panel and Project Diff) vs when we are disabling the commit
buttons (in Git Panel and Git commit editor modal).
- Deletes some unused code.

Release Notes:

- Unified disabling / enabling the button to open the Git commit editor
modal in the Git panel with the Project Diff commit button.
- Unified disabling / enabling the commit buttons, for the same cases,
between the Git panel and Git commit editor modal.
2025-03-04 05:35:18 +00:00
Conrad Irwin
1f7fa80166 git: Fix race condition loading project diff (#25992)
Release Notes:

- git: Fixed a race condition where some files would be missing from
project diff
2025-03-03 21:40:37 -07:00
Conrad Irwin
2ac952ee6b Git fix repo selection (#25996)
Release Notes:

- git: Fixed a bug where staging/unstaging of hunks could use the wrong
git repository if you had many open
2025-03-03 21:40:20 -07:00
Cole Miller
495612be2e Revert "git: Use worktree paths in the panel (#25950)" (#25995)
This reverts commit e7b3b8bf03.

Release Notes:

- N/A
2025-03-04 04:20:41 +00:00
Conrad Irwin
1086b282b8 git: New enter behaviour (#25986)
Closes #25951

Release Notes:

- git: Update "enter" in the list of changed files to preserve focus. If
you want the old behaviour, hit enter twice.
- git: Follow the cursor, not the scroll anchor, in the list. Although
the scroll anchor was nice for passive scrolling, it broke if you had
changed the overflow scroll settings.
2025-03-03 20:49:29 -07:00
Joseph T. Lyons
ffe2bed1e2 Refactor more code around commit button text (#25990)
Missed this when doing https://github.com/zed-industries/zed/pull/25988

Release Notes:

- N/A
2025-03-04 03:33:38 +00:00
Joseph T. Lyons
88940732ca Use same commit button text in panel and modal (#25988)
Release Notes:

- Fixed inconsistencies in commit button text between Git panel and modal.
2025-03-04 03:03:34 +00:00
Mikayla Maki
74fc52d5ce Git Beta: Fix a few cases of empty toasts showing up (#25985)
Improve parsing of git remote outputs

Release Notes:

- N/A
2025-03-04 02:16:50 +00:00
Conrad Irwin
2a7a4a80c6 Fix toggle fold in deleted hunk (#25967)
Updates #25835
Updates #25951

Closes #ISSUE

Release Notes:

- Fixed toggling folds from within deleted hunks
2025-03-03 18:51:09 -07:00
Finn Evers
c03bf1af36 gpui: Ensure hitbox is inserted when element has hover listener (#25981)
Currently, when an element has only a hover listener, the attached
listener will never trigger, because within the check for whether a
hitbox has to be inserted for the given element, this case it not
considered.
That leads to the behaviour as described in
https://github.com/zed-industries/zed/pull/25602#discussion_r1970720972,
where another event listener has to be attached to the element in order
for the hover listener to work.

This PR fixes the issue by ensuring that a hitbox is also inserted when
only a hover listener is attached to the element.

Release Notes:

- N/A
2025-03-04 01:46:18 +00:00
Max Brunsfeld
922aaa0534 Show git panel footer even when on a detached HEAD (#25968)
Previously, the git panel footer would accidentally hide when not on a
branch.

Release Notes:

- N/A

Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com>
2025-03-03 16:36:13 -08:00
Marshall Bowers
fc5ff318e3 markdown: Change the copy icon to a check once copied (#25970)
This PR makes it so the copy icon on code blocks will change to a check
once the code block has been copied.

Release Notes:

- N/A
2025-03-04 00:14:26 +00:00
Cole Miller
e7b3b8bf03 git: Use worktree paths in the panel (#25950)
This PR changes the git panel to use worktree-relative paths for its
entries, instead of repository-relative paths as before. Paths that lie
outside the active repository's worktree are no longer shown in the
panel. Note that in both respects this is how the project diff editor
already works, so this PR brings those two pieces of UI into harmony.

Release Notes:

- N/A
2025-03-03 18:32:03 -05:00
Conrad Irwin
6faa7cd722 Fix regex search colors (#25962)
In #25005 we added regex syntax highlighting to search; but the existing
regex grammar highlighted every character as a string which was hard to
read.

This flips so that characters are not highlighted, and brackets, etc.
are.
<img width="346" alt="Screenshot 2025-03-03 at 14 39 35"
src="https://github.com/user-attachments/assets/f7d3ae9c-fb5c-45eb-a5e9-41a330fbe940"
/>


Release Notes:

- Fixed regex search box being overly green
2025-03-03 15:55:07 -07:00
Marshall Bowers
0776fa8f31 markdown: Allow code blocks and tables to be horizontally scrollable (#25956)
This PR adds the ability for Markdown code blocks and tables to be made
horizontally scrollable.

This is a feature that the caller can opt in to.

Right now we're using it for the rendered Markdown in the Assistant 2
panel.

Release Notes:

- N/A
2025-03-03 22:52:59 +00:00
Marshall Bowers
7321c814ce gpui: Add restrict_scroll_to_axis to match web scrolling behavior (#25963)
This PR adds a new `restrict_scroll_to_axis` style to allow consumers to
opt-in to the scrolling behavior found on the web.

When this is enabled the behavior will be such that:

- Scrolling using the mouse wheel will only scroll the Y axis
- Scrolling using the mouse wheel with <kbd>Shift</kbd> held will only
scroll the X axis

This behavior is useful in scenarios where you have some
vertically-scrollable content that is interspersed with
horizontally-scrollable elements, as otherwise the scroll will be
constantly hijacked by the horizontally-scrollable elements while trying
to scroll up and down in the vertically-scrollable container.

I think that this behavior should be the default, but it's a bit of a
sweeping change to make all at once, so for now it remains opt-in.

Release Notes:

- N/A
2025-03-03 17:32:08 -05:00
Nate Butler
ac3cb3df05 git_ui: horizontal is not vertical (#25961)
Fixes an issue where I was missing some brain cells and changed the git
panel's `render_entries` to a `v_flex` instead of an `h_flex`.

But actually, fixes the git panel entries from disappearing when a
scrollbar is rendered.

**Before**

![CleanShot 2025-03-03 at 16 36
52@2x](https://github.com/user-attachments/assets/9dca7b9c-318d-4b3f-ab3e-e7242fa7f73a)

**After**

![CleanShot 2025-03-03 at 16 35
59@2x](https://github.com/user-attachments/assets/c1fe5fb1-ad57-4bca-ace4-365e70a74066)


Closes #25955

Release Notes:

- Git Beta: Fixed an issue where when the git panel would need to scroll
all the items are pushed off the screen.
2025-03-03 22:00:32 +00:00
Conrad Irwin
0bd40da546 vim: Fix key navigation on folded buffer headers (#25944)
Closes #24243

Release Notes:

- vim: Fix j/k on folded multibuffer headers

---------

Co-authored-by: João Marcos <marcospb19@hotmail.com>
2025-03-03 14:44:39 -07:00
Marshall Bowers
3bec4eb117 markdown: Add initial support for tables (#25954)
This PR adds initial support for displaying tables to the `markdown`
crate.

This allows us to render tables in Assistant 2:

| Before | After |
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------------------------------------------
|
| <img width="1309" alt="Screenshot 2025-03-03 at 1 39 39 PM"
src="https://github.com/user-attachments/assets/ad7ada01-f35d-4fcf-a20c-deb42b55b34e"
/> | <img width="1297" alt="Screenshot 2025-03-03 at 3 38 21 PM"
src="https://github.com/user-attachments/assets/9b771126-30a0-479b-8c29-f5f572936f56"
/> |

There are a few known issues that should be addressed as follow-ups:

- The horizontal scrolling within a table is linked with the scrolling
of the parent container (e.g., the Assistant 2 thread)
- Cells are currently cut off entirely when they are too wide, would be
nice to truncate them with an ellipsis

Release Notes:

- N/A
2025-03-03 21:01:26 +00:00
Kirill Bulatov
bf6cc2697a Do not detach reparse tasks (#25934)
Drop previous reparse task, if a new one is spawned.

Release Notes:

- N/A
2025-03-03 22:41:46 +02:00
Cole Miller
dc3158c8ce git: Don't consider $HOME as containing git repository unless it's opened directly (#25948)
When a worktree is created, we walk up the ancestors of the root path
trying to find a git repository. In particular, if your `$HOME` is a git
repository and you open some subdirectory of `$HOME` that's *not* a git
repository, we end up scanning `$HOME` and everything under it looking
for changed and untracked files, which is often pretty slow. Consistency
here is not very useful and leads to a bad experience.

This PR adds a special case to not consider `$HOME` as a containing git
repository, unless you ask for it by doing the equivalent of `zed ~`.

Release Notes:

- Changed the behavior of git features to not treat `$HOME` as a git
repository unless opened directly
2025-03-03 20:33:02 +00:00
Cole Miller
9e2b7bc5dc Fix missing hunks in project diff after revert (#25906)
Release Notes:

- N/A
2025-03-03 18:53:34 +00:00
Cole Miller
b774a4b8d1 Add some logging to debug missing parent git repositories (#25943)
We've had some issues reported with git repositories not getting
detected when they're a strict parent of the worktree root. Add a bit
more logging to understand what's going on here.

Release Notes:

- N/A
2025-03-03 18:39:04 +00:00
Nate Butler
16ab8701a2 git_ui: Prevent button overflow due to long names (#25940)
- Fix component preview widths for git panel
- Fix buttons getting pushed off the screen in git  panel

Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-03-03 18:38:15 +00:00
Marshall Bowers
b2add8c803 assistant2: Restore tool uses when loading saved threads (#25942)
This PR makes it so tool uses are restored when loading saved threads in
Assistant 2.

Release Notes:

- N/A
2025-03-03 18:32:26 +00:00
Joseph T. Lyons
6635462f7b Bump Zed to v0.178 (#25939)
Release Notes:

-N/A
2025-03-03 12:48:51 -05:00
Marshall Bowers
81ff6f7a3c assistant2: Persist threads using serde_json instead of bincode (#25938)
This PR changes how we persist threads in Assistant2 to use `serde_json`
instead of `bincode` for the representation.

This makes the format more flexible to work with (and will allow for
using things like `#[serde(default)]`) if the schema changes over time.

Note: We have to bump the LMDB database version for this, so any threads
created before now will be gone.

Release Notes:

- N/A
2025-03-03 17:47:10 +00:00
Bennet Bo Fenner
669082dbe0 assistant2: Fix keyboard navigation issues when a picker is open (#25928)
This fixes:
- Bug: Using "up" in model selector triggers assistant2::FocusUp not
menu::SelectPrev
- Bug: Pressing arrow up/down in the model selector opened in the inline
assistant doesn't work
- Bug: Dismissing the model selector with Esc is not working
- Bug: Dismissing context pickers with Esc no longer working

Release Notes:

- N/A
2025-03-03 17:02:25 +01:00
Marshall Bowers
d5bc7b9a79 extension_cli: Make use of scrollbar_thumb.background a hard error (#25932)
This PR updates the extension CLI to make the use of
`scrollbar_thumb.background` in a theme a hard error.

We're working to eradicate usage of this theme property, so this will
prevent new extensions from being published that use it.

Release Notes:

- N/A
2025-03-03 15:55:15 +00:00
smit
8bb2739e28 keymap: Update Prev to Previous follow-up (#25931)
Follow-up for https://github.com/zed-industries/zed/pull/25909

Add three more action replacements:

```
1. "pane::ActivatePrevItem" -> "pane::ActivatePreviousItem"
2. "vim::MoveToPrev" -> "vim::MoveToPrevious"
3. "vim:MoveToPrevMatch" -> "vim:MoveToPreviousMatch" 
```

Release Notes:

- N/A
2025-03-03 21:19:25 +05:30
Peter Tripp
466be14b56 Revert "Use multi-line regex for '\s'" (#25926)
Reverts zed-industries/zed#19241
Closes: https://github.com/zed-industries/zed/issues/25901

Although `\s` contains `\n` it is widely used in non-multiline regexes (unlike `\n`).
2025-03-03 10:32:49 -05:00
Kirill Bulatov
95446195af Skip .git/lfs FS events (#25927)
Closes https://github.com/zed-industries/zed/issues/25865
Closes https://github.com/zed-industries/zed/pull/25915

In the issue, Zed had caused `.git/lfs/tmp/466102258`-like files to
appear in the directory, which lead to background FS event listener to
handle this as an update, incrementing snapshot's `scan_id`, which lead
to git status rescan, which caused another increment to `status_scan_id`
— incrementing either of the IDs causes the related repo data to be
considered "changed:


41b45eaba7/crates/worktree/src/worktree.rs (L1590-L1605)

hence propagating events to the other parts of the system (e.g. git
blame, which was also active in the issue's case)

```
[2025-03-01T20:01:08+01:00 DEBUG worktree] ignoring event ".git/lfs/tmp/466102258" within unloaded directory
[2025-03-01T20:01:08+01:00 DEBUG worktree] received fs events []
[2025-03-01T20:01:08+01:00 DEBUG worktree] reloading repositories: ["/Users/alex/dev/monorepo/.git"]
[2025-03-01T20:01:08+01:00 DEBUG editor::git::blame] Status of git repositories updated. Regenerating blame data...
[2025-03-01T20:01:08+01:00 DEBUG editor::git::blame] Status of git repositories updated. Regenerating blame data...
[2025-03-01T20:01:08+01:00 DEBUG editor::git::blame] Status of git repositories updated. Regenerating blame data...
```

Due to repo update events sent, another `.git/lfs/tmp/` entry is
created, things start over...

The PR fixes this by ignoring any `.git/lfs/` directory-related FS
events, as needed for the current git status update heuristics.

https://github.com/zed-industries/zed/pull/25915 tried to follow further
and `scan_id` and `status_scan_id` but we do not store all git state in
memory, e.g. head

e0060b92cc/crates/editor/src/editor_tests.rs (L13686)
as
[tests](https://github.com/zed-industries/zed/actions/runs/13631960559/job/38101504549?pr=25915)
show.

Release Notes:

- Improved `.git` scan heuristics
2025-03-03 15:04:46 +00:00
Max Brunsfeld
b34c0fd71b git_ui: Fix item heights in git panel (#25833)
- Fixes items slightly overlapping in the git panel
- Fixes commit button in the project diff not opening modal

Release Notes:

- N/A

---------

Co-authored-by: Nate Butler <iamnbutler@gmail.com>
Co-authored-by: Cole Miller <m@cole-miller.net>
2025-03-03 09:39:24 -05:00
Danilo Leal
e0060b92cc assistant: Adjust slash command picker (#25920)
Mostly just fine-tuning its positioning. Other changes are mainly using
the Label's `buffer_font` method instead of using a div for that.

Release Notes:

- N/A
2025-03-03 11:09:49 -03:00
Danilo Leal
06bcc42652 Revert "assistant_context_editor: Close menus on send (#25440)" (#25916)
Reverting https://github.com/zed-industries/zed/pull/25440

This is a good change, but given the PR was open for a while, I guess it
didn't catch conflicts with main, and so it broke it. Will revert it for
now, to keep main fresh, but will look into adding this behavior back
again.

Release Notes:

- N/A
2025-03-03 12:40:01 +00:00
brian tan
f24c226af8 assistant_context_editor: Close menus on send (#25440)
Closes #ISSUE

Before:


https://github.com/user-attachments/assets/e63b6207-0c80-4fd6-99c0-febe3d639ba1

After:


https://github.com/user-attachments/assets/870f2c6d-9b7f-456d-a1e3-26e1c31b129d

Release Notes:

- N/A
2025-03-03 09:22:23 -03:00
smit
593f3dc1d5 keymap: Update Prev to Previous for consistency (#25909)
Closes #10167

This is take 2 on https://github.com/zed-industries/zed/pull/2341 which
was closed due to lack of migrator.

This PR contains rename of following keymap actions: 
```sh
1. ["editor::GoToPrevHunk", { "center_cursor": true }] -> ["editor::GoToPreviousHunk", { "center_cursor": true }]
2. "editor::GoToPrevDiagnostic" -> "editor::GoToPreviousDiagnostic"
3. "editor::ContextMenuPrev" -> "editor::ContextMenuPrevious"
4. "search::SelectPrevMatch" -> "search::SelectPreviousMatch"
5. "file_finder::SelectPrev" -> "file_finder::SelectPrevious"
6. "menu::SelectPrev" -> "menu::SelectPrevious"
7. "editor::TabPrev" -> "editor::Backtab"
```

Release Notes:

- Renamed several keymap actions for consistency (e.g., `GoToPrevHunk` →
`GoToPreviousHunk`, `TabPrev` → `Backtab`). Your existing configured
keybindings will still work. You can click **"Backup and Update"** at
the top of your keymap file to easily update to the new actions.


Co-authored-by: Joseph T. Lyons <JosephTLyons@gmail.com>
2025-03-03 17:44:49 +05:30
Danilo Leal
61d584db45 context menu: Adjust item disabled state when there is docs aside (#25860)
When a context menu item has a documentation aside element attached to
it, we're now hiding the keybinding (which wouldn't trigger anything
anyway) to make room for displaying an info icon, with the purpose of
indicating the existence of the docs aside, which will typically explain
the reason why the item’s disabled in the first place.

Also, changed the label color to use the `Disabled` token; more
appropriate for this, and just slightly darker, which is great!

<img
src="https://github.com/user-attachments/assets/a7f9f022-16d1-41d5-b1b5-3cbcc9630cc8"
width="500px"/>

Release Notes:

- N/A
2025-03-03 08:59:42 -03:00
Jason Lee
c37f616c3b gpui: Maintain img aspect ratio when max_width is set (#25632)
Release Notes:

- Fixed Markdown preview to display image with max width 100%.

## Before

<img width="1202" alt="image"
src="https://github.com/user-attachments/assets/359628df-8746-456f-a768-b3428923c937"
/>
<img width="750" alt="SCR-20250226-napv"
src="https://github.com/user-attachments/assets/f6154516-470e-41b2-84f5-ef0612c447ad"
/>


## After

<img width="1149" alt="image"
src="https://github.com/user-attachments/assets/2279347d-9c69-4a47-bb62-ccc8e55a98f6"
/>
<img width="520" alt="SCR-20250226-ngyz"
src="https://github.com/user-attachments/assets/03af5f14-1935-472e-822f-4c7f62630780"
/>
2025-03-03 12:36:27 +01:00
Mikayla Maki
73ac19958a Add user-visible output for remote operations (#25849)
This PR adds toasts for reporting success and errors from remote git
operations. This PR also adds a focus handle to notifications, in
anticipation of making them keyboard accessible.

Release Notes:

- N/A

---------

Co-authored-by: julia <julia@zed.dev>
2025-03-03 09:20:15 +00:00
Mikayla Maki
508b9d3b5d Add an informative tooltip to commit button when unable to commit (#25912)
Release Notes:

- N/A
2025-03-03 08:44:46 +00:00
Morgan Metz
0a4ff2f475 tab: Add setting to hide the close button entirely (#23880)
Closes #23744

Release Notes:

- Changed the `always_show_close_button` key to `show_close_button` and
introduced a new `hidden` value, that allows never displaying the close
button.

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: smit <0xtimsb@gmail.com>
2025-03-03 08:31:48 +05:30
Max Brunsfeld
ae6d350334 Use system-installed git binary for push/pull/fetch (#25900)
### Problem

When using HTTPS remotes, users are getting errors when trying to push
or pull via the git panel.

On macOS, Zed bundles a `git` binary that's part of
[`dugite-native`](https://github.com/desktop/dugite-native). But we
don't include the entire package. Additional binaries from
`dugite-native` are needed for pulling and pushing over HTTPS.

### Solution

Rather than bundling those additional binaries, I've changed the `push`,
`pull`, and `fetch` actions to rely on the *system-installed* `git`
binary. The downside of this is that, if the user does not have Git
installed, they wont' be able to push, pull, or fetch from within Zed.
But we believe that the vast majority of users will have Git installed.
Also, unlike `diff` and `status`, which Zed needs to call in the
background without any user interaction, `push`/`pull` and `fetch` are
explicit actions that the user takes in Zed, so there is an opportunity
to prompt them to install Git if they haven't.

### Background

There are three ways (that I know of) that users might authenticate when
pushing, pulling, or fetching over HTTPS.

1. Via a built-in [Git
`credential.helper`](https://git-scm.com/docs/gitcredentials). On macOS,
Git ships with a helper called `credential-osxkeychain` that stores
internet passwords in the OS Keychain. You can opt into this globally
with the command `git config --global credential.helper osxkeychain`,
which writes to your `~/.gitconfig`.
2. Via [`Git Credential Manager`
(GCM)](https://github.com/git-ecosystem/git-credential-manager), which
is a different `credential.helper`, [built by
GitHub](https://github.blog/security/application-security/git-credential-manager-authentication-for-everyone/),
which must be installed manually, and integrates with specific Git
hosting providers like GitHub and Azure.
3. By typing their Username and Password/Access-token interactively when
pushing/pulling/fetching.

### Testing Status

* [ ] 🚫 Interactive password auth - not yet supported, requires
https://github.com/zed-industries/zed/pull/25848
* [x] **credential-osxkeychain** - when using the built-in credential
helper, and the credentials are already stored in the keychain,
push/pull/fetch now work fine .
* [ ] **GCM**  - still testing.
* Right now, I'm seeing `git-credential-manager` just hang indefinitely
when pushing from Zed, even though it works when pushing from a
terminal.



Release Notes:

- N/A
2025-03-02 17:31:47 -08:00
Piotr Osiewicz
0e44f93178 lsp: Do not add trailing slash to workspace folders (#25903)
Closes #25390

Release Notes:

- Fixed issues with ansible-language-server sending phantom diagnostic
updates
2025-03-02 22:09:13 +00:00
Joseph T. Lyons
65d92d7278 Make more log files read only (#25887)
Closes https://github.com/zed-industries/zed/issues/5105

Makes the following logs read only

- Zed error / warning log
- Zed telemetry log

Release Notes:

- N/A
2025-03-02 08:05:55 +00:00
Devzeth
8b5ef2558b docs: Add documentation for Seed search query from cursor (#25875)
Missing documentation added for `seed_search_query_from_cursor`. 

Release Notes:

- N/A
2025-03-02 08:46:22 +02:00
Ben Kunkle
fec228bb23 Fix issue with cmd-w closing window in preview tabs on MacOS (#25878)
Closes #25810

Reorders default macOS keymap so that `cmd-w` in `"context":
"PromptLibrary"` bindings is not the last binding for
`workspace::CloseWindow` and therefore does not get rendered in the app
menu or intercepted by MacOS

Release Notes:

- N/A
2025-03-01 19:33:01 -06:00
Devzeth
e00d737196 Add constructor highlighting for JS/TS/TSX (#25207)
Closes #19267

Adds highlight field specifically for `constructor` in JS/TS/TSX so that
it can be highlighted as a different color than regular methods

Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-03-02 00:43:51 +00:00
Egor Krugletsov
b0dee94126 pane: Hide "Copy Relative Path" and "Reveal In Project Panel" actions for files outside of the projects (#25386)
"Copy Relative Path" action had a check for existence of relative path
but it always passed because
[`WorktreeStore::find_worktree()`](1d5499bee7/crates/project/src/worktree_store.rs (L148))
function returned empty path for these kinds of files. It feels correct
to make changes there, but I don't know what else could be impacted.

"Reveal In Project Panel" had no check whatsoever and so I made one.

Release Notes:

- N/A
2025-03-01 23:31:18 +02:00
greathongtu
e65471c7a1 Optimize JSON merging by removing redundant key clones in serde_json operations (#25866)
Hi Zed team! 👋
As a fan of Zed Editor who's excited to contribute, I noticed a small
optimization opportunity in the JSON merging utilities.

<b>Changes:</b>
Removed redundant key.clone() calls in insert() operations within 2
functions:
1. merge_json_value_into
2. merge_non_null_json_value_into

<b>Why:</b>
Since we're already moving ownership of `source_object` 's contents and
key is no longer used further, we could directly pass `key` to `insert`
without cloning.
Eliminates redundant allocations, improving performance slightly for
JSON-heavy operations.

<b>Testing:</b>
I have tested this locally and all existing tests passed. The change
preserves behavior while removing redundancy.

Love using Zed and happy to contribute! Let me know if any adjustments
are needed!

Release Notes:

- N/A
2025-03-01 14:13:38 -05:00
张小白
6713ec8cdf windows: Bring back restoration of tabs (#25870)
Closes #25022

Release Notes:

- N/A
2025-03-01 17:18:34 +00:00
Joseph T. Lyons
48e09c0026 Tweak the Git beta issue template (#25869)
Release Notes:

- N/A
2025-03-01 11:33:42 -05:00
Joseph T. Lyons
3f03d7b023 Add a temporary issue template for Git beta bugs (#25867)
Release Notes:

- N/A
2025-03-01 11:31:25 -05:00
Devzeth
fa96e2259b paths: Add support for clickable file paths in the Odin language format (#25842)
This PR adds support for clickable file paths in the Odin language format.

The odin compiler errors use the format `/path/to/file.odin(1:1)`. We
didn't recognize this format, making these paths non-clickable in the
terminal.

Also added tests for this. 


Release Notes:

- Added support for clickable file paths in the Odin language format.

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2025-03-01 16:22:12 +00:00
Maksim Bondarenkov
a8a05f208b cli: Add extra paths in detect() on Windows (#25765)
I'm already integrating CLI into MSYS2 package, and this patche helped
me to make it work like in Arch:
https://github.com/msys2/MINGW-packages/pull/23537. used the same way as
in [Linux
implementation](6856e869fc/crates/cli/src/main.rs (L314))

Closes #ISSUE

Release Notes:

- N/A
2025-03-02 00:17:55 +08:00
Peter Tripp
aa1ab50656 Add stop_at_indent for Editor::DeleteToBeginningOfLine (#25688)
Added test_beginning_of_line_stop_at_indent editor test

- Follow-up to: https://github.com/zed-industries/zed/pull/25428
- Replaces: https://github.com/zed-industries/zed/pull/25346

This is all authored by @felixpackard in #25346
I just updated it to use `stop_at_indent` instead of
`stop_at_first_char`.

Release Notes:

- Added support for `stop_at_indent` to
`Editor::DeleteToBeginningOfLine` (thanks
[@felixpackard](https://github.com/felixpackard))

Co-authored-by: Felix Packard <felix@rigr.gg>
2025-03-01 11:03:47 -05:00
张小白
d115cb1944 windows: Use dev drive instead of ReFS (#25858)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-03-01 22:43:10 +08:00
Kirill Bulatov
42571e405f Ensure inlay hint toggling with modifiers happens fast (#25852)
Follow-up of https://github.com/zed-industries/zed/pull/25766 
Fixes the bugs found:

* modifier toggle not happening instantly due to `edit_debounce_ms`
considered
* hint update race that ignored the cache clear

Release Notes:

- N/A
2025-03-01 08:15:57 +00:00
João Marcos
2d61a51ded Diff View: Scroll to center of hunks when reviewing (#25846)
When reviewing hunks, scroll to put them at the center of the screen
so you can better see the context around that hunk.

The field `center_cursor` was added to the actions `editor::GoToHunk`
and `editor::GoToPrevHunk`, this was set to `false` by default in
keymaps, as it wouldn't help with in-editor navigation.

The field is set to `true` for when you trigger `git::StageAndNext`
and `git::UnstageAndNext`, this is also `true` for the buttons in the
Diff View toolbar.

Release Notes:

- N/A
2025-03-01 03:20:26 +00:00
João Marcos
a2876f5d3e Support hunk-wise StageAndNext and UnstageAndNext (#25845)
This PR adds the `whole_excerpt` field to the actions:

- `git::StageAndNext`
- `git::UnstageAndNext`

Which is set by false by default, effectively, now staging and unstaging
with these actions is done hunk-by-hunk, this also affects the `Stage`
and
`Unstage` buttons in the Diff View toolbar.

A caveat: with this PR, there is no way to configure the buttons in the
Diff
View toolbar to restore the previous behavior, if we want, I think we
can make
it a setting, but let's see if anyone really wants that.

Release Notes:

- N/A
2025-03-01 02:39:08 +00:00
Cole Miller
13deaa3f69 Fix new git panel buttons (#25844)
Release Notes:

- N/A
2025-02-28 21:19:20 -05:00
Ben Kunkle
694afd15c9 Fix buffer search options not resetting when dismissed after Vim mode search then reopened with buffer: deploy search (#25838)
Closes #25315

Release Notes:

- Fixes an issue where the buffer search options would not be reset when
using `buffer: deploy search` after using Vim search (`*` & `#`) which
enable all search options
2025-02-28 18:59:22 -06:00
Cole Miller
eb648dd096 Follow-up tweaks to new git panel footer (#25832)
- Use a popover for the branch picker
- Don't deploy a repository selector if there's only one repo

Release Notes:

- N/A
2025-02-28 19:05:43 -05:00
Max Brunsfeld
1c4c568068 Allow unfolding deleted buffers in project diff w/ keyboard (#25835)
Release Notes:

- N/A
2025-02-28 16:02:35 -08:00
Ben Kunkle
ec88a6886f Fix active pane modifiers applying to parent pane axis if child pane is active (#25836)
Closes #25304

Release Notes:

- Fixed an issue where `active_pane_modifiers` settings would be applied
to a parent pane if one of it's child panes was active
2025-02-28 23:47:15 +00:00
Piotr Osiewicz
7fb16977ce chore: Extract PromptStore out of prompt_library (#25837)
One step closer to removing long pole with assistant/assistant2 builds

Release Notes:

- N/A
2025-03-01 00:34:28 +01:00
Peter Tripp
53b2792844 Improve script/mitm-proxy.sh to support podman (#25834) 2025-02-28 22:37:03 +00:00
Peter Tripp
760d08711c Update bundled JSON schemas (2025-02-28) (#25826)
Updated JSON schemas to
[SchemaStore/schemastore@b107b83](b107b83a50)
(2025-02-28)

-
[tsconfig.json](https://github.com/SchemaStore/schemastore/commits/master/src/schemas/json/tsconfig.json)
@
[b107b83](https://raw.githubusercontent.com/SchemaStore/schemastore/b107b83a50bad9ac06dd171201dc870901f92ca8/src/schemas/json/tsconfig.json)
-
[package.json](https://github.com/SchemaStore/schemastore/commits/master/src/schemas/json/package.json)
@
[b107b83](https://raw.githubusercontent.com/SchemaStore/schemastore/b107b83a50bad9ac06dd171201dc870901f92ca8/src/schemas/json/package.json)

Previously:
- https://github.com/zed-industries/zed/pull/20910

Release Notes:

- Updated bundled JSON schemas for package.json and tsconfig.json
2025-02-28 16:32:16 -05:00
Marshall Bowers
71866d6314 assistant2: Include some text in the tool result messages (#25825)
This PR makes it so we include some textual content in the user messages
that as used to send up tool results.

I observed that when sending up the tool results with no text, it would
lead the model to start replying with no text, which would then result
in an error when attaching later tool results.

I think there's a deeper issue at play here, but for now we just include
some text to keep the model on track.

Release Notes:

- N/A
2025-02-28 21:02:03 +00:00
Max Brunsfeld
0c2bbb3aa9 Optimistically update hunk states when staging and unstaging hunks (#25687)
This PR adds an optimistic update when staging or unstaging diff hunks.
In the process, I've also refactored the logic for staging and unstaging
hunks, to consolidate more of it in the `buffer_diff` crate.

I've also changed the way that we treat untracked files. Previously, we
maintained an empty diff for them, so as not to show unwanted
entire-file diff hunks in a regular editor. But then in the project diff
view, we had to account for this, and replace these empty diffs with
entire-file diffs. This form of state management made it more difficult
to store the pending hunks, so now we always use the same
`BufferDiff`/`BufferDiffSnapshot` for untracked files (with a single
hunk spanning the entire buffer), but we just have a special case in
regular buffers, that avoids showing that entire-file hunk.

* [x] Avoid creating a long queue of `set_index` operations when
staging/unstaging rapidly
* [x] Keep pending hunks when diff is recalculated without base text
changes
* [x] Be optimistic even when staging the single hunk in added/deleted
files
* Testing

Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
2025-02-28 20:55:29 +00:00
Nate Butler
9d8a163f5b git_ui: New panel design (#25821)
This PR updates the ui of the git panel. It removes the header from the
panel and unifies the repository, branch and commit controls in the
bottom section.

It also adds a secondary menu to the primary button giving access to a
variety of actions for managing local and remote changes:

![CleanShot 2025-02-28 at 12 18
15@2x](https://github.com/user-attachments/assets/0260c122-405f-46fc-8cc8-d6beac782b9d)

Known issues (will be fixed in a later pr)
- Spinner showing git operation progress was removed, will be re-added
- Clicking expand with the panel editor focused will commit (due to
shared action name. Already tracked)

Before | After

![CleanShot 2025-02-28 at 12 22
18@2x](https://github.com/user-attachments/assets/4c1e4ac9-b975-487f-bf4e-8815a8da4f4f)

(Also adds `component`, `linkme` to cargo-machete ignore as they are
used in the `IntoComponent` proc-macro and will always be incorrectly
flagged as unused)

Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Cole Miller <53574922+cole-miller@users.noreply.github.com>
Co-authored-by: Cole Miller <cole@zed.dev>
2025-02-28 20:00:39 +00:00
Nate Butler
8a22a07d14 git: Adjust rendering of git hunks (#25824)
- Light themes get their own values (creating better contrast and a
better distinction between staged and unstaged hunks in light themes.)
- Scrollbar git hunks indicators now use the correct colors

Before:

![CleanShot 2025-02-28 at 14 31
29@2x](https://github.com/user-attachments/assets/038fe11c-7163-4f1b-92b8-56b24c8e9443)

After:

![CleanShot 2025-02-28 at 14 32
04@2x](https://github.com/user-attachments/assets/869d33d9-d925-4cbe-84bd-e54caf971431)


Release Notes:

- Fixed an issue where git hunk indicators in editor scrollbars used the
incorrect colors.
2025-02-28 19:54:12 +00:00
smit
fad4df5e70 editor: Add Organize Imports Action (#25793)
Closes #10004

This PR adds support for the organize imports action. Previously, you
had to manually configure it in the settings and then use format to run
it.

Note: Default key binding will be `alt-shift-o` which is similar to
VSCode's organize import. Also, because `cmd-shift-o` is taken by
outline picker.

Todo:

- [x] Initial working
- [x] Handle remote
- [x] Handle multi buffer
- [x] Can we make it generic for executing any code action?

Release Notes:

- Added `editor:OrganizeImports` action to organize imports (sort,
remove unused, etc) for supported LSPs. You can trigger it by using the
`alt-shift-o` key binding.
2025-03-01 00:59:09 +05:30
Piotr Osiewicz
e4e758db3a Rust 1.85 (#25272)
Closes #ISSUE

Release Notes:

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

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-02-28 18:33:35 +01:00
Marshall Bowers
fc52b43159 assistant2: Factor out tool use into its own module (#25819)
This PR factors out the concerns related to tool use out of `Thread` and
into their own module.

Release Notes:

- N/A
2025-02-28 17:04:20 +00:00
Marshall Bowers
b445e4ce24 assistant2: Rework how tool results are stored and referred to (#25817)
This PR reworks how we store tool results and refer to them later.

We now maintain a mapping of the tool uses to their corresponding
results, with separate mappings for the messages and the tool uses they
correspond to.

Release Notes:

- N/A
2025-02-28 11:33:08 -05:00
Danilo Leal
508b581215 assistant: Refine settings view's instruction visuals (#25812)
I've been bothered by using simple hyphens for bullet lists here for a
while; it kinda looked cheap and not well-formatted. So, in this PR, I'm
adding a new, custom UI component in the `language_models` crate, called
`InstructionListItem`, based off the `ListItem` that's somewhat
mimic'ing what a `<li>` would be on the web.

It does have a "rigid" structure as in it's always a label followed by a
button (which is optional), but that seems okay given it has been the
overall shape of the copy we've been using here. Also, never really
loved that we were pasting URLs directly, that kinda felt cheap, too. I
could see an argument where it's just clearer, but it looks too
cluttered, as URLs aren't super pretty, necessarily.

| Before | After |
|--------|--------|
| <img
src="https://github.com/user-attachments/assets/ffd1ac27-b1f4-450d-abf5-079285fc9877"
width="700px" /> | <img
src="https://github.com/user-attachments/assets/28fb9d0d-205d-45d8-9e43-1aaa947adc96"
width="700px" /> |

Release Notes:

- N/A
2025-02-28 12:06:47 -03:00
张小白
c9aba6c10a windows: Use a clippy script instead of xtask (#25807)
Closes #ISSUE

Match the behaviour of our macOS and Linux tests

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-02-28 14:01:10 +00:00
Kirill Bulatov
5740fec9d5 Ensure search input always has regex language synced with search_options state (#25811)
Follow-up to https://github.com/zed-industries/zed/pull/25797

Release Notes:

- N/A
2025-02-28 13:15:25 +00:00
张小白
22220ed32e windows: Remove unnecessary code in #25412 (#25805)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-02-28 11:05:39 +00:00
Kirill Bulatov
7440833ff1 Add a way to toggle inlay hints with modifiers #2 (#25766)
https://github.com/zed-industries/zed/pull/25752 with fixes on top

* Ensures no flickering happens for all modifiers `: false` case
* Dismisses the toggled state on focus out
* Reworks cache state so that "enabled" and "toggled by modifiers" are
different states with their own lifecycle

Release Notes:

- N/A
2025-02-28 08:03:25 +00:00
Kirill Bulatov
bb3aef15eb Reset buffer language on buffer search redeploy (#25797)
Closes https://github.com/zed-industries/zed/issues/25792

Release Notes:

- Fixed search input regex highlight not going away after redeploy
2025-02-28 08:01:01 +00:00
Devzeth
ece1818301 docs: Add documentation for use_smartcase_search (#25786)
Closes #24795

Added missing documentation for `use_smartcase_search`. 

Release Notes:

- N/A
2025-02-28 09:45:40 +02:00
smit
604eb91a6c logging: Add runtime log replace upon max size limit (#25768)
Closes #25638

We currently only check the log size limit at startup and move `Zed.log`
to `Zed.log.old`. If a user runs Zed for an extended period, there's no
runtime restriction on the log file size, which can cause it to grow to
several gigabytes.

This PR fixes that by tracking the log file size while writing. If it
exceeds a certain threshold, we perform the same log replace and
continue logging.

Release Notes:

- Fixed an issue where `Zed.log` could grow excessively large during
long sessions of Zed.
2025-02-28 12:23:30 +05:30
5brian
472dde509f Capitalize default slash command description (#25794)
Update default slash command to use description constant format from
https://github.com/zed-industries/zed/pull/18595.

|Before|After|
|---|---|

|![image](https://github.com/user-attachments/assets/107a321e-0e91-40dd-8c38-4d55fd4f6d28)|![image](https://github.com/user-attachments/assets/f487a518-15bb-45c4-b524-25e6373a5886)|

^ This is when you type slash in the assistant panel.

Release Notes:

- N/A
2025-02-28 00:20:50 -03:00
Nathan Igo
212c8f4c31 html: Bump to v0.1.6 (#25791)
Includes:
- #25130

Release Notes:

- N/A
2025-02-27 20:38:55 -05:00
Shardul Vaidya
6092918be8 assistant: Improve Amazon Bedrock configuration instructions (#25699) 2025-02-27 21:36:41 -03:00
Joseph T. Lyons
a5f96909cb Update to suggest commit message based on file staging (#25790)
Currently, you only get a suggested commit message if you have a single
changed file in the repository. After the PR, the suggest happens per
single-staged file.

https://github.com/user-attachments/assets/4cc19fe6-099c-4690-967d-898b8ca7540b

Release Notes:

- N/A
2025-02-28 00:19:58 +00:00
Piotr Osiewicz
b15aa5e018 rust: Fix test/doctest tasks showing up outside of tests (#25787)
Closes #ISSUE

Release Notes:

- Fixes Rust test tasks showing up outside of tests
2025-02-28 00:48:19 +01:00
Cole Miller
62fb555e18 Use "restore" more consistently in the git panel (#25780)
This PR replaces almost all uses of "discard" in the git panel UI with
"restore", since that's the verb we settled on for the project diff.

The only exception is in the confirmation prompt for restoring files,
where I've kept the "discard changes" language. I think consistency is
less important here and it's helpful to rephrase the action that's being
taken to emphasize that it's destructive.

Release Notes:

- N/A
2025-02-27 18:45:56 -05:00
FalkWoldmann
c0ecf8684e Remove once_cell dependency (#25769)
Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-02-27 23:34:37 +00:00
Kirill Bulatov
df7beb4217 Use active worktree's task sources (#25784)
Follow-up of https://github.com/zed-industries/zed/pull/25605

Previous PR made global tasks with `ZED_WORKTREE_ROOT` available for
"nothing open" scenario, this PR also gets all related worktree task
templates, using the centralized `TextContexts`' active worktree
detection.

Release Notes:

- N/A
2025-02-27 22:57:59 +00:00
Marshall Bowers
6f30d5da71 Use consistent comment style in default.json (#25783)
This PR updates the comments in the `default.json` file consistently use
`//`.

Some comments were using `///`, which doesn't make sense in JSONC.

Release Notes:

- N/A
2025-02-27 22:50:23 +00:00
Marshall Bowers
b8387c6077 docs: Clarify wording around ... in language_servers setting (#25782)
This PR clarifies the wording around how `...` is used in the
`language_servers` setting.

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

Release Notes:

- N/A
2025-02-27 22:27:40 +00:00
Marshall Bowers
c05ce882e9 docs: Add note about path in extensions.toml (#25778)
This PR adds a note about the `path` field in `extensions.toml` and how
to use it.

Suggested in https://github.com/zed-industries/extensions/pull/2128.

Release Notes:

- N/A
2025-02-27 21:47:01 +00:00
Agus Zubiaga
eaf3949614 edit predictions: Remove enabled_in_assistant docs for now (#25777)
I shouldn't have added this because it's not out yet

Release Notes:

- N/A
2025-02-27 21:23:20 +00:00
Devzeth
c47305dd7b title_bar: Fix the order of the collab buttons (#25775)
My previous #24761 and #25192 PR's changed the order of the buttons in
the title_bar for collab, the logic is kept the same but the order is
now as it was previously.

Release Notes:

- N/A

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-02-27 16:22:05 -05:00
Cole Miller
482a45feac Fix broken merge (#25776)
Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-02-27 20:48:14 +00:00
Cole Miller
7ec3702b47 Fix cursor position when navigating to a multibuffer's first excerpt (#25723)
This PR fixes an unexpected cursor position when jumping to the
beginning of the project diff editor's first excerpt if that excerpt
starts with a deleted region. Previously, the cursor would end up in the
*following* region in this situation; now it ends up at the start of the
deleted region, as happens already for excerpts that are not the first.

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
2025-02-27 14:53:34 -05:00
smit
91862ddc9f markdown_preview: Fix markdown preview not updating on AcceptEditPrediction (#25772)
Closes #25384

Markdown preview now subscribes to `ExcerptsEdited` event which is
emited when edit prediction is accepted.

Release Notes:

- Fixed markdown preview not updating when edit prediction is accepted.
2025-02-28 01:22:46 +05:30
Cole Miller
eb4fad52df Fix panic when scrolling in project diff (#25771)
It may happen that the column for the scroll anchor is nonzero, and the
adjustment we're doing here could result in an invalid point in that
case.

Release Notes:

- N/A

Co-authored-by: Max <max@zed.dev>
2025-02-27 19:35:03 +00:00
Agus Zubiaga
541a5c01a4 edit predictions: Fix docs for enabled_in_assistant (#25770)
Remove mention of "prompt editor" since that feature isn't out yet.

Release Notes:

- N/A
2025-02-27 19:30:26 +00:00
Agus Zubiaga
82f793144e edit predictions: Add enabled_in_assistant setting (#25767)
Release Notes:

- edit predictions: Add `enabled_in_assistant` setting
2025-02-27 18:52:45 +00:00
Agus Zubiaga
6eb2ffe77a Support absolute disabled_globs (#25755)
Closes: #25556

We were always comparing `disabled_globs` against the relative file
path, we'll now use the absolute path if the glob is also absolute.

Release Notes:

- Support absolute globs in `edit_predictions.disabled_globs`
2025-02-27 15:29:32 -03:00
Mikayla Maki
c5632f8c31 Revert "Add a way to toggle inlay hints with modifiers" (#25764)
This PR caused inlay hints to show on every modifiers press

Reverts zed-industries/zed#25752

Release Notes:

- N/A
2025-02-27 10:11:36 -08:00
Cole Miller
6856e869fc Fix git panel's suggested commit message not updating (#25708)
Closes #ISSUE

Release Notes:

- N/A
2025-02-27 12:27:09 -05:00
João Marcos
cc3b5c729e Keep cursor at top when diff view is first opened (#25682)
Previously, we had the cursor at the bottom while the scroll stayed at
the top.

Now, if you run `git: diff`, the cursor will also be at the top.

The cursor moving to the end was possibly a side-effect of using
`Bias::Right` for selections.

---

Release Notes:

- N/A

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-02-27 17:19:44 +00:00
Cole Miller
4e60ebab5e Fix toggling deletion hunk with mouse at start of buffer (#25726)
Closes #ISSUE

Release Notes:

- N/A
2025-02-27 12:08:20 -05:00
Kirill Bulatov
e8ef36edcc Add a way to toggle inlay hints with modifiers (#25752) 2025-02-27 17:53:10 +02:00
Piotr Osiewicz
2e98bc17cb lsp: Use available workspace folders in initialize params (#25753)
Closes https://github.com/zed-industries/zed/issues/25743
Closes https://github.com/biomejs/biome-zed/issues/73

Release Notes:

- Fixed issues with launching Svelte/Biome language servers
2025-02-27 16:45:59 +01:00
Danilo Leal
5c400dac8d assistant2: Adjust empty state layout (#25745)
Going for a different, arguably simpler design for the Assistant 2 empty
state here. Also took the opportunity to adjust other elements like the
toolbar, message editor, and some items in the configuration page.

<img
src="https://github.com/user-attachments/assets/03fd1d48-a675-4eac-b694-bbe4eeaf06e9"
width="700px"/>

Release Notes:

- N/A
2025-02-27 11:33:53 -03:00
Danilo Leal
635b80ed51 assistant2: Fix submit button width depending on certain conditions (#25748)
This PR makes the Assistant 2 submit button have a different width if
the platform is Linux or Windows, or if Vim mode is turned on. That's
because we now use written out words instead of icons for keybindings
when in those conditions.

| Before | After |
|--------|--------|
| ![CleanShot 2025-02-27 at 9  59
10@2x](https://github.com/user-attachments/assets/f1f8e27c-71c2-402c-8f7b-f9ed91e8e3bf)
| ![CleanShot 2025-02-27 at 9  56
59@2x](https://github.com/user-attachments/assets/ad13b179-daf7-4b38-83e8-1511deb97d96)
|

Release Notes:

- N/A
2025-02-27 11:33:44 -03:00
Danilo Leal
73ab5abee1 assistant2: Adjust tool call accordion visuals (#25749)
Just fine-tuning it a bit more.

<img
src="https://github.com/user-attachments/assets/0d46af77-d111-40a3-9204-d5d8aa9d4886"
width="700px"/>

Release Notes:

- N/A
2025-02-27 11:33:37 -03:00
smit
1f52aab7c7 buffer: Fix panic when multi-byte character is used in languages like Swift (#25739)
Closes #25471

In languages like Swift, names can be concatinated in form like `class
Example: UI`, notice here `Example` and `:` are two different words.
Before, `name_ranges`translation of above text would look like:

```
"class" -> [0..5]
" Example" -> [5..13] (Spaces are intentional)
"e:" -> [12..14] (This is incorrect, and should be ":" -> [13..14])
" UI" -> [14..16]
```

Because this translation does not account for concatinated words, this
might affect queries, but most importantly this panics when multi-byte
character (`ф`) is used in place of `e`, as it then tries to access
index which lies inside that multi-byte. For example, it panics on
`class Examplф: UI`.

---

This PR fixes this by handing concatinated words when calculating
`name_ranges`.

Now, the corrected ranges will look like:

```
"class" -> [0..5]
" Example" -> [5..13]
":" -> [13..14] (Now it's correct)
" UI" -> [14..16]
```

and for multi-byte character

```
"class" -> [0..5]
" Examplф" -> [5..14] (Notice ф takes two bytes)
":" -> [14..15]
" UI" -> [15..17]
```

This way, it no longer tries to access a previous index, preventing a
panic when that index contains a multi-byte character.

Release Notes:

- Fixed a panic when Cyrillic characters are used in languages like
Swift.
2025-02-27 16:27:07 +05:30
renovate[bot]
1732cdb90a Update Rust crate sea-orm to v1.1.6 (#25696)
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.6` |
| [sea-orm](https://www.sea-ql.org/SeaORM)
([source](https://redirect.github.com/SeaQL/sea-orm)) | dependencies |
patch | `1.1.5` -> `1.1.6` |

---

### Release Notes

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

###
[`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
[https://github.com/SeaQL/sea-orm/pull/2500](https://redirect.github.com/SeaQL/sea-orm/pull/2500)
- 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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 08:13:25 +00:00
张小白
0625006a9e windows: Missing commit of #25412 (#25732)
Closes #ISSUE

Release Notes:

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

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-02-27 08:00:42 +00:00
renovate[bot]
0b96690446 Update Rust crate convert_case to 0.8.0 (#25711)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [convert_case](https://redirect.github.com/rutrum/convert-case) |
workspace.dependencies | minor | `0.7.0` -> `0.8.0` |

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 07:53:30 +00:00
renovate[bot]
bc22690620 Update Rust crate ctor to 0.4.0 (#25712)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [ctor](https://redirect.github.com/mmastrac/rust-ctor) |
workspace.dependencies | minor | `0.3.0` -> `0.4.0` |

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 07:48:46 +00:00
ishanray
4eb82c0731 docs: Add DeepSeek to list of providers (#25730)
Release Notes:

- N/A
2025-02-27 07:42:14 +00:00
renovate[bot]
fa91379119 Update Rust crate libc to v0.2.170 (#25690)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

##### Added

- Android: Declare `setdomainname` and `getdomainname`
[#&#8203;4212](https://redirect.github.com/rust-lang/libc/pull/4212)
- FreeBSD: Add `evdev` structures
[#&#8203;3756](https://redirect.github.com/rust-lang/libc/pull/3756)
- FreeBSD: Add the new `st_filerev` field to `stat32`
([#&#8203;4254](https://redirect.github.com/rust-lang/libc/pull/4254))
- Linux: Add ` SI_*`` and `TRAP_\*\`\` signal codes
[#&#8203;4225](https://redirect.github.com/rust-lang/libc/pull/4225)
- Linux: Add experimental configuration to enable 64-bit time in kernel
APIs, set by `RUST_LIBC_UNSTABLE_LINUX_TIME_BITS64`.
[#&#8203;4148](https://redirect.github.com/rust-lang/libc/pull/4148)
- Linux: Add recent socket timestamping flags
[#&#8203;4273](https://redirect.github.com/rust-lang/libc/pull/4273)
- Linux: Added new CANFD_FDF flag for the flags field of canfd_frame
[#&#8203;4223](https://redirect.github.com/rust-lang/libc/pull/4223)
- Musl: add CLONE_NEWTIME
[#&#8203;4226](https://redirect.github.com/rust-lang/libc/pull/4226)
- Solarish: add the posix_spawn family of functions
[#&#8203;4259](https://redirect.github.com/rust-lang/libc/pull/4259)

##### Deprecated

- Linux: deprecate kernel modules syscalls
[#&#8203;4228](https://redirect.github.com/rust-lang/libc/pull/4228)

##### Changed

- Emscripten: Assume version is at least 3.1.42
[#&#8203;4243](https://redirect.github.com/rust-lang/libc/pull/4243)

##### Fixed

- BSD: Correct the definition of `WEXITSTATUS`
[#&#8203;4213](https://redirect.github.com/rust-lang/libc/pull/4213)
- Hurd: Fix CMSG_DATA on 64bit systems
([#&#8203;4240](https://redirect.github.com/rust-lang/libc/pull/424))
- NetBSD: fix `getmntinfo`
([#&#8203;4265](https://redirect.github.com/rust-lang/libc/pull/4265)
- VxWorks: Fix the size of `time_t`
[#&#8203;426](https://redirect.github.com/rust-lang/libc/pull/426)

##### Other

- Add labels to FIXMEs
[#&#8203;4230](https://redirect.github.com/rust-lang/libc/pull/4230),
[#&#8203;4229](https://redirect.github.com/rust-lang/libc/pull/4229),
[#&#8203;4237](https://redirect.github.com/rust-lang/libc/pull/4237)
- CI: Bump FreeBSD CI to 13.4 and 14.2
[#&#8203;4260](https://redirect.github.com/rust-lang/libc/pull/4260)
- Copy definitions from core::ffi and centralize them
[#&#8203;4256](https://redirect.github.com/rust-lang/libc/pull/4256)
- Define c_char at top-level and remove per-target c_char definitions
[#&#8203;4202](https://redirect.github.com/rust-lang/libc/pull/4202)
- Port style.rs to syn and add tests for the style checker
[#&#8203;4220](https://redirect.github.com/rust-lang/libc/pull/4220)

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 09:28:52 +02:00
renovate[bot]
da2320fb40 Update Rust crate chrono to v0.4.40 (#25684)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [chrono](https://redirect.github.com/chronotope/chrono) |
workspace.dependencies | patch | `0.4.39` -> `0.4.40` |

---

### Release Notes

<details>
<summary>chronotope/chrono (chrono)</summary>

###
[`v0.4.40`](https://redirect.github.com/chronotope/chrono/releases/tag/v0.4.40):
0.4.40

[Compare
Source](https://redirect.github.com/chronotope/chrono/compare/v0.4.39...v0.4.40)

#### What's Changed

- Add Month::num_days() by
[@&#8203;djc](https://redirect.github.com/djc) in
[https://github.com/chronotope/chrono/pull/1645](https://redirect.github.com/chronotope/chrono/pull/1645)
- Update Windows dependencies by
[@&#8203;kennykerr](https://redirect.github.com/kennykerr) in
[https://github.com/chronotope/chrono/pull/1646](https://redirect.github.com/chronotope/chrono/pull/1646)
- Feature/round_up method on DurationRound trait by
[@&#8203;MagnumTrader](https://redirect.github.com/MagnumTrader) in
[https://github.com/chronotope/chrono/pull/1651](https://redirect.github.com/chronotope/chrono/pull/1651)
- Expose `write_to` for `DelayedFormat` by
[@&#8203;tugtugtug](https://redirect.github.com/tugtugtug) in
[https://github.com/chronotope/chrono/pull/1654](https://redirect.github.com/chronotope/chrono/pull/1654)
- Update LICENSE.txt by
[@&#8203;maximevtush](https://redirect.github.com/maximevtush) in
[https://github.com/chronotope/chrono/pull/1656](https://redirect.github.com/chronotope/chrono/pull/1656)
- docs: fix minor typo by
[@&#8203;samfolo](https://redirect.github.com/samfolo) in
[https://github.com/chronotope/chrono/pull/1659](https://redirect.github.com/chronotope/chrono/pull/1659)
- Use NaiveDateTime for internal tz_info methods. by
[@&#8203;AVee](https://redirect.github.com/AVee) in
[https://github.com/chronotope/chrono/pull/1658](https://redirect.github.com/chronotope/chrono/pull/1658)
- Upgrade to windows-bindgen 0.60 by
[@&#8203;djc](https://redirect.github.com/djc) in
[https://github.com/chronotope/chrono/pull/1665](https://redirect.github.com/chronotope/chrono/pull/1665)
- Add quarter (%q) date string specifier by
[@&#8203;drinkcat](https://redirect.github.com/drinkcat) in
[https://github.com/chronotope/chrono/pull/1666](https://redirect.github.com/chronotope/chrono/pull/1666)

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 09:28:33 +02:00
renovate[bot]
7bc31a69c3 Update aws-sdk-rust monorepo (#25704)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [aws-config](https://redirect.github.com/smithy-lang/smithy-rs) |
dependencies | patch | `1.5.16` -> `1.5.17` |
| [aws-config](https://redirect.github.com/smithy-lang/smithy-rs) |
workspace.dependencies | patch | `1.5.16` -> `1.5.17` |
|
[aws-sdk-bedrockruntime](https://redirect.github.com/awslabs/aws-sdk-rust)
| workspace.dependencies | minor | `1.74.0` -> `1.75.0` |
| [aws-sdk-kinesis](https://redirect.github.com/awslabs/aws-sdk-rust) |
dependencies | minor | `1.61.0` -> `1.62.0` |
| [aws-sdk-s3](https://redirect.github.com/awslabs/aws-sdk-rust) |
dependencies | minor | `1.76.0` -> `1.77.0` |

---

### Configuration

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

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

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

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 09:26:59 +02:00
renovate[bot]
1e4752972f Update Rust crate rust-embed to v8.6.0 (#25720)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [rust-embed](https://redirect.github.com/pyros2097/rust-embed) |
workspace.dependencies | minor | `8.5.0` -> `8.6.0` |

---

### Release Notes

<details>
<summary>pyros2097/rust-embed (rust-embed)</summary>

###
[`v8.6.0`](https://redirect.github.com/pyros2097/rust-embed/blob/HEAD/changelog.md#860---2025-02-25)

- Update include-flate to 0.3
[#&#8203;246](https://redirect.github.com/pyrossh/rust-embed/pull/246).
Thanks to [krant](https://redirect.github.com/krant)
- refactor: remove redundant reference and closure
[#&#8203;250](https://redirect.github.com/pyrossh/rust-embed/pull/250).
Thanks to [hamirmahal](https://redirect.github.com/hamirmahal)
- refactor: replace map().unwrap_or_else().
[#&#8203;250](https://redirect.github.com/pyrossh/rust-embed/pull/255).
Thanks to [hamirmahal](https://redirect.github.com/hamirmahal)
- Compatible with Axum 0.7.9
[#&#8203;253](https://redirect.github.com/pyrossh/rust-embed/pull/253).
Thanks to [wkmyws](https://redirect.github.com/wkmyws)
- Add allow_missing option to derive macro
[#&#8203;256](https://redirect.github.com/pyrossh/rust-embed/pull/256).
Thanks to [lirannl](https://redirect.github.com/lirannl)

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 09:21:16 +02:00
renovate[bot]
91148a72a3 Update Rust crate uuid to v1.15.1 (#25728)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [uuid](https://redirect.github.com/uuid-rs/uuid) |
workspace.dependencies | minor | `1.13.2` -> `1.15.1` |

---

### Release Notes

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

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

[Compare
Source](https://redirect.github.com/uuid-rs/uuid/compare/v1.15.0...v1.15.1)

#### What's Changed

- Guarantee v7 timestamp will never overflow by
[@&#8203;KodrAus](https://redirect.github.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/811](https://redirect.github.com/uuid-rs/uuid/pull/811)
- Prepare for 1.15.1 release by
[@&#8203;KodrAus](https://redirect.github.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/812](https://redirect.github.com/uuid-rs/uuid/pull/812)

**Full Changelog**:
https://github.com/uuid-rs/uuid/compare/v1.15.0...v1.15.1

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

[Compare
Source](https://redirect.github.com/uuid-rs/uuid/compare/v1.14.0...v1.15.0)

#### What's Changed

- Add a manual `Debug` implementation for NonNilUUid by
[@&#8203;rick-de-water](https://redirect.github.com/rick-de-water) in
[https://github.com/uuid-rs/uuid/pull/808](https://redirect.github.com/uuid-rs/uuid/pull/808)
- Support higher precision, shiftable timestamps in V7 UUIDs by
[@&#8203;KodrAus](https://redirect.github.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/809](https://redirect.github.com/uuid-rs/uuid/pull/809)
- Prepare for 1.15.0 release by
[@&#8203;KodrAus](https://redirect.github.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/810](https://redirect.github.com/uuid-rs/uuid/pull/810)

#### New Contributors

- [@&#8203;rick-de-water](https://redirect.github.com/rick-de-water)
made their first contribution in
[https://github.com/uuid-rs/uuid/pull/808](https://redirect.github.com/uuid-rs/uuid/pull/808)

**Full Changelog**:
https://github.com/uuid-rs/uuid/compare/v1.14.0...v1.15.0

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

[Compare
Source](https://redirect.github.com/uuid-rs/uuid/compare/v1.13.2...v1.14.0)

#### What's Changed

- Add FromStr impls to the fmt structs by
[@&#8203;tysen](https://redirect.github.com/tysen) in
[https://github.com/uuid-rs/uuid/pull/806](https://redirect.github.com/uuid-rs/uuid/pull/806)
- Prepare for 1.14.0 release by
[@&#8203;KodrAus](https://redirect.github.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/807](https://redirect.github.com/uuid-rs/uuid/pull/807)

#### New Contributors

- [@&#8203;tysen](https://redirect.github.com/tysen) made their first
contribution in
[https://github.com/uuid-rs/uuid/pull/806](https://redirect.github.com/uuid-rs/uuid/pull/806)

**Full Changelog**:
https://github.com/uuid-rs/uuid/compare/v1.13.2...v1.14.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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 09:10:49 +02:00
Anthony Eid
878b50c991 Update git panel entry checked box tooltip to say Stage/Unstage (#25678)
Before it would always say staged when a user hovered over the check
box. Now it will show the correct hover message depending on the state
of the entry

Release Notes:

- N/A
2025-02-27 00:29:26 -05:00
Marshall Bowers
e7df5ce61c assistant2: Avoid unnecessary String cloning in tool use (#25725)
This PR removes some unnecessary `String` cloning in the tool use paths.

We now store the data in `Arc<str>`s for cheap cloning.

Release Notes:

- N/A
2025-02-27 03:16:09 +00:00
Marshall Bowers
da22f21dec Move PopoverButton into ui (#25724)
This PR moves the `PopoverButton` component into the `ui` crate.

The `popover_button` crate only depended on `ui`, so there doesn't seem
to be a need for it to live in its own crate and add another step in the
crate graph.

Release Notes:

- N/A
2025-02-27 02:51:19 +00:00
Marshall Bowers
3505a17452 git_ui: Combine disjoint conditions into one (#25722)
This PR combines two disjoint conditions for the same value into one.

This makes it so the type checker can accurately reason about the
branches.

Release Notes:

- N/A
2025-02-27 02:33:25 +00:00
Marshall Bowers
81badd1fe6 Sort Cargo.tomls (#25721)
This PR sorts some `Cargo.toml`s that had become unsorted.

Release Notes:

- N/A
2025-02-27 02:28:59 +00:00
renovate[bot]
6dacc751fc Update Rust crate schemars to v0.8.22 (#25695)
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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-26 21:28:21 -05:00
张小白
a0d1555470 windows: Fix terminal inline assistant (#25715)
Closes #18518
Closes #20546

Release Notes:

- N/A
2025-02-27 10:03:46 +08:00
Mikayla Maki
8ba7b349a5 Make the branch picker in the commit modal a popover (#25697)
Release Notes:

- N/A

---------

Co-authored-by: Nate Butler <iamnbutler@gmail.com>
2025-02-27 01:56:07 +00:00
Shardul Vaidya
11838cf89e bedrock: Fix region bug (#25716)
Closes #25714

Internal team reported issue where the Bedrock provider defaulted to
"us-east-1" for all requests regardless of what is configured in the
credentials until first zed restart.

Release Notes:

- Fixed an issue where the Bedrock model provider would not always
respect the region.
2025-02-26 20:55:03 -05:00
renovate[bot]
84ded96cb2 Update serde monorepo to v1.0.218 (#25705)
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.217` -> `1.0.218` |
| [serde](https://serde.rs)
([source](https://redirect.github.com/serde-rs/serde)) |
workspace.dependencies | patch | `1.0.217` -> `1.0.218` |
| [serde_derive](https://serde.rs)
([source](https://redirect.github.com/serde-rs/serde)) |
workspace.dependencies | patch | `1.0.217` -> `1.0.218` |

---

### Release Notes

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

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

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

-   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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-26 19:59:20 -05:00
Marshall Bowers
afc61b9527 assistant2: Automatically respond to the model with tool results (#25706)
This PR updates the tool use flow in Assistant 2 to automatically
respond to the model with tool results when the tools have finished
running.

Release Notes:

- N/A
2025-02-27 00:37:44 +00:00
张小白
672a472a23 windows: Implement cli and handle open_urls (#25412)
Closes #ISSUE

Release Notes:

- N/A
2025-02-26 16:27:19 -08:00
Marshall Bowers
9822d9673c assistant2: Add Thread::send_to_model method (#25703)
This PR adds a new `send_to_model` method to the `Thread` to encapsulate
more of the thread-specific capabilities.

We then call this in `MessageEditor::send_to_model`.

Release Notes:

- N/A
2025-02-27 00:16:44 +00:00
Marshall Bowers
f0dec2f576 assistant2: Visualize tool use (#25692)
This PR adds visuals for tool use in Assistant 2:

<img width="1309" alt="Screenshot 2025-02-26 at 5 57 14 PM"
src="https://github.com/user-attachments/assets/4083ff65-a2f1-4a43-8815-0bade2c00af2"
/>

Release Notes:

- N/A
2025-02-26 23:19:24 +00:00
renovate[bot]
9f7c65df44 Update Rust crate clap to v4.5.31 (#25685)
This PR contains the following updates:

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

---

### Release Notes

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

###
[`v4.5.31`](https://redirect.github.com/clap-rs/clap/blob/HEAD/CHANGELOG.md#4531---2025-02-24)

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

##### Features

-   Add `ValueParserFactory` for `Saturating<T>`

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-26 17:55:48 -05:00
renovate[bot]
372e485ba8 Update Rust crate log to v0.4.26 (#25691)
This PR contains the following updates:

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

---

### Release Notes

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

###
[`v0.4.26`](https://redirect.github.com/rust-lang/log/blob/HEAD/CHANGELOG.md#0426---2025-02-18)

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

</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-26 17:55:29 -05:00
Marshall Bowers
b8fb416892 assistant2: Exclude tool uses and results when summarizing threads (#25689)
This PR fixes the generation of summaries for threads when tools are
being used.

Previously we were including the tool uses in the summarization request,
but this would result in invalid messages being sent to the model and
summaries not being generated.

We now exclude any tool uses or results from the model when summarizing
a thread.

Release Notes:

- N/A
2025-02-26 22:24:56 +00:00
Marshall Bowers
6a1c104522 language_settings: Add auto alias for subtle edit prediction mode (#25686)
This PR makes `auto` an alias for the `subtle` edit prediction mode.

Right now I'm in a state where I can't have valid settings in both
development and Nightly because the settings values are disparate.

Release Notes:

- N/A
2025-02-26 22:06:17 +00:00
Peter Tripp
b06da7f7fd ssh: Allow ssh -F ssh_config (#25619)
- Closes https://github.com/zed-industries/zed/issues/22818

Usage: `ssh -F ssh_config user@host.tld`

```
-F configfile
    Specifies an alternative per-user configuration file. If a configuration file
    is given on the command line, the system-wide configuration file
    (/etc/ssh/ssh_config) will be ignored. The default for the per-user
    configuration file is ~/.ssh/config. If set to “none”, no configuration files
    will be read.
```

Release Notes:

- ssh: Added support for specifying ssh_config files (`ssh -F
ssh_config`) in connection string
2025-02-26 16:23:25 -05:00
Peter Tripp
f80035e0ff Support busybox wget for downloading zed-remote-server (#25621)
- Closes: https://github.com/zed-industries/zed/issues/22380

Arch linux ships busybox wget not gnu wget.
BusyBox wget does not support `--max-redirect`.

Release Notes:

- ssh: Add support for downloading `zed-remote-server` with busybox wget (ArchLinux, etc)
2025-02-26 16:22:56 -05:00
renovate[bot]
7664c1cef5 Update actions/upload-artifact digest to 4cec3d8 (#25680)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/upload-artifact](https://redirect.github.com/actions/upload-artifact)
| action | digest | `65c4c4a` -> `4cec3d8` |

---

### 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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-26 16:07:26 -05:00
renovate[bot]
967792119b Pin clechasseur/rs-cargo action to 8435b10 (#25679)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[clechasseur/rs-cargo](https://redirect.github.com/clechasseur/rs-cargo)
| action | pinDigest | -> `8435b10` |

---

### 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:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-26 16:07:05 -05:00
João Marcos
be1ac78e11 Unfold buffers in multibuffers when editing them (#25677)
Release Notes:

- Multibuffers: Unfold excerpts when editing their contents.

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-02-26 17:39:33 -03:00
Kirill Bulatov
b5a1ae6526 Improve Zed tasks' ZED_WORKTREE_ROOT fallbacks (#25605)
Closes https://github.com/zed-industries/zed/issues/22912

Reworks the task context infrastructure so that it's possible to have
multiple contexts at the same time, and stores all possible worktree
context there.
Task UI code is now falling back to the "active" worktree context, if
active item's context did not produce a resolved task.

Current code does not produce meaningful results for projects with
multiple worktrees to avoid ambiguity and design changes: instead of
resolving tasks per worktree context available, extra worktree context
is only used when resolving tasks from the same worktree.

Release Notes:

- Improved Zed tasks' `ZED_WORKTREE_ROOT` fallbacks
2025-02-26 22:30:31 +02:00
Kirill Bulatov
d2b49de0e4 Dismiss active diagnostics on invalidation (#25646)
When migrating to gpui2,
588976d27a (diff-a3da3181e4ab4f73aa1697d7b6dc0caa0c17b2a187fb83b076dfc0234ec91f54R21)
removed the diagnostic style for "active but invalid" case: presumably,
it served as some sort of a cursor to show where to move on after the
diagnostics update, on the next `GoTo[Prev]Diagnostic` action call.

As this change went unchanged for some time, another approach is tested
now, to be more integrated with inline diagnostics: now, the active
state is cleared

Same as before this change, another `GoTo[Prev]Diagnostic` action call
will be needed to re-expand a new diagnostics, but this change makes
this expansion to happen after the cursor — before the change, Zed would
continue from the stale diagnostics.

Release Notes:

- Fixed active diagnostics becoming stale
2025-02-26 22:30:23 +02:00
Agus Zubiaga
d694458659 edit predictions: Rename edit prediction modes (#25657)
`auto` -> `stealth`
`eager_preview` -> `eager`

Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2025-02-26 20:23:39 +00:00
Cole Miller
7a34dd9888 Save buffers after restoring hunks in the project diff (#25620)
This PR fixes a bug where using the project diff editor to restore hunks
from a file that's not open in its own buffer would cause those reverts
to be lost once the project diff drops its excerpts for that file.

The fix is to save the buffers after restoring them but before the
excerpts are (potentially) dropped. This is done for the project diff
editor only. If we fail to save the affected files, we add their buffers
to the active workspace, so that the reverted contents are preserved and
the user can try again to save them.

- [x] Get it working
- [x] Test
- [ ] ~~Clean up boolean soup~~

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

Release Notes:

- N/A
2025-02-26 15:16:17 -05:00
Michael Sloan
add7ae8052 Try to reveal selection changing issues in undo/redo via logging (#25676)
This will hopefully help debug #22692. I tried this for a while locally
and saw neither these logs nor the issue.

Release Notes:

- N/A
2025-02-26 19:48:15 +00:00
Marshall Bowers
c53020ceaf vim: Combine match arms in Mode::is_visual (#25675)
This PR refactors the `Mode::is_visual` implementation to combine some
of the `match` arms.

Release Notes:

- N/A
2025-02-26 19:45:04 +00:00
Cole Miller
eeac1a9287 Style filenames and paths in project diff buffer headers according to git status (#25653)
This substitutes for the icons that we previously kept in these headers.

cc @iamnbutler 

Release Notes:

- N/A
2025-02-26 14:43:10 -05:00
0x2CA
e83ebd1fab vim: Add more tests for replace with register (#25316)
Closes #ISSUE

Add more tests

Release Notes:

- N/A
2025-02-26 12:38:50 -07:00
Conrad Irwin
afb0fd609b Chunk git status entries (#25627)
Prevents us trying to write 5M untracked files to postgres in one commit

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-02-26 12:38:16 -07:00
Conrad Irwin
b2a685f00a Fix staging error reporting (#25630)
Closes #ISSUE

Release Notes:

- N/A
2025-02-26 12:38:04 -07:00
Yicheng Liu
089ea5da50 vim: Fix back quotes not recognized as object (#24999)
Currently back quotes ``` `` ``` not recognized as an object in vim
mode, so ```c i ` ```, ```d i ` ``` not working.

It seems to be a typo introduced in #22632 : The`DoubleQuotes` line was
doubled while the `BackQuotes` line was missing.

Release Notes:

- vim: Fixed back quotes ``` `` ``` not recognized as object.

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-02-26 14:21:12 -05:00
smit
b6e8db244c vim: Fix search submit panic (#25673)
In file search submit action, handle unwrap when there are no prior
selection.

Fix is for recently made commits, hence no release notes.

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <anthony@zed.dev>
2025-02-27 00:40:46 +05:30
Brandon Li
6267ab0396 vim: Add ability to change default mode (#25067)
Closes #13881, and technically resolves #14927.

Release Notes:

- Added the ability to set the default Vim mode.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-02-26 13:51:07 -05:00
Joseph T. Lyons
d105f04be5 Bump Zed to v0.177 (#25669)
Release Notes:

-N/A
2025-02-26 18:31:58 +00:00
Mikayla Maki
5edded5c02 Simplify project git code (#25662)
This was originally a part of another PR, but I wanted to get the
refactoring in and shift focus to working on bugs.

This causes all git commands via the `Repository` entity to be
serialized, and allows us to return values other than `Result<()>`

Release Notes:

- N/A
2025-02-26 18:16:10 +00:00
Marshall Bowers
78da39e19b assistant2: Add ability to delete past prompt editors (#25667)
This PR adds the ability to delete past prompt editors in Assistant 2,
the same way you can with threads.

Release Notes:

- N/A
2025-02-26 18:02:36 +00:00
Marshall Bowers
d82a132477 language_model: Use LanguageModelToolUseId instead of a String (#25666)
This PR updates the `LanguageModelToolResult` type to use a
`LanguageModelToolUseId` for the tool use ID instead of a `String`.

Release Notes:

- N/A
2025-02-26 17:34:16 +00:00
Federico Dionisi
f11357db7c context_server: Abstract server transport (#24528)
This PR abstracts the communication layer for context servers, laying
the groundwork for supporting multiple transport mechanisms and taking
one step towards enabling remote servers.

Key changes centre around creating a new `Transport` trait with methods
for sending and receiving messages. I've implemented this trait for the
existing stdio-based communication, which is now encapsulated in a
`StdioTransport` struct. The `Client` struct has been refactored to use
this new `Transport` trait instead of directly managing stdin and
stdout.

The next steps will involve implementing an SSE + HTTP transport and
defining alternative context server settings for remote servers.

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-02-26 17:19:19 +00:00
Cole Miller
6d17546b1a Fix panic in file finder path elision (#25658)
Release Notes:

- N/A
2025-02-26 11:45:53 -05:00
Caleb!
60a96ab799 image_viewer: Hide breadcrumb (#25654)
Closes #25279 


![image](https://github.com/user-attachments/assets/15a0b092-449b-493c-8eea-10a3d9d1a912)

Release Notes:

- Added the ability to hide breadcrumb showing image path
2025-02-26 17:24:35 +01:00
Nate Butler
1f80f58104 git_ui: Commit modal editor cleanup (#25645)
- Fixes cursor style in the commit modal
- Use commit button instead of kb hint
- Update layout to scale better for large commit messages

No message:

![CleanShot 2025-02-26 at 10 33
22@2x](https://github.com/user-attachments/assets/fb6fc4ff-1e1d-46ae-aee0-7e21d73aaf70)

Long Message:
![CleanShot 2025-02-26 at 10 33
55@2x](https://github.com/user-attachments/assets/71a7773c-386b-47d5-abed-9a301360c35b)

![CleanShot 2025-02-26 at 10 34
06@2x](https://github.com/user-attachments/assets/dc41f0b9-2277-4034-9af2-d77fec0488d8)

Release Notes:

- N/A
2025-02-26 15:55:38 +00:00
Danilo Leal
bab65011b4 edit prediction: Refine the stealth mode (#25599)
Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <agus@zed.dev>
2025-02-26 12:50:11 -03:00
Piotr Osiewicz
c0b6d86c41 go: Do not fill out root_uri in initialization params to prevent stale notifications (#25644)
Closes #25381

Release Notes:

- N/A
2025-02-26 13:24:27 +01:00
Viktor Zahorodnii
39728cfc59 Add docs on keybindings to trigger runnables (#25582)
Addresses
https://github.com/zed-industries/zed/discussions/22810#discussioncomment-12239661

Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2025-02-26 09:38:22 +00:00
Max Brunsfeld
ebccef1aa4 Fix staging and unstaging of added and deleted files (#25631)
* When staging in a buffer whose file has been deleted, do not save the
file
* Fix logic for writing to index when file is deleted

Release Notes:

- N/A
2025-02-26 07:25:31 +00:00
Conrad Irwin
33754f8eac Fix search skipping in vim mode (#25580)
Closes #8049

Co-authored-by: nilehmann <nico.lehmannm@gmail.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>

Release Notes:

- vim: Fix skipping of search results occasionally

Co-authored-by: nilehmann <nico.lehmannm@gmail.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2025-02-25 23:29:54 -07:00
Conrad Irwin
dd1ff9b998 Git: Fix prompts with a very large number of filenames (#25629)
Closes #ISSUE

Release Notes:

- N/A
2025-02-25 23:29:17 -07:00
Conrad Irwin
7f214ed25a git: Fix cmd-enter (#25628)
Closes #ISSUE

Release Notes:

- N/A
2025-02-25 23:07:55 -07:00
Conrad Irwin
08539b32d0 Fix some syncing issues with git statuses (#25535)
Like the real app, this one infinite loops if you have a diff in an
UnsharedFile.

Release Notes:

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

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-02-25 22:09:02 -07:00
5brian
88baf171c3 docs: Add apostrophe (#25624)
Release Notes:

- N/A
2025-02-26 03:46:02 +00:00
Peter Tripp
2f34af7811 docs: Fix SSH projects example settings (#25622) 2025-02-25 22:15:35 -05:00
Cole Miller
2978be95d7 Don't deploy git panel when opening the diff view (#25611)
Release Notes:

- N/A
2025-02-25 21:29:08 -05:00
Nate Butler
30568e6dd1 Add overflow menu to the git panel (#25618)
Before: 

![CleanShot 2025-02-25 at 20 11
48@2x](https://github.com/user-attachments/assets/2400270e-64fe-4711-a2aa-31588e73367a)

After:

![CleanShot 2025-02-25 at 20 13
18@2x](https://github.com/user-attachments/assets/70c88d2f-5e16-4f2d-9cc5-666b2f9b8de0)

Release Notes:

- N/A
2025-02-25 21:07:10 -05:00
Michael Sloan
a5698a430d Use carriage return instead of newline symbol for single line text (#25616)
I think this is clearer in the cases where it does appear. Use of NL
symbol was added in #10231

Release Notes:

- N/A
2025-02-25 18:26:50 -07:00
Marshall Bowers
57659b5552 assistant2: Fix "Open Prompt Library" button (#25612)
This PR fixes the "Open Prompt Library" button after the GPUI 3 changes.

Release Notes:

- N/A
2025-02-26 00:05:59 +00:00
Piotr Osiewicz
3db18ff053 lsp: Add support for dynamic registration of rename capability (#25610)
While looking at Biome LSP implementation I've noticed that they
register their rename capability dynamically, which we don't handle.

Release Notes:

- N/A
2025-02-25 23:54:29 +00:00
Cole Miller
198f56c763 Fix gutter highlights not matching diff hunks near excerpt boundaries (#25600)
Release Notes:

- Fixed gutter highlights not matching diff hunks in multibuffers in
some cases

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2025-02-25 15:33:16 -08:00
Max Brunsfeld
d68d858a10 Fix crash in BlockMap::sync when there are inlay hints w/ newlines ri… (#25598)
Closes https://github.com/zed-industries/zed/issues/25377

Release Notes:

- Fixed a crash that could happen when typing in the assistant panel
with edit predictions enabled.

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
2025-02-25 22:32:14 +00:00
Marshall Bowers
7f166298db collab: Adjust maximum spending limit check (#25596)
This is a follow-up to https://github.com/zed-industries/zed/pull/25573.

We were still using the spend for a particular model when determining if
the user was over their maximum monthly spend instead of looking at the
usage across all models.

Release Notes:

- N/A
2025-02-25 16:45:01 -05:00
Piotr Osiewicz
0066071a89 lsp: Query first capable language server for requests using primary LS (#25591)
Release Notes:

- Improved Zed's handling of the following requests when the first
language server in language server settings for a given language is not
capable of handling them:
  - Perform Rename
  - Prepare Rename
  - Document Highlights
  - Find all references
  - Go to implementation
  - Go to definition
  - Go to declaration
  - Go to type definition
2025-02-25 22:12:13 +01:00
520 changed files with 23836 additions and 11196 deletions

View File

@@ -0,0 +1,51 @@
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

@@ -110,13 +110,13 @@ jobs:
run: ./script/clippy
- name: Install cargo-machete
uses: clechasseur/rs-cargo@v2
uses: clechasseur/rs-cargo@8435b10f6e71c2e3d4d3b7573003a8ce4bfc6386 # v2
with:
command: install
args: cargo-machete@0.7.0
- name: Check unused dependencies
uses: clechasseur/rs-cargo@v2
uses: clechasseur/rs-cargo@8435b10f6e71c2e3d4d3b7573003a8ce4bfc6386 # v2
with:
command: machete
@@ -236,12 +236,24 @@ jobs:
if: always()
run: rm -rf ./../.cargo
windows_tests:
windows_clippy:
timeout-minutes: 60
name: (Windows) Run Clippy and tests
name: (Windows) Run Clippy
if: github.repository_owner == 'zed-industries'
runs-on: hosted-windows-1
runs-on: hosted-windows-2
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
@@ -273,8 +285,70 @@ jobs:
- name: cargo clippy
working-directory: ${{ env.ZED_WORKSPACE }}
# Windows can't run shell scripts, so we need to use `cargo xtask`.
run: cargo xtask clippy
run: ./script/clippy.ps1
- name: Check dev drive space
working-directory: ${{ env.ZED_WORKSPACE }}
# `setup-dev-driver.ps1` creates a 100GB drive, with CI taking up ~45GB of the drive.
run: ./script/exit-ci-if-dev-drive-is-full.ps1 95
# Since the Windows runners are stateful, so we need to remove the config file to prevent potential bug.
- name: Clean CI config file
if: always()
run: |
if (Test-Path "${{ env.CARGO_HOME }}/config.toml") {
Remove-Item -Path "${{ env.CARGO_HOME }}/config.toml" -Force
}
# Windows CI takes twice as long as our other platforms and fast github hosted runners are expensive.
# But we still want to do CI, so let's only run tests on main and come back to this when we're
# ready to self host our Windows CI (e.g. during the push for full Windows support)
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
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
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
- name: Create Dev Drive using ReFS
run: ./script/setup-dev-driver.ps1
# actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone...
- name: Copy Git Repo to Dev Drive
run: |
Copy-Item -Path "${{ github.workspace }}" -Destination "${{ env.ZED_WORKSPACE }}" -Recurse
- name: Cache dependencies
uses: swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
workspaces: ${{ env.ZED_WORKSPACE }}
cache-provider: "github"
- name: Configure CI
run: |
mkdir -p ${{ env.CARGO_HOME }} -ErrorAction Ignore
cp ./.cargo/ci-config.toml ${{ env.CARGO_HOME }}/config.toml
- name: Run tests
uses: ./.github/actions/run_tests_windows
@@ -293,7 +367,10 @@ jobs:
# Since the Windows runners are stateful, so we need to remove the config file to prevent potential bug.
- name: Clean CI config file
if: always()
run: Remove-Item -Path "${{ env.CARGO_HOME }}/config.toml" -Force
run: |
if (Test-Path "${{ env.CARGO_HOME }}/config.toml") {
Remove-Item -Path "${{ env.CARGO_HOME }}/config.toml" -Force
}
bundle-mac:
timeout-minutes: 120
@@ -359,14 +436,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@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # 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@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # 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
@@ -417,12 +494,19 @@ jobs:
run: script/bundle-linux
- name: Upload Linux bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # 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') }}
with:
name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.gz
path: target/zed-remote-server-linux-x86_64.gz
- name: Upload app bundle to release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
with:
@@ -465,12 +549,19 @@ jobs:
run: script/bundle-linux
- name: Upload Linux bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # 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') }}
with:
name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.gz
path: target/zed-remote-server-linux-aarch64.gz
- name: Upload app bundle to release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
with:

View File

@@ -65,7 +65,7 @@ jobs:
command: deploy .cloudflare/docs-proxy/src/worker.js
- name: Preserve Wrangler logs
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
if: always()
with:
name: wrangler_logs

1852
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@ resolver = "2"
members = [
"crates/activity_indicator",
"crates/anthropic",
"crates/askpass",
"crates/assets",
"crates/assistant",
"crates/assistant2",
@@ -103,6 +104,7 @@ members = [
"crates/project_panel",
"crates/project_symbols",
"crates/prompt_library",
"crates/prompt_store",
"crates/proto",
"crates/recent_projects",
"crates/refineable",
@@ -116,6 +118,7 @@ members = [
"crates/rope",
"crates/rpc",
"crates/schema_generator",
"crates/scripting_tool",
"crates/search",
"crates/semantic_index",
"crates/semantic_version",
@@ -166,15 +169,10 @@ members = [
# Extensions
#
"extensions/csharp",
"extensions/deno",
"extensions/elixir",
"extensions/emmet",
"extensions/erlang",
"extensions/glsl",
"extensions/haskell",
"extensions/html",
"extensions/lua",
"extensions/perplexity",
"extensions/proto",
"extensions/purescript",
@@ -208,6 +206,7 @@ edition = "2021"
activity_indicator = { path = "crates/activity_indicator" }
ai = { path = "crates/ai" }
anthropic = { path = "crates/anthropic" }
askpass = { path = "crates/askpass" }
assets = { path = "crates/assets" }
assistant = { path = "crates/assistant" }
assistant2 = { path = "crates/assistant2" }
@@ -308,6 +307,7 @@ project = { path = "crates/project" }
project_panel = { path = "crates/project_panel" }
project_symbols = { path = "crates/project_symbols" }
prompt_library = { path = "crates/prompt_library" }
prompt_store = { path = "crates/prompt_store" }
proto = { path = "crates/proto" }
recent_projects = { path = "crates/recent_projects" }
refineable = { path = "crates/refineable" }
@@ -319,6 +319,7 @@ 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" }
@@ -370,7 +371,7 @@ zeta = { path = "crates/zeta" }
#
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", rev = "03c2907b44b4189aac5fdeaea331f5aab5c7072e" }
any_vec = "0.14"
anyhow = "1.0.86"
arrayvec = { version = "0.7.4", features = ["serde"] }
@@ -405,10 +406,10 @@ chrono = { version = "0.4", features = ["serde"] }
clap = { version = "4.4", features = ["derive"] }
cocoa = "0.26"
cocoa-foundation = "0.2.0"
convert_case = "0.7.0"
convert_case = "0.8.0"
core-foundation = "0.9.3"
core-foundation-sys = "0.8.6"
ctor = "0.3.0"
ctor = "0.4.0"
dashmap = "6.0"
derive_more = "0.99.17"
dirs = "4.0"
@@ -451,11 +452,11 @@ livekit = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "
], default-features = false }
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
markup5ever_rcdom = "0.3.0"
mlua = { version = "0.10", features = ["lua54", "vendored"] }
nanoid = "0.4"
nbformat = { version = "0.10.0" }
nix = "0.29"
num-format = "0.4.4"
once_cell = "1.20"
ordered-float = "2.1.1"
palette = { version = "0.7.5", default-features = false, features = ["std"] }
parking_lot = "0.12.1"
@@ -537,14 +538,14 @@ tiny_http = "0.8"
toml = "0.8"
tokio = { version = "1" }
tower-http = "0.4.4"
tree-sitter = { version = "0.25.2", features = ["wasm"] }
tree-sitter = { version = "0.25.3", features = ["wasm"] }
tree-sitter-bash = "0.23"
tree-sitter-c = "0.23"
tree-sitter-cpp = "0.23"
tree-sitter-css = "0.23"
tree-sitter-elixir = "0.3"
tree-sitter-embedded-template = "0.23.0"
tree-sitter-gitcommit = {git = "https://github.com/zed-industries/tree-sitter-git-commit", rev = "88309716a69dd13ab83443721ba6e0b491d37ee9"}
tree-sitter-gitcommit = { git = "https://github.com/zed-industries/tree-sitter-git-commit", rev = "88309716a69dd13ab83443721ba6e0b491d37ee9" }
tree-sitter-go = "0.23"
tree-sitter-go-mod = { git = "https://github.com/camdencheek/tree-sitter-go-mod", rev = "6efb59652d30e0e9cd5f3b3a669afd6f1a926d3c", package = "tree-sitter-gomod" }
tree-sitter-gowork = { git = "https://github.com/zed-industries/tree-sitter-go-work", rev = "acb0617bf7f4fda02c6217676cc64acb89536dc7" }
@@ -600,9 +601,11 @@ features = [
version = "0.58"
features = [
"implement",
"Foundation_Collections",
"Foundation_Numerics",
"Storage",
"System_Threading",
"UI_StartScreen",
"UI_ViewManagement",
"Wdk_System_SystemServices",
"Win32_Globalization",
@@ -619,6 +622,7 @@ features = [
"Win32_Storage_FileSystem",
"Win32_System_Com",
"Win32_System_Com_StructuredStorage",
"Win32_System_Console",
"Win32_System_DataExchange",
"Win32_System_LibraryLoader",
"Win32_System_Memory",
@@ -639,7 +643,7 @@ features = [
# TODO livekit https://github.com/RustAudio/cpal/pull/891
[patch.crates-io]
cpal = { git = "https://github.com/zed-industries/cpal", rev = "fd8bc2fd39f1f5fdee5a0690656caff9a26d9d50" }
real-async-tls = { git = "https://github.com/zed-industries/async-tls", rev = "1e759a4b5e370f87dc15e40756ac4f8815b61d9d", package = "async-tls"}
real-async-tls = { git = "https://github.com/zed-industries/async-tls", rev = "1e759a4b5e370f87dc15e40756ac4f8815b61d9d", package = "async-tls" }
[profile.dev]
split-debuginfo = "unpacked"
@@ -709,6 +713,9 @@ debug = "full"
lto = false
codegen-units = 16
[workspace.lints.rust]
unexpected_cfgs = { level = "allow" }
[workspace.lints.clippy]
dbg_macro = "deny"
todo = "deny"
@@ -746,4 +753,4 @@ should_implement_trait = { level = "allow" }
let_underscore_future = "allow"
[workspace.metadata.cargo-machete]
ignored = ["bindgen", "cbindgen", "prost_build", "serde"]
ignored = ["bindgen", "cbindgen", "prost_build", "serde", "component", "linkme"]

10
assets/icons/ai_edit.svg Normal file
View File

@@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.5871 5.40624C12.8514 5.14195 13 4.78346 13 4.40965C13 4.03583 12.8516 3.67731 12.5873 3.41295C12.323 3.14859 11.9645 3.00005 11.5907 3C11.2169 2.99995 10.8584 3.14841 10.594 3.4127L3.92098 10.0874C3.80488 10.2031 3.71903 10.3456 3.67097 10.5024L3.01047 12.6784C2.99754 12.7217 2.99657 12.7676 3.00764 12.8113C3.01872 12.8551 3.04143 12.895 3.07337 12.9269C3.1053 12.9588 3.14528 12.9815 3.18905 12.9925C3.23282 13.0035 3.27875 13.0024 3.32197 12.9894L5.49849 12.3294C5.65508 12.2818 5.79758 12.1964 5.91349 12.0809L12.5871 5.40624Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 4L12 6" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.38818 3.53598V2.53598" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.56982 12.6995L9.56982 13.6995" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.38818 6.53598H3.38818" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.5698 9.69949L12.5698 9.69949" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.38818 4.53598L3.38818 3.53598" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.5698 11.6995L12.5698 12.6995" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

1
assets/icons/cloud.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-cloud"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"/></svg>

After

Width:  |  Height:  |  Size: 279 B

View File

@@ -0,0 +1,6 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 3.25C4.02614 3.25 4.25 3.02614 4.25 2.75C4.25 2.47386 4.02614 2.25 3.75 2.25C3.47386 2.25 3.25 2.47386 3.25 2.75C3.25 3.02614 3.47386 3.25 3.75 3.25ZM3.75 4.25C4.57843 4.25 5.25 3.57843 5.25 2.75C5.25 1.92157 4.57843 1.25 3.75 1.25C2.92157 1.25 2.25 1.92157 2.25 2.75C2.25 3.57843 2.92157 4.25 3.75 4.25Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.25 3.25C8.52614 3.25 8.75 3.02614 8.75 2.75C8.75 2.47386 8.52614 2.25 8.25 2.25C7.97386 2.25 7.75 2.47386 7.75 2.75C7.75 3.02614 7.97386 3.25 8.25 3.25ZM8.25 4.25C9.07843 4.25 9.75 3.57843 9.75 2.75C9.75 1.92157 9.07843 1.25 8.25 1.25C7.42157 1.25 6.75 1.92157 6.75 2.75C6.75 3.57843 7.42157 4.25 8.25 4.25Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 9.75C4.02614 9.75 4.25 9.52614 4.25 9.25C4.25 8.97386 4.02614 8.75 3.75 8.75C3.47386 8.75 3.25 8.97386 3.25 9.25C3.25 9.52614 3.47386 9.75 3.75 9.75ZM3.75 10.75C4.57843 10.75 5.25 10.0784 5.25 9.25C5.25 8.42157 4.57843 7.75 3.75 7.75C2.92157 7.75 2.25 8.42157 2.25 9.25C2.25 10.0784 2.92157 10.75 3.75 10.75Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.25 3.75H4.25V5.59609C4.67823 5.35824 5.24991 5.25 6 5.25H7.25017C7.5262 5.25 7.75 5.02625 7.75 4.75V3.75H8.75V4.75C8.75 5.57832 8.07871 6.25 7.25017 6.25H6C5.14559 6.25 4.77639 6.41132 4.59684 6.56615C4.42571 6.71373 4.33877 6.92604 4.25 7.30651V8.25H3.25V3.75Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,12 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2131_1193)">
<circle cx="7" cy="7" r="6" stroke="black" stroke-width="1.5"/>
<path d="M6 10H7M8 10H7M7 10V7.1C7 7.04477 6.95523 7 6.9 7H6" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<circle cx="7" cy="4.5" r="1" fill="black"/>
</g>
<defs>
<clipPath id="clip0_2131_1193">
<rect width="14" height="14" fill="white"/>
</clipPath>
</defs>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 14C11.3137 14 14 11.3137 14 8C14 4.68629 11.3137 2 8 2C4.68629 2 2 4.68629 2 8C2 11.3137 4.68629 14 8 14Z" stroke="black" stroke-width="1.5"/>
<path d="M7 11H8M8 11H9M8 11V8.1C8 8.04477 7.95523 8 7.9 8H7" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M8 6.5C8.55228 6.5 9 6.05228 9 5.5C9 4.94772 8.55228 4.5 8 4.5C7.44772 4.5 7 4.94772 7 5.5C7 6.05228 7.44772 6.5 8 6.5Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 479 B

After

Width:  |  Height:  |  Size: 524 B

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.6" d="M7.5 8.9V11C5.43097 11 4.56903 11 2.5 11V10.4L7.5 5.6V5H2.5V7.1" stroke="black" stroke-width="1.5"/>
<path d="M14 8L10 12M14 12L10 8" stroke="black" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 296 B

View File

@@ -10,8 +10,8 @@
"pagedown": "menu::SelectLast",
"ctrl-n": "menu::SelectNext",
"tab": "menu::SelectNext",
"ctrl-p": "menu::SelectPrev",
"shift-tab": "menu::SelectPrev",
"ctrl-p": "menu::SelectPrevious",
"shift-tab": "menu::SelectPrevious",
"enter": "menu::Confirm",
"ctrl-enter": "menu::SecondaryConfirm",
"ctrl-escape": "menu::Cancel",
@@ -38,14 +38,14 @@
{
"context": "Picker || menu",
"bindings": {
"up": "menu::SelectPrev",
"up": "menu::SelectPrevious",
"down": "menu::SelectNext"
}
},
{
"context": "Prompt",
"bindings": {
"left": "menu::SelectPrev",
"left": "menu::SelectPrevious",
"right": "menu::SelectNext"
}
},
@@ -57,7 +57,7 @@
"backspace": "editor::Backspace",
"delete": "editor::Delete",
"tab": "editor::Tab",
"shift-tab": "editor::TabPrev",
"shift-tab": "editor::Backtab",
"ctrl-k": "editor::CutToEndOfLine",
// "ctrl-t": "editor::Transpose",
"ctrl-k ctrl-q": "editor::Rewrap",
@@ -180,7 +180,7 @@
"ctrl-k c": "assistant::CopyCode",
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-g": "search::SelectNextMatch",
"ctrl-shift-g": "search::SelectPrevMatch",
"ctrl-shift-g": "search::SelectPreviousMatch",
"ctrl-alt-/": "assistant::ToggleModelSelector",
"ctrl-k h": "assistant::DeployHistory",
"ctrl-k l": "assistant::DeployPromptLibrary",
@@ -203,7 +203,7 @@
"escape": "buffer_search::Dismiss",
"tab": "buffer_search::FocusEditor",
"enter": "search::SelectNextMatch",
"shift-enter": "search::SelectPrevMatch",
"shift-enter": "search::SelectPreviousMatch",
"alt-enter": "search::SelectAllMatches",
"find": "search::FocusSearch",
"ctrl-f": "search::FocusSearch",
@@ -272,7 +272,7 @@
"alt-8": ["pane::ActivateItem", 7],
"alt-9": ["pane::ActivateItem", 8],
"alt-0": "pane::ActivateLastItem",
"ctrl-pageup": "pane::ActivatePrevItem",
"ctrl-pageup": "pane::ActivatePreviousItem",
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-shift-pageup": "pane::SwapItemLeft",
"ctrl-shift-pagedown": "pane::SwapItemRight",
@@ -290,8 +290,8 @@
"forward": "pane::GoForward",
"ctrl-alt-g": "search::SelectNextMatch",
"f3": "search::SelectNextMatch",
"ctrl-alt-shift-g": "search::SelectPrevMatch",
"shift-f3": "search::SelectPrevMatch",
"ctrl-alt-shift-g": "search::SelectPreviousMatch",
"shift-f3": "search::SelectPreviousMatch",
"shift-find": "project_search::ToggleFocus",
"ctrl-shift-f": "project_search::ToggleFocus",
"ctrl-alt-shift-h": "search::ToggleReplace",
@@ -334,7 +334,7 @@
"ctrl-u": "editor::UndoSelection",
"ctrl-shift-u": "editor::RedoSelection",
"f8": "editor::GoToDiagnostic",
"shift-f8": "editor::GoToPrevDiagnostic",
"shift-f8": "editor::GoToPreviousDiagnostic",
"f2": "editor::Rename",
"f12": "editor::GoToDefinition",
"alt-f12": "editor::GoToDefinitionSplit",
@@ -373,7 +373,7 @@
"alt-y": "git::StageAndNext",
"alt-shift-y": "git::UnstageAndNext",
"alt-.": "editor::GoToHunk",
"alt-,": "editor::GoToPrevHunk"
"alt-,": "editor::GoToPreviousHunk"
}
},
{
@@ -536,8 +536,8 @@
{
"context": "Editor && (showing_code_actions || showing_completions)",
"bindings": {
"ctrl-p": "editor::ContextMenuPrev",
"up": "editor::ContextMenuPrev",
"ctrl-p": "editor::ContextMenuPrevious",
"up": "editor::ContextMenuPrevious",
"ctrl-n": "editor::ContextMenuNext",
"down": "editor::ContextMenuNext",
"pageup": "editor::ContextMenuFirst",
@@ -565,7 +565,7 @@
"ctrl-alt-enter": "editor::OpenExcerptsSplit",
"ctrl-shift-e": "pane::RevealInProjectPanel",
"ctrl-f8": "editor::GoToHunk",
"ctrl-shift-f8": "editor::GoToPrevHunk",
"ctrl-shift-f8": "editor::GoToPreviousHunk",
"ctrl-enter": "assistant::InlineAssist",
"ctrl-:": "editor::ToggleInlayHints"
}
@@ -606,18 +606,35 @@
"ctrl-n": "assistant2::NewThread",
"new": "assistant2::NewThread",
"ctrl-shift-h": "assistant2::OpenHistory",
"ctrl-alt-/": "assistant2::ToggleModelSelector",
"ctrl-alt-/": "assistant::ToggleModelSelector",
"ctrl-shift-a": "assistant2::ToggleContextPicker",
"ctrl-e": "assistant2::ChatMode",
"ctrl-alt-e": "assistant2::RemoveAllContext"
}
},
{
"context": "AssistantPanel2 && prompt_editor",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "assistant2::NewPromptEditor",
"cmd-alt-t": "assistant2::NewThread"
}
},
{
"context": "MessageEditor > Editor",
"bindings": {
"enter": "assistant2::Chat"
}
},
{
"context": "EditMessageEditor > Editor",
"use_key_equivalents": true,
"bindings": {
"escape": "menu::Cancel",
"enter": "menu::Confirm",
"alt-enter": "editor::Newline"
}
},
{
"context": "ContextStrip",
"bindings": {
@@ -662,7 +679,7 @@
"alt-ctrl-r": "outline_panel::RevealInFileManager",
"space": "outline_panel::Open",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev",
"shift-up": "menu::SelectPrevious",
"alt-enter": "editor::OpenExcerpts",
"ctrl-alt-enter": "editor::OpenExcerptsSplit"
}
@@ -700,7 +717,7 @@
"shift-find": "project_panel::NewSearchInDirectory",
"ctrl-shift-f": "project_panel::NewSearchInDirectory",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev",
"shift-up": "menu::SelectPrevious",
"escape": "menu::Cancel"
}
},
@@ -713,7 +730,7 @@
{
"context": "GitPanel && ChangesList",
"bindings": {
"up": "menu::SelectPrev",
"up": "menu::SelectPrevious",
"down": "menu::SelectNext",
"enter": "menu::Confirm",
"space": "git::ToggleStaged",
@@ -730,9 +747,22 @@
"context": "GitCommit > Editor",
"bindings": {
"enter": "editor::Newline",
"ctrl-enter": "git::Commit",
"alt-l": "git::GenerateCommitMessage"
}
},
{
"context": "GitDiff > Editor",
"bindings": {
"ctrl-enter": "git::Commit"
}
},
{
"context": "AskPass > Editor",
"bindings": {
"enter": "menu::Confirm"
}
},
{
"context": "GitPanel > Editor",
"bindings": {
@@ -740,15 +770,8 @@
"tab": "git_panel::FocusChanges",
"shift-tab": "git_panel::FocusChanges",
"ctrl-enter": "git::Commit",
"alt-up": "git_panel::FocusChanges"
}
},
{
"context": "GitCommit > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::Newline",
"ctrl-enter": "git::Commit"
"alt-up": "git_panel::FocusChanges",
"alt-l": "git::GenerateCommitMessage"
}
},
{
@@ -773,6 +796,9 @@
{
"context": "Picker > Editor",
"bindings": {
"escape": "menu::Cancel",
"up": "menu::SelectPrevious",
"down": "menu::SelectNext",
"tab": "picker::ConfirmCompletion",
"alt-enter": ["picker::ConfirmInput", { "secondary": false }]
}
@@ -786,7 +812,7 @@
{
"context": "FileFinder || (FileFinder > Picker > Editor) || (FileFinder > Picker > menu)",
"bindings": {
"ctrl-shift-p": "file_finder::SelectPrev",
"ctrl-shift-p": "file_finder::SelectPrevious",
"ctrl-j": "pane::SplitDown",
"ctrl-k": "pane::SplitUp",
"ctrl-h": "pane::SplitLeft",
@@ -796,8 +822,8 @@
{
"context": "TabSwitcher",
"bindings": {
"ctrl-shift-tab": "menu::SelectPrev",
"ctrl-up": "menu::SelectPrev",
"ctrl-shift-tab": "menu::SelectPrevious",
"ctrl-up": "menu::SelectPrevious",
"ctrl-down": "menu::SelectNext",
"ctrl-backspace": "tab_switcher::CloseSelectedItem"
}

View File

@@ -1,4 +1,15 @@
[
// Moved before Standard macOS bindings so that `cmd-w` is not the last binding for
// `workspace::CloseWindow` and displayed/intercepted by macOS
{
"context": "PromptLibrary",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "prompt_library::NewPrompt",
"cmd-shift-s": "prompt_library::ToggleDefaultPrompt",
"cmd-w": "workspace::CloseWindow"
}
},
// Standard macOS bindings
{
"use_key_equivalents": true,
@@ -14,19 +25,19 @@
"tab": "menu::SelectNext",
"ctrl-n": "menu::SelectNext",
"down": "menu::SelectNext",
"shift-tab": "menu::SelectPrev",
"ctrl-p": "menu::SelectPrev",
"up": "menu::SelectPrev",
"shift-tab": "menu::SelectPrevious",
"ctrl-p": "menu::SelectPrevious",
"up": "menu::SelectPrevious",
"enter": "menu::Confirm",
"ctrl-enter": "menu::SecondaryConfirm",
"cmd-enter": "menu::SecondaryConfirm",
"ctrl-escape": "menu::Cancel",
"cmd-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 }],
@@ -54,7 +65,7 @@
"ctrl-d": "editor::Delete",
"delete": "editor::Delete",
"tab": "editor::Tab",
"shift-tab": "editor::TabPrev",
"shift-tab": "editor::Backtab",
"ctrl-t": "editor::Transpose",
"ctrl-k": "editor::KillRingCut",
"ctrl-y": "editor::KillRingYank",
@@ -118,6 +129,7 @@
"cmd-a": "editor::SelectAll",
"cmd-l": "editor::SelectLine",
"cmd-shift-i": "editor::Format",
"alt-shift-o": "editor::OrganizeImports",
"cmd-shift-left": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true, "stop_at_indent": true }],
"shift-home": ["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 }],
@@ -207,7 +219,7 @@
"cmd-k c": "assistant::CopyCode",
"cmd-shift-e": "project_panel::ToggleFocus",
"cmd-g": "search::SelectNextMatch",
"cmd-shift-g": "search::SelectPrevMatch",
"cmd-shift-g": "search::SelectPreviousMatch",
"cmd-alt-/": "assistant::ToggleModelSelector",
"cmd-k h": "assistant::DeployHistory",
"cmd-k l": "assistant::DeployPromptLibrary",
@@ -238,12 +250,20 @@
"cmd-n": "assistant2::NewThread",
"cmd-alt-p": "assistant2::NewPromptEditor",
"cmd-shift-h": "assistant2::OpenHistory",
"cmd-alt-/": "assistant2::ToggleModelSelector",
"cmd-alt-/": "assistant::ToggleModelSelector",
"cmd-shift-a": "assistant2::ToggleContextPicker",
"cmd-e": "assistant2::ChatMode",
"cmd-alt-e": "assistant2::RemoveAllContext"
}
},
{
"context": "AssistantPanel2 && prompt_editor",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "assistant2::NewPromptEditor",
"cmd-alt-t": "assistant2::NewThread"
}
},
{
"context": "MessageEditor > Editor",
"use_key_equivalents": true,
@@ -251,6 +271,15 @@
"enter": "assistant2::Chat"
}
},
{
"context": "EditMessageEditor > Editor",
"use_key_equivalents": true,
"bindings": {
"escape": "menu::Cancel",
"enter": "menu::Confirm",
"alt-enter": "editor::Newline"
}
},
{
"context": "ContextStrip",
"use_key_equivalents": true,
@@ -269,15 +298,6 @@
"backspace": "assistant2::RemoveSelectedThread"
}
},
{
"context": "PromptLibrary",
"use_key_equivalents": true,
"bindings": {
"cmd-n": "prompt_library::NewPrompt",
"cmd-shift-s": "prompt_library::ToggleDefaultPrompt",
"cmd-w": "workspace::CloseWindow"
}
},
{
"context": "BufferSearchBar",
"use_key_equivalents": true,
@@ -285,7 +305,7 @@
"escape": "buffer_search::Dismiss",
"tab": "buffer_search::FocusEditor",
"enter": "search::SelectNextMatch",
"shift-enter": "search::SelectPrevMatch",
"shift-enter": "search::SelectPreviousMatch",
"alt-enter": "search::SelectAllMatches",
"cmd-f": "search::FocusSearch",
"cmd-alt-f": "search::ToggleReplace",
@@ -352,8 +372,8 @@
"context": "Pane",
"use_key_equivalents": true,
"bindings": {
"alt-cmd-left": "pane::ActivatePrevItem",
"cmd-{": "pane::ActivatePrevItem",
"alt-cmd-left": "pane::ActivatePreviousItem",
"cmd-{": "pane::ActivatePreviousItem",
"alt-cmd-right": "pane::ActivateNextItem",
"cmd-}": "pane::ActivateNextItem",
"ctrl-shift-pageup": "pane::SwapItemLeft",
@@ -367,7 +387,7 @@
"cmd-k cmd-w": ["pane::CloseAllItems", { "close_pinned": false }],
"cmd-f": "project_search::ToggleFocus",
"cmd-g": "search::SelectNextMatch",
"cmd-shift-g": "search::SelectPrevMatch",
"cmd-shift-g": "search::SelectPreviousMatch",
"cmd-shift-h": "search::ToggleReplace",
"cmd-alt-l": "search::ToggleSelection",
"alt-enter": "search::SelectAllMatches",
@@ -407,7 +427,7 @@
"cmd-u": "editor::UndoSelection",
"cmd-shift-u": "editor::RedoSelection",
"f8": "editor::GoToDiagnostic",
"shift-f8": "editor::GoToPrevDiagnostic",
"shift-f8": "editor::GoToPreviousDiagnostic",
"f2": "editor::Rename",
"f12": "editor::GoToDefinition",
"alt-f12": "editor::GoToDefinitionSplit",
@@ -613,8 +633,8 @@
"context": "Editor && (showing_code_actions || showing_completions)",
"use_key_equivalents": true,
"bindings": {
"up": "editor::ContextMenuPrev",
"ctrl-p": "editor::ContextMenuPrev",
"up": "editor::ContextMenuPrevious",
"ctrl-p": "editor::ContextMenuPrevious",
"down": "editor::ContextMenuNext",
"ctrl-n": "editor::ContextMenuNext",
"pageup": "editor::ContextMenuFirst",
@@ -640,7 +660,7 @@
"cmd-alt-enter": "editor::OpenExcerptsSplit",
"cmd-shift-e": "pane::RevealInProjectPanel",
"cmd-f8": "editor::GoToHunk",
"cmd-shift-f8": "editor::GoToPrevHunk",
"cmd-shift-f8": "editor::GoToPreviousHunk",
"ctrl-enter": "assistant::InlineAssist",
"ctrl-:": "editor::ToggleInlayHints"
}
@@ -658,7 +678,7 @@
"use_key_equivalents": true,
"bindings": {
"cmd-shift-a": "assistant2::ToggleContextPicker",
"cmd-alt-/": "assistant2::ToggleModelSelector",
"cmd-alt-/": "assistant::ToggleModelSelector",
"cmd-alt-e": "assistant2::RemoveAllContext",
"ctrl-[": "assistant::CyclePreviousInlineAssist",
"ctrl-]": "assistant::CycleNextInlineAssist"
@@ -683,7 +703,7 @@
"alt-cmd-r": "outline_panel::RevealInFileManager",
"space": "outline_panel::Open",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev",
"shift-up": "menu::SelectPrevious",
"alt-enter": "editor::OpenExcerpts",
"cmd-alt-enter": "editor::OpenExcerptsSplit"
}
@@ -713,7 +733,7 @@
"cmd-alt-backspace": ["project_panel::Delete", { "skip_prompt": false }],
"cmd-shift-f": "project_panel::NewSearchInDirectory",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev",
"shift-up": "menu::SelectPrevious",
"escape": "menu::Cancel"
}
},
@@ -728,7 +748,7 @@
"context": "GitPanel && ChangesList",
"use_key_equivalents": true,
"bindings": {
"up": "menu::SelectPrev",
"up": "menu::SelectPrevious",
"down": "menu::SelectNext",
"cmd-up": "menu::SelectFirst",
"cmd-down": "menu::SelectLast",
@@ -739,7 +759,22 @@
"alt-down": "git_panel::FocusEditor",
"tab": "git_panel::FocusEditor",
"shift-tab": "git_panel::FocusEditor",
"escape": "git_panel::ToggleFocus"
"escape": "git_panel::ToggleFocus",
"cmd-enter": "git::Commit"
}
},
{
"context": "GitDiff > Editor",
"use_key_equivalents": true,
"bindings": {
"cmd-enter": "git::Commit"
}
},
{
"context": "AskPass > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "menu::Confirm"
}
},
{
@@ -750,7 +785,9 @@
"cmd-enter": "git::Commit",
"tab": "git_panel::FocusChanges",
"shift-tab": "git_panel::FocusChanges",
"alt-up": "git_panel::FocusChanges"
"alt-up": "git_panel::FocusChanges",
"shift-escape": "git::ExpandCommitEditor",
"alt-tab": "git::GenerateCommitMessage"
}
},
{
@@ -758,7 +795,8 @@
"use_key_equivalents": true,
"bindings": {
"enter": "editor::Newline",
"cmd-enter": "git::Commit"
"cmd-enter": "git::Commit",
"alt-tab": "git::GenerateCommitMessage"
}
},
{
@@ -787,6 +825,9 @@
"context": "Picker > Editor",
"use_key_equivalents": true,
"bindings": {
"escape": "menu::Cancel",
"up": "menu::SelectPrevious",
"down": "menu::SelectNext",
"tab": "picker::ConfirmCompletion",
"alt-enter": ["picker::ConfirmInput", { "secondary": false }],
"cmd-alt-enter": ["picker::ConfirmInput", { "secondary": true }]
@@ -803,7 +844,7 @@
"context": "FileFinder || (FileFinder > Picker > Editor) || (FileFinder > Picker > menu)",
"use_key_equivalents": true,
"bindings": {
"cmd-shift-p": "file_finder::SelectPrev",
"cmd-shift-p": "file_finder::SelectPrevious",
"cmd-j": "pane::SplitDown",
"cmd-k": "pane::SplitUp",
"cmd-h": "pane::SplitLeft",
@@ -814,8 +855,8 @@
"context": "TabSwitcher",
"use_key_equivalents": true,
"bindings": {
"ctrl-shift-tab": "menu::SelectPrev",
"ctrl-up": "menu::SelectPrev",
"ctrl-shift-tab": "menu::SelectPrevious",
"ctrl-up": "menu::SelectPrevious",
"ctrl-down": "menu::SelectNext",
"ctrl-backspace": "tab_switcher::CloseSelectedItem"
}

View File

@@ -39,7 +39,7 @@
"context": "BufferSearchBar",
"bindings": {
"ctrl-f3": "search::SelectNextMatch", // find-and-replace:find-next-selected
"ctrl-shift-f3": "search::SelectPrevMatch" // find-and-replace:find-previous-selected
"ctrl-shift-f3": "search::SelectPreviousMatch" // find-and-replace:find-previous-selected
}
},
{

View File

@@ -122,7 +122,7 @@
"context": "BufferSearchBar > Editor",
"bindings": {
"ctrl-s": "search::SelectNextMatch",
"ctrl-r": "search::SelectPrevMatch",
"ctrl-r": "search::SelectPreviousMatch",
"ctrl-g": "buffer_search::Dismiss"
}
},

View File

@@ -2,7 +2,7 @@
{
"bindings": {
"ctrl-alt-s": "zed::OpenSettings",
"ctrl-{": "pane::ActivatePrevItem",
"ctrl-{": "pane::ActivatePreviousItem",
"ctrl-}": "pane::ActivateNextItem"
}
},
@@ -41,9 +41,9 @@
"ctrl-shift-b": "editor::GoToTypeDefinition",
"ctrl-alt-shift-b": "editor::GoToTypeDefinitionSplit",
"f2": "editor::GoToDiagnostic",
"shift-f2": "editor::GoToPrevDiagnostic",
"shift-f2": "editor::GoToPreviousDiagnostic",
"ctrl-alt-shift-down": "editor::GoToHunk",
"ctrl-alt-shift-up": "editor::GoToPrevHunk",
"ctrl-alt-shift-up": "editor::GoToPreviousHunk",
"ctrl-alt-z": "git::Restore",
"ctrl-home": "editor::MoveToBeginning",
"ctrl-end": "editor::MoveToEnd",

View File

@@ -1,9 +1,9 @@
[
{
"bindings": {
"ctrl-{": "pane::ActivatePrevItem",
"ctrl-{": "pane::ActivatePreviousItem",
"ctrl-}": "pane::ActivateNextItem",
"ctrl-pageup": "pane::ActivatePrevItem",
"ctrl-pageup": "pane::ActivatePreviousItem",
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-1": ["workspace::ActivatePane", 0],
"ctrl-2": ["workspace::ActivatePane", 1],
@@ -44,7 +44,7 @@
"shift-f12": "editor::FindAllReferences",
"ctrl-shift-f12": "editor::FindAllReferences",
"ctrl-.": "editor::GoToHunk",
"ctrl-,": "editor::GoToPrevHunk",
"ctrl-,": "editor::GoToPreviousHunk",
"ctrl-k ctrl-u": "editor::ConvertToUpperCase",
"ctrl-k ctrl-l": "editor::ConvertToLowerCase",
"shift-alt-m": "markdown::OpenPreviewToTheSide",
@@ -62,7 +62,7 @@
"context": "Pane",
"bindings": {
"f4": "search::SelectNextMatch",
"shift-f4": "search::SelectPrevMatch",
"shift-f4": "search::SelectPreviousMatch",
"alt-1": ["pane::ActivateItem", 0],
"alt-2": ["pane::ActivateItem", 1],
"alt-3": ["pane::ActivateItem", 2],

View File

@@ -40,7 +40,7 @@
"context": "BufferSearchBar",
"bindings": {
"cmd-f3": "search::SelectNextMatch",
"cmd-shift-f3": "search::SelectPrevMatch"
"cmd-shift-f3": "search::SelectPreviousMatch"
}
},
{

View File

@@ -122,7 +122,7 @@
"context": "BufferSearchBar > Editor",
"bindings": {
"ctrl-s": "search::SelectNextMatch",
"ctrl-r": "search::SelectPrevMatch",
"ctrl-r": "search::SelectPreviousMatch",
"ctrl-g": "buffer_search::Dismiss"
}
},

View File

@@ -1,7 +1,7 @@
[
{
"bindings": {
"cmd-{": "pane::ActivatePrevItem",
"cmd-{": "pane::ActivatePreviousItem",
"cmd-}": "pane::ActivateNextItem"
}
},
@@ -39,9 +39,9 @@
"cmd-shift-b": "editor::GoToTypeDefinition",
"cmd-alt-shift-b": "editor::GoToTypeDefinitionSplit",
"f2": "editor::GoToDiagnostic",
"shift-f2": "editor::GoToPrevDiagnostic",
"shift-f2": "editor::GoToPreviousDiagnostic",
"ctrl-alt-shift-down": "editor::GoToHunk",
"ctrl-alt-shift-up": "editor::GoToPrevHunk",
"ctrl-alt-shift-up": "editor::GoToPreviousHunk",
"cmd-home": "editor::MoveToBeginning",
"cmd-end": "editor::MoveToEnd",
"cmd-shift-home": "editor::SelectToBeginning",
@@ -61,7 +61,7 @@
{
"context": "BufferSearchBar > Editor",
"bindings": {
"shift-enter": "search::SelectPrevMatch"
"shift-enter": "search::SelectPreviousMatch"
}
},
{

View File

@@ -1,9 +1,9 @@
[
{
"bindings": {
"cmd-{": "pane::ActivatePrevItem",
"cmd-{": "pane::ActivatePreviousItem",
"cmd-}": "pane::ActivateNextItem",
"ctrl-pageup": "pane::ActivatePrevItem",
"ctrl-pageup": "pane::ActivatePreviousItem",
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-1": ["workspace::ActivatePane", 0],
"ctrl-2": ["workspace::ActivatePane", 1],
@@ -45,7 +45,7 @@
"ctrl-alt-cmd-down": "editor::GoToDefinitionSplit",
"alt-shift-cmd-down": "editor::FindAllReferences",
"ctrl-.": "editor::GoToHunk",
"ctrl-,": "editor::GoToPrevHunk",
"ctrl-,": "editor::GoToPreviousHunk",
"cmd-k cmd-u": "editor::ConvertToUpperCase",
"cmd-k cmd-l": "editor::ConvertToLowerCase",
"cmd-shift-j": "editor::JoinLines",
@@ -64,7 +64,7 @@
"context": "Pane",
"bindings": {
"f4": "search::SelectNextMatch",
"shift-f4": "search::SelectPrevMatch",
"shift-f4": "search::SelectPreviousMatch",
"cmd-1": ["pane::ActivateItem", 0],
"cmd-2": ["pane::ActivateItem", 1],
"cmd-3": ["pane::ActivateItem", 2],

View File

@@ -47,7 +47,7 @@
"context": "BufferSearchBar",
"bindings": {
"ctrl-s": "search::SelectNextMatch",
"ctrl-shift-s": "search::SelectPrevMatch"
"ctrl-shift-s": "search::SelectPreviousMatch"
}
},
{

View File

@@ -13,9 +13,9 @@
"tab": "menu::SelectNext",
"ctrl-n": "menu::SelectNext",
"down": "menu::SelectNext",
"shift-tab": "menu::SelectPrev",
"ctrl-p": "menu::SelectPrev",
"up": "menu::SelectPrev",
"shift-tab": "menu::SelectPrevious",
"ctrl-p": "menu::SelectPrevious",
"up": "menu::SelectPrevious",
"enter": "menu::Confirm",
"ctrl-enter": "menu::SecondaryConfirm",
"cmd-enter": "menu::SecondaryConfirm",

View File

@@ -6,7 +6,7 @@
"a": ["vim::PushObject", { "around": true }],
"left": "vim::Left",
"h": "vim::Left",
"backspace": "vim::Backspace",
"backspace": "vim::WrappingLeft",
"down": "vim::Down",
"ctrl-j": "vim::Down",
"j": "vim::Down",
@@ -20,7 +20,7 @@
"k": "vim::Up",
"right": "vim::Right",
"l": "vim::Right",
"space": "vim::Space",
"space": "vim::WrappingRight",
"end": "vim::EndOfLine",
"$": "vim::EndOfLine",
"^": "vim::FirstNonWhitespace",
@@ -62,9 +62,9 @@
"g /": "pane::DeploySearch",
"?": ["vim::Search", { "backwards": true }],
"*": "vim::MoveToNext",
"#": "vim::MoveToPrev",
"#": "vim::MoveToPrevious",
"n": "vim::MoveToNextMatch",
"shift-n": "vim::MoveToPrevMatch",
"shift-n": "vim::MoveToPreviousMatch",
"%": "vim::Matching",
"] }": ["vim::UnmatchedForward", { "char": "}" }],
"[ {": ["vim::UnmatchedBackward", { "char": "{" }],
@@ -106,7 +106,7 @@
"g g": "vim::StartOfDocument",
"g h": "editor::Hover",
"g t": "pane::ActivateNextItem",
"g shift-t": "pane::ActivatePrevItem",
"g shift-t": "pane::ActivatePreviousItem",
"g d": "editor::GoToDefinition",
"g shift-d": "editor::GoToDeclaration",
"g y": "editor::GoToTypeDefinition",
@@ -126,7 +126,7 @@
"g shift-a": "editor::FindAllReferences", // zed specific
"g space": "editor::OpenExcerpts", // zed specific
"g *": ["vim::MoveToNext", { "partial_word": true }],
"g #": ["vim::MoveToPrev", { "partial_word": true }],
"g #": ["vim::MoveToPrevious", { "partial_word": true }],
"g j": ["vim::Down", { "display_lines": true }],
"g down": ["vim::Down", { "display_lines": true }],
"g k": ["vim::Up", { "display_lines": true }],
@@ -138,7 +138,7 @@
"g ^": ["vim::FirstNonWhitespace", { "display_lines": true }],
"g v": "vim::RestoreVisualSelection",
"g ]": "editor::GoToDiagnostic",
"g [": "editor::GoToPrevDiagnostic",
"g [": "editor::GoToPreviousDiagnostic",
"g i": "vim::InsertAtPrevious",
"g ,": "vim::ChangeListNewer",
"g ;": "vim::ChangeListOlder",
@@ -231,15 +231,15 @@
"g w": "vim::PushRewrap",
"g q": "vim::PushRewrap",
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-pageup": "pane::ActivatePrevItem",
"ctrl-pageup": "pane::ActivatePreviousItem",
"insert": "vim::InsertBefore",
// tree-sitter related commands
"[ x": "vim::SelectLargerSyntaxNode",
"] x": "vim::SelectSmallerSyntaxNode",
"] d": "editor::GoToDiagnostic",
"[ d": "editor::GoToPrevDiagnostic",
"[ d": "editor::GoToPreviousDiagnostic",
"] c": "editor::GoToHunk",
"[ c": "editor::GoToPrevHunk",
"[ c": "editor::GoToPreviousHunk",
"g c": "vim::PushToggleComments"
}
},
@@ -247,7 +247,8 @@
"context": "VimControl && VimCount",
"bindings": {
"0": ["vim::Number", 0],
":": "vim::CountCommand"
":": "vim::CountCommand",
"%": "vim::GoToPercentage"
}
},
{
@@ -272,7 +273,7 @@
"shift-s": "vim::SubstituteLine",
"~": "vim::ChangeCase",
"*": ["vim::MoveToNext", { "partial_word": true }],
"#": ["vim::MoveToPrev", { "partial_word": true }],
"#": ["vim::MoveToPrevious", { "partial_word": true }],
"ctrl-a": "vim::Increment",
"ctrl-x": "vim::Decrement",
"g ctrl-a": ["vim::Increment", { "step": true }],
@@ -448,7 +449,10 @@
"d": "vim::CurrentLine",
"s": "vim::PushDeleteSurrounds",
"o": "editor::ToggleSelectedDiffHunks", // "d o"
"p": "git::Restore" // "d p"
"shift-o": "git::ToggleStaged",
"p": "git::Restore", // "d p"
"u": "git::StageAndNext", // "d u"
"shift-u": "git::UnstageAndNext" // "d shift-u"
}
},
{
@@ -620,8 +624,8 @@
"ctrl-w =": "vim::ResetPaneSizes",
"ctrl-w g t": "pane::ActivateNextItem",
"ctrl-w ctrl-g t": "pane::ActivateNextItem",
"ctrl-w g shift-t": "pane::ActivatePrevItem",
"ctrl-w ctrl-g shift-t": "pane::ActivatePrevItem",
"ctrl-w g shift-t": "pane::ActivatePreviousItem",
"ctrl-w ctrl-g shift-t": "pane::ActivatePreviousItem",
"ctrl-w w": "workspace::ActivateNextPane",
"ctrl-w ctrl-w": "workspace::ActivateNextPane",
"ctrl-w p": "workspace::ActivatePreviousPane",
@@ -664,7 +668,7 @@
"escape": "project_panel::ToggleFocus",
"h": "project_panel::CollapseSelectedEntry",
"j": "menu::SelectNext",
"k": "menu::SelectPrev",
"k": "menu::SelectPrevious",
"l": "project_panel::ExpandSelectedEntry",
"o": "project_panel::OpenPermanent",
"shift-d": "project_panel::Delete",
@@ -690,7 +694,7 @@
"context": "OutlinePanel && not_editing",
"bindings": {
"j": "menu::SelectNext",
"k": "menu::SelectPrev",
"k": "menu::SelectPrevious",
"shift-g": "menu::SelectLast",
"g g": "menu::SelectFirst"
}
@@ -699,7 +703,7 @@
"context": "GitPanel && ChangesList",
"use_key_equivalents": true,
"bindings": {
"k": "menu::SelectPrev",
"k": "menu::SelectPrevious",
"j": "menu::SelectNext",
"g g": "menu::SelectFirst",
"shift-g": "menu::SelectLast",

View File

@@ -176,8 +176,8 @@
"show_completion_documentation": true,
// Show method signatures in the editor, when inside parentheses.
"auto_signature_help": false,
/// 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.
// 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,
// Whether to show wrap guides (vertical rulers) in the editor.
// Setting this to true will show a guide at the 'preferred_line_length' value
@@ -298,11 +298,11 @@
// - "information": show only errors, warnings, and information
// - "all" or true: show all diagnostics
"diagnostics": "all",
/// Forcefully enable or disable the scrollbar for each axis
// Forcefully enable or disable the scrollbar for each axis
"axes": {
/// When false, forcefully disables the horizontal scrollbar. Otherwise, obey other settings.
// When false, forcefully disables the horizontal scrollbar. Otherwise, obey other settings.
"horizontal": true,
/// When false, forcefully disables the vertical scrollbar. Otherwise, obey other settings.
// When false, forcefully disables the vertical scrollbar. Otherwise, obey other settings.
"vertical": true
}
},
@@ -328,24 +328,24 @@
"folds": true
},
"indent_guides": {
/// Whether to show indent guides in the editor.
// Whether to show indent guides in the editor.
"enabled": true,
/// The width of the indent guides in pixels, between 1 and 10.
// The width of the indent guides in pixels, between 1 and 10.
"line_width": 1,
/// The width of the active indent guide in pixels, between 1 and 10.
// The width of the active indent guide in pixels, between 1 and 10.
"active_line_width": 1,
/// Determines how indent guides are colored.
/// This setting can take the following three values:
// Determines how indent guides are colored.
// This setting can take the following three values:
///
/// 1. "disabled"
/// 2. "fixed"
/// 3. "indent_aware"
// 1. "disabled"
// 2. "fixed"
// 3. "indent_aware"
"coloring": "fixed",
/// Determines how indent guide backgrounds are colored.
/// This setting can take the following two values:
// Determines how indent guide backgrounds are colored.
// This setting can take the following two values:
///
/// 1. "disabled"
/// 2. "indent_aware"
// 1. "disabled"
// 2. "indent_aware"
"background_coloring": "disabled"
},
// Whether the editor will scroll beyond the last line.
@@ -379,6 +379,9 @@
// 3. Never populate the search query
// "never"
"seed_search_query_from_cursor": "always",
// When enabled, automatically adjusts search case sensitivity based on your query.
// If your search query contains any uppercase letters, the search becomes case-sensitive;
// if it contains only lowercase letters, the search becomes case-insensitive.
"use_smartcase_search": false,
// Inlay hint related settings
"inlay_hints": {
@@ -398,7 +401,16 @@
"edit_debounce_ms": 700,
// Time to wait after scrolling the buffer, before requesting the hints,
// set to 0 to disable debouncing.
"scroll_debounce_ms": 50
"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.
"toggle_on_modifiers_press": {
"control": false,
"shift": false,
"alt": false,
"platform": false,
"function": false
}
},
"project_panel": {
// Whether to show the project panel button in the status bar
@@ -424,32 +436,32 @@
// Whether to fold directories automatically and show compact folders
// (e.g. "a/b/c" ) when a directory has only one subdirectory inside.
"auto_fold_dirs": true,
/// Scrollbar-related settings
// Scrollbar-related settings
"scrollbar": {
/// When to show the scrollbar in the project panel.
/// This setting can take five values:
// 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):
/// "auto"
/// 3. Match the system's configured behavior:
/// "system"
/// 4. Always show the scrollbar:
/// "always"
/// 5. Never show the scrollbar:
/// "never"
// 1. null (default): Inherit editor settings
// 2. Show the scrollbar if there's important information or
// follow the system's configured behavior (default):
// "auto"
// 3. Match the system's configured behavior:
// "system"
// 4. Always show the scrollbar:
// "always"
// 5. Never show the scrollbar:
// "never"
"show": null
},
/// Which files containing diagnostic errors/warnings to mark in the project panel.
/// This setting can take the following three values:
// 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:
/// "errors"
/// 3. Mark files with errors and warnings:
/// "all"
// 1. Do not mark any files:
// "off"
// 2. Only mark files with errors:
// "errors"
// 3. Mark files with errors and warnings:
// "all"
"show_diagnostics": "all",
// Settings related to indent guides in the project panel.
"indent_guides": {
@@ -482,8 +494,8 @@
// when a corresponding outline entry becomes active.
// Gitignored entries are never auto revealed.
"auto_reveal_entries": true,
/// Whether to fold directories automatically
/// when a directory has only one directory inside.
// Whether to fold directories automatically
// when a directory has only one directory inside.
"auto_fold_dirs": true,
// Settings related to indent guides in the outline panel.
"indent_guides": {
@@ -496,21 +508,21 @@
// "never"
"show": "always"
},
/// Scrollbar-related settings
// Scrollbar-related settings
"scrollbar": {
/// When to show the scrollbar in the project panel.
/// This setting can take five values:
// 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):
/// "auto"
/// 3. Match the system's configured behavior:
/// "system"
/// 4. Always show the scrollbar:
/// "always"
/// 5. Never show the scrollbar:
/// "never"
// 1. null (default): Inherit editor settings
// 2. Show the scrollbar if there's important information or
// follow the system's configured behavior (default):
// "auto"
// 3. Match the system's configured behavior:
// "system"
// 4. Always show the scrollbar:
// "always"
// 5. Never show the scrollbar:
// "never"
"show": null
}
},
@@ -628,7 +640,7 @@
"show": true,
// Whether or not to show the navigation history buttons.
"show_nav_history_buttons": true,
/// Whether or not to show the tab bar buttons.
// Whether or not to show the tab bar buttons.
"show_tab_bar_buttons": true
},
// Settings related to the editor's tabs
@@ -636,11 +648,19 @@
// Show git status colors in the editor tabs.
"git_status": false,
// Position of the close button on the editor tabs.
// One of: ["right", "left", "hidden"]
"close_position": "right",
// Whether to show the file icon for a tab.
"file_icons": false,
// Whether to always show the close button on tabs.
"always_show_close_button": false,
// Controls the appearance behavior of the tab's close button.
//
// 1. Show it just upon hovering the tab. (default)
// "hover"
// 2. Show it persistently.
// "always"
// 3. Never show it, even if hovering it.
// "hidden"
"show_close_button": "hover",
// What to do after closing the current tab.
//
// 1. Activate the tab that was open previously (default)
@@ -650,16 +670,16 @@
// 3. Activate the left neighbour tab if present
// "left_neighbour"
"activate_on_close": "history",
/// 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:
// 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:
/// "errors"
/// 3. Mark files with errors and warnings:
/// "all"
// 1. Do not mark any files:
// "off"
// 2. Only mark files with errors:
// "errors"
// 3. Mark files with errors and warnings:
// "all"
"show_diagnostics": "off"
},
// Settings related to preview tabs.
@@ -700,8 +720,8 @@
"remove_trailing_whitespace_on_save": true,
// Whether to start a new line with a comment when a previous line is a comment as well.
"extend_comment_on_newline": true,
// Whether or not to ensure there's a single newline at the end of a buffer
// when saving it.
// Removes any lines containing only whitespace at the end of the file and
// ensures just one newline at the end.
"ensure_final_newline_on_save": true,
// Whether or not to perform a buffer format before saving
//
@@ -817,7 +837,15 @@
//
// The minimum column number to show the inline blame information at
// "min_column": 0
}
},
// 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": "transparent"
},
// Configuration for how direnv configuration should be loaded. May take 2 values:
// 1. Load direnv configuration using `direnv export json` directly.
@@ -829,22 +857,19 @@
// A list of globs representing files that edit predictions should be disabled for.
// There's a sensible default list of globs already included.
// Any addition to this list will be merged with the default list.
"disabled_globs": [
"**/.env*",
"**/*.pem",
"**/*.key",
"**/*.cert",
"**/*.crt",
"**/.dev.vars",
"**/secrets.yml"
],
// Globs are matched relative to the worktree root,
// except when starting with a slash (/) or equivalent in Windows.
"disabled_globs": ["**/.env*", "**/*.pem", "**/*.key", "**/*.cert", "**/*.crt", "**/.dev.vars", "**/secrets.yml"],
// When to show edit predictions previews in buffer.
// This setting takes two possible values:
// 1. Display inline when there are no language server completions available.
// "mode": "eager_preview"
// 2. Display inline when holding modifier key (alt by default).
// "mode": "auto"
"mode": "eager_preview"
// 1. Display predictions inline when there are no language server completions available.
// "mode": "eager"
// 2. Display predictions inline only when holding a modifier key (alt by default).
// "mode": "subtle"
"mode": "eager",
// Whether edit predictions are enabled in the assistant panel.
// This setting has no effect if globally disabled.
"enabled_in_assistant": true
},
// Settings specific to journaling
"journal": {
@@ -980,21 +1005,21 @@
// Example: `echo -e "\e]2;New Title\007";`
"breadcrumbs": true
},
/// Scrollbar-related settings
// Scrollbar-related settings
"scrollbar": {
/// When to show the scrollbar in the terminal.
/// This setting can take five values:
// 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):
/// "auto"
/// 3. Match the system's configured behavior:
/// "system"
/// 4. Always show the scrollbar:
/// "always"
/// 5. Never show the scrollbar:
/// "never"
// 1. null (default): Inherit editor settings
// 2. Show the scrollbar if there's important information or
// follow the system's configured behavior (default):
// "auto"
// 3. Match the system's configured behavior:
// "system"
// 4. Always show the scrollbar:
// "always"
// 5. Never show the scrollbar:
// "never"
"show": null
}
// Set the terminal's font size. If this option is not included,
@@ -1013,7 +1038,7 @@
// "max_scroll_history_lines": 10000,
},
"code_actions_on_format": {},
/// Settings related to running tasks.
// Settings related to running tasks.
"tasks": {
"variables": {}
},
@@ -1034,20 +1059,20 @@
"JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json", "**/Zed/**/*.json", "**/.vscode/**/*.json"],
"Shell Script": [".env.*"]
},
/// By default use a recent system version of node, or install our own.
/// You can override this to use a version of node that is not in $PATH with:
/// {
/// "node": {
/// "path": "/path/to/node"
/// "npm_path": "/path/to/npm" (defaults to node_path/../npm)
/// }
/// }
/// or to ensure Zed always downloads and installs an isolated version of node:
/// {
/// "node": {
/// "ignore_system_version": true,
/// }
/// NOTE: changing this setting currently requires restarting Zed.
// By default use a recent system version of node, or install our own.
// You can override this to use a version of node that is not in $PATH with:
// {
// "node": {
// "path": "/path/to/node"
// "npm_path": "/path/to/npm" (defaults to node_path/../npm)
// }
// }
// or to ensure Zed always downloads and installs an isolated version of node:
// {
// "node": {
// "ignore_system_version": true,
// }
// NOTE: changing this setting currently requires restarting Zed.
"node": {},
// The extensions that Zed should automatically install on startup.
//
@@ -1150,6 +1175,7 @@
"format_on_save": "off",
"use_on_type_format": false,
"allow_rewrap": "anywhere",
"soft_wrap": "bounded",
"prettier": {
"allowed": true
}
@@ -1273,6 +1299,11 @@
// "semi": false,
// "singleQuote": true
},
// Settings for auto-closing of JSX tags.
"jsx_tag_auto_close": {
// // Whether to auto-close JSX tags.
// "enabled": true
},
// LSP Specific settings.
"lsp": {
// Specify the LSP name as a key here.
@@ -1296,6 +1327,7 @@
},
// Vim settings
"vim": {
"default_mode": "normal",
"toggle_relative_line_numbers": false,
"use_system_clipboard": "always",
"use_multiline_find": false,

21
crates/askpass/Cargo.toml Normal file
View File

@@ -0,0 +1,21 @@
[package]
name = "askpass"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/askpass.rs"
[dependencies]
anyhow.workspace = true
futures.workspace = true
gpui.workspace = true
smol.workspace = true
tempfile.workspace = true
util.workspace = true
which.workspace = true

View File

@@ -0,0 +1,194 @@
use std::path::{Path, PathBuf};
use std::time::Duration;
#[cfg(unix)]
use anyhow::Context as _;
use futures::channel::{mpsc, oneshot};
#[cfg(unix)]
use futures::{io::BufReader, AsyncBufReadExt as _};
#[cfg(unix)]
use futures::{select_biased, AsyncWriteExt as _, FutureExt as _};
use futures::{SinkExt, StreamExt};
use gpui::{AsyncApp, BackgroundExecutor, Task};
#[cfg(unix)]
use smol::fs;
#[cfg(unix)]
use smol::{fs::unix::PermissionsExt as _, net::unix::UnixListener};
#[cfg(unix)]
use util::ResultExt as _;
#[derive(PartialEq, Eq)]
pub enum AskPassResult {
CancelledByUser,
Timedout,
}
pub struct AskPassDelegate {
tx: mpsc::UnboundedSender<(String, oneshot::Sender<String>)>,
_task: Task<()>,
}
impl AskPassDelegate {
pub fn new(
cx: &mut AsyncApp,
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 {
while let Some((prompt, channel)) = rx.next().await {
password_prompt(prompt, channel, &mut cx);
}
});
Self { tx, _task: task }
}
pub async fn ask_password(&mut self, prompt: String) -> anyhow::Result<String> {
let (tx, rx) = oneshot::channel();
self.tx.send((prompt, tx)).await?;
Ok(rx.await?)
}
}
#[cfg(unix)]
pub struct AskPassSession {
script_path: PathBuf,
_askpass_task: Task<()>,
askpass_opened_rx: Option<oneshot::Receiver<()>>,
askpass_kill_master_rx: Option<oneshot::Receiver<()>>,
}
#[cfg(unix)]
impl AskPassSession {
/// This will create a new AskPassSession.
/// You must retain this session until the master process exits.
#[must_use]
pub async fn new(
executor: &BackgroundExecutor,
mut delegate: AskPassDelegate,
) -> anyhow::Result<Self> {
let temp_dir = tempfile::Builder::new().prefix("zed-askpass").tempdir()?;
let askpass_socket = temp_dir.path().join("askpass.sock");
let askpass_script_path = temp_dir.path().join("askpass.sh");
let (askpass_opened_tx, askpass_opened_rx) = oneshot::channel::<()>();
let listener =
UnixListener::bind(&askpass_socket).context("failed to create askpass socket")?;
let (askpass_kill_master_tx, askpass_kill_master_rx) = oneshot::channel::<()>();
let mut kill_tx = Some(askpass_kill_master_tx);
let askpass_task = executor.spawn(async move {
let mut askpass_opened_tx = Some(askpass_opened_tx);
while let Ok((mut stream, _)) = listener.accept().await {
if let Some(askpass_opened_tx) = askpass_opened_tx.take() {
askpass_opened_tx.send(()).ok();
}
let mut buffer = Vec::new();
let mut reader = BufReader::new(&mut stream);
if reader.read_until(b'\0', &mut buffer).await.is_err() {
buffer.clear();
}
let prompt = String::from_utf8_lossy(&buffer);
if let Some(password) = delegate
.ask_password(prompt.to_string())
.await
.context("failed to get askpass password")
.log_err()
{
stream.write_all(password.as_bytes()).await.log_err();
} else {
if let Some(kill_tx) = kill_tx.take() {
kill_tx.send(()).log_err();
}
// note: we expect the caller to drop this task when it's done.
// We need to keep the stream open until the caller is done to avoid
// spurious errors from ssh.
std::future::pending::<()>().await;
drop(stream);
}
}
drop(temp_dir)
});
anyhow::ensure!(
which::which("nc").is_ok(),
"Cannot find `nc` command (netcat), which is required to connect over SSH."
);
// Create an askpass script that communicates back to this process.
let askpass_script = format!(
"{shebang}\n{print_args} | {nc} -U {askpass_socket} 2> /dev/null \n",
// on macOS `brew install netcat` provides the GNU netcat implementation
// which does not support -U.
nc = if cfg!(target_os = "macos") {
"/usr/bin/nc"
} else {
"nc"
},
askpass_socket = askpass_socket.display(),
print_args = "printf '%s\\0' \"$@\"",
shebang = "#!/bin/sh",
);
fs::write(&askpass_script_path, askpass_script).await?;
fs::set_permissions(&askpass_script_path, std::fs::Permissions::from_mode(0o755)).await?;
Ok(Self {
script_path: askpass_script_path,
_askpass_task: askpass_task,
askpass_kill_master_rx: Some(askpass_kill_master_rx),
askpass_opened_rx: Some(askpass_opened_rx),
})
}
pub fn script_path(&self) -> &Path {
&self.script_path
}
// This will run the askpass task forever, resolving as many authentication requests as needed.
// The caller is responsible for examining the result of their own commands and cancelling this
// future when this is no longer needed. Note that this can only be called once, but due to the
// drop order this takes an &mut, so you can `drop()` it after you're done with the master process.
pub async fn run(&mut self) -> AskPassResult {
let connection_timeout = Duration::from_secs(10);
let askpass_opened_rx = self.askpass_opened_rx.take().expect("Only call run once");
let askpass_kill_master_rx = self
.askpass_kill_master_rx
.take()
.expect("Only call run once");
select_biased! {
_ = askpass_opened_rx.fuse() => {
// Note: this await can only resolve after we are dropped.
askpass_kill_master_rx.await.ok();
return AskPassResult::CancelledByUser
}
_ = futures::FutureExt::fuse(smol::Timer::after(connection_timeout)) => {
return AskPassResult::Timedout
}
}
}
}
#[cfg(not(unix))]
pub struct AskPassSession {
path: PathBuf,
}
#[cfg(not(unix))]
impl AskPassSession {
pub async fn new(_: &BackgroundExecutor, _: AskPassDelegate) -> anyhow::Result<Self> {
Ok(Self {
path: PathBuf::new(),
})
}
pub fn script_path(&self) -> &Path {
&self.path
}
pub async fn run(&mut self) -> AskPassResult {
futures::FutureExt::fuse(smol::Timer::after(Duration::from_secs(10))).await;
AskPassResult::Timedout
}
}

View File

@@ -51,6 +51,7 @@ parking_lot.workspace = true
paths.workspace = true
project.workspace = true
prompt_library.workspace = true
prompt_store.workspace = true
proto.workspace = true
rope.workspace = true
schemars.workspace = true

View File

@@ -19,7 +19,7 @@ use gpui::{actions, App, Global, UpdateGlobal};
use language_model::{
LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
};
use prompt_library::PromptBuilder;
use prompt_store::PromptBuilder;
use semantic_index::{CloudEmbeddingProvider, SemanticDb};
use serde::Deserialize;
use settings::{Settings, SettingsStore};

View File

@@ -110,7 +110,7 @@ impl ConfigurationView {
.bg(cx.theme().colors().surface_background)
.border_1()
.border_color(cx.theme().colors().border_variant)
.rounded_md()
.rounded_sm()
.when(configuration_view.is_none(), |this| {
this.child(div().child(Label::new(format!(
"No configuration view for {}",

View File

@@ -24,7 +24,8 @@ use language_model::{
AuthenticateError, LanguageModelProviderId, LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID,
};
use project::Project;
use prompt_library::{open_prompt_library, PromptBuilder, PromptLibrary};
use prompt_library::{open_prompt_library, PromptLibrary};
use prompt_store::PromptBuilder;
use search::{buffer_search::DivRegistrar, BufferSearchBar};
use settings::{update_settings_file, Settings};
use smol::stream::StreamExt;

View File

@@ -38,8 +38,8 @@ use language_model::{
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
use multi_buffer::MultiBufferRow;
use parking_lot::Mutex;
use project::{CodeAction, ProjectTransaction};
use prompt_library::PromptBuilder;
use project::{ActionVariant, CodeAction, ProjectTransaction};
use prompt_store::PromptBuilder;
use rope::Rope;
use settings::{update_settings_file, Settings, SettingsStore};
use smol::future::FutureExt;
@@ -3569,10 +3569,10 @@ impl CodeActionProvider for AssistantCodeActionProvider {
Task::ready(Ok(vec![CodeAction {
server_id: language::LanguageServerId(0),
range: snapshot.anchor_before(range.start)..snapshot.anchor_after(range.end),
lsp_action: lsp::CodeAction {
lsp_action: ActionVariant::Action(Box::new(lsp::CodeAction {
title: "Fix with Assistant".into(),
..Default::default()
},
})),
}]))
} else {
Task::ready(Ok(Vec::new()))

View File

@@ -20,7 +20,7 @@ use language_model::{
LanguageModelRequestMessage, Role,
};
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
use prompt_library::PromptBuilder;
use prompt_store::PromptBuilder;
use settings::{update_settings_file, Settings};
use std::{
cmp,
@@ -506,7 +506,7 @@ struct PromptEditor {
impl EventEmitter<PromptEditorEvent> for PromptEditor {}
impl Render for PromptEditor {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let status = &self.codegen.read(cx).status;
let buttons = match status {
CodegenStatus::Idle => {
@@ -643,7 +643,7 @@ impl Render for PromptEditor {
.gap_2()
.child(LanguageModelSelectorPopoverMenu::new(
self.language_model_selector.clone(),
IconButton::new("context", IconName::SettingsAlt)
IconButton::new("change-model", IconName::SettingsAlt)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.icon_color(Color::Muted),
@@ -1073,7 +1073,10 @@ pub enum CodegenEvent {
impl EventEmitter<CodegenEvent> for Codegen {}
#[cfg(not(target_os = "windows"))]
const CLEAR_INPUT: &str = "\x15";
#[cfg(target_os = "windows")]
const CLEAR_INPUT: &str = "\x03";
const CARRIAGE_RETURN: &str = "\x0d";
struct TerminalTransaction {

View File

@@ -56,6 +56,7 @@ paths.workspace = true
picker.workspace = true
project.workspace = true
prompt_library.workspace = true
prompt_store.workspace = true
proto.workspace = true
rope.workspace = true
serde.workspace = true
@@ -73,6 +74,7 @@ time_format.workspace = true
ui.workspace = true
util.workspace = true
uuid.workspace = true
vim_mode_setting.workspace = true
workspace.workspace = true
zed_actions.workspace = true

View File

@@ -2,21 +2,24 @@ use std::sync::Arc;
use assistant_tool::ToolWorkingSet;
use collections::HashMap;
use editor::{Editor, MultiBuffer};
use gpui::{
list, AbsoluteLength, AnyElement, App, DefiniteLength, EdgesRefinement, Empty, Entity, Length,
ListAlignment, ListOffset, ListState, StyleRefinement, Subscription, TextStyleRefinement,
UnderlineStyle, WeakEntity,
list, AbsoluteLength, AnyElement, App, ClickEvent, DefiniteLength, EdgesRefinement, Empty,
Entity, Focusable, Length, ListAlignment, ListOffset, ListState, StyleRefinement, Subscription,
Task, TextStyleRefinement, UnderlineStyle, WeakEntity,
};
use language::LanguageRegistry;
use language_model::Role;
use language::{Buffer, LanguageRegistry};
use language_model::{LanguageModelRegistry, LanguageModelToolUseId, Role};
use markdown::{Markdown, MarkdownStyle};
use settings::Settings as _;
use theme::ThemeSettings;
use ui::prelude::*;
use ui::{prelude::*, Disclosure, KeyBinding};
use util::ResultExt as _;
use workspace::Workspace;
use crate::thread::{MessageId, Thread, ThreadError, ThreadEvent};
use crate::thread::{MessageId, RequestKind, Thread, ThreadError, ThreadEvent};
use crate::thread_store::ThreadStore;
use crate::tool_use::{ToolUse, ToolUseStatus};
use crate::ui::ContextPill;
pub struct ActiveThread {
@@ -25,13 +28,20 @@ pub struct ActiveThread {
tools: Arc<ToolWorkingSet>,
thread_store: Entity<ThreadStore>,
thread: Entity<Thread>,
save_thread_task: Option<Task<()>>,
messages: Vec<MessageId>,
list_state: ListState,
rendered_messages_by_id: HashMap<MessageId, Entity<Markdown>>,
editing_message: Option<(MessageId, EditMessageState)>,
expanded_tool_uses: HashMap<LanguageModelToolUseId, bool>,
last_error: Option<ThreadError>,
_subscriptions: Vec<Subscription>,
}
struct EditMessageState {
editor: Entity<Editor>,
}
impl ActiveThread {
pub fn new(
thread: Entity<Thread>,
@@ -53,15 +63,18 @@ impl ActiveThread {
tools,
thread_store,
thread: thread.clone(),
save_thread_task: None,
messages: Vec::new(),
rendered_messages_by_id: HashMap::default(),
expanded_tool_uses: HashMap::default(),
list_state: ListState::new(0, ListAlignment::Bottom, px(1024.), {
let this = cx.entity().downgrade();
move |ix, _: &mut Window, cx: &mut App| {
this.update(cx, |this, cx| this.render_message(ix, cx))
move |ix, window: &mut Window, cx: &mut App| {
this.update(cx, |this, cx| this.render_message(ix, window, cx))
.unwrap()
}
}),
editing_message: None,
last_error: None,
_subscriptions: subscriptions,
};
@@ -114,6 +127,44 @@ impl ActiveThread {
self.messages.push(*id);
self.list_state.splice(old_len..old_len, 1);
let markdown = self.render_markdown(text.into(), window, cx);
self.rendered_messages_by_id.insert(*id, markdown);
self.list_state.scroll_to(ListOffset {
item_ix: old_len,
offset_in_item: Pixels(0.0),
});
}
fn edited_message(
&mut self,
id: &MessageId,
text: String,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(index) = self.messages.iter().position(|message_id| message_id == id) else {
return;
};
self.list_state.splice(index..index + 1, 1);
let markdown = self.render_markdown(text.into(), window, cx);
self.rendered_messages_by_id.insert(*id, markdown);
}
fn deleted_message(&mut self, id: &MessageId) {
let Some(index) = self.messages.iter().position(|message_id| message_id == id) else {
return;
};
self.messages.remove(index);
self.list_state.splice(index..index + 1, 0);
self.rendered_messages_by_id.remove(id);
}
fn render_markdown(
&self,
text: SharedString,
window: &Window,
cx: &mut Context<Self>,
) -> Entity<Markdown> {
let theme_settings = ThemeSettings::get_global(cx);
let colors = cx.theme().colors();
let ui_font_size = TextSize::Default.rems(cx);
@@ -131,6 +182,8 @@ impl ActiveThread {
base_text_style: text_style,
syntax: cx.theme().syntax().clone(),
selection_background_color: cx.theme().players().local().selection,
code_block_overflow_x_scroll: true,
table_overflow_x_scroll: true,
code_block: StyleRefinement {
margin: EdgesRefinement {
top: Some(Length::Definite(rems(0.).into())),
@@ -177,20 +230,15 @@ impl ActiveThread {
..Default::default()
};
let markdown = cx.new(|cx| {
cx.new(|cx| {
Markdown::new(
text.into(),
text,
markdown_style,
Some(self.language_registry.clone()),
None,
cx,
)
});
self.rendered_messages_by_id.insert(*id, markdown);
self.list_state.scroll_to(ListOffset {
item_ix: old_len,
offset_in_item: Pixels(0.0),
});
})
}
fn handle_thread_event(
@@ -205,11 +253,7 @@ impl ActiveThread {
self.last_error = Some(error.clone());
}
ThreadEvent::StreamedCompletion | ThreadEvent::SummaryChanged => {
self.thread_store
.update(cx, |thread_store, cx| {
thread_store.save_thread(&self.thread, cx)
})
.detach_and_log_err(cx);
self.save_thread(cx);
}
ThreadEvent::StreamedAssistantText(message_id, text) => {
if let Some(markdown) = self.rendered_messages_by_id.get_mut(&message_id) {
@@ -228,12 +272,25 @@ impl ActiveThread {
self.push_message(message_id, message_text, window, cx);
}
self.thread_store
.update(cx, |thread_store, cx| {
thread_store.save_thread(&self.thread, cx)
})
.detach_and_log_err(cx);
self.save_thread(cx);
cx.notify();
}
ThreadEvent::MessageEdited(message_id) => {
if let Some(message_text) = self
.thread
.read(cx)
.message(*message_id)
.map(|message| message.text.clone())
{
self.edited_message(message_id, message_text, window, cx);
}
self.save_thread(cx);
cx.notify();
}
ThreadEvent::MessageDeleted(message_id) => {
self.deleted_message(message_id);
self.save_thread(cx);
cx.notify();
}
ThreadEvent::UsePendingTools => {
@@ -251,21 +308,166 @@ impl ActiveThread {
let task = tool.run(tool_use.input, self.workspace.clone(), window, cx);
self.thread.update(cx, |thread, cx| {
thread.insert_tool_output(
tool_use.assistant_message_id,
tool_use.id.clone(),
task,
cx,
);
thread.insert_tool_output(tool_use.id.clone(), task, cx);
});
}
}
}
ThreadEvent::ToolFinished { .. } => {
let all_tools_finished = self
.thread
.read(cx)
.pending_tool_uses()
.into_iter()
.all(|tool_use| tool_use.status.is_error());
if all_tools_finished {
let model_registry = LanguageModelRegistry::read_global(cx);
if let Some(model) = model_registry.active_model() {
self.thread.update(cx, |thread, cx| {
// Insert a user message to contain the tool results.
thread.insert_user_message(
// TODO: Sending up a user message without any content results in the model sending back
// responses that also don't have any content. We currently don't handle this case well,
// so for now we provide some text to keep the model on track.
"Here are the tool results.",
Vec::new(),
cx,
);
thread.send_to_model(model, RequestKind::Chat, true, cx);
});
}
}
}
ThreadEvent::ToolFinished { .. } => {}
}
}
fn render_message(&self, ix: usize, cx: &mut Context<Self>) -> AnyElement {
/// Spawns a task to save the active thread.
///
/// Only one task to save the thread will be in flight at a time.
fn save_thread(&mut self, cx: &mut Context<Self>) {
let thread = self.thread.clone();
self.save_thread_task = Some(cx.spawn(|this, mut cx| async move {
let task = this
.update(&mut cx, |this, cx| {
this.thread_store
.update(cx, |thread_store, cx| thread_store.save_thread(&thread, cx))
})
.ok();
if let Some(task) = task {
task.await.log_err();
}
}));
}
fn start_editing_message(
&mut self,
message_id: MessageId,
message_text: String,
window: &mut Window,
cx: &mut Context<Self>,
) {
let buffer = cx.new(|cx| {
MultiBuffer::singleton(cx.new(|cx| Buffer::local(message_text.clone(), cx)), cx)
});
let editor = cx.new(|cx| {
let mut editor = Editor::new(
editor::EditorMode::AutoHeight { max_lines: 8 },
buffer,
None,
false,
window,
cx,
);
editor.focus_handle(cx).focus(window);
editor.move_to_end(&editor::actions::MoveToEnd, window, cx);
editor
});
self.editing_message = Some((
message_id,
EditMessageState {
editor: editor.clone(),
},
));
cx.notify();
}
fn cancel_editing_message(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
self.editing_message.take();
cx.notify();
}
fn confirm_editing_message(
&mut self,
_: &menu::Confirm,
_: &mut Window,
cx: &mut Context<Self>,
) {
let Some((message_id, state)) = self.editing_message.take() else {
return;
};
let edited_text = state.editor.read(cx).text(cx);
self.thread.update(cx, |thread, cx| {
thread.edit_message(message_id, Role::User, edited_text, cx);
for message_id in self.messages_after(message_id) {
thread.delete_message(*message_id, cx);
}
});
let provider = LanguageModelRegistry::read_global(cx).active_provider();
if provider
.as_ref()
.map_or(false, |provider| provider.must_accept_terms(cx))
{
cx.notify();
return;
}
let model_registry = LanguageModelRegistry::read_global(cx);
let Some(model) = model_registry.active_model() else {
return;
};
self.thread.update(cx, |thread, cx| {
thread.send_to_model(model, RequestKind::Chat, false, cx)
});
cx.notify();
}
fn last_user_message(&self, cx: &Context<Self>) -> Option<MessageId> {
self.messages
.iter()
.rev()
.find(|message_id| {
self.thread
.read(cx)
.message(**message_id)
.map_or(false, |message| message.role == Role::User)
})
.cloned()
}
fn messages_after(&self, message_id: MessageId) -> &[MessageId] {
self.messages
.iter()
.position(|id| *id == message_id)
.map(|index| &self.messages[index + 1..])
.unwrap_or(&[])
}
fn handle_cancel_click(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
self.cancel_editing_message(&menu::Cancel, window, cx);
}
fn handle_regenerate_click(
&mut self,
_: &ClickEvent,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.confirm_editing_message(&menu::Confirm, window, cx);
}
fn render_message(&self, ix: usize, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
let message_id = self.messages[ix];
let Some(message) = self.thread.read(cx).message(message_id) else {
return Empty.into_any();
@@ -276,10 +478,36 @@ impl ActiveThread {
};
let context = self.thread.read(cx).context_for_message(message_id);
let tool_uses = self.thread.read(cx).tool_uses_for_message(message_id);
let colors = cx.theme().colors();
// Don't render user messages that are just there for returning tool results.
if message.role == Role::User && self.thread.read(cx).message_has_tool_results(message_id) {
return Empty.into_any();
}
let allow_editing_message =
message.role == Role::User && self.last_user_message(cx) == Some(message_id);
let edit_message_editor = self
.editing_message
.as_ref()
.filter(|(id, _)| *id == message_id)
.map(|(_, state)| state.editor.clone());
let message_content = v_flex()
.child(div().p_2p5().text_ui(cx).child(markdown.clone()))
.child(
if let Some(edit_message_editor) = edit_message_editor.clone() {
div()
.key_context("EditMessageEditor")
.on_action(cx.listener(Self::cancel_editing_message))
.on_action(cx.listener(Self::confirm_editing_message))
.p_2p5()
.child(edit_message_editor)
} else {
div().p_2p5().text_ui(cx).child(markdown.clone())
},
)
.when_some(context, |parent, context| {
if !context.is_empty() {
parent.child(
@@ -309,7 +537,8 @@ impl ActiveThread {
.child(
h_flex()
.py_1()
.px_2()
.pl_2()
.pr_1()
.bg(colors.editor_foreground.opacity(0.05))
.border_b_1()
.border_color(colors.border)
@@ -328,21 +557,197 @@ impl ActiveThread {
.size(LabelSize::Small)
.color(Color::Muted),
),
)
.when_some(
edit_message_editor.clone(),
|this, edit_message_editor| {
let focus_handle = edit_message_editor.focus_handle(cx);
this.child(
h_flex()
.gap_1()
.child(
Button::new("cancel-edit-message", "Cancel")
.label_size(LabelSize::Small)
.key_binding(
KeyBinding::for_action_in(
&menu::Cancel,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click(
cx.listener(Self::handle_cancel_click),
),
)
.child(
Button::new(
"confirm-edit-message",
"Regenerate",
)
.label_size(LabelSize::Small)
.key_binding(
KeyBinding::for_action_in(
&menu::Confirm,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click(
cx.listener(Self::handle_regenerate_click),
),
),
)
},
)
.when(
edit_message_editor.is_none() && allow_editing_message,
|this| {
this.child(
Button::new("edit-message", "Edit")
.label_size(LabelSize::Small)
.on_click(cx.listener({
let message_text = message.text.clone();
move |this, _, window, cx| {
this.start_editing_message(
message_id,
message_text.clone(),
window,
cx,
);
}
})),
)
},
),
)
.child(message_content),
),
Role::Assistant => div().id(("message-container", ix)).child(message_content),
Role::Assistant => div()
.id(("message-container", ix))
.child(message_content)
.map(|parent| {
if tool_uses.is_empty() {
return parent;
}
parent.child(
v_flex().children(
tool_uses
.into_iter()
.map(|tool_use| self.render_tool_use(tool_use, cx)),
),
)
}),
Role::System => div().id(("message-container", ix)).py_1().px_2().child(
v_flex()
.bg(colors.editor_background)
.rounded_md()
.rounded_sm()
.child(message_content),
),
};
styled_message.into_any()
}
fn render_tool_use(&self, tool_use: ToolUse, cx: &mut Context<Self>) -> impl IntoElement {
let is_open = self
.expanded_tool_uses
.get(&tool_use.id)
.copied()
.unwrap_or_default();
div().px_2p5().child(
v_flex()
.gap_1()
.rounded_lg()
.border_1()
.border_color(cx.theme().colors().border)
.child(
h_flex()
.justify_between()
.py_0p5()
.pl_1()
.pr_2()
.bg(cx.theme().colors().editor_foreground.opacity(0.02))
.when(is_open, |element| element.border_b_1().rounded_t(px(6.)))
.when(!is_open, |element| element.rounded_md())
.border_color(cx.theme().colors().border)
.child(
h_flex()
.gap_1()
.child(Disclosure::new("tool-use-disclosure", is_open).on_click(
cx.listener({
let tool_use_id = tool_use.id.clone();
move |this, _event, _window, _cx| {
let is_open = this
.expanded_tool_uses
.entry(tool_use_id.clone())
.or_insert(false);
*is_open = !*is_open;
}
}),
))
.child(Label::new(tool_use.name)),
)
.child(
Label::new(match tool_use.status {
ToolUseStatus::Pending => "Pending",
ToolUseStatus::Running => "Running",
ToolUseStatus::Finished(_) => "Finished",
ToolUseStatus::Error(_) => "Error",
})
.size(LabelSize::XSmall)
.buffer_font(cx),
),
)
.map(|parent| {
if !is_open {
return parent;
}
parent.child(
v_flex()
.child(
v_flex()
.gap_0p5()
.py_1()
.px_2p5()
.border_b_1()
.border_color(cx.theme().colors().border)
.child(Label::new("Input:"))
.child(Label::new(
serde_json::to_string_pretty(&tool_use.input)
.unwrap_or_default(),
)),
)
.map(|parent| match tool_use.status {
ToolUseStatus::Finished(output) => parent.child(
v_flex()
.gap_0p5()
.py_1()
.px_2p5()
.child(Label::new("Result:"))
.child(Label::new(output)),
),
ToolUseStatus::Error(err) => parent.child(
v_flex()
.gap_0p5()
.py_1()
.px_2p5()
.child(Label::new("Error:"))
.child(Label::new(err)),
),
ToolUseStatus::Pending | ToolUseStatus::Running => parent,
}),
)
}),
)
}
}
impl Render for ActiveThread {

View File

@@ -16,6 +16,7 @@ mod terminal_inline_assistant;
mod thread;
mod thread_history;
mod thread_store;
mod tool_use;
mod ui;
use std::sync::Arc;
@@ -26,7 +27,7 @@ use command_palette_hooks::CommandPaletteFilter;
use feature_flags::{Assistant2FeatureFlag, FeatureFlagAppExt};
use fs::Fs;
use gpui::{actions, App};
use prompt_library::PromptBuilder;
use prompt_store::PromptBuilder;
use settings::Settings as _;
pub use crate::assistant_panel::{AssistantPanel, ConcreteAssistantPanelDelegate};
@@ -38,7 +39,6 @@ actions!(
NewThread,
NewPromptEditor,
ToggleContextPicker,
ToggleModelSelector,
RemoveAllContext,
OpenHistory,
OpenConfiguration,

View File

@@ -1,7 +1,7 @@
use std::sync::Arc;
use collections::HashMap;
use gpui::{AnyView, App, EventEmitter, FocusHandle, Focusable, Subscription};
use gpui::{Action, AnyView, App, EventEmitter, FocusHandle, Focusable, Subscription};
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
use ui::{prelude::*, Divider, DividerColor, ElevationIndex};
use zed_actions::assistant::DeployPromptLibrary;
@@ -134,7 +134,7 @@ impl AssistantConfiguration {
.bg(cx.theme().colors().editor_background)
.border_1()
.border_color(cx.theme().colors().border_variant)
.rounded_md()
.rounded_sm()
.map(|parent| match configuration_view {
Some(configuration_view) => parent.child(configuration_view),
None => parent.child(div().child(Label::new(format!(
@@ -158,8 +158,16 @@ impl Render for AssistantConfiguration {
.child(
v_flex()
.p(DynamicSpacing::Base16.rems(cx))
.gap_1()
.child(Headline::new("Prompt Library").size(HeadlineSize::Small))
.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)
@@ -168,8 +176,8 @@ impl Render for AssistantConfiguration {
.icon(IconName::Book)
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
.on_click(|_event, _window, cx| {
cx.dispatch_action(&DeployPromptLibrary)
.on_click(|_event, window, cx| {
window.dispatch_action(DeployPromptLibrary.boxed_clone(), cx)
}),
),
)

View File

@@ -2,13 +2,13 @@ use assistant_settings::AssistantSettings;
use fs::Fs;
use gpui::{Entity, FocusHandle, SharedString};
use language_model::LanguageModelRegistry;
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
use language_model_selector::{
LanguageModelSelector, LanguageModelSelectorPopoverMenu, ToggleModelSelector,
};
use settings::update_settings_file;
use std::sync::Arc;
use ui::{prelude::*, ButtonLike, PopoverMenuHandle, Tooltip};
use crate::ToggleModelSelector;
pub struct AssistantModelSelector {
selector: Entity<LanguageModelSelector>,
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
@@ -42,6 +42,10 @@ impl AssistantModelSelector {
focus_handle,
}
}
pub fn toggle(&self, window: &mut Window, cx: &mut Context<Self>) {
self.menu_handle.toggle(window, cx);
}
}
impl Render for AssistantModelSelector {

View File

@@ -14,13 +14,15 @@ use client::zed_urls;
use editor::Editor;
use fs::Fs;
use gpui::{
prelude::*, px, svg, Action, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter,
FocusHandle, Focusable, FontWeight, Pixels, Subscription, Task, UpdateGlobal, WeakEntity,
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};
use project::Project;
use prompt_library::{open_prompt_library, PromptBuilder, PromptLibrary};
use prompt_library::{open_prompt_library, PromptLibrary};
use prompt_store::PromptBuilder;
use settings::{update_settings_file, Settings};
use time::UtcOffset;
use ui::{prelude::*, ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle, Tab, Tooltip};
@@ -458,6 +460,12 @@ impl AssistantPanel {
pub(crate) fn active_context_editor(&self) -> Option<Entity<ContextEditor>> {
self.context_editor.clone()
}
pub(crate) fn delete_context(&mut self, path: PathBuf, cx: &mut Context<Self>) {
self.context_store
.update(cx, |this, cx| this.delete_local_context(path, cx))
.detach_and_log_err(cx);
}
}
impl Focusable for AssistantPanel {
@@ -590,7 +598,6 @@ impl AssistantPanel {
h_flex()
.id("assistant-toolbar")
.px(DynamicSpacing::Base08.rems(cx))
.h(Tab::container_height(cx))
.flex_none()
.justify_between()
@@ -598,72 +605,86 @@ impl AssistantPanel {
.bg(cx.theme().colors().tab_bar_background)
.border_b_1()
.border_color(cx.theme().colors().border)
.child(
div()
.id("title")
.overflow_x_scroll()
.px(DynamicSpacing::Base08.rems(cx))
.child(Label::new(title).truncate()),
)
.child(
h_flex()
.w_full()
.gap_1()
.justify_between()
.child(Label::new(title))
.h_full()
.pl_2()
.gap_2()
.bg(cx.theme().colors().tab_bar_background)
.children(if matches!(self.active_view, ActiveView::PromptEditor) {
self.context_editor
.as_ref()
.and_then(|editor| render_remaining_tokens(editor, cx))
} else {
None
}),
)
.child(
h_flex()
.h_full()
.pl_1p5()
.border_l_1()
.border_color(cx.theme().colors().border)
.gap(DynamicSpacing::Base02.rems(cx))
})
.child(
PopoverMenu::new("assistant-toolbar-new-popover-menu")
.trigger_with_tooltip(
IconButton::new("new", IconName::Plus)
.icon_size(IconSize::Small)
.style(ButtonStyle::Subtle),
Tooltip::text("New…"),
)
.anchor(Corner::TopRight)
.with_handle(self.new_item_context_menu_handle.clone())
.menu(move |window, cx| {
Some(ContextMenu::build(window, cx, |menu, _window, _cx| {
menu.action("New Thread", NewThread.boxed_clone())
.action("New Prompt Editor", NewPromptEditor.boxed_clone())
}))
}),
)
.child(
IconButton::new("open-history", IconName::HistoryRerun)
.icon_size(IconSize::Small)
.style(ButtonStyle::Subtle)
.tooltip({
let focus_handle = self.focus_handle(cx);
move |window, cx| {
Tooltip::for_action_in(
"History",
&OpenHistory,
&focus_handle,
window,
cx,
h_flex()
.h_full()
.px(DynamicSpacing::Base08.rems(cx))
.border_l_1()
.border_color(cx.theme().colors().border)
.gap(DynamicSpacing::Base02.rems(cx))
.child(
PopoverMenu::new("assistant-toolbar-new-popover-menu")
.trigger_with_tooltip(
IconButton::new("new", IconName::Plus)
.icon_size(IconSize::Small)
.style(ButtonStyle::Subtle),
Tooltip::text("New…"),
)
}
})
.on_click(move |_event, window, cx| {
window.dispatch_action(OpenHistory.boxed_clone(), cx);
}),
)
.child(
IconButton::new("configure-assistant", IconName::Settings)
.icon_size(IconSize::Small)
.style(ButtonStyle::Subtle)
.tooltip(Tooltip::text("Assistant Settings"))
.on_click(move |_event, window, cx| {
window.dispatch_action(OpenConfiguration.boxed_clone(), cx);
}),
.anchor(Corner::TopRight)
.with_handle(self.new_item_context_menu_handle.clone())
.menu(move |window, cx| {
Some(ContextMenu::build(
window,
cx,
|menu, _window, _cx| {
menu.action("New Thread", NewThread.boxed_clone())
.action(
"New Prompt Editor",
NewPromptEditor.boxed_clone(),
)
},
))
}),
)
.child(
IconButton::new("open-history", IconName::HistoryRerun)
.icon_size(IconSize::Small)
.style(ButtonStyle::Subtle)
.tooltip({
let focus_handle = self.focus_handle(cx);
move |window, cx| {
Tooltip::for_action_in(
"History",
&OpenHistory,
&focus_handle,
window,
cx,
)
}
})
.on_click(move |_event, window, cx| {
window.dispatch_action(OpenHistory.boxed_clone(), cx);
}),
)
.child(
IconButton::new("configure-assistant", IconName::Settings)
.icon_size(IconSize::Small)
.style(ButtonStyle::Subtle)
.tooltip(Tooltip::text("Assistant Settings"))
.on_click(move |_event, window, cx| {
window.dispatch_action(OpenConfiguration.boxed_clone(), cx);
}),
),
),
)
}
@@ -705,12 +726,11 @@ impl AssistantPanel {
) -> impl IntoElement {
let recent_history = self
.history_store
.update(cx, |this, cx| this.recent_entries(3, cx));
.update(cx, |this, cx| this.recent_entries(6, cx));
let create_welcome_heading = || {
h_flex()
.w_full()
.justify_center()
.child(Headline::new("Welcome to the Assistant Panel").size(HeadlineSize::Small))
};
@@ -718,36 +738,27 @@ impl AssistantPanel {
let no_error = configuration_error.is_none();
v_flex()
.gap_2()
.child(
v_flex().w_full().child(
svg()
.path("icons/logo_96.svg")
.text_color(cx.theme().colors().text)
.w(px(40.))
.h(px(40.))
.mx_auto()
.mb_4(),
),
)
.p_1p5()
.size_full()
.justify_end()
.gap_1()
.map(|parent| {
match configuration_error {
Some(ConfigurationError::ProviderNotAuthenticated)
| Some(ConfigurationError::NoProvider) => {
parent.child(
v_flex()
.px_1p5()
.gap_0p5()
.child(create_welcome_heading())
.child(
h_flex().mb_2().w_full().justify_center().child(
Label::new(
"To start using the assistant, configure at least one LLM provider.",
)
.color(Color::Muted),
),
Label::new(
"To start using the assistant, configure at least one LLM provider.",
)
.color(Color::Muted),
)
.child(
h_flex().w_full().justify_center().child(
h_flex().mt_1().w_full().child(
Button::new("open-configuration", "Configure a Provider")
.size(ButtonSize::Compact)
.icon(Some(IconName::Sliders))
@@ -761,7 +772,7 @@ impl AssistantPanel {
)
}
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => parent
.child(v_flex().gap_0p5().child(create_welcome_heading()).children(
.child(v_flex().px_1p5().gap_0p5().child(create_welcome_heading()).children(
provider.render_accept_terms(
LanguageModelProviderTosView::ThreadEmptyState,
cx,
@@ -772,21 +783,40 @@ impl AssistantPanel {
})
.when(recent_history.is_empty() && no_error, |parent| {
parent.child(v_flex().gap_0p5().child(create_welcome_heading()).child(
h_flex().w_full().justify_center().child(
Label::new("Start typing to chat with your codebase").color(Color::Muted),
),
Label::new("Start typing to chat with your codebase").color(Color::Muted),
))
})
.when(!recent_history.is_empty(), |parent| {
parent
.child(
h_flex().w_full().justify_center().child(
Label::new("Recent Threads:")
.size(LabelSize::Small)
.color(Color::Muted),
),
h_flex()
.pl_1p5()
.pb_1()
.w_full()
.justify_between()
.border_b_1()
.border_color(cx.theme().colors().border_variant)
.child(
Label::new("Past Interactions")
.size(LabelSize::Small)
.color(Color::Muted),
)
.child(
Button::new("view-history", "View All")
.style(ButtonStyle::Subtle)
.label_size(LabelSize::Small)
.key_binding(KeyBinding::for_action_in(
&OpenHistory,
&self.focus_handle(cx),
window,
cx,
))
.on_click(move |_event, window, cx| {
window.dispatch_action(OpenHistory.boxed_clone(), cx);
}),
),
)
.child(v_flex().mx_auto().w_4_5().gap_2().children(
.child(v_flex().gap_1().children(
recent_history.into_iter().map(|entry| {
// TODO: Add keyboard navigation.
match entry {
@@ -801,22 +831,6 @@ impl AssistantPanel {
}
}),
))
.child(
h_flex().w_full().justify_center().child(
Button::new("view-all-past-threads", "View All Past Threads")
.style(ButtonStyle::Subtle)
.label_size(LabelSize::Small)
.key_binding(KeyBinding::for_action_in(
&OpenHistory,
&self.focus_handle(cx),
window,
cx,
))
.on_click(move |_event, window, cx| {
window.dispatch_action(OpenHistory.boxed_clone(), cx);
}),
),
)
})
}
@@ -980,12 +994,21 @@ impl AssistantPanel {
)
.into_any()
}
fn key_context(&self) -> KeyContext {
let mut key_context = KeyContext::new_with_defaults();
key_context.add("AssistantPanel2");
if matches!(self.active_view, ActiveView::PromptEditor) {
key_context.add("prompt_editor");
}
key_context
}
}
impl Render for AssistantPanel {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.key_context("AssistantPanel2")
.key_context(self.key_context())
.justify_between()
.size_full()
.on_action(cx.listener(Self::cancel))
@@ -1000,12 +1023,7 @@ impl Render for AssistantPanel {
.map(|parent| match self.active_view {
ActiveView::Thread => parent
.child(self.render_active_thread_or_empty_state(window, cx))
.child(
h_flex()
.border_t_1()
.border_color(cx.theme().colors().border)
.child(self.message_editor.clone()),
)
.child(h_flex().child(self.message_editor.clone()))
.children(self.render_last_error(cx)),
ActiveView::History => parent.child(self.history.clone()),
ActiveView::PromptEditor => parent.children(self.context_editor.clone()),

View File

@@ -14,7 +14,7 @@ use language_model::{
};
use multi_buffer::MultiBufferRow;
use parking_lot::Mutex;
use prompt_library::PromptBuilder;
use prompt_store::PromptBuilder;
use rope::Rope;
use smol::future::FutureExt;
use std::{

View File

@@ -208,12 +208,18 @@ impl ContextStore {
let mut text_tasks = Vec::new();
this.update(&mut cx, |_, cx| {
for (path, buffer_entity) in files.into_iter().zip(buffers) {
let buffer_entity = buffer_entity?;
let buffer = buffer_entity.read(cx);
let (buffer_info, text_task) =
collect_buffer_info_and_text(path, buffer_entity, buffer, cx.to_async());
buffer_infos.push(buffer_info);
text_tasks.push(text_task);
// Skip all binary files and other non-UTF8 files
if let Ok(buffer_entity) = buffer_entity {
let buffer = buffer_entity.read(cx);
let (buffer_info, text_task) = collect_buffer_info_and_text(
path,
buffer_entity,
buffer,
cx.to_async(),
);
buffer_infos.push(buffer_info);
text_tasks.push(text_task);
}
}
anyhow::Ok(())
})??;

View File

@@ -27,8 +27,9 @@ use language::{Buffer, Point, Selection, TransactionId};
use language_model::{report_assistant_event, LanguageModelRegistry};
use multi_buffer::MultiBufferRow;
use parking_lot::Mutex;
use project::ActionVariant;
use project::{CodeAction, ProjectTransaction};
use prompt_library::PromptBuilder;
use prompt_store::PromptBuilder;
use settings::{Settings, SettingsStore};
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
@@ -1727,10 +1728,10 @@ impl CodeActionProvider for AssistantCodeActionProvider {
Task::ready(Ok(vec![CodeAction {
server_id: language::LanguageServerId(0),
range: snapshot.anchor_before(range.start)..snapshot.anchor_after(range.end),
lsp_action: lsp::CodeAction {
lsp_action: ActionVariant::Action(Box::new(lsp::CodeAction {
title: "Fix with Assistant".into(),
..Default::default()
},
})),
}]))
} else {
Task::ready(Ok(Vec::new()))

View File

@@ -6,7 +6,7 @@ use crate::context_strip::{ContextStrip, ContextStripEvent, SuggestContextKind};
use crate::terminal_codegen::TerminalCodegen;
use crate::thread_store::ThreadStore;
use crate::{CycleNextInlineAssist, CyclePreviousInlineAssist};
use crate::{RemoveAllContext, ToggleContextPicker, ToggleModelSelector};
use crate::{RemoveAllContext, ToggleContextPicker};
use client::ErrorExt;
use collections::VecDeque;
use editor::{
@@ -20,7 +20,7 @@ use gpui::{
EventEmitter, FocusHandle, Focusable, FontWeight, Subscription, TextStyle, WeakEntity, Window,
};
use language_model::{LanguageModel, LanguageModelRegistry};
use language_model_selector::LanguageModelSelector;
use language_model_selector::ToggleModelSelector;
use parking_lot::Mutex;
use settings::Settings;
use std::cmp;
@@ -40,7 +40,6 @@ pub struct PromptEditor<T> {
context_strip: Entity<ContextStrip>,
context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
model_selector: Entity<AssistantModelSelector>,
model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
edited_since_done: bool,
prompt_history: VecDeque<String>,
prompt_history_ix: Option<usize>,
@@ -104,7 +103,10 @@ impl<T: 'static> Render for PromptEditor<T> {
.items_start()
.cursor(CursorStyle::Arrow)
.on_action(cx.listener(Self::toggle_context_picker))
.on_action(cx.listener(Self::toggle_model_selector))
.on_action(cx.listener(|this, _: &ToggleModelSelector, window, cx| {
this.model_selector
.update(cx, |model_selector, cx| model_selector.toggle(window, cx));
}))
.on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::cancel))
.on_action(cx.listener(Self::move_up))
@@ -347,15 +349,6 @@ impl<T: 'static> PromptEditor<T> {
self.context_picker_menu_handle.toggle(window, cx);
}
fn toggle_model_selector(
&mut self,
_: &ToggleModelSelector,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.model_selector_menu_handle.toggle(window, cx);
}
pub fn remove_all_context(
&mut self,
_: &RemoveAllContext,
@@ -890,13 +883,12 @@ impl PromptEditor<BufferCodegen> {
model_selector: cx.new(|cx| {
AssistantModelSelector::new(
fs,
model_selector_menu_handle.clone(),
model_selector_menu_handle,
prompt_editor.focus_handle(cx),
window,
cx,
)
}),
model_selector_menu_handle,
edited_since_done: false,
prompt_history,
prompt_history_ix: None,
@@ -1052,7 +1044,6 @@ impl PromptEditor<TerminalCodegen> {
cx,
)
}),
model_selector_menu_handle,
edited_since_done: false,
prompt_history,
prompt_history_ix: None,

View File

@@ -4,19 +4,21 @@ use editor::actions::MoveUp;
use editor::{Editor, EditorElement, EditorEvent, EditorStyle};
use fs::Fs;
use gpui::{
pulsating_between, Animation, AnimationExt, App, DismissEvent, Entity, Focusable, Subscription,
TextStyle, WeakEntity,
Animation, AnimationExt, App, DismissEvent, Entity, Focusable, Subscription, TextStyle,
WeakEntity,
};
use language_model::{LanguageModelRegistry, LanguageModelRequestTool};
use language_model_selector::LanguageModelSelector;
use language_model::LanguageModelRegistry;
use language_model_selector::ToggleModelSelector;
use rope::Point;
use settings::Settings;
use std::time::Duration;
use text::Bias;
use theme::ThemeSettings;
use ui::{
prelude::*, ButtonLike, KeyBinding, PopoverMenu, PopoverMenuHandle, Switch, TintColor, Tooltip,
prelude::*, ButtonLike, KeyBinding, PlatformStyle, PopoverMenu, PopoverMenuHandle, Switch,
Tooltip,
};
use vim_mode_setting::VimModeSetting;
use workspace::Workspace;
use crate::assistant_model_selector::AssistantModelSelector;
@@ -25,7 +27,7 @@ use crate::context_store::{refresh_context_store_text, ContextStore};
use crate::context_strip::{ContextStrip, ContextStripEvent, SuggestContextKind};
use crate::thread::{RequestKind, Thread};
use crate::thread_store::ThreadStore;
use crate::{Chat, ChatMode, RemoveAllContext, ToggleContextPicker, ToggleModelSelector};
use crate::{Chat, ChatMode, RemoveAllContext, ToggleContextPicker};
pub struct MessageEditor {
thread: Entity<Thread>,
@@ -36,7 +38,6 @@ pub struct MessageEditor {
inline_context_picker: Entity<ContextPicker>,
inline_context_picker_menu_handle: PopoverMenuHandle<ContextPicker>,
model_selector: Entity<AssistantModelSelector>,
model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
use_tools: bool,
_subscriptions: Vec<Subscription>,
}
@@ -109,27 +110,17 @@ impl MessageEditor {
model_selector: cx.new(|cx| {
AssistantModelSelector::new(
fs,
model_selector_menu_handle.clone(),
model_selector_menu_handle,
editor.focus_handle(cx),
window,
cx,
)
}),
model_selector_menu_handle,
use_tools: false,
_subscriptions: subscriptions,
}
}
fn toggle_model_selector(
&mut self,
_: &ToggleModelSelector,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.model_selector_menu_handle.toggle(window, cx)
}
fn toggle_chat_mode(&mut self, _: &ChatMode, _window: &mut Window, cx: &mut Context<Self>) {
self.use_tools = !self.use_tools;
cx.notify();
@@ -205,22 +196,7 @@ impl MessageEditor {
.update(&mut cx, |thread, cx| {
let context = context_store.read(cx).snapshot(cx).collect::<Vec<_>>();
thread.insert_user_message(user_message, context, cx);
let mut request = thread.to_completion_request(request_kind, cx);
if use_tools {
request.tools = thread
.tools()
.tools(cx)
.into_iter()
.map(|tool| LanguageModelRequestTool {
name: tool.name(),
description: tool.description(),
input_schema: tool.input_schema(),
})
.collect();
}
thread.stream_completion(request, model, cx)
thread.send_to_model(model, request_kind, use_tools, cx);
})
.ok();
})
@@ -309,7 +285,6 @@ impl Render for MessageEditor {
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 button_width = px(64.);
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 {
@@ -318,158 +293,215 @@ impl Render for MessageEditor {
Color::Default
};
let vim_mode_enabled = VimModeSetting::get_global(cx).0;
let platform = PlatformStyle::platform();
let linux = platform == PlatformStyle::Linux;
let windows = platform == PlatformStyle::Windows;
let button_width = if linux || windows || vim_mode_enabled {
px(82.)
} else {
px(64.)
};
v_flex()
.key_context("MessageEditor")
.on_action(cx.listener(Self::chat))
.on_action(cx.listener(Self::toggle_model_selector))
.on_action(cx.listener(Self::toggle_context_picker))
.on_action(cx.listener(Self::remove_all_context))
.on_action(cx.listener(Self::move_up))
.on_action(cx.listener(Self::toggle_chat_mode))
.size_full()
.gap_2()
.p_2()
.bg(bg_color)
.child(self.context_strip.clone())
.when(is_streaming_completion, |parent| {
let focus_handle = self.editor.focus_handle(cx).clone();
parent.child(
h_flex().py_3().w_full().justify_center().child(
h_flex()
.flex_none()
.pl_2()
.pr_1()
.py_1()
.bg(cx.theme().colors().editor_background)
.border_1()
.border_color(cx.theme().colors().border_variant)
.rounded_lg()
.shadow_md()
.gap_1()
.child(
Icon::new(IconName::ArrowCircle)
.size(IconSize::XSmall)
.color(Color::Muted)
.with_animation(
"arrow-circle",
Animation::new(Duration::from_secs(2)).repeat(),
|icon, delta| {
icon.transform(gpui::Transformation::rotate(
gpui::percentage(delta),
))
},
),
)
.child(
Label::new("Generating…")
.size(LabelSize::XSmall)
.color(Color::Muted),
)
.child(ui::Divider::vertical())
.child(
Button::new("cancel-generation", "Cancel")
.label_size(LabelSize::XSmall)
.key_binding(
KeyBinding::for_action_in(
&editor::actions::Cancel,
&focus_handle,
window,
cx,
)
.map(|kb| kb.size(rems_from_px(10.))),
)
.on_click(move |_event, window, cx| {
focus_handle.dispatch_action(
&editor::actions::Cancel,
window,
cx,
);
}),
),
),
)
})
.child(
v_flex()
.gap_4()
.child({
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: cx.theme().colors().text,
font_family: settings.ui_font.family.clone(),
font_features: settings.ui_font.features.clone(),
font_size: font_size.into(),
font_weight: settings.ui_font.weight,
line_height: line_height.into(),
..Default::default()
};
EditorElement::new(
&self.editor,
EditorStyle {
background: bg_color,
local_player: cx.theme().players().local(),
text: text_style,
..Default::default()
},
)
})
.key_context("MessageEditor")
.on_action(cx.listener(Self::chat))
.on_action(cx.listener(|this, _: &ToggleModelSelector, window, cx| {
this.model_selector
.update(cx, |model_selector, cx| model_selector.toggle(window, cx));
}))
.on_action(cx.listener(Self::toggle_context_picker))
.on_action(cx.listener(Self::remove_all_context))
.on_action(cx.listener(Self::move_up))
.on_action(cx.listener(Self::toggle_chat_mode))
.gap_2()
.p_2()
.bg(bg_color)
.border_t_1()
.border_color(cx.theme().colors().border)
.child(self.context_strip.clone())
.child(
PopoverMenu::new("inline-context-picker")
.menu(move |window, cx| {
inline_context_picker.update(cx, |this, cx| {
this.init(window, cx);
});
v_flex()
.gap_5()
.child({
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: cx.theme().colors().text,
font_family: settings.ui_font.family.clone(),
font_features: settings.ui_font.features.clone(),
font_size: font_size.into(),
font_weight: settings.ui_font.weight,
line_height: line_height.into(),
..Default::default()
};
Some(inline_context_picker.clone())
EditorElement::new(
&self.editor,
EditorStyle {
background: bg_color,
local_player: cx.theme().players().local(),
text: text_style,
..Default::default()
},
)
})
.attach(gpui::Corner::TopLeft)
.anchor(gpui::Corner::BottomLeft)
.offset(gpui::Point {
x: px(0.0),
y: (-ThemeSettings::get_global(cx).ui_font_size(cx) * 2) - px(4.0),
})
.with_handle(self.inline_context_picker_menu_handle.clone()),
)
.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,
)),
PopoverMenu::new("inline-context-picker")
.menu(move |window, cx| {
inline_context_picker.update(cx, |this, cx| {
this.init(window, cx);
});
Some(inline_context_picker.clone())
})
.attach(gpui::Corner::TopLeft)
.anchor(gpui::Corner::BottomLeft)
.offset(gpui::Point {
x: px(0.0),
y: (-ThemeSettings::get_global(cx).ui_font_size(cx) * 2)
- px(4.0),
})
.with_handle(self.inline_context_picker_menu_handle.clone()),
)
.child(h_flex().gap_1().child(self.model_selector.clone()).child(
if is_streaming_completion {
ButtonLike::new("cancel-generation")
.width(button_width.into())
.style(ButtonStyle::Tinted(TintColor::Accent))
.child(
h_flex()
.w_full()
.justify_between()
.child(
Label::new("Cancel")
.size(LabelSize::Small)
.with_animation(
"pulsating-label",
Animation::new(Duration::from_secs(2))
.repeat()
.with_easing(pulsating_between(
0.4, 0.8,
)),
|label, delta| label.alpha(delta),
),
)
.children(
KeyBinding::for_action_in(
&editor::actions::Cancel,
&focus_handle,
window,
cx,
)
.map(|binding| binding.into_any_element()),
),
)
.on_click(move |_event, window, cx| {
focus_handle.dispatch_action(
&editor::actions::Cancel,
.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,
);
})
} else {
ButtonLike::new("submit-message")
.width(button_width.into())
.style(ButtonStyle::Filled)
.disabled(is_editor_empty || !is_model_selected)
.child(
h_flex()
.w_full()
.justify_between()
.child(
Label::new("Submit")
.size(LabelSize::Small)
.color(submit_label_color),
)),
)
.child(
h_flex().gap_1().child(self.model_selector.clone()).child(
ButtonLike::new("submit-message")
.width(button_width.into())
.style(ButtonStyle::Filled)
.disabled(
is_editor_empty
|| !is_model_selected
|| is_streaming_completion,
)
.children(
KeyBinding::for_action_in(
&Chat,
&focus_handle,
window,
cx,
)
.map(|binding| binding.into_any_element()),
),
)
.on_click(move |_event, window, cx| {
focus_handle.dispatch_action(&Chat, window, cx);
})
.when(is_editor_empty, |button| {
button
.tooltip(Tooltip::text("Type a message to submit"))
})
.when(!is_model_selected, |button| {
button.tooltip(Tooltip::text(
"Select a model to continue",
))
})
},
)),
.child(
h_flex()
.w_full()
.justify_between()
.child(
Label::new("Submit")
.size(LabelSize::Small)
.color(submit_label_color),
)
.children(
KeyBinding::for_action_in(
&Chat,
&focus_handle,
window,
cx,
)
.map(|binding| {
binding
.when(vim_mode_enabled, |kb| {
kb.size(rems_from_px(12.))
})
.into_any_element()
}),
),
)
.on_click(move |_event, window, cx| {
focus_handle.dispatch_action(&Chat, window, cx);
})
.when(is_editor_empty, |button| {
button.tooltip(Tooltip::text(
"Type a message to submit",
))
})
.when(is_streaming_completion, |button| {
button.tooltip(Tooltip::text(
"Cancel to submit a new message",
))
})
.when(!is_model_selected, |button| {
button.tooltip(Tooltip::text(
"Select a model to continue",
))
}),
),
),
),
),
)
}

View File

@@ -155,7 +155,10 @@ pub enum CodegenEvent {
Finished,
}
#[cfg(not(target_os = "windows"))]
pub const CLEAR_INPUT: &str = "\x15";
#[cfg(target_os = "windows")]
pub const CLEAR_INPUT: &str = "\x03";
const CARRIAGE_RETURN: &str = "\x0d";
struct TerminalTransaction {

View File

@@ -16,7 +16,7 @@ use language_model::{
report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
LanguageModelRequestMessage, Role,
};
use prompt_library::PromptBuilder;
use prompt_store::PromptBuilder;
use std::sync::Arc;
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
use terminal_view::TerminalView;

View File

@@ -4,12 +4,11 @@ use anyhow::Result;
use assistant_tool::ToolWorkingSet;
use chrono::{DateTime, Utc};
use collections::{BTreeMap, HashMap, HashSet};
use futures::future::Shared;
use futures::{FutureExt as _, StreamExt as _};
use futures::StreamExt as _;
use gpui::{App, Context, EventEmitter, SharedString, Task};
use language_model::{
LanguageModel, LanguageModelCompletionEvent, LanguageModelRegistry, LanguageModelRequest,
LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse,
LanguageModelRequestMessage, LanguageModelRequestTool, LanguageModelToolResult,
LanguageModelToolUseId, MaxMonthlySpendReachedError, MessageContent, PaymentRequiredError,
Role, StopReason,
};
@@ -19,10 +18,13 @@ use uuid::Uuid;
use crate::context::{attach_context_to_message, ContextId, ContextSnapshot};
use crate::thread_store::SavedThread;
use crate::tool_use::{PendingToolUse, ToolUse, ToolUseState};
#[derive(Debug, Clone, Copy)]
pub enum RequestKind {
Chat,
/// Used when summarizing a thread.
Summarize,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)]
@@ -41,7 +43,7 @@ impl std::fmt::Display for ThreadId {
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize)]
pub struct MessageId(usize);
pub struct MessageId(pub(crate) usize);
impl MessageId {
fn post_inc(&mut self) -> Self {
@@ -70,9 +72,7 @@ pub struct Thread {
completion_count: usize,
pending_completions: Vec<PendingCompletion>,
tools: Arc<ToolWorkingSet>,
tool_uses_by_message: HashMap<MessageId, Vec<LanguageModelToolUse>>,
tool_results_by_message: HashMap<MessageId, Vec<LanguageModelToolResult>>,
pending_tool_uses_by_id: HashMap<LanguageModelToolUseId, PendingToolUse>,
tool_use: ToolUseState,
}
impl Thread {
@@ -89,9 +89,7 @@ impl Thread {
completion_count: 0,
pending_completions: Vec::new(),
tools,
tool_uses_by_message: HashMap::default(),
tool_results_by_message: HashMap::default(),
pending_tool_uses_by_id: HashMap::default(),
tool_use: ToolUseState::new(),
}
}
@@ -101,7 +99,14 @@ impl Thread {
tools: Arc<ToolWorkingSet>,
_cx: &mut Context<Self>,
) -> Self {
let next_message_id = MessageId(saved.messages.len());
let next_message_id = MessageId(
saved
.messages
.last()
.map(|message| message.id.0 + 1)
.unwrap_or(0),
);
let tool_use = ToolUseState::from_saved_messages(&saved.messages);
Self {
id,
@@ -123,9 +128,7 @@ impl Thread {
completion_count: 0,
pending_completions: Vec::new(),
tools,
tool_uses_by_message: HashMap::default(),
tool_results_by_message: HashMap::default(),
pending_tool_uses_by_id: HashMap::default(),
tool_use,
}
}
@@ -187,7 +190,19 @@ impl Thread {
}
pub fn pending_tool_uses(&self) -> Vec<&PendingToolUse> {
self.pending_tool_uses_by_id.values().collect()
self.tool_use.pending_tool_uses()
}
pub fn tool_uses_for_message(&self, id: MessageId) -> Vec<ToolUse> {
self.tool_use.tool_uses_for_message(id)
}
pub fn tool_results_for_message(&self, id: MessageId) -> Vec<&LanguageModelToolResult> {
self.tool_use.tool_results_for_message(id)
}
pub fn message_has_tool_results(&self, message_id: MessageId) -> bool {
self.tool_use.message_has_tool_results(message_id)
}
pub fn insert_user_message(
@@ -220,6 +235,34 @@ impl Thread {
id
}
pub fn edit_message(
&mut self,
id: MessageId,
new_role: Role,
new_text: String,
cx: &mut Context<Self>,
) -> bool {
let Some(message) = self.messages.iter_mut().find(|message| message.id == id) else {
return false;
};
message.role = new_role;
message.text = new_text;
self.touch_updated_at();
cx.emit(ThreadEvent::MessageEdited(id));
true
}
pub fn delete_message(&mut self, id: MessageId, cx: &mut Context<Self>) -> bool {
let Some(index) = self.messages.iter().position(|message| message.id == id) else {
return false;
};
self.messages.remove(index);
self.context_by_message.remove(&id);
self.touch_updated_at();
cx.emit(ThreadEvent::MessageDeleted(id));
true
}
/// Returns the representation of this [`Thread`] in a textual form.
///
/// This is the representation we use when attaching a thread as context to another thread.
@@ -241,9 +284,34 @@ impl Thread {
text
}
pub fn send_to_model(
&mut self,
model: Arc<dyn LanguageModel>,
request_kind: RequestKind,
use_tools: bool,
cx: &mut Context<Self>,
) {
let mut request = self.to_completion_request(request_kind, cx);
if use_tools {
request.tools = self
.tools()
.tools(cx)
.into_iter()
.map(|tool| LanguageModelRequestTool {
name: tool.name(),
description: tool.description(),
input_schema: tool.input_schema(),
})
.collect();
}
self.stream_completion(request, model, cx);
}
pub fn to_completion_request(
&self,
_request_kind: RequestKind,
request_kind: RequestKind,
_cx: &App,
) -> LanguageModelRequest {
let mut request = LanguageModelRequest {
@@ -265,12 +333,13 @@ impl Thread {
content: Vec::new(),
cache: false,
};
if let Some(tool_results) = self.tool_results_by_message.get(&message.id) {
for tool_result in tool_results {
request_message
.content
.push(MessageContent::ToolResult(tool_result.clone()));
match request_kind {
RequestKind::Chat => {
self.tool_use
.attach_tool_results(message.id, &mut request_message);
}
RequestKind::Summarize => {
// We don't care about tool use during summarization.
}
}
@@ -280,11 +349,13 @@ impl Thread {
.push(MessageContent::Text(message.text.clone()));
}
if let Some(tool_uses) = self.tool_uses_by_message.get(&message.id) {
for tool_use in tool_uses {
request_message
.content
.push(MessageContent::ToolUse(tool_use.clone()));
match request_kind {
RequestKind::Chat => {
self.tool_use
.attach_tool_uses(message.id, &mut request_message);
}
RequestKind::Summarize => {
// We don't care about tool use during summarization.
}
}
@@ -360,21 +431,8 @@ impl Thread {
.rfind(|message| message.role == Role::Assistant)
{
thread
.tool_uses_by_message
.entry(last_assistant_message.id)
.or_default()
.push(tool_use.clone());
thread.pending_tool_uses_by_id.insert(
tool_use.id.clone(),
PendingToolUse {
assistant_message_id: last_assistant_message.id,
id: tool_use.id,
name: tool_use.name,
input: tool_use.input,
status: PendingToolUseStatus::Idle,
},
);
.tool_use
.request_tool_use(last_assistant_message.id, tool_use);
}
}
}
@@ -451,7 +509,7 @@ impl Thread {
return;
}
let mut request = self.to_completion_request(RequestKind::Chat, cx);
let mut request = self.to_completion_request(RequestKind::Summarize, cx);
request.messages.push(LanguageModelRequestMessage {
role: Role::User,
content: vec![
@@ -494,7 +552,6 @@ impl Thread {
pub fn insert_tool_output(
&mut self,
assistant_message_id: MessageId,
tool_use_id: LanguageModelToolUseId,
output: Task<Result<String>>,
cx: &mut Context<Self>,
@@ -505,50 +562,18 @@ impl Thread {
let output = output.await;
thread
.update(&mut cx, |thread, cx| {
// The tool use was requested by an Assistant message,
// so we want to attach the tool results to the next
// user message.
let next_user_message = MessageId(assistant_message_id.0 + 1);
thread
.tool_use
.insert_tool_output(tool_use_id.clone(), output);
let tool_results = thread
.tool_results_by_message
.entry(next_user_message)
.or_default();
match output {
Ok(output) => {
tool_results.push(LanguageModelToolResult {
tool_use_id: tool_use_id.to_string(),
content: output,
is_error: false,
});
cx.emit(ThreadEvent::ToolFinished { tool_use_id });
}
Err(err) => {
tool_results.push(LanguageModelToolResult {
tool_use_id: tool_use_id.to_string(),
content: err.to_string(),
is_error: true,
});
if let Some(tool_use) =
thread.pending_tool_uses_by_id.get_mut(&tool_use_id)
{
tool_use.status = PendingToolUseStatus::Error(err.to_string());
}
}
}
cx.emit(ThreadEvent::ToolFinished { tool_use_id });
})
.ok();
}
});
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
tool_use.status = PendingToolUseStatus::Running {
_task: insert_output_task.shared(),
};
}
self.tool_use
.run_pending_tool(tool_use_id, insert_output_task);
}
/// Cancels the last pending completion, if there are any pending.
@@ -576,6 +601,8 @@ pub enum ThreadEvent {
StreamedCompletion,
StreamedAssistantText(MessageId, String),
MessageAdded(MessageId),
MessageEdited(MessageId),
MessageDeleted(MessageId),
SummaryChanged,
UsePendingTools,
ToolFinished {
@@ -590,26 +617,3 @@ struct PendingCompletion {
id: usize,
_task: Task<()>,
}
#[derive(Debug, Clone)]
pub struct PendingToolUse {
pub id: LanguageModelToolUseId,
/// The ID of the Assistant message in which the tool use was requested.
pub assistant_message_id: MessageId,
pub name: String,
pub input: serde_json::Value,
pub status: PendingToolUseStatus,
}
#[derive(Debug, Clone)]
pub enum PendingToolUseStatus {
Idle,
Running { _task: Shared<Task<()>> },
Error(#[allow(unused)] String),
}
impl PendingToolUseStatus {
pub fn is_idle(&self) -> bool {
matches!(self, PendingToolUseStatus::Idle)
}
}

View File

@@ -33,9 +33,9 @@ impl ThreadHistory {
}
}
pub fn select_prev(
pub fn select_previous(
&mut self,
_: &menu::SelectPrev,
_: &menu::SelectPrevious,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -134,7 +134,13 @@ impl ThreadHistory {
})
.ok();
}
HistoryEntry::Context(_context) => {}
HistoryEntry::Context(context) => {
self.assistant_panel
.update(cx, |this, cx| {
this.delete_context(context.path.clone(), cx);
})
.ok();
}
}
cx.notify();
@@ -160,7 +166,7 @@ impl Render for ThreadHistory {
.overflow_y_scroll()
.size_full()
.p_1()
.on_action(cx.listener(Self::select_prev))
.on_action(cx.listener(Self::select_previous))
.on_action(cx.listener(Self::select_next))
.on_action(cx.listener(Self::select_first))
.on_action(cx.listener(Self::select_last))
@@ -248,18 +254,28 @@ impl RenderOnce for PastThread {
);
ListItem::new(SharedString::from(self.thread.id.to_string()))
.outlined()
.rounded()
.toggle_state(self.selected)
.start_slot(
Icon::new(IconName::MessageCircle)
.size(IconSize::Small)
.color(Color::Muted),
)
.spacing(ListItemSpacing::Sparse)
.child(Label::new(summary).size(LabelSize::Small).text_ellipsis())
.start_slot(
div()
.max_w_4_5()
.child(Label::new(summary).size(LabelSize::Small).truncate()),
)
.end_slot(
h_flex()
.gap_1p5()
.child(
Label::new("Thread")
.color(Color::Muted)
.size(LabelSize::XSmall),
)
.child(
div()
.size(px(3.))
.rounded_full()
.bg(cx.theme().colors().text_disabled),
)
.child(
Label::new(thread_timestamp)
.color(Color::Muted)
@@ -334,21 +350,50 @@ impl RenderOnce for PastContext {
ListItem::new(SharedString::from(
self.context.path.to_string_lossy().to_string(),
))
.outlined()
.rounded()
.toggle_state(self.selected)
.start_slot(
Icon::new(IconName::Code)
.size(IconSize::Small)
.color(Color::Muted),
)
.spacing(ListItemSpacing::Sparse)
.child(Label::new(summary).size(LabelSize::Small).text_ellipsis())
.start_slot(
div()
.max_w_4_5()
.child(Label::new(summary).size(LabelSize::Small).truncate()),
)
.end_slot(
h_flex().gap_1p5().child(
Label::new(context_timestamp)
.color(Color::Muted)
.size(LabelSize::XSmall),
),
h_flex()
.gap_1p5()
.child(
Label::new("Prompt Editor")
.color(Color::Muted)
.size(LabelSize::XSmall),
)
.child(
div()
.size(px(3.))
.rounded_full()
.bg(cx.theme().colors().text_disabled),
)
.child(
Label::new(context_timestamp)
.color(Color::Muted)
.size(LabelSize::XSmall),
)
.child(
IconButton::new("delete", IconName::TrashAlt)
.shape(IconButtonShape::Square)
.icon_size(IconSize::XSmall)
.tooltip(Tooltip::text("Delete Prompt Editor"))
.on_click({
let assistant_panel = self.assistant_panel.clone();
let path = self.context.path.clone();
move |_event, _window, cx| {
assistant_panel
.update(cx, |this, cx| {
this.delete_context(path.clone(), cx);
})
.ok();
}
}),
),
)
.on_click({
let assistant_panel = self.assistant_panel.clone();

View File

@@ -12,9 +12,9 @@ use futures::FutureExt as _;
use gpui::{
prelude::*, App, BackgroundExecutor, Context, Entity, Global, ReadGlobal, SharedString, Task,
};
use heed::types::SerdeBincode;
use heed::types::{SerdeBincode, SerdeJson};
use heed::Database;
use language_model::Role;
use language_model::{LanguageModelToolUseId, Role};
use project::Project;
use serde::{Deserialize, Serialize};
use util::ResultExt as _;
@@ -113,6 +113,24 @@ impl ThreadStore {
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(),
};
@@ -239,11 +257,29 @@ pub struct SavedThread {
pub messages: Vec<SavedMessage>,
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct SavedMessage {
pub id: MessageId,
pub role: Role,
pub text: String,
#[serde(default)]
pub tool_uses: Vec<SavedToolUse>,
#[serde(default)]
pub tool_results: Vec<SavedToolResult>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SavedToolUse {
pub id: LanguageModelToolUseId,
pub name: SharedString,
pub input: serde_json::Value,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SavedToolResult {
pub tool_use_id: LanguageModelToolUseId,
pub is_error: bool,
pub content: Arc<str>,
}
struct GlobalThreadsDatabase(
@@ -255,7 +291,7 @@ impl Global for GlobalThreadsDatabase {}
pub(crate) struct ThreadsDatabase {
executor: BackgroundExecutor,
env: heed::Env,
threads: Database<SerdeBincode<ThreadId>, SerdeBincode<SavedThread>>,
threads: Database<SerdeBincode<ThreadId>, SerdeJson<SavedThread>>,
}
impl ThreadsDatabase {
@@ -270,7 +306,7 @@ impl ThreadsDatabase {
let database_future = executor
.spawn({
let executor = executor.clone();
let database_path = paths::support_dir().join("threads/threads-db.0.mdb");
let database_path = paths::support_dir().join("threads/threads-db.1.mdb");
async move { ThreadsDatabase::new(database_path, executor) }
})
.then(|result| future::ready(result.map(Arc::new).map_err(Arc::new)))

View File

@@ -0,0 +1,291 @@
use std::sync::Arc;
use anyhow::Result;
use collections::HashMap;
use futures::future::Shared;
use futures::FutureExt as _;
use gpui::{SharedString, Task};
use language_model::{
LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse,
LanguageModelToolUseId, MessageContent, Role,
};
use crate::thread::MessageId;
use crate::thread_store::SavedMessage;
#[derive(Debug)]
pub struct ToolUse {
pub id: LanguageModelToolUseId,
pub name: SharedString,
pub status: ToolUseStatus,
pub input: serde_json::Value,
}
#[derive(Debug, Clone)]
pub enum ToolUseStatus {
Pending,
Running,
Finished(SharedString),
Error(SharedString),
}
pub struct ToolUseState {
tool_uses_by_assistant_message: HashMap<MessageId, Vec<LanguageModelToolUse>>,
tool_uses_by_user_message: HashMap<MessageId, Vec<LanguageModelToolUseId>>,
tool_results: HashMap<LanguageModelToolUseId, LanguageModelToolResult>,
pending_tool_uses_by_id: HashMap<LanguageModelToolUseId, PendingToolUse>,
}
impl ToolUseState {
pub fn new() -> Self {
Self {
tool_uses_by_assistant_message: HashMap::default(),
tool_uses_by_user_message: HashMap::default(),
tool_results: HashMap::default(),
pending_tool_uses_by_id: HashMap::default(),
}
}
pub fn from_saved_messages(messages: &[SavedMessage]) -> Self {
let mut this = Self::new();
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
.iter()
.map(|tool_use| LanguageModelToolUse {
id: tool_use.id.clone(),
name: tool_use.name.clone().into(),
input: tool_use.input.clone(),
})
.collect(),
);
}
}
Role::User => {
if !message.tool_results.is_empty() {
let tool_uses_by_user_message = this
.tool_uses_by_user_message
.entry(message.id)
.or_default();
for tool_result in &message.tool_results {
let tool_use_id = tool_result.tool_use_id.clone();
tool_uses_by_user_message.push(tool_use_id.clone());
this.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id,
is_error: tool_result.is_error,
content: tool_result.content.clone(),
},
);
}
}
}
Role::System => {}
}
}
this
}
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> {
let Some(tool_uses_for_message) = &self.tool_uses_by_assistant_message.get(&id) else {
return Vec::new();
};
let mut tool_uses = Vec::new();
for tool_use in tool_uses_for_message.iter() {
let tool_result = self.tool_results.get(&tool_use.id);
let status = (|| {
if let Some(tool_result) = tool_result {
return if tool_result.is_error {
ToolUseStatus::Error(tool_result.content.clone().into())
} else {
ToolUseStatus::Finished(tool_result.content.clone().into())
};
}
if let Some(pending_tool_use) = self.pending_tool_uses_by_id.get(&tool_use.id) {
return match pending_tool_use.status {
PendingToolUseStatus::Idle => ToolUseStatus::Pending,
PendingToolUseStatus::Running { .. } => ToolUseStatus::Running,
PendingToolUseStatus::Error(ref err) => {
ToolUseStatus::Error(err.clone().into())
}
};
}
ToolUseStatus::Pending
})();
tool_uses.push(ToolUse {
id: tool_use.id.clone(),
name: tool_use.name.clone().into(),
input: tool_use.input.clone(),
status,
})
}
tool_uses
}
pub fn tool_results_for_message(&self, message_id: MessageId) -> Vec<&LanguageModelToolResult> {
let empty = Vec::new();
self.tool_uses_by_user_message
.get(&message_id)
.unwrap_or(&empty)
.iter()
.filter_map(|tool_use_id| self.tool_results.get(&tool_use_id))
.collect()
}
pub fn message_has_tool_results(&self, message_id: MessageId) -> bool {
self.tool_uses_by_user_message
.get(&message_id)
.map_or(false, |results| !results.is_empty())
}
pub fn request_tool_use(
&mut self,
assistant_message_id: MessageId,
tool_use: LanguageModelToolUse,
) {
self.tool_uses_by_assistant_message
.entry(assistant_message_id)
.or_default()
.push(tool_use.clone());
// The tool use is being requested by the Assistant, so we want to
// attach the tool results to the next user message.
let next_user_message_id = MessageId(assistant_message_id.0 + 1);
self.tool_uses_by_user_message
.entry(next_user_message_id)
.or_default()
.push(tool_use.id.clone());
self.pending_tool_uses_by_id.insert(
tool_use.id.clone(),
PendingToolUse {
assistant_message_id,
id: tool_use.id,
name: tool_use.name,
input: tool_use.input,
status: PendingToolUseStatus::Idle,
},
);
}
pub fn run_pending_tool(&mut self, tool_use_id: LanguageModelToolUseId, task: Task<()>) {
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
tool_use.status = PendingToolUseStatus::Running {
_task: task.shared(),
};
}
}
pub fn insert_tool_output(
&mut self,
tool_use_id: LanguageModelToolUseId,
output: Result<String>,
) {
match output {
Ok(output) => {
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
content: output.into(),
is_error: false,
},
);
self.pending_tool_uses_by_id.remove(&tool_use_id);
}
Err(err) => {
self.tool_results.insert(
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
content: err.to_string().into(),
is_error: true,
},
);
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());
}
}
}
}
pub fn attach_tool_uses(
&self,
message_id: MessageId,
request_message: &mut LanguageModelRequestMessage,
) {
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()));
}
}
}
pub fn attach_tool_results(
&self,
message_id: MessageId,
request_message: &mut LanguageModelRequestMessage,
) {
if let Some(tool_uses) = self.tool_uses_by_user_message.get(&message_id) {
for tool_use_id in tool_uses {
if let Some(tool_result) = self.tool_results.get(tool_use_id) {
request_message
.content
.push(MessageContent::ToolResult(tool_result.clone()));
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct PendingToolUse {
pub id: LanguageModelToolUseId,
/// The ID of the Assistant message in which the tool use was requested.
pub assistant_message_id: MessageId,
pub name: Arc<str>,
pub input: serde_json::Value,
pub status: PendingToolUseStatus,
}
#[derive(Debug, Clone)]
pub enum PendingToolUseStatus {
Idle,
Running { _task: Shared<Task<()>> },
Error(#[allow(unused)] Arc<str>),
}
impl PendingToolUseStatus {
pub fn is_idle(&self) -> bool {
matches!(self, PendingToolUseStatus::Idle)
}
pub fn is_error(&self) -> bool {
matches!(self, PendingToolUseStatus::Error(_))
}
}

View File

@@ -103,7 +103,7 @@ impl RenderOnce for ContextPill {
.pl_1()
.pb(px(1.))
.border_1()
.rounded_md()
.rounded_sm()
.gap_1()
.child(self.icon().size(IconSize::XSmall).color(Color::Muted));

View File

@@ -37,7 +37,7 @@ parking_lot.workspace = true
paths.workspace = true
picker.workspace = true
project.workspace = true
prompt_library.workspace = true
prompt_store.workspace = true
regex.workspace = true
rope.workspace = true
rpc.workspace = true

View File

@@ -27,7 +27,7 @@ use language_model::{
use open_ai::Model as OpenAiModel;
use paths::contexts_dir;
use project::Project;
use prompt_library::PromptBuilder;
use prompt_store::PromptBuilder;
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use std::{

View File

@@ -20,7 +20,7 @@ use language_model::{LanguageModelCacheConfiguration, LanguageModelRegistry, Rol
use parking_lot::Mutex;
use pretty_assertions::assert_eq;
use project::Project;
use prompt_library::PromptBuilder;
use prompt_store::PromptBuilder;
use rand::prelude::*;
use serde_json::json;
use settings::SettingsStore;
@@ -671,7 +671,7 @@ async fn test_slash_commands(cx: &mut TestAppContext) {
#[gpui::test]
async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
cx.update(prompt_library::init);
cx.update(prompt_store::init);
let mut settings_store = cx.update(SettingsStore::test);
cx.update(|cx| {
settings_store

View File

@@ -29,19 +29,24 @@ use gpui::{
WeakEntity,
};
use indexed_docs::IndexedDocsStore;
use language::{language_settings::SoftWrap, BufferSnapshot, LspAdapterDelegate, ToOffset};
use language::{
language_settings::{all_language_settings, SoftWrap},
BufferSnapshot, LspAdapterDelegate, ToOffset,
};
use language_model::{
LanguageModelImage, LanguageModelProvider, LanguageModelProviderTosView, LanguageModelRegistry,
Role,
};
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
use language_model_selector::{
LanguageModelSelector, LanguageModelSelectorPopoverMenu, ToggleModelSelector,
};
use multi_buffer::MultiBufferRow;
use picker::Picker;
use project::lsp_store::LocalLspAdapterDelegate;
use project::{Project, Worktree};
use rope::Point;
use serde::{Deserialize, Serialize};
use settings::{update_settings_file, Settings};
use settings::{update_settings_file, Settings, SettingsStore};
use std::{any::TypeId, borrow::Cow, cmp, ops::Range, path::PathBuf, sync::Arc, time::Duration};
use text::SelectionGoal;
use ui::{
@@ -49,7 +54,7 @@ use ui::{
Tooltip,
};
use util::{maybe, ResultExt};
use workspace::searchable::SearchableItemHandle;
use workspace::searchable::{Direction, SearchableItemHandle};
use workspace::{
item::{self, FollowableItem, Item, ItemHandle},
notifications::NotificationId,
@@ -77,7 +82,6 @@ actions!(
InsertIntoEditor,
QuoteSelection,
Split,
ToggleModelSelector,
]
);
@@ -230,6 +234,13 @@ impl ContextEditor {
editor.set_completion_provider(Some(Box::new(completion_provider)));
editor.set_menu_inline_completions_policy(MenuInlineCompletionsPolicy::Never);
editor.set_collaboration_hub(Box::new(project.clone()));
let show_edit_predictions = all_language_settings(None, cx)
.edit_predictions
.enabled_in_assistant;
editor.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
editor
});
@@ -238,24 +249,9 @@ impl ContextEditor {
cx.subscribe_in(&context, window, Self::handle_context_event),
cx.subscribe_in(&editor, window, Self::handle_editor_event),
cx.subscribe_in(&editor, window, Self::handle_editor_search_event),
cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
];
let fs_clone = fs.clone();
let language_model_selector = cx.new(|cx| {
LanguageModelSelector::new(
move |model, cx| {
update_settings_file::<AssistantSettings>(
fs_clone.clone(),
cx,
move |settings, _| settings.set_model(model.clone()),
);
},
window,
cx,
)
});
let language_model_selector_menu_handle = PopoverMenuHandle::default();
let sections = context.read(cx).slash_command_output_sections().to_vec();
let patch_ranges = context.read(cx).patch_ranges().collect::<Vec<_>>();
let slash_commands = context.read(cx).slash_commands().clone();
@@ -268,7 +264,7 @@ impl ContextEditor {
image_blocks: Default::default(),
scroll_position: None,
remote_id: None,
fs,
fs: fs.clone(),
workspace,
project,
pending_slash_command_creases: HashMap::default(),
@@ -280,8 +276,20 @@ impl ContextEditor {
show_accept_terms: false,
slash_menu_handle: Default::default(),
dragged_file_worktrees: Vec::new(),
language_model_selector,
language_model_selector_menu_handle,
language_model_selector: cx.new(|cx| {
LanguageModelSelector::new(
move |model, cx| {
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _| settings.set_model(model.clone()),
);
},
window,
cx,
)
}),
language_model_selector_menu_handle: PopoverMenuHandle::default(),
};
this.update_message_headers(cx);
this.update_image_blocks(cx);
@@ -290,6 +298,16 @@ impl ContextEditor {
this
}
fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.editor.update(cx, |editor, cx| {
let show_edit_predictions = all_language_settings(None, cx)
.edit_predictions
.enabled_in_assistant;
editor.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
});
}
pub fn context(&self) -> &Entity<AssistantContext> {
&self.context
}
@@ -1223,7 +1241,7 @@ impl ContextEditor {
.child("Press")
.child(
h_flex()
.rounded_md()
.rounded_sm()
.px_1()
.mr_0p5()
.border_1()
@@ -2024,15 +2042,6 @@ impl ContextEditor {
});
}
fn toggle_model_selector(
&mut self,
_: &ToggleModelSelector,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.language_model_selector_menu_handle.toggle(window, cx);
}
fn save(&mut self, _: &Save, _window: &mut Window, cx: &mut Context<Self>) {
self.context.update(cx, |context, cx| {
context.save(Some(Duration::from_millis(500)), self.fs.clone(), cx)
@@ -2083,7 +2092,7 @@ impl ContextEditor {
.ml(gutter_width)
.pb_1()
.w(max_width - gutter_width)
.rounded_md()
.rounded_sm()
.border_1()
.border_color(theme.colors().border_variant)
.overflow_hidden()
@@ -2864,6 +2873,7 @@ impl Render for ContextEditor {
None
};
let language_model_selector = self.language_model_selector_menu_handle.clone();
v_flex()
.key_context("ContextEditor")
.capture_action(cx.listener(ContextEditor::cancel))
@@ -2876,7 +2886,9 @@ impl Render for ContextEditor {
.on_action(cx.listener(ContextEditor::edit))
.on_action(cx.listener(ContextEditor::assist))
.on_action(cx.listener(ContextEditor::split))
.on_action(cx.listener(ContextEditor::toggle_model_selector))
.on_action(move |_: &ToggleModelSelector, window, cx| {
language_model_selector.toggle(window, cx);
})
.size_full()
.children(self.render_notice(cx))
.child(
@@ -3094,12 +3106,13 @@ impl SearchableItem for ContextEditor {
fn active_match_index(
&mut self,
direction: Direction,
matches: &[Self::Match],
window: &mut Window,
cx: &mut Context<Self>,
) -> Option<usize> {
self.editor.update(cx, |editor, cx| {
editor.active_match_index(matches, window, cx)
editor.active_match_index(direction, matches, window, cx)
})
}
}
@@ -3409,7 +3422,7 @@ fn invoked_slash_command_fold_placeholder(
.ml_6()
.gap_2()
.bg(cx.theme().colors().surface_background)
.rounded_md()
.rounded_sm()
.child(Label::new(format!("/{}", command.name.clone())))
.map(|parent| match &command.status {
InvokedSlashCommandStatus::Running(_) => {

View File

@@ -9,14 +9,14 @@ use clock::ReplicaId;
use collections::HashMap;
use context_server::manager::ContextServerManager;
use context_server::ContextServerFactoryRegistry;
use fs::Fs;
use fs::{Fs, RemoveOptions};
use futures::StreamExt;
use fuzzy::StringMatchCandidate;
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, WeakEntity};
use language::LanguageRegistry;
use paths::contexts_dir;
use project::Project;
use prompt_library::PromptBuilder;
use prompt_store::PromptBuilder;
use regex::Regex;
use rpc::AnyProtoClient;
use std::sync::LazyLock;
@@ -475,6 +475,38 @@ impl ContextStore {
})
}
pub fn delete_local_context(
&mut self,
path: PathBuf,
cx: &mut Context<Self>,
) -> Task<Result<()>> {
let fs = self.fs.clone();
cx.spawn(|this, mut cx| async move {
fs.remove_file(
&path,
RemoveOptions {
recursive: false,
ignore_if_not_exists: true,
},
)
.await?;
this.update(&mut cx, |this, cx| {
this.contexts.retain(|context| {
context
.upgrade()
.and_then(|context| context.read(cx).path())
!= Some(&path)
});
this.contexts_metadata
.retain(|context| context.path != path);
})?;
Ok(())
})
}
fn loaded_context_for_path(&self, path: &Path, cx: &App) -> Option<Entity<AssistantContext>> {
self.contexts.iter().find_map(|context| {
let context = context.upgrade()?;

View File

@@ -140,7 +140,7 @@ impl ResolvedPatch {
buffer.edit(
edits,
Some(AutoindentMode::Block {
original_start_columns: Vec::new(),
original_indent_columns: Vec::new(),
}),
cx,
);

View File

@@ -207,24 +207,31 @@ impl PickerDelegate for SlashCommandDelegate {
.child(
h_flex()
.gap_1p5()
.child(Icon::new(info.icon).size(IconSize::XSmall))
.child(div().font_buffer(cx).child({
.child(
Icon::new(info.icon)
.size(IconSize::XSmall)
.color(Color::Muted),
)
.child({
let mut label = format!("{}", info.name);
if let Some(args) = info.args.as_ref().filter(|_| selected)
{
label.push_str(&args);
}
Label::new(label).single_line().size(LabelSize::Small)
}))
Label::new(label)
.single_line()
.size(LabelSize::Small)
.buffer_font(cx)
})
.children(info.args.clone().filter(|_| !selected).map(
|args| {
div()
.font_buffer(cx)
.child(
Label::new(args)
.single_line()
.size(LabelSize::Small)
.color(Color::Muted),
.color(Color::Muted)
.buffer_font(cx),
)
.visible_on_hover(format!(
"command-entry-label-{ix}"
@@ -236,7 +243,7 @@ impl PickerDelegate for SlashCommandDelegate {
Label::new(info.description.clone())
.size(LabelSize::Small)
.color(Color::Muted)
.text_ellipsis(),
.truncate(),
),
),
),
@@ -294,10 +301,9 @@ where
.gap_1p5()
.child(Icon::new(IconName::Plus).size(IconSize::XSmall))
.child(
div().font_buffer(cx).child(
Label::new("create-your-command")
.size(LabelSize::Small),
),
Label::new("create-your-command")
.size(LabelSize::Small)
.buffer_font(cx),
),
)
.child(
@@ -341,7 +347,7 @@ where
.anchor(gpui::Corner::BottomLeft)
.offset(gpui::Point {
x: px(0.0),
y: px(-16.0),
y: px(-2.0),
})
.when_some(handle, |this, handle| this.with_handle(handle))
}

View File

@@ -32,7 +32,7 @@ language.workspace = true
language_model.workspace = true
log.workspace = true
project.workspace = true
prompt_library.workspace = true
prompt_store.workspace = true
rope.workspace = true
schemars.workspace = true
semantic_index.workspace = true

View File

@@ -5,7 +5,7 @@ use assistant_slash_command::{
};
use gpui::{Task, WeakEntity};
use language::{BufferSnapshot, LspAdapterDelegate};
use prompt_library::PromptStore;
use prompt_store::PromptStore;
use std::{
fmt::Write,
sync::{atomic::AtomicBool, Arc},
@@ -21,11 +21,11 @@ impl SlashCommand for DefaultSlashCommand {
}
fn description(&self) -> String {
"insert default prompt".into()
"Insert default prompt".into()
}
fn menu_text(&self) -> String {
"Insert Default Prompt".into()
self.description()
}
fn requires_argument(&self) -> bool {

View File

@@ -13,7 +13,7 @@ use feature_flags::FeatureFlag;
use gpui::{App, Task, WeakEntity};
use language::{Anchor, CodeLabel, LspAdapterDelegate};
use language_model::{LanguageModelRegistry, LanguageModelTool};
use prompt_library::PromptBuilder;
use prompt_store::PromptBuilder;
use schemars::JsonSchema;
use semantic_index::SemanticDb;
use serde::Deserialize;

View File

@@ -5,7 +5,7 @@ use assistant_slash_command::{
};
use gpui::{Task, WeakEntity};
use language::{BufferSnapshot, LspAdapterDelegate};
use prompt_library::PromptStore;
use prompt_store::PromptStore;
use std::sync::{atomic::AtomicBool, Arc};
use ui::prelude::*;
use workspace::Workspace;

View File

@@ -16,6 +16,7 @@ anyhow.workspace = true
assistant_tool.workspace = true
chrono.workspace = true
gpui.workspace = true
project.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true

View File

@@ -1,13 +1,19 @@
mod list_worktrees_tool;
mod now_tool;
mod read_file_tool;
use assistant_tool::ToolRegistry;
use gpui::App;
use crate::list_worktrees_tool::ListWorktreesTool;
use crate::now_tool::NowTool;
use crate::read_file_tool::ReadFileTool;
pub fn init(cx: &mut App) {
assistant_tool::init(cx);
let registry = ToolRegistry::global(cx);
registry.register_tool(NowTool);
registry.register_tool(ListWorktreesTool);
registry.register_tool(ReadFileTool);
}

View File

@@ -0,0 +1,84 @@
use std::sync::Arc;
use anyhow::{anyhow, Result};
use assistant_tool::Tool;
use gpui::{App, Task, WeakEntity, Window};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use workspace::Workspace;
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct ListWorktreesToolInput {}
pub struct ListWorktreesTool;
impl Tool for ListWorktreesTool {
fn name(&self) -> String {
"list-worktrees".into()
}
fn description(&self) -> String {
"Lists all worktrees in the current project. Use this tool when you need to find available worktrees and their IDs.".into()
}
fn input_schema(&self) -> serde_json::Value {
serde_json::json!(
{
"type": "object",
"properties": {},
"required": []
}
)
}
fn run(
self: Arc<Self>,
_input: serde_json::Value,
workspace: WeakEntity<Workspace>,
_window: &mut Window,
cx: &mut App,
) -> Task<Result<String>> {
let Some(workspace) = workspace.upgrade() else {
return Task::ready(Err(anyhow!("workspace dropped")));
};
let project = workspace.read(cx).project().clone();
cx.spawn(|cx| async move {
cx.update(|cx| {
#[derive(Debug, Serialize)]
struct WorktreeInfo {
id: usize,
root_name: String,
root_dir: Option<String>,
}
let worktrees = project.update(cx, |project, cx| {
project
.visible_worktrees(cx)
.map(|worktree| {
worktree.read_with(cx, |worktree, _cx| WorktreeInfo {
id: worktree.id().to_usize(),
root_dir: worktree
.root_dir()
.map(|root_dir| root_dir.to_string_lossy().to_string()),
root_name: worktree.root_name().to_string(),
})
})
.collect::<Vec<_>>()
});
if worktrees.is_empty() {
return Ok("No worktrees found in the current project.".to_string());
}
let mut result = String::from("Worktrees in the current project:\n\n");
for worktree in worktrees {
result.push_str(&serde_json::to_string(&worktree)?);
}
Ok(result)
})?
})
}
}

View File

@@ -0,0 +1,69 @@
use std::path::Path;
use std::sync::Arc;
use anyhow::{anyhow, Result};
use assistant_tool::Tool;
use gpui::{App, Task, WeakEntity, Window};
use project::{ProjectPath, WorktreeId};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use workspace::Workspace;
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct ReadFileToolInput {
/// The ID of the worktree in which the file resides.
pub worktree_id: usize,
/// The path to the file to read.
///
/// This path is relative to the worktree root, it must not be an absolute path.
pub path: Arc<Path>,
}
pub struct ReadFileTool;
impl Tool for ReadFileTool {
fn name(&self) -> String {
"read-file".into()
}
fn description(&self) -> String {
"Reads the content of a file specified by a worktree ID and path. Use this tool when you need to access the contents of a file in the project.".into()
}
fn input_schema(&self) -> serde_json::Value {
let schema = schemars::schema_for!(ReadFileToolInput);
serde_json::to_value(&schema).unwrap()
}
fn run(
self: Arc<Self>,
input: serde_json::Value,
workspace: WeakEntity<Workspace>,
_window: &mut Window,
cx: &mut App,
) -> Task<Result<String>> {
let Some(workspace) = workspace.upgrade() else {
return Task::ready(Err(anyhow!("workspace dropped")));
};
let input = match serde_json::from_value::<ReadFileToolInput>(input) {
Ok(input) => input,
Err(err) => return Task::ready(Err(anyhow!(err))),
};
let project = workspace.read(cx).project().clone();
let project_path = ProjectPath {
worktree_id: WorktreeId::from_usize(input.worktree_id),
path: input.path,
};
cx.spawn(|cx| async move {
let buffer = cx
.update(|cx| {
project.update(cx, |project, cx| project.open_buffer(project_path, cx))
})?
.await?;
cx.update(|cx| buffer.read(cx).text())
})
}
}

View File

@@ -141,19 +141,20 @@ pub fn notify_if_app_was_updated(cx: &mut App) {
cx,
move |cx| {
let workspace_handle = cx.entity().downgrade();
cx.new(|_cx| {
MessageNotification::new(format!("Updated to {app_name} {}", version))
.primary_message("View Release Notes")
.primary_on_click(move |window, cx| {
if let Some(workspace) = workspace_handle.upgrade() {
workspace.update(cx, |workspace, cx| {
crate::view_release_notes_locally(
workspace, window, cx,
);
})
}
cx.emit(DismissEvent);
})
cx.new(|cx| {
MessageNotification::new(
format!("Updated to {app_name} {}", version),
cx,
)
.primary_message("View Release Notes")
.primary_on_click(move |window, cx| {
if let Some(workspace) = workspace_handle.upgrade() {
workspace.update(cx, |workspace, cx| {
crate::view_release_notes_locally(workspace, window, cx);
})
}
cx.emit(DismissEvent);
})
})
},
);

View File

@@ -81,8 +81,8 @@ impl Render for Breadcrumbs {
}
text_style.color = Color::Muted.color(cx);
StyledText::new(segment.text.replace('\n', ""))
.with_highlights(&text_style, segment.highlights.unwrap_or_default())
StyledText::new(segment.text.replace('\n', ""))
.with_default_highlights(&text_style, segment.highlights.unwrap_or_default())
.into_any()
});
let breadcrumbs = Itertools::intersperse_with(highlighted_segments, || {

View File

@@ -16,6 +16,7 @@ test-support = []
[dependencies]
anyhow.workspace = true
clock.workspace = true
futures.workspace = true
git2.workspace = true
gpui.workspace = true

View File

@@ -1,11 +1,12 @@
use futures::{channel::oneshot, future::OptionFuture};
use futures::channel::oneshot;
use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch};
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter};
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task};
use language::{Language, LanguageRegistry};
use rope::Rope;
use std::cmp::Ordering;
use std::mem;
use std::{future::Future, iter, ops::Range, sync::Arc};
use sum_tree::SumTree;
use sum_tree::{SumTree, TreeMap};
use text::ToOffset as _;
use text::{Anchor, Bias, BufferId, OffsetRangeExt, Point};
use util::ResultExt;
@@ -20,13 +21,14 @@ pub struct BufferDiff {
pub struct BufferDiffSnapshot {
inner: BufferDiffInner,
secondary_diff: Option<Box<BufferDiffSnapshot>>,
pub is_single_insertion: bool,
}
#[derive(Clone)]
struct BufferDiffInner {
hunks: SumTree<InternalDiffHunk>,
base_text: Option<language::BufferSnapshot>,
pending_hunks: TreeMap<usize, PendingHunk>,
base_text: language::BufferSnapshot,
base_text_exists: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -47,23 +49,15 @@ pub enum DiffHunkSecondaryStatus {
HasSecondaryHunk,
OverlapsWithSecondaryHunk,
None,
}
impl DiffHunkSecondaryStatus {
pub fn is_secondary(&self) -> bool {
match self {
DiffHunkSecondaryStatus::HasSecondaryHunk => true,
DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk => true,
DiffHunkSecondaryStatus::None => false,
}
}
SecondaryHunkAdditionPending,
SecondaryHunkRemovalPending,
}
/// A diff hunk resolved to rows in the buffer.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DiffHunk {
/// The buffer range, expressed in terms of rows.
pub row_range: Range<u32>,
/// The buffer range as points.
pub range: Range<Point>,
/// The range in the buffer to which this hunk corresponds.
pub buffer_range: Range<Anchor>,
/// The range in the buffer's diff base text to which this hunk corresponds.
@@ -78,6 +72,17 @@ struct InternalDiffHunk {
diff_base_byte_range: Range<usize>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct PendingHunk {
buffer_version: clock::Global,
new_status: DiffHunkSecondaryStatus,
}
#[derive(Debug, Default, Clone)]
pub struct DiffHunkSummary {
buffer_range: Range<Anchor>,
}
impl sum_tree::Item for InternalDiffHunk {
type Summary = DiffHunkSummary;
@@ -88,11 +93,6 @@ impl sum_tree::Item for InternalDiffHunk {
}
}
#[derive(Debug, Default, Clone)]
pub struct DiffHunkSummary {
buffer_range: Range<Anchor>,
}
impl sum_tree::Summary for DiffHunkSummary {
type Context = text::BufferSnapshot;
@@ -109,7 +109,7 @@ impl sum_tree::Summary for DiffHunkSummary {
}
}
impl<'a> sum_tree::SeekTarget<'a, DiffHunkSummary, DiffHunkSummary> for Anchor {
impl sum_tree::SeekTarget<'_, DiffHunkSummary, DiffHunkSummary> for Anchor {
fn cmp(&self, cursor_location: &DiffHunkSummary, buffer: &text::BufferSnapshot) -> Ordering {
if self
.cmp(&cursor_location.buffer_range.start, buffer)
@@ -159,117 +159,166 @@ impl BufferDiffSnapshot {
self.inner.hunks_intersecting_range_rev(range, buffer)
}
pub fn base_text(&self) -> Option<&language::BufferSnapshot> {
self.inner.base_text.as_ref()
pub fn base_text(&self) -> &language::BufferSnapshot {
&self.inner.base_text
}
pub fn base_texts_eq(&self, other: &Self) -> bool {
match (other.base_text(), self.base_text()) {
(None, None) => true,
(None, Some(_)) => false,
(Some(_), None) => false,
(Some(old), Some(new)) => {
let (old_id, old_empty) = (old.remote_id(), old.is_empty());
let (new_id, new_empty) = (new.remote_id(), new.is_empty());
new_id == old_id || (new_empty && old_empty)
}
if self.inner.base_text_exists != other.inner.base_text_exists {
return false;
}
let left = &self.inner.base_text;
let right = &other.inner.base_text;
let (old_id, old_empty) = (left.remote_id(), left.is_empty());
let (new_id, new_empty) = (right.remote_id(), right.is_empty());
new_id == old_id || (new_empty && old_empty)
}
}
pub fn new_secondary_text_for_stage_or_unstage(
&self,
impl BufferDiffInner {
fn stage_or_unstage_hunks(
&mut self,
unstaged_diff: &Self,
stage: bool,
hunks: impl Iterator<Item = (Range<Anchor>, Range<usize>)>,
hunks: &[DiffHunk],
buffer: &text::BufferSnapshot,
cx: &mut App,
) -> Option<Rope> {
let secondary_diff = self.secondary_diff()?;
let index_base = if let Some(index_base) = secondary_diff.base_text() {
index_base.text.as_rope().clone()
} else if stage {
Rope::from("")
} else {
return None;
};
let head_base = self.base_text().map_or_else(
|| Rope::from(""),
|snapshot| snapshot.text.as_rope().clone(),
);
file_exists: bool,
) -> (Option<Rope>, Vec<(usize, PendingHunk)>) {
let head_text = self
.base_text_exists
.then(|| self.base_text.as_rope().clone());
let index_text = unstaged_diff
.base_text_exists
.then(|| unstaged_diff.base_text.as_rope().clone());
let mut secondary_cursor = secondary_diff.inner.hunks.cursor::<DiffHunkSummary>(buffer);
secondary_cursor.next(buffer);
// If the file doesn't exist in either HEAD or the index, then the
// entire file must be either created or deleted in the index.
let (index_text, head_text) = match (index_text, head_text) {
(Some(index_text), Some(head_text)) if file_exists || !stage => (index_text, head_text),
(_, head_text @ _) => {
if stage {
log::debug!("stage all");
return (
file_exists.then(|| buffer.as_rope().clone()),
vec![(
0,
PendingHunk {
buffer_version: buffer.version().clone(),
new_status: DiffHunkSecondaryStatus::SecondaryHunkRemovalPending,
},
)],
);
} else {
log::debug!("unstage all");
return (
head_text,
vec![(
0,
PendingHunk {
buffer_version: buffer.version().clone(),
new_status: DiffHunkSecondaryStatus::SecondaryHunkAdditionPending,
},
)],
);
}
}
};
let mut unstaged_hunk_cursor = unstaged_diff.hunks.cursor::<DiffHunkSummary>(buffer);
unstaged_hunk_cursor.next(buffer);
let mut edits = Vec::new();
let mut prev_secondary_hunk_buffer_offset = 0;
let mut prev_secondary_hunk_base_text_offset = 0;
for (buffer_range, diff_base_byte_range) in hunks {
let skipped_hunks = secondary_cursor.slice(&buffer_range.start, Bias::Left, buffer);
let mut pending_hunks = Vec::new();
let mut prev_unstaged_hunk_buffer_offset = 0;
let mut prev_unstaged_hunk_base_text_offset = 0;
for DiffHunk {
buffer_range,
diff_base_byte_range,
secondary_status,
..
} in hunks.iter().cloned()
{
if (stage && secondary_status == DiffHunkSecondaryStatus::None)
|| (!stage && secondary_status == DiffHunkSecondaryStatus::HasSecondaryHunk)
{
continue;
}
let skipped_hunks = unstaged_hunk_cursor.slice(&buffer_range.start, Bias::Left, buffer);
if let Some(secondary_hunk) = skipped_hunks.last() {
prev_secondary_hunk_base_text_offset = secondary_hunk.diff_base_byte_range.end;
prev_secondary_hunk_buffer_offset =
prev_unstaged_hunk_base_text_offset = secondary_hunk.diff_base_byte_range.end;
prev_unstaged_hunk_buffer_offset =
secondary_hunk.buffer_range.end.to_offset(buffer);
}
let mut buffer_offset_range = buffer_range.to_offset(buffer);
let start_overshoot = buffer_offset_range.start - prev_secondary_hunk_buffer_offset;
let mut secondary_base_text_start =
prev_secondary_hunk_base_text_offset + start_overshoot;
let start_overshoot = buffer_offset_range.start - prev_unstaged_hunk_buffer_offset;
let mut index_start = prev_unstaged_hunk_base_text_offset + start_overshoot;
while let Some(secondary_hunk) = secondary_cursor.item().filter(|item| {
while let Some(unstaged_hunk) = unstaged_hunk_cursor.item().filter(|item| {
item.buffer_range
.start
.cmp(&buffer_range.end, buffer)
.is_le()
}) {
let secondary_hunk_offset_range = secondary_hunk.buffer_range.to_offset(buffer);
prev_secondary_hunk_base_text_offset = secondary_hunk.diff_base_byte_range.end;
prev_secondary_hunk_buffer_offset = secondary_hunk_offset_range.end;
let unstaged_hunk_offset_range = unstaged_hunk.buffer_range.to_offset(buffer);
prev_unstaged_hunk_base_text_offset = unstaged_hunk.diff_base_byte_range.end;
prev_unstaged_hunk_buffer_offset = unstaged_hunk_offset_range.end;
secondary_base_text_start =
secondary_base_text_start.min(secondary_hunk.diff_base_byte_range.start);
index_start = index_start.min(unstaged_hunk.diff_base_byte_range.start);
buffer_offset_range.start = buffer_offset_range
.start
.min(secondary_hunk_offset_range.start);
.min(unstaged_hunk_offset_range.start);
secondary_cursor.next(buffer);
unstaged_hunk_cursor.next(buffer);
}
let end_overshoot = buffer_offset_range
.end
.saturating_sub(prev_secondary_hunk_buffer_offset);
let secondary_base_text_end = prev_secondary_hunk_base_text_offset + end_overshoot;
.saturating_sub(prev_unstaged_hunk_buffer_offset);
let index_end = prev_unstaged_hunk_base_text_offset + end_overshoot;
let secondary_base_text_range = secondary_base_text_start..secondary_base_text_end;
let index_range = index_start..index_end;
buffer_offset_range.end = buffer_offset_range
.end
.max(prev_secondary_hunk_buffer_offset);
.max(prev_unstaged_hunk_buffer_offset);
let replacement_text = if stage {
log::debug!("staging");
log::debug!("stage hunk {:?}", buffer_offset_range);
buffer
.text_for_range(buffer_offset_range)
.collect::<String>()
} else {
log::debug!("unstaging");
head_base
log::debug!("unstage hunk {:?}", buffer_offset_range);
head_text
.chunks_in_range(diff_base_byte_range.clone())
.collect::<String>()
};
edits.push((secondary_base_text_range, replacement_text));
pending_hunks.push((
diff_base_byte_range.start,
PendingHunk {
buffer_version: buffer.version().clone(),
new_status: if stage {
DiffHunkSecondaryStatus::SecondaryHunkRemovalPending
} else {
DiffHunkSecondaryStatus::SecondaryHunkAdditionPending
},
},
));
edits.push((index_range, replacement_text));
}
let buffer = cx.new(|cx| {
language::Buffer::local_normalized(index_base, text::LineEnding::default(), cx)
});
let new_text = buffer.update(cx, |buffer, cx| {
buffer.edit(edits, None, cx);
buffer.as_rope().clone()
});
Some(new_text)
let mut new_index_text = Rope::new();
let mut index_cursor = index_text.cursor(0);
for (old_range, replacement_text) in edits {
new_index_text.append(index_cursor.slice(old_range.start));
index_cursor.seek_forward(old_range.end);
new_index_text.push(&replacement_text);
}
new_index_text.append(index_cursor.suffix());
(Some(new_index_text), pending_hunks)
}
}
impl BufferDiffInner {
fn hunks_intersecting_range<'a>(
&'a self,
range: Range<Anchor>,
@@ -304,12 +353,16 @@ impl BufferDiffInner {
]
});
let mut secondary_cursor = secondary.as_ref().map(|diff| {
let mut cursor = diff.hunks.cursor::<DiffHunkSummary>(buffer);
let mut secondary_cursor = None;
let mut pending_hunks = TreeMap::default();
if let Some(secondary) = secondary.as_ref() {
let mut cursor = secondary.hunks.cursor::<DiffHunkSummary>(buffer);
cursor.next(buffer);
cursor
});
secondary_cursor = Some(cursor);
pending_hunks = secondary.pending_hunks.clone();
}
let max_point = buffer.max_point();
let mut summaries = buffer.summaries_for_anchors_with_payload::<Point, _, _>(anchor_iter);
iter::from_fn(move || loop {
let (start_point, (start_anchor, start_base)) = summaries.next()?;
@@ -319,14 +372,26 @@ impl BufferDiffInner {
continue;
}
if end_point.column > 0 {
if end_point.column > 0 && end_point < max_point {
end_point.row += 1;
end_point.column = 0;
end_anchor = buffer.anchor_before(end_point);
}
let mut secondary_status = DiffHunkSecondaryStatus::None;
if let Some(secondary_cursor) = secondary_cursor.as_mut() {
let mut has_pending = false;
if let Some(pending_hunk) = pending_hunks.get(&start_base) {
if !buffer.has_edits_since_in_range(
&pending_hunk.buffer_version,
start_anchor..end_anchor,
) {
has_pending = true;
secondary_status = pending_hunk.new_status;
}
}
if let (Some(secondary_cursor), false) = (secondary_cursor.as_mut(), has_pending) {
if start_anchor
.cmp(&secondary_cursor.start().buffer_range.start, buffer)
.is_gt()
@@ -340,18 +405,19 @@ impl BufferDiffInner {
secondary_range.end.row += 1;
secondary_range.end.column = 0;
}
if secondary_range == (start_point..end_point) {
if secondary_range.is_empty() && secondary_hunk.diff_base_byte_range.is_empty()
{
// ignore
} else if secondary_range == (start_point..end_point) {
secondary_status = DiffHunkSecondaryStatus::HasSecondaryHunk;
} else if secondary_range.start <= end_point {
secondary_status = DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk;
}
}
} else {
log::debug!("no secondary cursor!!");
}
return Some(DiffHunk {
row_range: start_point.row..end_point.row,
range: start_point..end_point,
diff_base_byte_range: start_base..end_base,
buffer_range: start_anchor..end_anchor,
secondary_status,
@@ -377,14 +443,9 @@ impl BufferDiffInner {
let hunk = cursor.item()?;
let range = hunk.buffer_range.to_point(buffer);
let end_row = if range.end.column > 0 {
range.end.row + 1
} else {
range.end.row
};
Some(DiffHunk {
row_range: range.start.row..end_row,
range,
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
buffer_range: hunk.buffer_range.clone(),
// The secondary status is not used by callers of this method.
@@ -504,6 +565,14 @@ fn compute_hunks(
tree.push(hunk, &buffer);
}
}
} else {
tree.push(
InternalDiffHunk {
buffer_range: Anchor::MIN..Anchor::MAX,
diff_base_byte_range: 0..0,
},
&buffer,
);
}
tree
@@ -594,11 +663,13 @@ impl std::fmt::Debug for BufferDiff {
}
}
#[derive(Clone, Debug)]
pub enum BufferDiffEvent {
DiffChanged {
changed_range: Option<Range<text::Anchor>>,
},
LanguageChanged,
HunksStagedOrUnstaged(Option<Rope>),
}
impl EventEmitter<BufferDiffEvent> for BufferDiff {}
@@ -617,95 +688,71 @@ impl BufferDiff {
fn build(
buffer: text::BufferSnapshot,
diff_base: Option<Arc<String>>,
base_text: Option<Arc<String>>,
language: Option<Arc<Language>>,
language_registry: Option<Arc<LanguageRegistry>>,
cx: &mut App,
) -> impl Future<Output = BufferDiffInner> {
let diff_base =
diff_base.map(|diff_base| (diff_base.clone(), Rope::from(diff_base.as_str())));
let base_text_snapshot = diff_base.as_ref().map(|(_, diff_base)| {
language::Buffer::build_snapshot(
diff_base.clone(),
let base_text_pair;
let base_text_exists;
let base_text_snapshot;
if let Some(text) = &base_text {
let base_text_rope = Rope::from(text.as_str());
base_text_pair = Some((text.clone(), base_text_rope.clone()));
let snapshot = language::Buffer::build_snapshot(
base_text_rope,
language.clone(),
language_registry.clone(),
cx,
)
});
let base_text_snapshot = cx.background_spawn(OptionFuture::from(base_text_snapshot));
);
base_text_snapshot = cx.background_spawn(snapshot);
base_text_exists = true;
} else {
base_text_pair = None;
base_text_snapshot = Task::ready(language::Buffer::build_empty_snapshot(cx));
base_text_exists = false;
};
let hunks = cx.background_spawn({
let buffer = buffer.clone();
async move { compute_hunks(diff_base, buffer) }
async move { compute_hunks(base_text_pair, buffer) }
});
async move {
let (base_text, hunks) = futures::join!(base_text_snapshot, hunks);
BufferDiffInner { base_text, hunks }
BufferDiffInner {
base_text,
hunks,
base_text_exists,
pending_hunks: TreeMap::default(),
}
}
}
fn build_with_base_buffer(
buffer: text::BufferSnapshot,
diff_base: Option<Arc<String>>,
diff_base_buffer: Option<language::BufferSnapshot>,
base_text: Option<Arc<String>>,
base_text_snapshot: language::BufferSnapshot,
cx: &App,
) -> impl Future<Output = BufferDiffInner> {
let diff_base = diff_base.clone().zip(
diff_base_buffer
.clone()
.map(|buffer| buffer.as_rope().clone()),
);
let base_text_exists = base_text.is_some();
let base_text_pair = base_text.map(|text| (text, base_text_snapshot.as_rope().clone()));
cx.background_spawn(async move {
BufferDiffInner {
hunks: compute_hunks(diff_base, buffer),
base_text: diff_base_buffer,
base_text: base_text_snapshot,
hunks: compute_hunks(base_text_pair, buffer),
pending_hunks: TreeMap::default(),
base_text_exists,
}
})
}
fn build_empty(buffer: &text::BufferSnapshot) -> BufferDiffInner {
fn build_empty(buffer: &text::BufferSnapshot, cx: &mut App) -> BufferDiffInner {
BufferDiffInner {
base_text: language::Buffer::build_empty_snapshot(cx),
hunks: SumTree::new(buffer),
base_text: None,
}
}
pub fn build_with_single_insertion(
insertion_present_in_secondary_diff: bool,
buffer: language::BufferSnapshot,
cx: &mut App,
) -> BufferDiffSnapshot {
let base_text = language::Buffer::build_empty_snapshot(cx);
let hunks = SumTree::from_item(
InternalDiffHunk {
buffer_range: Anchor::MIN..Anchor::MAX,
diff_base_byte_range: 0..0,
},
&base_text,
);
BufferDiffSnapshot {
inner: BufferDiffInner {
hunks: hunks.clone(),
base_text: Some(base_text.clone()),
},
secondary_diff: Some(Box::new(BufferDiffSnapshot {
inner: BufferDiffInner {
hunks: if insertion_present_in_secondary_diff {
hunks
} else {
SumTree::new(&buffer.text)
},
base_text: Some(if insertion_present_in_secondary_diff {
base_text
} else {
buffer
}),
},
secondary_diff: None,
is_single_insertion: true,
})),
is_single_insertion: true,
pending_hunks: TreeMap::default(),
base_text_exists: false,
}
}
@@ -714,7 +761,52 @@ impl BufferDiff {
}
pub fn secondary_diff(&self) -> Option<Entity<BufferDiff>> {
Some(self.secondary_diff.as_ref()?.clone())
self.secondary_diff.clone()
}
pub fn clear_pending_hunks(&mut self, cx: &mut Context<Self>) {
if let Some(secondary_diff) = &self.secondary_diff {
secondary_diff.update(cx, |diff, _| {
diff.inner.pending_hunks.clear();
});
cx.emit(BufferDiffEvent::DiffChanged {
changed_range: Some(Anchor::MIN..Anchor::MAX),
});
}
}
pub fn stage_or_unstage_hunks(
&mut self,
stage: bool,
hunks: &[DiffHunk],
buffer: &text::BufferSnapshot,
file_exists: bool,
cx: &mut Context<Self>,
) -> Option<Rope> {
let (new_index_text, pending_hunks) = self.inner.stage_or_unstage_hunks(
&self.secondary_diff.as_ref()?.read(cx).inner,
stage,
&hunks,
buffer,
file_exists,
);
if let Some(unstaged_diff) = &self.secondary_diff {
unstaged_diff.update(cx, |diff, _| {
for (offset, pending_hunk) in pending_hunks {
diff.inner.pending_hunks.insert(offset, pending_hunk);
}
});
}
cx.emit(BufferDiffEvent::HunksStagedOrUnstaged(
new_index_text.clone(),
));
if let Some((first, last)) = hunks.first().zip(hunks.last()) {
let changed_range = first.buffer_range.start..last.buffer_range.end;
cx.emit(BufferDiffEvent::DiffChanged {
changed_range: Some(changed_range),
});
}
new_index_text
}
pub fn range_to_hunk_range(
@@ -763,7 +855,7 @@ impl BufferDiff {
Self::build_with_base_buffer(
buffer.clone(),
base_text,
this.base_text().cloned(),
this.base_text().clone(),
cx,
)
})?
@@ -785,22 +877,33 @@ impl BufferDiff {
fn set_state(
&mut self,
inner: BufferDiffInner,
new_state: BufferDiffInner,
buffer: &text::BufferSnapshot,
) -> Option<Range<Anchor>> {
let changed_range = match (self.inner.base_text.as_ref(), inner.base_text.as_ref()) {
(None, None) => None,
(Some(old), Some(new)) if old.remote_id() == new.remote_id() => {
inner.compare(&self.inner, buffer)
}
_ => Some(text::Anchor::MIN..text::Anchor::MAX),
};
self.inner = inner;
let (base_text_changed, changed_range) =
match (self.inner.base_text_exists, new_state.base_text_exists) {
(false, false) => (true, None),
(true, true)
if self.inner.base_text.remote_id() == new_state.base_text.remote_id() =>
{
(false, new_state.compare(&self.inner, buffer))
}
_ => (true, Some(text::Anchor::MIN..text::Anchor::MAX)),
};
let pending_hunks = mem::take(&mut self.inner.pending_hunks);
self.inner = new_state;
if !base_text_changed {
self.inner.pending_hunks = pending_hunks;
}
changed_range
}
pub fn base_text(&self) -> Option<&language::BufferSnapshot> {
self.inner.base_text.as_ref()
pub fn base_text(&self) -> &language::BufferSnapshot {
&self.inner.base_text
}
pub fn base_text_exists(&self) -> bool {
self.inner.base_text_exists
}
pub fn snapshot(&self, cx: &App) -> BufferDiffSnapshot {
@@ -810,10 +913,17 @@ impl BufferDiff {
.secondary_diff
.as_ref()
.map(|diff| Box::new(diff.read(cx).snapshot(cx))),
is_single_insertion: false,
}
}
pub fn hunks<'a>(
&'a self,
buffer_snapshot: &'a text::BufferSnapshot,
cx: &'a App,
) -> impl 'a + Iterator<Item = DiffHunk> {
self.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, buffer_snapshot, cx)
}
pub fn hunks_intersecting_range<'a>(
&'a self,
range: Range<text::Anchor>,
@@ -887,15 +997,16 @@ impl BufferDiff {
rx
}
#[cfg(any(test, feature = "test-support"))]
pub fn base_text_string(&self) -> Option<String> {
self.inner.base_text.as_ref().map(|buffer| buffer.text())
self.inner
.base_text_exists
.then(|| self.inner.base_text.text())
}
pub fn new(buffer: &text::BufferSnapshot) -> Self {
pub fn new(buffer: &text::BufferSnapshot, cx: &mut App) -> Self {
BufferDiff {
buffer_id: buffer.remote_id(),
inner: BufferDiff::build_empty(buffer),
inner: BufferDiff::build_empty(buffer, cx),
secondary_diff: None,
}
}
@@ -925,14 +1036,10 @@ impl BufferDiff {
#[cfg(any(test, feature = "test-support"))]
pub fn recalculate_diff_sync(&mut self, buffer: text::BufferSnapshot, cx: &mut Context<Self>) {
let base_text = self
.inner
.base_text
.as_ref()
.map(|base_text| base_text.text());
let base_text = self.base_text_string().map(Arc::new);
let snapshot = BufferDiff::build_with_base_buffer(
buffer.clone(),
base_text.clone().map(Arc::new),
base_text,
self.inner.base_text.clone(),
cx,
);
@@ -943,6 +1050,10 @@ impl BufferDiff {
}
impl DiffHunk {
pub fn is_created_file(&self) -> bool {
self.diff_base_byte_range == (0..0) && self.buffer_range == (Anchor::MIN..Anchor::MAX)
}
pub fn status(&self) -> DiffHunkStatus {
let kind = if self.buffer_range.start == self.buffer_range.end {
DiffHunkStatusKind::Deleted
@@ -959,6 +1070,23 @@ impl DiffHunk {
}
impl DiffHunkStatus {
pub fn has_secondary_hunk(&self) -> bool {
matches!(
self.secondary,
DiffHunkSecondaryStatus::HasSecondaryHunk
| DiffHunkSecondaryStatus::SecondaryHunkAdditionPending
| DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
)
}
pub fn is_pending(&self) -> bool {
matches!(
self.secondary,
DiffHunkSecondaryStatus::SecondaryHunkAdditionPending
| DiffHunkSecondaryStatus::SecondaryHunkRemovalPending
)
}
pub fn is_deleted(&self) -> bool {
self.kind == DiffHunkStatusKind::Deleted
}
@@ -992,7 +1120,6 @@ impl DiffHunkStatus {
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn deleted_none() -> Self {
Self {
kind: DiffHunkStatusKind::Deleted,
@@ -1000,7 +1127,6 @@ impl DiffHunkStatus {
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn added_none() -> Self {
Self {
kind: DiffHunkStatusKind::Added,
@@ -1008,7 +1134,6 @@ impl DiffHunkStatus {
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn modified_none() -> Self {
Self {
kind: DiffHunkStatusKind::Modified,
@@ -1031,12 +1156,10 @@ pub fn assert_hunks<Iter>(
let actual_hunks = diff_hunks
.map(|hunk| {
(
hunk.row_range.clone(),
hunk.range.clone(),
&diff_base[hunk.diff_base_byte_range.clone()],
buffer
.text_for_range(
Point::new(hunk.row_range.start, 0)..Point::new(hunk.row_range.end, 0),
)
.text_for_range(hunk.range.clone())
.collect::<String>(),
hunk.status(),
)
@@ -1045,7 +1168,14 @@ pub fn assert_hunks<Iter>(
let expected_hunks: Vec<_> = expected_hunks
.iter()
.map(|(r, s, h, status)| (r.clone(), *s, h.to_string(), *status))
.map(|(r, old_text, new_text, status)| {
(
Point::new(r.start, 0)..Point::new(r.end, 0),
*old_text,
new_text.to_string(),
*status,
)
})
.collect();
assert_eq!(actual_hunks, expected_hunks);
@@ -1106,7 +1236,7 @@ mod tests {
],
);
diff = BufferDiff::build_empty(&buffer);
diff = cx.update(|cx| BufferDiff::build_empty(&buffer, cx));
assert_hunks(
diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer, None),
&buffer,
@@ -1421,43 +1551,55 @@ mod tests {
for example in table {
let (buffer_text, ranges) = marked_text_ranges(&example.buffer_marked_text, false);
let buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text);
let uncommitted_diff =
BufferDiff::build_sync(buffer.clone(), example.head_text.clone(), cx);
let unstaged_diff =
BufferDiff::build_sync(buffer.clone(), example.index_text.clone(), cx);
let uncommitted_diff = BufferDiffSnapshot {
inner: uncommitted_diff,
secondary_diff: Some(Box::new(BufferDiffSnapshot {
inner: unstaged_diff,
is_single_insertion: false,
secondary_diff: None,
})),
is_single_insertion: false,
};
let hunk_range =
buffer.anchor_before(ranges[0].start)..buffer.anchor_before(ranges[0].end);
let range = buffer.anchor_before(ranges[0].start)..buffer.anchor_before(ranges[0].end);
let unstaged = BufferDiff::build_sync(buffer.clone(), example.index_text.clone(), cx);
let uncommitted = BufferDiff::build_sync(buffer.clone(), example.head_text.clone(), cx);
let new_index_text = cx
.update(|cx| {
uncommitted_diff.new_secondary_text_for_stage_or_unstage(
true,
uncommitted_diff
.hunks_intersecting_range(range, &buffer)
.map(|hunk| {
(hunk.buffer_range.clone(), hunk.diff_base_byte_range.clone())
}),
&buffer,
cx,
let unstaged_diff = cx.new(|cx| {
let mut diff = BufferDiff::new(&buffer, cx);
diff.set_state(unstaged, &buffer);
diff
});
let uncommitted_diff = cx.new(|cx| {
let mut diff = BufferDiff::new(&buffer, cx);
diff.set_state(uncommitted, &buffer);
diff.set_secondary_diff(unstaged_diff);
diff
});
uncommitted_diff.update(cx, |diff, cx| {
let hunks = diff
.hunks_intersecting_range(hunk_range.clone(), &buffer, &cx)
.collect::<Vec<_>>();
for hunk in &hunks {
assert_ne!(hunk.secondary_status, DiffHunkSecondaryStatus::None)
}
let new_index_text = diff
.stage_or_unstage_hunks(true, &hunks, &buffer, true, cx)
.unwrap()
.to_string();
let hunks = diff
.hunks_intersecting_range(hunk_range.clone(), &buffer, &cx)
.collect::<Vec<_>>();
for hunk in &hunks {
assert_eq!(
hunk.secondary_status,
DiffHunkSecondaryStatus::SecondaryHunkRemovalPending
)
})
.unwrap()
.to_string();
pretty_assertions::assert_eq!(
new_index_text,
example.final_index_text,
"example: {}",
example.name
);
}
pretty_assertions::assert_eq!(
new_index_text,
example.final_index_text,
"example: {}",
example.name
);
});
}
}
@@ -1491,7 +1633,7 @@ mod tests {
let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text_1);
let empty_diff = BufferDiff::build_empty(&buffer);
let empty_diff = cx.update(|cx| BufferDiff::build_empty(&buffer, cx));
let diff_1 = BufferDiff::build_sync(buffer.clone(), base_text.clone(), cx);
let range = diff_1.compare(&empty_diff, &buffer).unwrap();
assert_eq!(range.to_point(&buffer), Point::new(0, 0)..Point::new(8, 0));
@@ -1654,7 +1796,7 @@ mod tests {
index_text: &Rope,
head_text: String,
cx: &mut TestAppContext,
) -> BufferDiff {
) -> Entity<BufferDiff> {
let inner = BufferDiff::build_sync(working_copy.text.clone(), head_text, cx);
let secondary = BufferDiff {
buffer_id: working_copy.remote_id(),
@@ -1666,11 +1808,11 @@ mod tests {
secondary_diff: None,
};
let secondary = cx.new(|_| secondary);
BufferDiff {
cx.new(|_| BufferDiff {
buffer_id: working_copy.remote_id(),
inner,
secondary_diff: Some(secondary),
}
})
}
let operations = std::env::var("OPERATIONS")
@@ -1698,7 +1840,7 @@ mod tests {
};
let mut diff = uncommitted_diff(&working_copy, &index_text, head_text.clone(), cx);
let mut hunks = cx.update(|cx| {
let mut hunks = diff.update(cx, |diff, cx| {
diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &working_copy, cx)
.collect::<Vec<_>>()
});
@@ -1709,6 +1851,7 @@ mod tests {
for _ in 0..operations {
let i = rng.gen_range(0..hunks.len());
let hunk = &mut hunks[i];
let hunk_to_change = hunk.clone();
let stage = match hunk.secondary_status {
DiffHunkSecondaryStatus::HasSecondaryHunk => {
hunk.secondary_status = DiffHunkSecondaryStatus::None;
@@ -1721,21 +1864,13 @@ mod tests {
_ => unreachable!(),
};
let snapshot = cx.update(|cx| diff.snapshot(cx));
index_text = cx.update(|cx| {
snapshot
.new_secondary_text_for_stage_or_unstage(
stage,
[(hunk.buffer_range.clone(), hunk.diff_base_byte_range.clone())]
.into_iter(),
&working_copy,
cx,
)
index_text = diff.update(cx, |diff, cx| {
diff.stage_or_unstage_hunks(stage, &[hunk_to_change], &working_copy, true, cx)
.unwrap()
});
diff = uncommitted_diff(&working_copy, &index_text, head_text.clone(), cx);
let found_hunks = cx.update(|cx| {
let found_hunks = diff.update(cx, |diff, cx| {
diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &working_copy, cx)
.collect::<Vec<_>>()
});

View File

@@ -48,7 +48,7 @@ pub struct ChannelPathsInsertGuard<'a> {
channels_by_id: &'a mut BTreeMap<ChannelId, Arc<Channel>>,
}
impl<'a> ChannelPathsInsertGuard<'a> {
impl ChannelPathsInsertGuard<'_> {
pub fn insert(&mut self, channel_proto: proto::Channel) -> bool {
let mut ret = false;
let parent_path = channel_proto
@@ -86,7 +86,7 @@ impl<'a> ChannelPathsInsertGuard<'a> {
}
}
impl<'a> Drop for ChannelPathsInsertGuard<'a> {
impl Drop for ChannelPathsInsertGuard<'_> {
fn drop(&mut self) {
self.channels_ordered.sort_by(|a, b| {
let a = channel_path_sorting_key(*a, self.channels_by_id);

View File

@@ -33,10 +33,13 @@ util.workspace = true
tempfile.workspace = true
[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
exec.workspace = true
exec.workspace = true
fork.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation.workspace = true
core-services = "0.2"
plist = "1.3"
[target.'cfg(target_os = "windows")'.dependencies]
windows.workspace = true

View File

@@ -521,30 +521,115 @@ mod flatpak {
}
}
// todo("windows")
#[cfg(target_os = "windows")]
mod windows {
use anyhow::Context;
use release_channel::app_identifier;
use windows::{
core::HSTRING,
Win32::{
Foundation::{CloseHandle, GetLastError, ERROR_ALREADY_EXISTS, GENERIC_WRITE},
Storage::FileSystem::{
CreateFileW, WriteFile, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_MODE, OPEN_EXISTING,
},
System::Threading::CreateMutexW,
},
};
use crate::{Detect, InstalledApp};
use std::io;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::process::ExitStatus;
struct App;
fn check_single_instance() -> bool {
let mutex = unsafe {
CreateMutexW(
None,
false,
&HSTRING::from(format!("{}-Instance-Mutex", app_identifier())),
)
.expect("Unable to create instance sync event")
};
let last_err = unsafe { GetLastError() };
let _ = unsafe { CloseHandle(mutex) };
last_err != ERROR_ALREADY_EXISTS
}
struct App(PathBuf);
impl InstalledApp for App {
fn zed_version_string(&self) -> String {
unimplemented!()
format!(
"Zed {}{}{} {}",
if *release_channel::RELEASE_CHANNEL_NAME == "stable" {
"".to_string()
} else {
format!("{} ", *release_channel::RELEASE_CHANNEL_NAME)
},
option_env!("RELEASE_VERSION").unwrap_or_default(),
match option_env!("ZED_COMMIT_SHA") {
Some(commit_sha) => format!(" {commit_sha} "),
None => "".to_string(),
},
self.0.display(),
)
}
fn launch(&self, _ipc_url: String) -> anyhow::Result<()> {
unimplemented!()
fn launch(&self, ipc_url: String) -> anyhow::Result<()> {
if check_single_instance() {
std::process::Command::new(self.0.clone())
.arg(ipc_url)
.spawn()?;
} else {
unsafe {
let pipe = CreateFileW(
&HSTRING::from(format!("\\\\.\\pipe\\{}-Named-Pipe", app_identifier())),
GENERIC_WRITE.0,
FILE_SHARE_MODE::default(),
None,
OPEN_EXISTING,
FILE_FLAGS_AND_ATTRIBUTES::default(),
None,
)?;
let message = ipc_url.as_bytes();
let mut bytes_written = 0;
WriteFile(pipe, Some(message), Some(&mut bytes_written), None)?;
CloseHandle(pipe)?;
}
}
Ok(())
}
fn run_foreground(&self, _ipc_url: String) -> io::Result<ExitStatus> {
unimplemented!()
fn run_foreground(&self, ipc_url: String) -> io::Result<ExitStatus> {
std::process::Command::new(self.0.clone())
.arg(ipc_url)
.arg("--foreground")
.spawn()?
.wait()
}
}
impl Detect {
pub fn detect(_path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
Ok(App)
pub fn detect(path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
let path = if let Some(path) = path {
path.to_path_buf().canonicalize()?
} else {
let cli = std::env::current_exe()?;
let dir = cli.parent().context("no parent path for cli")?;
// ../Zed.exe is the standard, lib/zed is for MSYS2, ./zed.exe is for the target
// directory in development builds.
let possible_locations = ["../Zed.exe", "../lib/zed/zed-editor.exe", "./zed.exe"];
possible_locations
.iter()
.find_map(|p| dir.join(p).canonicalize().ok().filter(|path| path != &cli))
.context(format!(
"could not find any of: {}",
possible_locations.join(", ")
))?
};
Ok(App(path))
}
}
}

View File

@@ -418,6 +418,8 @@ impl Telemetry {
fn report_event(self: &Arc<Self>, event: Event) {
let mut state = self.state.lock();
// RUST_LOG=telemetry=trace to debug telemetry events
log::trace!(target: "telemetry", "{:?}", event);
if !state.settings.metrics {
return;

View File

@@ -97,6 +97,7 @@ extension.workspace = true
file_finder.workspace = true
fs = { workspace = true, features = ["test-support"] }
git = { workspace = true, features = ["test-support"] }
git_ui = { workspace = true, features = ["test-support"] }
git_hosting_providers.workspace = true
gpui = { workspace = true, features = ["test-support"] }
hyper.workspace = true
@@ -110,7 +111,7 @@ node_runtime.workspace = true
notifications = { workspace = true, features = ["test-support"] }
pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
prompt_library.workspace = true
prompt_store.workspace = true
recent_projects = { workspace = true }
release_channel.workspace = true
remote = { workspace = true, features = ["test-support"] }

View File

@@ -202,7 +202,7 @@ impl Database {
.await
}
pub async fn get_known_extension_versions<'a>(&self) -> Result<HashMap<String, Vec<String>>> {
pub async fn get_known_extension_versions(&self) -> Result<HashMap<String, Vec<String>>> {
self.transaction(|tx| async move {
let mut extension_external_ids_by_id = HashMap::default();

View File

@@ -453,10 +453,6 @@ async fn check_usage_limit(
let user_id = UserId::from_proto(claims.user_id);
let model = state.db.model(provider, model_name)?;
let usage = state
.db
.get_usage(user_id, provider, model_name, Utc::now())
.await?;
let free_tier = claims.free_tier_monthly_spending_limit();
let spending_this_month = state
@@ -471,7 +467,8 @@ async fn check_usage_limit(
));
}
if (usage.spending_this_month - free_tier) >= Cents(claims.max_monthly_spend_in_cents) {
let monthly_spend = spending_this_month.saturating_sub(free_tier);
if monthly_spend >= Cents(claims.max_monthly_spend_in_cents) {
return Err(Error::Http(
StatusCode::FORBIDDEN,
"Maximum spending limit reached for this month.".to_string(),
@@ -496,6 +493,11 @@ async fn check_usage_limit(
model.max_tokens_per_minute as usize / users_in_recent_minutes;
let per_user_max_tokens_per_day = model.max_tokens_per_day as usize / users_in_recent_days;
let usage = state
.db
.get_usage(user_id, provider, model_name, Utc::now())
.await?;
let checks = [
(
usage.requests_this_minute,

View File

@@ -308,7 +308,7 @@ impl Server {
.add_request_handler(forward_read_only_project_request::<proto::InlayHints>)
.add_request_handler(forward_read_only_project_request::<proto::ResolveInlayHint>)
.add_request_handler(forward_read_only_project_request::<proto::OpenBufferByPath>)
.add_request_handler(forward_read_only_project_request::<proto::GitBranches>)
.add_request_handler(forward_read_only_project_request::<proto::GitGetBranches>)
.add_request_handler(forward_read_only_project_request::<proto::OpenUnstagedDiff>)
.add_request_handler(forward_read_only_project_request::<proto::OpenUncommittedDiff>)
.add_request_handler(
@@ -328,6 +328,7 @@ impl Server {
.add_request_handler(forward_mutating_project_request::<proto::PrepareRename>)
.add_request_handler(forward_mutating_project_request::<proto::PerformRename>)
.add_request_handler(forward_mutating_project_request::<proto::ReloadBuffers>)
.add_request_handler(forward_mutating_project_request::<proto::ApplyCodeActionKind>)
.add_request_handler(forward_mutating_project_request::<proto::FormatBuffers>)
.add_request_handler(forward_mutating_project_request::<proto::CreateProjectEntry>)
.add_request_handler(forward_mutating_project_request::<proto::RenameProjectEntry>)
@@ -392,9 +393,6 @@ impl Server {
.add_request_handler(forward_mutating_project_request::<proto::OpenContext>)
.add_request_handler(forward_mutating_project_request::<proto::CreateContext>)
.add_request_handler(forward_mutating_project_request::<proto::SynchronizeContexts>)
.add_request_handler(forward_mutating_project_request::<proto::Push>)
.add_request_handler(forward_mutating_project_request::<proto::Pull>)
.add_request_handler(forward_mutating_project_request::<proto::Fetch>)
.add_request_handler(forward_mutating_project_request::<proto::Stage>)
.add_request_handler(forward_mutating_project_request::<proto::Unstage>)
.add_request_handler(forward_mutating_project_request::<proto::Commit>)
@@ -404,6 +402,10 @@ impl Server {
.add_request_handler(forward_read_only_project_request::<proto::GitCheckoutFiles>)
.add_request_handler(forward_mutating_project_request::<proto::SetIndexText>)
.add_request_handler(forward_mutating_project_request::<proto::OpenCommitMessageBuffer>)
.add_request_handler(forward_mutating_project_request::<proto::GitDiff>)
.add_request_handler(forward_mutating_project_request::<proto::GitCreateBranch>)
.add_request_handler(forward_mutating_project_request::<proto::GitChangeBranch>)
.add_request_handler(forward_mutating_project_request::<proto::CheckForPushedCommits>)
.add_message_handler(broadcast_project_message_from_host::<proto::AdvertiseContexts>)
.add_message_handler(update_context)
.add_request_handler({
@@ -975,7 +977,7 @@ impl Server {
}
}
impl<'a> Deref for ConnectionPoolGuard<'a> {
impl Deref for ConnectionPoolGuard<'_> {
type Target = ConnectionPool;
fn deref(&self) -> &Self::Target {
@@ -983,13 +985,13 @@ impl<'a> Deref for ConnectionPoolGuard<'a> {
}
}
impl<'a> DerefMut for ConnectionPoolGuard<'a> {
impl DerefMut for ConnectionPoolGuard<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.guard
}
}
impl<'a> Drop for ConnectionPoolGuard<'a> {
impl Drop for ConnectionPoolGuard<'_> {
fn drop(&mut self) {
#[cfg(test)]
self.check_invariants();

View File

@@ -13,6 +13,7 @@ mod channel_message_tests;
mod channel_tests;
mod editor_tests;
mod following_tests;
mod git_tests;
mod integration_tests;
mod notification_tests;
mod random_channel_buffer_tests;

View File

@@ -1537,6 +1537,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
show_parameter_hints: false,
show_other_hints: true,
show_background: false,
toggle_on_modifiers_press: None,
})
});
});
@@ -1552,6 +1553,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
show_parameter_hints: false,
show_other_hints: true,
show_background: false,
toggle_on_modifiers_press: None,
})
});
});
@@ -1770,6 +1772,7 @@ async fn test_inlay_hint_refresh_is_forwarded(
show_parameter_hints: false,
show_other_hints: false,
show_background: false,
toggle_on_modifiers_press: None,
})
});
});
@@ -1785,6 +1788,7 @@ async fn test_inlay_hint_refresh_is_forwarded(
show_parameter_hints: true,
show_other_hints: true,
show_background: false,
toggle_on_modifiers_press: None,
})
});
});
@@ -2023,6 +2027,15 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
.unwrap()
.downcast::<Editor>()
.unwrap();
let buffer_id_b = editor_b.update(cx_b, |editor_b, cx| {
editor_b
.buffer()
.read(cx)
.as_singleton()
.unwrap()
.read(cx)
.remote_id()
});
// client_b now requests git blame for the open buffer
editor_b.update_in(cx_b, |editor_b, window, cx| {
@@ -2041,6 +2054,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
&(0..4)
.map(|row| RowInfo {
buffer_row: Some(row),
buffer_id: Some(buffer_id_b),
..Default::default()
})
.collect::<Vec<_>>(),
@@ -2088,6 +2102,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
&(0..4)
.map(|row| RowInfo {
buffer_row: Some(row),
buffer_id: Some(buffer_id_b),
..Default::default()
})
.collect::<Vec<_>>(),
@@ -2123,6 +2138,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
&(0..4)
.map(|row| RowInfo {
buffer_row: Some(row),
buffer_id: Some(buffer_id_b),
..Default::default()
})
.collect::<Vec<_>>(),

View File

@@ -0,0 +1,130 @@
use std::{
path::{Path, PathBuf},
sync::Arc,
};
use call::ActiveCall;
use git::status::{FileStatus, StatusCode, TrackedStatus};
use git_ui::project_diff::ProjectDiff;
use gpui::{TestAppContext, VisualTestContext};
use project::ProjectPath;
use serde_json::json;
use workspace::Workspace;
//
use crate::tests::TestServer;
#[gpui::test]
async fn test_project_diff(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
let mut server = TestServer::start(cx_a.background_executor.clone()).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
cx_a.set_name("cx_a");
cx_b.set_name("cx_b");
server
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
.await;
client_a
.fs()
.insert_tree(
"/a",
json!({
".git": {},
"changed.txt": "after\n",
"unchanged.txt": "unchanged\n",
"created.txt": "created\n",
"secret.pem": "secret-changed\n",
}),
)
.await;
client_a.fs().set_git_content_for_repo(
Path::new("/a/.git"),
&[
("changed.txt".into(), "before\n".to_string(), None),
("unchanged.txt".into(), "unchanged\n".to_string(), None),
("deleted.txt".into(), "deleted\n".to_string(), None),
("secret.pem".into(), "shh\n".to_string(), None),
],
);
let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
let active_call_a = cx_a.read(ActiveCall::global);
let project_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
.await
.unwrap();
cx_b.update(editor::init);
cx_b.update(git_ui::init);
let project_b = client_b.join_remote_project(project_id, cx_b).await;
let workspace_b = cx_b.add_window(|window, cx| {
Workspace::new(
None,
project_b.clone(),
client_b.app_state.clone(),
window,
cx,
)
});
let cx_b = &mut VisualTestContext::from_window(*workspace_b, cx_b);
let workspace_b = workspace_b.root(cx_b).unwrap();
cx_b.update(|window, cx| {
window
.focused(cx)
.unwrap()
.dispatch_action(&git_ui::project_diff::Diff, window, cx)
});
let diff = workspace_b.update(cx_b, |workspace, cx| {
workspace.active_item(cx).unwrap().act_as::<ProjectDiff>(cx)
});
let diff = diff.unwrap();
cx_b.run_until_parked();
diff.update(cx_b, |diff, cx| {
assert_eq!(
diff.excerpt_paths(cx),
vec!["changed.txt", "deleted.txt", "created.txt"]
);
});
client_a
.fs()
.insert_tree(
"/a",
json!({
".git": {},
"changed.txt": "before\n",
"unchanged.txt": "changed\n",
"created.txt": "created\n",
"secret.pem": "secret-changed\n",
}),
)
.await;
client_a.fs().recalculate_git_status(Path::new("/a/.git"));
cx_b.run_until_parked();
project_b.update(cx_b, |project, cx| {
let project_path = ProjectPath {
worktree_id,
path: Arc::from(PathBuf::from("unchanged.txt")),
};
let status = project.project_path_git_status(&project_path, cx);
assert_eq!(
status.unwrap(),
FileStatus::Tracked(TrackedStatus {
worktree_status: StatusCode::Modified,
index_status: StatusCode::Unmodified,
})
);
});
diff.update(cx_b, |diff, cx| {
assert_eq!(
diff.excerpt_paths(cx),
vec!["deleted.txt", "unchanged.txt", "created.txt"]
);
});
}

View File

@@ -14,7 +14,7 @@ use client::{User, RECEIVE_TIMEOUT};
use collections::{HashMap, HashSet};
use fs::{FakeFs, Fs as _, RemoveOptions};
use futures::{channel::mpsc, StreamExt as _};
use prompt_library::PromptBuilder;
use prompt_store::PromptBuilder;
use git::status::{FileStatus, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode};
use gpui::{
@@ -6354,7 +6354,7 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
// Open item 1 as preview
workspace
.update_in(cx, |workspace, window, cx| {
workspace.open_path_preview(path_1.clone(), None, true, true, window, cx)
workspace.open_path_preview(path_1.clone(), None, true, true, true, window, cx)
})
.await
.unwrap();
@@ -6375,7 +6375,7 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
// Open item 2 as preview
workspace
.update_in(cx, |workspace, window, cx| {
workspace.open_path_preview(path_2.clone(), None, true, true, window, cx)
workspace.open_path_preview(path_2.clone(), None, true, true, true, window, cx)
})
.await
.unwrap();
@@ -6507,7 +6507,7 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
// Open item 2 as preview in right pane
workspace
.update_in(cx, |workspace, window, cx| {
workspace.open_path_preview(path_2.clone(), None, true, true, window, cx)
workspace.open_path_preview(path_2.clone(), None, true, true, true, window, cx)
})
.await
.unwrap();
@@ -6545,7 +6545,7 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
// Open item 2 as preview in left pane
workspace
.update_in(cx, |workspace, window, cx| {
workspace.open_path_preview(path_2.clone(), None, true, true, window, cx)
workspace.open_path_preview(path_2.clone(), None, true, true, true, window, cx)
})
.await
.unwrap();
@@ -6741,19 +6741,24 @@ async fn test_remote_git_branches(
.collect::<HashSet<_>>();
let (project_a, worktree_id) = client_a.build_local_project("/project", cx_a).await;
let project_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
.await
.unwrap();
let project_b = client_b.join_remote_project(project_id, cx_b).await;
let root_path = ProjectPath::root_path(worktree_id);
// Client A sees that a guest has joined.
// Client A sees that a guest has joined and the repo has been populated
executor.run_until_parked();
let repo_b = cx_b.update(|cx| project_b.read(cx).active_repository(cx).unwrap());
let root_path = ProjectPath::root_path(worktree_id);
let branches_b = cx_b
.update(|cx| project_b.update(cx, |project, cx| project.branches(root_path.clone(), cx)))
.update(|cx| repo_b.update(cx, |repository, _| repository.branches()))
.await
.unwrap()
.unwrap();
let new_branch = branches[2];
@@ -6765,13 +6770,10 @@ async fn test_remote_git_branches(
assert_eq!(branches_b, branches_set);
cx_b.update(|cx| {
project_b.update(cx, |project, cx| {
project.update_or_create_branch(root_path.clone(), new_branch.to_string(), cx)
})
})
.await
.unwrap();
cx_b.update(|cx| repo_b.read(cx).change_branch(new_branch.to_string()))
.await
.unwrap()
.unwrap();
executor.run_until_parked();
@@ -6789,11 +6791,21 @@ async fn test_remote_git_branches(
// Also try creating a new branch
cx_b.update(|cx| {
project_b.update(cx, |project, cx| {
project.update_or_create_branch(root_path.clone(), "totally-new-branch".to_string(), cx)
})
repo_b
.read(cx)
.create_branch("totally-new-branch".to_string())
})
.await
.unwrap()
.unwrap();
cx_b.update(|cx| {
repo_b
.read(cx)
.change_branch("totally-new-branch".to_string())
})
.await
.unwrap()
.unwrap();
executor.run_until_parked();

View File

@@ -276,11 +276,13 @@ async fn test_ssh_collaboration_git_branches(
// has some git repositories
executor.run_until_parked();
let repo_b = cx_b.update(|cx| project_b.read(cx).active_repository(cx).unwrap());
let root_path = ProjectPath::root_path(worktree_id);
let branches_b = cx_b
.update(|cx| project_b.update(cx, |project, cx| project.branches(root_path.clone(), cx)))
.update(|cx| repo_b.read(cx).branches())
.await
.unwrap()
.unwrap();
let new_branch = branches[2];
@@ -292,13 +294,10 @@ async fn test_ssh_collaboration_git_branches(
assert_eq!(&branches_b, &branches_set);
cx_b.update(|cx| {
project_b.update(cx, |project, cx| {
project.update_or_create_branch(root_path.clone(), new_branch.to_string(), cx)
})
})
.await
.unwrap();
cx_b.update(|cx| repo_b.read(cx).change_branch(new_branch.to_string()))
.await
.unwrap()
.unwrap();
executor.run_until_parked();
@@ -318,11 +317,21 @@ async fn test_ssh_collaboration_git_branches(
// Also try creating a new branch
cx_b.update(|cx| {
project_b.update(cx, |project, cx| {
project.update_or_create_branch(root_path.clone(), "totally-new-branch".to_string(), cx)
})
repo_b
.read(cx)
.create_branch("totally-new-branch".to_string())
})
.await
.unwrap()
.unwrap();
cx_b.update(|cx| {
repo_b
.read(cx)
.change_branch("totally-new-branch".to_string())
})
.await
.unwrap()
.unwrap();
executor.run_until_parked();

View File

@@ -323,7 +323,7 @@ impl ChatPanel {
.my_0p5()
.px_0p5()
.gap_x_1()
.rounded_md()
.rounded_sm()
.child(Icon::new(IconName::ReplyArrowRight).color(Color::Muted))
.when(reply_to_message.is_none(), |el| {
el.child(
@@ -358,7 +358,7 @@ impl ChatPanel {
.my_0p5()
.px_0p5()
.gap_x_1()
.rounded_md()
.rounded_sm()
.overflow_hidden()
.hover(|style| style.bg(cx.theme().colors().element_background))
.child(Icon::new(IconName::ReplyArrowRight).color(Color::Muted))
@@ -476,7 +476,7 @@ impl ChatPanel {
div()
.group("")
.bg(background)
.rounded_md()
.rounded_sm()
.overflow_hidden()
.px_1p5()
.py_0p5()
@@ -563,7 +563,7 @@ impl ChatPanel {
.child(
div()
.px_1()
.rounded_md()
.rounded_sm()
.text_ui_xs(cx)
.bg(cx.theme().colors().background)
.child("New messages"),
@@ -589,7 +589,7 @@ impl ChatPanel {
div()
.w_6()
.bg(cx.theme().colors().element_background)
.hover(|style| style.bg(cx.theme().colors().element_hover).rounded_md())
.hover(|style| style.bg(cx.theme().colors().element_hover).rounded_sm())
.child(child)
}
@@ -604,7 +604,7 @@ impl ChatPanel {
.absolute()
.right_2()
.overflow_hidden()
.rounded_md()
.rounded_sm()
.border_color(cx.theme().colors().element_selected)
.border_1()
.when(!self.has_open_menu(message_id), |el| {

View File

@@ -531,7 +531,7 @@ impl Render for MessageEditor {
.px_2()
.py_1()
.bg(cx.theme().colors().editor_background)
.rounded_md()
.rounded_sm()
.child(EditorElement::new(
&self.editor,
EditorStyle {

View File

@@ -17,7 +17,7 @@ use gpui::{
ListState, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, SharedString,
Styled, Subscription, Task, TextStyle, WeakEntity, Window,
};
use menu::{Cancel, Confirm, SecondaryConfirm, SelectNext, SelectPrev};
use menu::{Cancel, Confirm, SecondaryConfirm, SelectNext, SelectPrevious};
use project::{Fs, Project};
use rpc::{
proto::{self, ChannelVisibility, PeerId},
@@ -1430,7 +1430,7 @@ impl CollabPanel {
cx.notify();
}
fn select_prev(&mut self, _: &SelectPrev, _: &mut Window, cx: &mut Context<Self>) {
fn select_previous(&mut self, _: &SelectPrevious, _: &mut Window, cx: &mut Context<Self>) {
let ix = self.selection.take().unwrap_or(0);
if ix > 0 {
self.selection = Some(ix - 1);
@@ -2878,7 +2878,7 @@ impl Render for CollabPanel {
.key_context("CollabPanel")
.on_action(cx.listener(CollabPanel::cancel))
.on_action(cx.listener(CollabPanel::select_next))
.on_action(cx.listener(CollabPanel::select_prev))
.on_action(cx.listener(CollabPanel::select_previous))
.on_action(cx.listener(CollabPanel::confirm))
.on_action(cx.listener(CollabPanel::insert_space))
.on_action(cx.listener(CollabPanel::remove_selected_channel))

View File

@@ -22,7 +22,7 @@ use ui::{
h_flex, prelude::*, v_flex, Avatar, Button, Icon, IconButton, IconName, Label, Tab, Tooltip,
};
use util::{ResultExt, TryFutureExt};
use workspace::notifications::NotificationId;
use workspace::notifications::{Notification as WorkspaceNotification, NotificationId};
use workspace::{
dock::{DockPosition, Panel, PanelEvent},
Workspace,
@@ -300,7 +300,7 @@ impl NotificationPanel {
.hover(|style| {
style
.bg(cx.theme().colors().element_selected)
.rounded_md()
.rounded_sm()
})
.child(Label::new(relative_timestamp).color(Color::Muted))
.tooltip(move |_, cx| {
@@ -570,11 +570,12 @@ impl NotificationPanel {
workspace.dismiss_notification(&id, cx);
workspace.show_notification(id, cx, |cx| {
let workspace = cx.entity().downgrade();
cx.new(|_| NotificationToast {
cx.new(|cx| NotificationToast {
notification_id,
actor,
text,
workspace,
focus_handle: cx.focus_handle(),
})
})
})
@@ -771,8 +772,17 @@ pub struct NotificationToast {
actor: Option<Arc<User>>,
text: String,
workspace: WeakEntity<Workspace>,
focus_handle: FocusHandle,
}
impl Focusable for NotificationToast {
fn focus_handle(&self, _cx: &App) -> FocusHandle {
self.focus_handle.clone()
}
}
impl WorkspaceNotification for NotificationToast {}
impl NotificationToast {
fn focus_notification_panel(&self, window: &mut Window, cx: &mut Context<Self>) {
let workspace = self.workspace.clone();

View File

@@ -27,7 +27,7 @@ impl<'de> Deserialize<'de> for ChatPanelButton {
{
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
impl serde::de::Visitor<'_> for Visitor {
type Value = ChatPanelButton;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {

View File

@@ -15,7 +15,6 @@ path = "src/component.rs"
collections.workspace = true
gpui.workspace = true
linkme.workspace = true
once_cell.workspace = true
parking_lot.workspace = true
theme.workspace = true

View File

@@ -1,14 +1,15 @@
use std::fmt::Display;
use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
use collections::HashMap;
use gpui::{div, prelude::*, px, AnyElement, App, IntoElement, RenderOnce, SharedString, Window};
use linkme::distributed_slice;
use once_cell::sync::Lazy;
use parking_lot::RwLock;
use theme::ActiveTheme;
pub trait Component {
fn scope() -> Option<&'static str>;
fn scope() -> Option<ComponentScope>;
fn name() -> &'static str {
std::any::type_name::<Self>()
}
@@ -18,7 +19,7 @@ pub trait Component {
}
pub trait ComponentPreview: Component {
fn preview(_window: &mut Window, _cx: &App) -> AnyElement;
fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement;
}
#[distributed_slice]
@@ -27,12 +28,12 @@ pub static __ALL_COMPONENTS: [fn()] = [..];
#[distributed_slice]
pub static __ALL_PREVIEWS: [fn()] = [..];
pub static COMPONENT_DATA: Lazy<RwLock<ComponentRegistry>> =
Lazy::new(|| RwLock::new(ComponentRegistry::new()));
pub static COMPONENT_DATA: LazyLock<RwLock<ComponentRegistry>> =
LazyLock::new(|| RwLock::new(ComponentRegistry::new()));
pub struct ComponentRegistry {
components: Vec<(Option<&'static str>, &'static str, Option<&'static str>)>,
previews: HashMap<&'static str, fn(&mut Window, &App) -> AnyElement>,
components: Vec<(Option<ComponentScope>, &'static str, Option<&'static str>)>,
previews: HashMap<&'static str, fn(&mut Window, &mut App) -> AnyElement>,
}
impl ComponentRegistry {
@@ -62,7 +63,10 @@ pub fn register_component<T: Component>() {
}
pub fn register_preview<T: ComponentPreview>() {
let preview_data = (T::name(), T::preview as fn(&mut Window, &App) -> AnyElement);
let preview_data = (
T::name(),
T::preview as fn(&mut Window, &mut App) -> AnyElement,
);
COMPONENT_DATA
.write()
.previews
@@ -75,9 +79,9 @@ pub struct ComponentId(pub &'static str);
#[derive(Clone)]
pub struct ComponentMetadata {
name: SharedString,
scope: Option<SharedString>,
scope: Option<ComponentScope>,
description: Option<SharedString>,
preview: Option<fn(&mut Window, &App) -> AnyElement>,
preview: Option<fn(&mut Window, &mut App) -> AnyElement>,
}
impl ComponentMetadata {
@@ -85,7 +89,7 @@ impl ComponentMetadata {
self.name.clone()
}
pub fn scope(&self) -> Option<SharedString> {
pub fn scope(&self) -> Option<ComponentScope> {
self.scope.clone()
}
@@ -93,7 +97,7 @@ impl ComponentMetadata {
self.description.clone()
}
pub fn preview(&self) -> Option<fn(&mut Window, &App) -> AnyElement> {
pub fn preview(&self) -> Option<fn(&mut Window, &mut App) -> AnyElement> {
self.preview
}
}
@@ -149,14 +153,14 @@ pub fn components() -> AllComponents {
let data = COMPONENT_DATA.read();
let mut all_components = AllComponents::new();
for &(scope, name, description) in &data.components {
let scope = scope.map(Into::into);
for (ref scope, name, description) in &data.components {
let preview = data.previews.get(name).cloned();
let component_name = SharedString::new_static(name);
all_components.insert(
ComponentId(name),
ComponentMetadata {
name: name.into(),
scope,
name: component_name,
scope: scope.clone(),
description: description.map(Into::into),
preview,
},
@@ -166,6 +170,59 @@ pub fn components() -> AllComponents {
all_components
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ComponentScope {
Layout,
Input,
Notification,
Editor,
Collaboration,
VersionControl,
Unknown(SharedString),
}
impl Display for ComponentScope {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ComponentScope::Layout => write!(f, "Layout"),
ComponentScope::Input => write!(f, "Input"),
ComponentScope::Notification => write!(f, "Notification"),
ComponentScope::Editor => write!(f, "Editor"),
ComponentScope::Collaboration => write!(f, "Collaboration"),
ComponentScope::VersionControl => write!(f, "Version Control"),
ComponentScope::Unknown(name) => write!(f, "Unknown: {}", name),
}
}
}
impl From<&str> for ComponentScope {
fn from(value: &str) -> Self {
match value {
"Layout" => ComponentScope::Layout,
"Input" => ComponentScope::Input,
"Notification" => ComponentScope::Notification,
"Editor" => ComponentScope::Editor,
"Collaboration" => ComponentScope::Collaboration,
"Version Control" | "VersionControl" => ComponentScope::VersionControl,
_ => ComponentScope::Unknown(SharedString::new(value)),
}
}
}
impl From<String> for ComponentScope {
fn from(value: String) -> Self {
match value.as_str() {
"Layout" => ComponentScope::Layout,
"Input" => ComponentScope::Input,
"Notification" => ComponentScope::Notification,
"Editor" => ComponentScope::Editor,
"Collaboration" => ComponentScope::Collaboration,
"Version Control" | "VersionControl" => ComponentScope::VersionControl,
_ => ComponentScope::Unknown(SharedString::new(value)),
}
}
}
/// Which side of the preview to show labels on
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExampleLabelSide {
@@ -174,8 +231,8 @@ pub enum ExampleLabelSide {
/// Right side
Right,
/// Top side
Top,
#[default]
Top,
/// Bottom side
Bottom,
}
@@ -205,6 +262,7 @@ impl RenderOnce for ComponentExample {
.text_size(px(10.))
.text_color(cx.theme().colors().text_muted)
.when(self.grow, |this| this.flex_1())
.when(!self.grow, |this| this.flex_none())
.child(self.element)
.child(self.variant_name)
.into_any_element()
@@ -235,6 +293,7 @@ pub struct ComponentExampleGroup {
pub title: Option<SharedString>,
pub examples: Vec<ComponentExample>,
pub grow: bool,
pub vertical: bool,
}
impl RenderOnce for ComponentExampleGroup {
@@ -270,6 +329,7 @@ impl RenderOnce for ComponentExampleGroup {
.child(
div()
.flex()
.when(self.vertical, |this| this.flex_col())
.items_start()
.w_full()
.gap_6()
@@ -287,6 +347,7 @@ impl ComponentExampleGroup {
title: None,
examples,
grow: false,
vertical: false,
}
}
@@ -296,6 +357,7 @@ impl ComponentExampleGroup {
title: Some(title.into()),
examples,
grow: false,
vertical: false,
}
}
@@ -304,6 +366,12 @@ impl ComponentExampleGroup {
self.grow = true;
self
}
/// Lay the group out vertically.
pub fn vertical(mut self) -> Self {
self.vertical = true;
self
}
}
/// Create a single example

View File

@@ -15,7 +15,11 @@ path = "src/component_preview.rs"
default = []
[dependencies]
client.workspace = true
component.workspace = true
gpui.workspace = true
languages.workspace = true
project.workspace = true
ui.workspace = true
workspace.workspace = true
notifications.workspace = true

View File

@@ -2,18 +2,49 @@
//!
//! A view for exploring Zed components.
use std::iter::Iterator;
use std::sync::Arc;
use client::UserStore;
use component::{components, ComponentMetadata};
use gpui::{list, prelude::*, uniform_list, App, EventEmitter, FocusHandle, Focusable, Window};
use gpui::{
list, prelude::*, uniform_list, App, Entity, EventEmitter, FocusHandle, Focusable, Task,
WeakEntity, Window,
};
use gpui::{ListState, ScrollHandle, UniformListScrollHandle};
use ui::{prelude::*, ListItem};
use languages::LanguageRegistry;
use notifications::status_toast::{StatusToast, ToastIcon};
use project::Project;
use ui::{prelude::*, Divider, ListItem, ListSubHeader};
use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId};
use workspace::{AppState, ItemId, SerializableItem};
pub fn init(app_state: Arc<AppState>, cx: &mut App) {
let app_state = app_state.clone();
cx.observe_new(move |workspace: &mut Workspace, _, cx| {
let app_state = app_state.clone();
let weak_workspace = cx.entity().downgrade();
pub fn init(cx: &mut App) {
cx.observe_new(|workspace: &mut Workspace, _, _cx| {
workspace.register_action(
|workspace, _: &workspace::OpenComponentPreview, window, cx| {
let component_preview = cx.new(|cx| ComponentPreview::new(window, cx));
move |workspace, _: &workspace::OpenComponentPreview, window, cx| {
let app_state = app_state.clone();
let language_registry = app_state.languages.clone();
let user_store = app_state.user_store.clone();
let component_preview = cx.new(|cx| {
ComponentPreview::new(
weak_workspace.clone(),
language_registry,
user_store,
None,
cx,
)
});
workspace.add_item_to_active_pane(
Box::new(component_preview),
None,
@@ -27,6 +58,23 @@ pub fn init(cx: &mut App) {
.detach();
}
enum PreviewEntry {
Component(ComponentMetadata),
SectionHeader(SharedString),
}
impl From<ComponentMetadata> for PreviewEntry {
fn from(component: ComponentMetadata) -> Self {
PreviewEntry::Component(component)
}
}
impl From<SharedString> for PreviewEntry {
fn from(section_header: SharedString) -> Self {
PreviewEntry::SectionHeader(section_header)
}
}
struct ComponentPreview {
focus_handle: FocusHandle,
_view_scroll_handle: ScrollHandle,
@@ -34,31 +82,55 @@ struct ComponentPreview {
components: Vec<ComponentMetadata>,
component_list: ListState,
selected_index: usize,
language_registry: Arc<LanguageRegistry>,
workspace: WeakEntity<Workspace>,
user_store: Entity<UserStore>,
}
impl ComponentPreview {
pub fn new(_window: &mut Window, cx: &mut Context<Self>) -> Self {
pub fn new(
workspace: WeakEntity<Workspace>,
language_registry: Arc<LanguageRegistry>,
user_store: Entity<UserStore>,
selected_index: impl Into<Option<usize>>,
cx: &mut Context<Self>,
) -> Self {
let components = components().all_sorted();
let initial_length = components.len();
let selected_index = selected_index.into().unwrap_or(0);
let component_list = ListState::new(initial_length, gpui::ListAlignment::Top, px(500.0), {
let this = cx.entity().downgrade();
move |ix, window: &mut Window, cx: &mut App| {
this.update(cx, |this, cx| {
this.render_preview(ix, window, cx).into_any_element()
})
.unwrap()
}
});
let component_list =
ListState::new(initial_length, gpui::ListAlignment::Top, px(1500.0), {
let this = cx.entity().downgrade();
move |ix, window: &mut Window, cx: &mut App| {
this.update(cx, |this, cx| {
let component = this.get_component(ix);
this.render_preview(ix, &component, window, cx)
.into_any_element()
})
.unwrap()
}
});
Self {
let mut component_preview = Self {
focus_handle: cx.focus_handle(),
_view_scroll_handle: ScrollHandle::new(),
nav_scroll_handle: UniformListScrollHandle::new(),
language_registry,
user_store,
workspace,
components,
component_list,
selected_index: 0,
selected_index,
};
if component_preview.selected_index > 0 {
component_preview.scroll_to_preview(component_preview.selected_index, cx);
}
component_preview.update_component_list(cx);
component_preview
}
fn scroll_to_preview(&mut self, ix: usize, cx: &mut Context<Self>) {
@@ -71,32 +143,158 @@ impl ComponentPreview {
self.components[ix].clone()
}
fn scope_ordered_entries(&self) -> Vec<PreviewEntry> {
use std::collections::HashMap;
// Group components by scope
let mut scope_groups: HashMap<Option<ComponentScope>, Vec<ComponentMetadata>> =
HashMap::default();
for component in &self.components {
scope_groups
.entry(component.scope())
.or_insert_with(Vec::new)
.push(component.clone());
}
// Sort components within each scope by name
for components in scope_groups.values_mut() {
components.sort_by_key(|c| c.name().to_lowercase());
}
// Build entries with scopes in a defined order
let mut entries = Vec::new();
// Define scope order (we want Unknown at the end)
let known_scopes = [
ComponentScope::Layout,
ComponentScope::Input,
ComponentScope::Editor,
ComponentScope::Notification,
ComponentScope::Collaboration,
ComponentScope::VersionControl,
];
// First add components with known scopes
for scope in known_scopes.iter() {
let scope_key = Some(scope.clone());
if let Some(components) = scope_groups.remove(&scope_key) {
if !components.is_empty() {
// Add section header
entries.push(PreviewEntry::SectionHeader(scope.to_string().into()));
// Add all components under this scope
for component in components {
entries.push(PreviewEntry::Component(component));
}
}
}
}
// Handle components with Unknown scope
for (scope, components) in &scope_groups {
if let Some(ComponentScope::Unknown(_)) = scope {
if !components.is_empty() {
// Add the unknown scope header
if let Some(scope_value) = scope {
entries.push(PreviewEntry::SectionHeader(scope_value.to_string().into()));
}
// Add all components under this unknown scope
for component in components {
entries.push(PreviewEntry::Component(component.clone()));
}
}
}
}
// Handle components with no scope
if let Some(components) = scope_groups.get(&None) {
if !components.is_empty() {
entries.push(PreviewEntry::SectionHeader("Uncategorized".into()));
for component in components {
entries.push(PreviewEntry::Component(component.clone()));
}
}
}
entries
}
fn render_sidebar_entry(
&self,
ix: usize,
entry: &PreviewEntry,
selected: bool,
cx: &Context<Self>,
) -> impl IntoElement {
let component = self.get_component(ix);
match entry {
PreviewEntry::Component(component_metadata) => ListItem::new(ix)
.child(Label::new(component_metadata.name().clone()).color(Color::Default))
.selectable(true)
.toggle_state(selected)
.inset(true)
.on_click(cx.listener(move |this, _, _, cx| {
this.scroll_to_preview(ix, cx);
}))
.into_any_element(),
PreviewEntry::SectionHeader(shared_string) => ListSubHeader::new(shared_string)
.inset(true)
.into_any_element(),
}
}
ListItem::new(ix)
.child(Label::new(component.name().clone()).color(Color::Default))
.selectable(true)
.toggle_state(selected)
.inset(true)
.on_click(cx.listener(move |this, _, _, cx| {
this.scroll_to_preview(ix, cx);
}))
fn update_component_list(&mut self, cx: &mut Context<Self>) {
let new_len = self.scope_ordered_entries().len();
let entries = self.scope_ordered_entries();
let weak_entity = cx.entity().downgrade();
let new_list = ListState::new(
new_len,
gpui::ListAlignment::Top,
px(1500.0),
move |ix, window, cx| {
let entry = &entries[ix];
weak_entity
.update(cx, |this, cx| match entry {
PreviewEntry::Component(component) => this
.render_preview(ix, component, window, cx)
.into_any_element(),
PreviewEntry::SectionHeader(shared_string) => this
.render_scope_header(ix, shared_string.clone(), window, cx)
.into_any_element(),
})
.unwrap()
},
);
self.component_list = new_list;
}
fn render_scope_header(
&self,
_ix: usize,
title: SharedString,
_window: &Window,
_cx: &App,
) -> impl IntoElement {
h_flex()
.w_full()
.h_10()
.items_center()
.child(Headline::new(title).size(HeadlineSize::XSmall))
.child(Divider::horizontal())
}
fn render_preview(
&self,
ix: usize,
_ix: usize,
component: &ComponentMetadata,
window: &mut Window,
cx: &Context<Self>,
cx: &mut App,
) -> impl IntoElement {
let component = self.get_component(ix);
let name = component.name();
let scope = component.scope();
@@ -108,7 +306,7 @@ impl ComponentPreview {
v_flex()
.border_1()
.border_color(cx.theme().colors().border)
.rounded_md()
.rounded_sm()
.w_full()
.gap_4()
.py_4()
@@ -142,10 +340,32 @@ impl ComponentPreview {
)
.into_any_element()
}
fn test_status_toast(&self, window: &mut Window, cx: &mut Context<Self>) {
if let Some(workspace) = self.workspace.upgrade() {
workspace.update(cx, |workspace, cx| {
let status_toast = StatusToast::new(
"`zed/new-notification-system` created!",
window,
cx,
|this, _, cx| {
this.icon(ToastIcon::new(IconName::GitBranchSmall).color(Color::Muted))
.action(
"Open Pull Request",
cx.listener(|_, _, _, cx| cx.open_url("https://github.com/")),
)
},
);
workspace.toggle_status_toast(window, cx, status_toast)
});
}
}
}
impl Render for ComponentPreview {
fn render(&mut self, _window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
let sidebar_entries = self.scope_ordered_entries();
h_flex()
.id("component-preview")
.key_context("ComponentPreview")
@@ -156,21 +376,44 @@ impl Render for ComponentPreview {
.px_2()
.bg(cx.theme().colors().editor_background)
.child(
uniform_list(
cx.entity().clone(),
"component-nav",
self.components.len(),
move |this, range, _window, cx| {
range
.map(|ix| this.render_sidebar_entry(ix, ix == this.selected_index, cx))
.collect()
},
)
.track_scroll(self.nav_scroll_handle.clone())
.pt_4()
.w(px(240.))
.h_full()
.flex_grow(),
v_flex()
.h_full()
.child(
uniform_list(
cx.entity().clone(),
"component-nav",
sidebar_entries.len(),
move |this, range, _window, cx| {
range
.map(|ix| {
this.render_sidebar_entry(
ix,
&sidebar_entries[ix],
ix == this.selected_index,
cx,
)
})
.collect()
},
)
.track_scroll(self.nav_scroll_handle.clone())
.pt_4()
.w(px(240.))
.h_full()
.flex_1(),
)
.child(
div().w_full().pb_4().child(
Button::new("toast-test", "Launch Toast")
.on_click(cx.listener({
move |this, _, window, cx| {
this.test_status_toast(window, cx);
cx.notify();
}
}))
.full_width(),
),
),
)
.child(
v_flex()
@@ -213,16 +456,86 @@ impl Item for ComponentPreview {
fn clone_on_split(
&self,
_workspace_id: Option<WorkspaceId>,
window: &mut Window,
_window: &mut Window,
cx: &mut Context<Self>,
) -> Option<gpui::Entity<Self>>
where
Self: Sized,
{
Some(cx.new(|cx| Self::new(window, cx)))
let language_registry = self.language_registry.clone();
let user_store = self.user_store.clone();
let weak_workspace = self.workspace.clone();
let selected_index = self.selected_index;
Some(cx.new(|cx| {
Self::new(
weak_workspace,
language_registry,
user_store,
selected_index,
cx,
)
}))
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
f(*event)
}
}
impl SerializableItem for ComponentPreview {
fn serialized_item_kind() -> &'static str {
"ComponentPreview"
}
fn deserialize(
project: Entity<Project>,
workspace: WeakEntity<Workspace>,
_workspace_id: WorkspaceId,
_item_id: ItemId,
window: &mut Window,
cx: &mut App,
) -> Task<gpui::Result<Entity<Self>>> {
let user_store = project.read(cx).user_store().clone();
let language_registry = project.read(cx).languages().clone();
window.spawn(cx, |mut cx| async move {
let user_store = user_store.clone();
let language_registry = language_registry.clone();
let weak_workspace = workspace.clone();
cx.update(|_, cx| {
Ok(cx.new(|cx| {
ComponentPreview::new(weak_workspace, language_registry, user_store, None, cx)
}))
})?
})
}
fn cleanup(
_workspace_id: WorkspaceId,
_alive_items: Vec<ItemId>,
_window: &mut Window,
_cx: &mut App,
) -> Task<gpui::Result<()>> {
Task::ready(Ok(()))
// window.spawn(cx, |_| {
// ...
// })
}
fn serialize(
&mut self,
_workspace: &mut Workspace,
_item_id: ItemId,
_closing: bool,
_window: &mut Window,
_cx: &mut Context<Self>,
) -> Option<Task<gpui::Result<()>>> {
// TODO: Serialize the active index so we can re-open to the same place
None
}
fn should_serialize(&self, _event: &Self::Event) -> bool {
false
}
}

View File

@@ -14,6 +14,7 @@ path = "src/context_server.rs"
[dependencies]
anyhow.workspace = true
assistant_tool.workspace = true
async-trait.workspace = true
collections.workspace = true
command_palette_hooks.workspace = true
context_server_settings.workspace = true

View File

@@ -1,16 +1,12 @@
use anyhow::{anyhow, Context as _, Result};
use anyhow::{anyhow, Context, Result};
use collections::HashMap;
use futures::{channel::oneshot, io::BufWriter, select, AsyncRead, AsyncWrite, FutureExt};
use futures::{channel::oneshot, select, FutureExt, StreamExt};
use gpui::{AppContext as _, AsyncApp, BackgroundExecutor, Task};
use parking_lot::Mutex;
use postage::barrier;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::{value::RawValue, Value};
use smol::{
channel,
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
process::Child,
};
use smol::channel;
use std::{
fmt,
path::PathBuf,
@@ -22,6 +18,8 @@ use std::{
};
use util::TryFutureExt;
use crate::transport::{StdioTransport, Transport};
const JSON_RPC_VERSION: &str = "2.0";
const REQUEST_TIMEOUT: Duration = Duration::from_secs(60);
@@ -55,7 +53,8 @@ pub struct Client {
#[allow(dead_code)]
output_done_rx: Mutex<Option<barrier::Receiver>>,
executor: BackgroundExecutor,
server: Arc<Mutex<Option<Child>>>,
#[allow(dead_code)]
transport: Arc<dyn Transport>,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -152,25 +151,13 @@ impl Client {
&binary.args
);
let mut command = util::command::new_smol_command(&binary.executable);
command
.args(&binary.args)
.envs(binary.env.unwrap_or_default())
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.kill_on_drop(true);
let server_name = binary
.executable
.file_name()
.map(|name| name.to_string_lossy().to_string())
.unwrap_or_else(String::new);
let mut server = command.spawn().with_context(|| {
format!(
"failed to spawn command. (path={:?}, args={:?})",
binary.executable, &binary.args
)
})?;
let stdin = server.stdin.take().unwrap();
let stdout = server.stdout.take().unwrap();
let stderr = server.stderr.take().unwrap();
let transport = Arc::new(StdioTransport::new(binary, &cx)?);
let (outbound_tx, outbound_rx) = channel::unbounded::<String>();
let (output_done_tx, output_done_rx) = barrier::channel();
@@ -183,18 +170,22 @@ impl Client {
let stdout_input_task = cx.spawn({
let notification_handlers = notification_handlers.clone();
let response_handlers = response_handlers.clone();
let transport = transport.clone();
move |cx| {
Self::handle_input(stdout, notification_handlers, response_handlers, cx).log_err()
Self::handle_input(transport, notification_handlers, response_handlers, cx)
.log_err()
}
});
let stderr_input_task = cx.spawn(|_| Self::handle_stderr(stderr).log_err());
let stderr_input_task = cx.spawn(|_| Self::handle_stderr(transport.clone()).log_err());
let input_task = cx.spawn(|_| async move {
let (stdout, stderr) = futures::join!(stdout_input_task, stderr_input_task);
stdout.or(stderr)
});
let output_task = cx.background_spawn({
let transport = transport.clone();
Self::handle_output(
stdin,
transport,
outbound_rx,
output_done_tx,
response_handlers.clone(),
@@ -202,24 +193,18 @@ impl Client {
.log_err()
});
let mut context_server = Self {
Ok(Self {
server_id,
notification_handlers,
response_handlers,
name: "".into(),
name: server_name.into(),
next_id: Default::default(),
outbound_tx,
executor: cx.background_executor().clone(),
io_tasks: Mutex::new(Some((input_task, output_task))),
output_done_rx: Mutex::new(Some(output_done_rx)),
server: Arc::new(Mutex::new(Some(server))),
};
if let Some(name) = binary.executable.file_name() {
context_server.name = name.to_string_lossy().into();
}
Ok(context_server)
transport,
})
}
/// Handles input from the server's stdout.
@@ -228,79 +213,53 @@ impl Client {
/// parses them as JSON-RPC responses or notifications, and dispatches them
/// to the appropriate handlers. It processes both responses (which are matched
/// to pending requests) and notifications (which trigger registered handlers).
async fn handle_input<Stdout>(
stdout: Stdout,
async fn handle_input(
transport: Arc<dyn Transport>,
notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
cx: AsyncApp,
) -> anyhow::Result<()>
where
Stdout: AsyncRead + Unpin + Send + 'static,
{
let mut stdout = BufReader::new(stdout);
let mut buffer = String::new();
) -> anyhow::Result<()> {
let mut receiver = transport.receive();
loop {
buffer.clear();
if stdout.read_line(&mut buffer).await? == 0 {
return Ok(());
}
let content = buffer.trim();
if !content.is_empty() {
if let Ok(response) = serde_json::from_str::<AnyResponse>(content) {
if let Some(handlers) = response_handlers.lock().as_mut() {
if let Some(handler) = handlers.remove(&response.id) {
handler(Ok(content.to_string()));
}
}
} else if let Ok(notification) = serde_json::from_str::<AnyNotification>(content) {
let mut notification_handlers = notification_handlers.lock();
if let Some(handler) =
notification_handlers.get_mut(notification.method.as_str())
{
handler(notification.params.unwrap_or(Value::Null), cx.clone());
while let Some(message) = receiver.next().await {
if let Ok(response) = serde_json::from_str::<AnyResponse>(&message) {
if let Some(handlers) = response_handlers.lock().as_mut() {
if let Some(handler) = handlers.remove(&response.id) {
handler(Ok(message.to_string()));
}
}
} else if let Ok(notification) = serde_json::from_str::<AnyNotification>(&message) {
let mut notification_handlers = notification_handlers.lock();
if let Some(handler) = notification_handlers.get_mut(notification.method.as_str()) {
handler(notification.params.unwrap_or(Value::Null), cx.clone());
}
}
smol::future::yield_now().await;
}
smol::future::yield_now().await;
Ok(())
}
/// Handles the stderr output from the context server.
/// Continuously reads and logs any error messages from the server.
async fn handle_stderr<Stderr>(stderr: Stderr) -> anyhow::Result<()>
where
Stderr: AsyncRead + Unpin + Send + 'static,
{
let mut stderr = BufReader::new(stderr);
let mut buffer = String::new();
loop {
buffer.clear();
if stderr.read_line(&mut buffer).await? == 0 {
return Ok(());
}
log::warn!("context server stderr: {}", buffer.trim());
smol::future::yield_now().await;
async fn handle_stderr(transport: Arc<dyn Transport>) -> anyhow::Result<()> {
while let Some(err) = transport.receive_err().next().await {
log::warn!("context server stderr: {}", err.trim());
}
Ok(())
}
/// Handles the output to the context server's stdin.
/// This function continuously receives messages from the outbound channel,
/// writes them to the server's stdin, and manages the lifecycle of response handlers.
async fn handle_output<Stdin>(
stdin: Stdin,
async fn handle_output(
transport: Arc<dyn Transport>,
outbound_rx: channel::Receiver<String>,
output_done_tx: barrier::Sender,
response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
) -> anyhow::Result<()>
where
Stdin: AsyncWrite + Unpin + Send + 'static,
{
let mut stdin = BufWriter::new(stdin);
) -> anyhow::Result<()> {
let _clear_response_handlers = util::defer({
let response_handlers = response_handlers.clone();
move || {
@@ -309,10 +268,7 @@ impl Client {
});
while let Ok(message) = outbound_rx.recv().await {
log::trace!("outgoing message: {}", message);
stdin.write_all(message.as_bytes()).await?;
stdin.write_all(b"\n").await?;
stdin.flush().await?;
transport.send(message).await?;
}
drop(output_done_tx);
Ok(())
@@ -416,14 +372,6 @@ impl Client {
}
}
impl Drop for Client {
fn drop(&mut self) {
if let Some(mut server) = self.server.lock().take() {
let _ = server.kill();
}
}
}
impl fmt::Display for ContextServerId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)

View File

@@ -4,6 +4,7 @@ mod extension_context_server;
pub mod manager;
pub mod protocol;
mod registry;
mod transport;
pub mod types;
use command_palette_hooks::CommandPaletteFilter;

View File

@@ -0,0 +1,16 @@
mod stdio_transport;
use std::pin::Pin;
use anyhow::Result;
use async_trait::async_trait;
use futures::Stream;
pub use stdio_transport::*;
#[async_trait]
pub trait Transport: Send + Sync {
async fn send(&self, message: String) -> Result<()>;
fn receive(&self) -> Pin<Box<dyn Stream<Item = String> + Send>>;
fn receive_err(&self) -> Pin<Box<dyn Stream<Item = String> + Send>>;
}

View File

@@ -0,0 +1,140 @@
use std::pin::Pin;
use anyhow::{Context as _, Result};
use async_trait::async_trait;
use futures::io::{BufReader, BufWriter};
use futures::{
AsyncBufReadExt as _, AsyncRead, AsyncWrite, AsyncWriteExt as _, Stream, StreamExt as _,
};
use gpui::AsyncApp;
use smol::channel;
use smol::process::Child;
use util::TryFutureExt as _;
use crate::client::ModelContextServerBinary;
use crate::transport::Transport;
pub struct StdioTransport {
stdout_sender: channel::Sender<String>,
stdin_receiver: channel::Receiver<String>,
stderr_receiver: channel::Receiver<String>,
server: Child,
}
impl StdioTransport {
pub fn new(binary: ModelContextServerBinary, cx: &AsyncApp) -> Result<Self> {
let mut command = util::command::new_smol_command(&binary.executable);
command
.args(&binary.args)
.envs(binary.env.unwrap_or_default())
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.kill_on_drop(true);
let mut server = command.spawn().with_context(|| {
format!(
"failed to spawn command. (path={:?}, args={:?})",
binary.executable, &binary.args
)
})?;
let stdin = server.stdin.take().unwrap();
let stdout = server.stdout.take().unwrap();
let stderr = server.stderr.take().unwrap();
let (stdin_sender, stdin_receiver) = channel::unbounded::<String>();
let (stdout_sender, stdout_receiver) = channel::unbounded::<String>();
let (stderr_sender, stderr_receiver) = channel::unbounded::<String>();
cx.spawn(|_| Self::handle_output(stdin, stdout_receiver).log_err())
.detach();
cx.spawn(|_| async move { Self::handle_input(stdout, stdin_sender).await })
.detach();
cx.spawn(|_| async move { Self::handle_err(stderr, stderr_sender).await })
.detach();
Ok(Self {
stdout_sender,
stdin_receiver,
stderr_receiver,
server,
})
}
async fn handle_input<Stdout>(stdin: Stdout, inbound_rx: channel::Sender<String>)
where
Stdout: AsyncRead + Unpin + Send + 'static,
{
let mut stdin = BufReader::new(stdin);
let mut line = String::new();
while let Ok(n) = stdin.read_line(&mut line).await {
if n == 0 {
break;
}
if inbound_rx.send(line.clone()).await.is_err() {
break;
}
line.clear();
}
}
async fn handle_output<Stdin>(
stdin: Stdin,
outbound_rx: channel::Receiver<String>,
) -> Result<()>
where
Stdin: AsyncWrite + Unpin + Send + 'static,
{
let mut stdin = BufWriter::new(stdin);
let mut pinned_rx = Box::pin(outbound_rx);
while let Some(message) = pinned_rx.next().await {
log::trace!("outgoing message: {}", message);
stdin.write_all(message.as_bytes()).await?;
stdin.write_all(b"\n").await?;
stdin.flush().await?;
}
Ok(())
}
async fn handle_err<Stderr>(stderr: Stderr, stderr_tx: channel::Sender<String>)
where
Stderr: AsyncRead + Unpin + Send + 'static,
{
let mut stderr = BufReader::new(stderr);
let mut line = String::new();
while let Ok(n) = stderr.read_line(&mut line).await {
if n == 0 {
break;
}
if stderr_tx.send(line.clone()).await.is_err() {
break;
}
line.clear();
}
}
}
#[async_trait]
impl Transport for StdioTransport {
async fn send(&self, message: String) -> Result<()> {
Ok(self.stdout_sender.send(message).await?)
}
fn receive(&self) -> Pin<Box<dyn Stream<Item = String> + Send>> {
Box::pin(self.stdin_receiver.clone())
}
fn receive_err(&self) -> Pin<Box<dyn Stream<Item = String> + Send>> {
Box::pin(self.stderr_receiver.clone())
}
}
impl Drop for StdioTransport {
fn drop(&mut self) {
let _ = self.server.kill();
}
}

View File

@@ -475,6 +475,7 @@ impl Copilot {
binary,
root_path,
None,
Default::default(),
cx.clone(),
)?;

View File

@@ -411,7 +411,7 @@ async fn stream_completion(
match serde_json::from_str::<ResponseEvent>(line) {
Ok(response) => {
if response.choices.first().is_none()
if response.choices.is_empty()
|| response.choices.first().unwrap().finish_reason.is_some()
{
None

View File

@@ -192,7 +192,7 @@ impl EditPredictionProvider for CopilotCompletionProvider {
fn discard(&mut self, cx: &mut Context<Self>) {
let settings = AllLanguageSettings::get_global(cx);
let copilot_enabled = settings.show_inline_completions(None, cx);
let copilot_enabled = settings.show_edit_predictions(None, cx);
if !copilot_enabled {
return;

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