Compare commits

...

371 Commits

Author SHA1 Message Date
Mikayla
fdc5999d79 test 2024-07-29 13:00:59 -07:00
Anthony Eid
74931bd472 Make active debug line stand out more & set unique DAP client ids (#8) 2024-07-29 19:27:07 +02:00
Remco Smits
7f8c28877f Wip start debugging request 2024-07-27 10:46:55 +02:00
Remco Smits
1ff23477de Clean up run in terminal code 2024-07-27 10:45:31 +02:00
Remco Smits
d28950c633 Line up toggle debug panel focus with other panels action names 2024-07-26 23:29:08 +02:00
Remco Smits
6ff5e00740 Fix don't go to stack frame if only the thread id matches of the stopped event
This makes sure if you have multiple debug adapters running we don't match a thread id that belongs to a different client
2024-07-25 22:21:27 +02:00
Remco Smits
b70acdfa4a Wip run in terminal request 2024-07-25 21:30:49 +02:00
Remco Smits
403ae10087 Fix infinite loop of threads, because sequence id was wrong
This was added to make sure we had the same sequence id as python debugger. But resulting in breaking xdebug(php) debug adapter
2024-07-25 21:22:51 +02:00
Anthony Eid
9a8a54109e Fix race condition in debugger (#6)
* Add support for DAP to use std for communication

* Add more descriptive error logs for DAP

* Implement handler for continued event

* Add PR feedback to handle_continued_event function

* Updated debugger

* Fix race condition when using late case debug adapters

The main thing this commit does is fix race conditions between a
dap client's event handler and sending a launch/attach request.
Some adapters would only respond to a starting request after the
client handled an init event, which could never happen because
the client used to start it's handler after it sent a launch request.

This commit also ignores undefined errors instead of crashing. This
allows the client to work with adapters that send custom events.

Finially, I added some more descriptive error messages and change
client's starting request seq from 0 to 1 to be more in line with
the specs.

* Get clippy to run successfully

* Add some function docs to dap client

* Fix debugger's highlight when stepping through code

Merging with the main zed branch removed a function that
the debugger panel relied on. I added the function back
and changed it to work with the updated internals of zed.

* Get clippy to pass & add an error log instead of using dbg!()

* Get sequence id to be incremented after getting respond and event

---------

Co-authored-by: Remco Smits <djsmits12@gmail.com>
2024-07-25 20:24:19 +02:00
Remco Smits
11c740b47a Merge branch 'main' into debugger 2024-07-25 20:03:06 +02:00
Harsh Narayan Jha
b7c6f3e98e linux: Update TryExec value in desktop file (#15149)
Release Notes:

- Fixed ([#15148](https://github.com/zed-industries/zed/issues/15148)).
2024-07-25 10:16:23 -06:00
Thorsten Ball
7146087b44 zed: Mark restored buffers as conflicted if file changed on disk between store & restore (#15207)
Previously, we've only marked restored buffers as dirty. This PR changes
that behavior in case the buffer has been associated with a file and
that file has changed on disk since the last time Zed stored its
contents.

Example timeline:

1. User edits file in Zed, buffer is dirty
2. User quites Zed with `cmd-q`
3. User changes file on disk: `echo foobar >> file.txt` or `git checkout
file.txt`
4. User starts Zed
5. File/buffer are now marked as having a conflict (yellow icon)

Release Notes:

- Unsaved files that are restored when Zed starts are now marked as
having a conflict if they have been changed on disk since the last time
they were stored.

Demo:



https://github.com/user-attachments/assets/6098b485-b325-49b7-b694-fd2fc60cce64
2024-07-25 18:04:47 +02:00
Marshall Bowers
6d3eaa055f renovate: Fix wasmtime package prefix 2024-07-25 11:45:14 -04:00
Marshall Bowers
f31c55a76f zig: Bump to v0.1.5 (#15203)
This PR bumps the Zig extension to v0.1.5.

Changes:

- #15197

Release Notes:

- N/A
2024-07-25 11:36:50 -04:00
Marshall Bowers
97750529fe renovate: Fix selector for wasmtime group (#15202)
This PR fixes the package name selector for the `wasmtime` group.

Release Notes:

- N/A
2024-07-25 11:30:31 -04:00
Uberlicious
cd9dd5ccf7 zig: Add Windows support (#15197)
Release Notes:

- N/A

Currently Windows environments do not have a `shell_env`. This causes
the Zig extension to error when trying to call `worktree.shell_env()`
since extensions api isn't yet on `0.0.7` and thus not using wasm-host
`0.0.7` we need to only call for the shell env only on non-windows
systems. 0.0.7 and onward at the moment return a Result from
`shell_env()`. The binary path is also slightly different on windows.

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-25 11:30:02 -04:00
Marshall Bowers
3ce864e69e renovate: Group wasmtime updates (#15199)
This PR updates the Renovate config to group `wasmtime` crates together
(e.g., `wasmtime` and `wasmtime-wasi`).

Release Notes:

- N/A
2024-07-25 11:21:51 -04:00
Marshall Bowers
9eeb564c5c danger: Upgrade danger to v12 (#15194)
This PR upgrades `danger` to v12.

Release Notes:

- N/A
2024-07-25 11:07:04 -04:00
Conrad Irwin
847bd35bd9 vim remap 2 (#15193)
Release Notes:

- N/A
2024-07-25 09:00:53 -06:00
renovate[bot]
b8e5ddf456 Update actions/checkout action to v4 (#15189)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://togithub.com/actions/checkout) | action |
major | `v2` -> `v4` |

---

### Release Notes

<details>
<summary>actions/checkout (actions/checkout)</summary>

###
[`v4`](https://togithub.com/actions/checkout/blob/HEAD/CHANGELOG.md#v417)

[Compare Source](https://togithub.com/actions/checkout/compare/v3...v4)

- Bump the minor-npm-dependencies group across 1 directory with 4
updates by [@&#8203;dependabot](https://togithub.com/dependabot) in
[https://github.com/actions/checkout/pull/1739](https://togithub.com/actions/checkout/pull/1739)
- Bump actions/checkout from 3 to 4 by
[@&#8203;dependabot](https://togithub.com/dependabot) in
[https://github.com/actions/checkout/pull/1697](https://togithub.com/actions/checkout/pull/1697)
- Check out other refs/\* by commit by
[@&#8203;orhantoy](https://togithub.com/orhantoy) in
[https://github.com/actions/checkout/pull/1774](https://togithub.com/actions/checkout/pull/1774)
- Pin actions/checkout's own workflows to a known, good, stable version.
by [@&#8203;jww3](https://togithub.com/jww3) in
[https://github.com/actions/checkout/pull/1776](https://togithub.com/actions/checkout/pull/1776)

###
[`v3`](https://togithub.com/actions/checkout/blob/HEAD/CHANGELOG.md#v360)

[Compare Source](https://togithub.com/actions/checkout/compare/v2...v3)

- [Fix: Mark test scripts with Bash'isms to be run via
Bash](https://togithub.com/actions/checkout/pull/1377)
- [Add option to fetch tags even if fetch-depth >
0](https://togithub.com/actions/checkout/pull/579)

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-25 09:48:15 -04:00
Peter Tripp
6998c03c59 Document git permalinks (GitHub, Gitlab, Bitbucket, SourceHut, Codeberg, etc) (#15113)
- Docs: Added "Copy Permalink to Line" and "Open Permalink to Line"
2024-07-25 09:39:57 -04:00
Antonio Scandurra
8631180e43 Avoid buffering line content to compute indent guides (#15167)
Release Notes:

- Improved performance when computing indent guides for buffers with
extremely long lines.

---------

Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Bennet <bennet@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-07-25 15:21:50 +02:00
renovate[bot]
cd9a42e8da Pin dependencies (#15188)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[2428392/gh-truncate-string-action](https://togithub.com/2428392/gh-truncate-string-action)
| action | pinDigest | -> `67b1b81` |
| [actions/checkout](https://togithub.com/actions/checkout) | action |
pinDigest | -> `692973e` |
| [actions/checkout](https://togithub.com/actions/checkout) | action |
pinDigest | -> `ee0669b` |
| [actions/setup-node](https://togithub.com/actions/setup-node) | action
| pinDigest | -> `1e60f62` |
| [actions/setup-python](https://togithub.com/actions/setup-python) |
action | pinDigest | -> `39cd149` |
|
[actions/upload-artifact](https://togithub.com/actions/upload-artifact)
| action | pinDigest | -> `0b2256b` |
|
[cloudflare/wrangler-action](https://togithub.com/cloudflare/wrangler-action)
| action | pinDigest | -> `f84a562` |
|
[dcarbone/install-jq-action](https://togithub.com/dcarbone/install-jq-action)
| action | pinDigest | -> `8867ddb` |
|
[peaceiris/actions-mdbook](https://togithub.com/peaceiris/actions-mdbook)
| action | pinDigest | -> `ee69d23` |
| [rui314/setup-mold](https://togithub.com/rui314/setup-mold) | action |
pinDigest | -> `2e332a0` |
|
[softprops/action-gh-release](https://togithub.com/softprops/action-gh-release)
| action | pinDigest | -> `de2c0eb` |
| [swatinem/rust-cache](https://togithub.com/swatinem/rust-cache) |
action | pinDigest | -> `23bce25` |
|
[tsickert/discord-webhook](https://togithub.com/tsickert/discord-webhook)
| action | pinDigest | -> `c840d45` |

---

### 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://togithub.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:eyJjcmVhdGVkSW5WZXIiOiIzNy40MzguMCIsInVwZGF0ZWRJblZlciI6IjM3LjQzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-25 09:19:05 -04:00
Marshall Bowers
3246a932ca renovate: Pin GitHub Action versions with SHAs (#15184)
This PR updates the Renovate config to pin all GitHub Action versions to
SHAs.

From the Renovate docs:

> The [GitHub Docs, using third-party
actions](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-third-party-actions)
recommend that you pin third-party GitHub Actions to a full-length
commit SHA.
>
> We recommend pinning all Actions. That's why the
helpers:pinGitHubActionDigests preset pins all GitHub Actions.
>
> For an in-depth explanation why you should pin your Github Actions,
read the [Palo Alto Networks blog post about the GitHub Actions
worm](https://www.paloaltonetworks.com/blog/prisma-cloud/github-actions-worm-dependencies/).

Release Notes:

- N/A
2024-07-25 09:02:48 -04:00
Marshall Bowers
8ba392bba6 purescript: Upgrade zed_extension_api to v0.0.6 (#15181)
This PR upgrades the PureScript extension to use v0.0.6 of the
`zed_extension_api`.

Release Notes:

- N/A
2024-07-25 08:55:16 -04:00
Kirill Bulatov
856a8ef5e8 Layout gutter hunk diff close button (X) better (#15178)
Closes https://github.com/zed-industries/zed/issues/15164

* Deleted hunk
Before:

![before_top](https://github.com/user-attachments/assets/27c72ee5-719f-4787-b222-4f597840936a)

After:

![after_top](https://github.com/user-attachments/assets/58095b21-698d-4778-8412-b680d5253e2c)

* Modified hunk
Before:

![before_down](https://github.com/user-attachments/assets/3fffb73b-7101-493c-b63b-15c3ccaf5362)

After:

![after_down](https://github.com/user-attachments/assets/f07a05ed-1260-4e2a-9388-c9cb93020d78)

* Added hunk
Before:

![before_mid](https://github.com/user-attachments/assets/0dd3f0f9-51e8-449a-bdd7-4c3ba5cd7791)

After:

![after_mid](https://github.com/user-attachments/assets/92f749bd-2d95-4650-8334-1d5c38c6aeb6)

Release Notes:

- N/A
2024-07-25 15:50:57 +03:00
renovate[bot]
6dd9ce1376 Update Rust crate smallvec to v1.13.2 (#15179)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [smallvec](https://togithub.com/servo/rust-smallvec) |
workspace.dependencies | minor | `1.11.1` -> `1.13.2` |

---

### Release Notes

<details>
<summary>servo/rust-smallvec (smallvec)</summary>

###
[`v1.13.2`](https://togithub.com/servo/rust-smallvec/releases/tag/v1.13.2)

[Compare
Source](https://togithub.com/servo/rust-smallvec/compare/v1.13.1...v1.13.2)

#### What's Changed

- Add more tests for UB by
[@&#8203;workingjubilee](https://togithub.com/workingjubilee) in
[https://github.com/servo/rust-smallvec/pull/346](https://togithub.com/servo/rust-smallvec/pull/346)
- Fix UB on out-of-bounds insert() by
[@&#8203;mbrubeck](https://togithub.com/mbrubeck) in
[https://github.com/servo/rust-smallvec/pull/345](https://togithub.com/servo/rust-smallvec/pull/345)

**Full Changelog**:
https://github.com/servo/rust-smallvec/compare/v1.13.1...v1.13.2

###
[`v1.13.1`](https://togithub.com/servo/rust-smallvec/releases/tag/v1.13.1)

[Compare
Source](https://togithub.com/servo/rust-smallvec/compare/v1.13.0...v1.13.1)

- Remove the optional `get-size` feature, to avoid a cyclic dependency
([#&#8203;335](https://togithub.com/servo/rust-smallvec/issues/335)).

**Full Changelog**:
https://github.com/servo/rust-smallvec/compare/v1.13.0...v1.13.1

###
[`v1.13.0`](https://togithub.com/servo/rust-smallvec/releases/tag/v1.13.0)

[Compare
Source](https://togithub.com/servo/rust-smallvec/compare/v1.12.0...v1.13.0)

#### What's Changed

- Impl get_size::GetSize (behind feature flag) by
[@&#8203;amandasaurus](https://togithub.com/amandasaurus) in
[https://github.com/servo/rust-smallvec/pull/335](https://togithub.com/servo/rust-smallvec/pull/335)

**Full Changelog**:
https://github.com/servo/rust-smallvec/compare/v1.12.0...v1.13.0

###
[`v1.12.0`](https://togithub.com/servo/rust-smallvec/releases/tag/v1.12.0)

[Compare
Source](https://togithub.com/servo/rust-smallvec/compare/v1.11.2...v1.12.0)

#### What's Changed

- Add `from_const_with_len_unchecked` by
[@&#8203;Expyron](https://togithub.com/Expyron) in
[https://github.com/servo/rust-smallvec/pull/329](https://togithub.com/servo/rust-smallvec/pull/329)

#### New Contributors

- [@&#8203;Expyron](https://togithub.com/Expyron) made their first
contribution in
[https://github.com/servo/rust-smallvec/pull/329](https://togithub.com/servo/rust-smallvec/pull/329)

**Full Changelog**:
https://github.com/servo/rust-smallvec/compare/v1.11.2...v1.12.0

###
[`v1.11.2`](https://togithub.com/servo/rust-smallvec/releases/tag/v1.11.2)

[Compare
Source](https://togithub.com/servo/rust-smallvec/compare/v1.11.1...v1.11.2)

#### What's Changed

- Automated testing improvements by
[@&#8203;waywardmonkeys](https://togithub.com/waywardmonkeys) in
[https://github.com/servo/rust-smallvec/pull/322](https://togithub.com/servo/rust-smallvec/pull/322)
and
[https://github.com/servo/rust-smallvec/pull/326](https://togithub.com/servo/rust-smallvec/pull/326)
- fix: don't special-case `doc` for `feature = "const_generics"` by
[@&#8203;mkroening](https://togithub.com/mkroening) in
[https://github.com/servo/rust-smallvec/pull/328](https://togithub.com/servo/rust-smallvec/pull/328)
- Code cleanup by [@&#8203;emilio](https://togithub.com/emilio) in
[https://github.com/servo/rust-smallvec/pull/316](https://togithub.com/servo/rust-smallvec/pull/316)
and [@&#8203;waywardmonkeys](https://togithub.com/waywardmonkeys) in
[https://github.com/servo/rust-smallvec/pull/323](https://togithub.com/servo/rust-smallvec/pull/323)
- Minor tweaks to doc formatting. by
[@&#8203;waywardmonkeys](https://togithub.com/waywardmonkeys) in
[https://github.com/servo/rust-smallvec/pull/318](https://togithub.com/servo/rust-smallvec/pull/318)

#### New Contributors

- [@&#8203;mkroening](https://togithub.com/mkroening) made their first
contribution in
[https://github.com/servo/rust-smallvec/pull/328](https://togithub.com/servo/rust-smallvec/pull/328)

**Full Changelog**:
https://github.com/servo/rust-smallvec/compare/v1.11.1...v1.11.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:eyJjcmVhdGVkSW5WZXIiOiIzNy40MzguMCIsInVwZGF0ZWRJblZlciI6IjM3LjQzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-25 08:48:56 -04:00
Marshall Bowers
fbbea7ab01 prisma: Upgrade zed_extension_api to v0.0.6 (#15180)
This PR upgrades the Prisma extension to use v0.0.6 of the
`zed_extension_api`.

Release Notes:

- N/A
2024-07-25 08:46:17 -04:00
Marshall Bowers
aded3dfb05 emmet: Upgrade zed_extension_api to v0.0.6 (#15177)
This PR upgrades the Emmet extension to use v0.0.6 of the
`zed_extension_api`.

Release Notes:

- N/A
2024-07-25 08:34:45 -04:00
Marshall Bowers
3053f98652 csharp: Upgrade zed_extension_api to v0.0.6 (#15175)
This PR upgrades the C# extension to use v0.0.6 of the
`zed_extension_api`.

Release Notes:

- N/A
2024-07-25 08:26:04 -04:00
Peter Tripp
ebd407deb6 Fix broken link in initial settings (#15119)
Fixes #15114
2024-07-25 08:25:27 -04:00
Marshall Bowers
5e44f5fa44 terraform: Bump to v0.0.4 (#15174)
This PR bumps the Terraform extension to v0.0.4.

Changes:

- #10937
- #15171

Release Notes:

- N/A
2024-07-25 08:22:28 -04:00
Marshall Bowers
a07122d78f deno: Bump to v0.0.2 (#15173)
This PR bumps the Deno extension to v0.0.2.

Changes:

- #14410

Release Notes:

- N/A
2024-07-25 08:19:43 -04:00
Marshall Bowers
6a079cbdc3 astro: Bump to v0.1.0 (#15172)
This PR bumps the Astro extension to v0.1.0.

Changes:

- #14849
- #15010
- #15011

Release Notes:

- N/A
2024-07-25 08:16:28 -04:00
Marshall Bowers
c7e2d5bd89 terraform: Make downloaded language server binary executable (#15171)
This PR updates the Terraform extension to make the downloaded language
server binary executable.

Resolves #14502.

Release Notes:

- N/A
2024-07-25 08:08:38 -04:00
renovate[bot]
6bff8ecb73 Update Rust crate heed to v0.20.3 (#15169)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [heed](https://togithub.com/Kerollmops/heed) | workspace.dependencies
| patch | `0.20.1` -> `0.20.3` |

---

### Release Notes

<details>
<summary>Kerollmops/heed (heed)</summary>

###
[`v0.20.3`](https://togithub.com/meilisearch/heed/releases/tag/v0.20.3):
🛁

[Compare
Source](https://togithub.com/Kerollmops/heed/compare/v0.20.2...v0.20.3)

<p align="center"><img width="280px"
src="https://raw.githubusercontent.com/meilisearch/heed/main/assets/heed-pigeon-logo.png"></a></p>
<h1 align="center" >heed</h1>

##### What's Changed
* Update dependencies by
@&#8203;irevoi[https://github.com/meilisearch/heed/pull/265](https://togithub.com/meilisearch/heed/pull/265)ll/265

###
[`v0.20.2`](https://togithub.com/meilisearch/heed/releases/tag/v0.20.2):
🛁

[Compare
Source](https://togithub.com/Kerollmops/heed/compare/v0.20.1...v0.20.2)

<p align="center"><img width="280px"
src="https://raw.githubusercontent.com/meilisearch/heed/main/assets/heed-pigeon-logo.png"></a></p>
<h1 align="center" >heed</h1>

##### What's Changed
* Introduce the `longer-keys` feature which sets `-DMDB_MAXKEYSIZE=0` by
@&#8203;tpund[https://github.com/meilisearch/heed/pull/263](https://togithub.com/meilisearch/heed/pull/263)ll/263
* Bump the internal LMDB version to v0.9.33 by
@&#8203;Kerollmo[https://github.com/meilisearch/heed/pull/264](https://togithub.com/meilisearch/heed/pull/264)ll/264

##### New Contributors
* @&#8203;tpunder made their first
contributi[https://github.com/meilisearch/heed/pull/263](https://togithub.com/meilisearch/heed/pull/263)ll/263

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-25 07:55:15 -04:00
Thorsten Ball
e2113e4895 repl: Add ability to evaluate Markdown code blocks (#15100)
This adds the ability to evaluate TypeScript and Python code blocks in
Markdown files.

cc @rgbkrk 

Demo:


https://github.com/user-attachments/assets/55352de5-68f3-4aef-920a-78ca205651ba



Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Antonio <antonio@zed.dev>
2024-07-25 12:11:19 +02:00
renovate[bot]
b56e4ff2af Update Rust crate any_vec to 0.14 (#15147)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [any_vec](https://togithub.com/tower120/any_vec) |
workspace.dependencies | minor | `0.13` -> `0.14` |

---

### Release Notes

<details>
<summary>tower120/any_vec (any_vec)</summary>

###
[`v0.14.0`](https://togithub.com/tower120/any_vec/blob/HEAD/CHANGELOG.md#0140)

[Compare
Source](https://togithub.com/tower120/any_vec/compare/v0.13.0...v0.14.0)

##### Added

-   Now library `no_std` friendly.

##### Removed

- Helpers `any_value::move_out`, `any_value::move_out_w_size` removed as
redundant.

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-25 12:38:09 +03:00
Marshall Bowers
88f68101d4 renovate: Separate major versions into multiple PRs (#15146)
This PR updates the Renovate config to split major versions changes into
multiple PRs.

This way we only have to deal with one set of breaking changes at a time
instead of jumping across multiple major versions.

Release Notes:

- N/A
2024-07-24 23:48:53 -04:00
renovate[bot]
d852a32ef1 Update Rust crate async-broadcast to v0.7.1 (#15142)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [async-broadcast](https://togithub.com/smol-rs/async-broadcast) |
dependencies | patch | `0.7.0` -> `0.7.1` |

---

### Release Notes

<details>
<summary>smol-rs/async-broadcast (async-broadcast)</summary>

###
[`v0.7.1`](https://togithub.com/smol-rs/async-broadcast/blob/HEAD/CHANGELOG.md#Version-071)

[Compare
Source](https://togithub.com/smol-rs/async-broadcast/compare/0.7.0...v0.7.1)

- Add a `poll_recv()` method to the `Receiver` type. This allows for
`Receiver`
to be used in `poll`-based contexts.
([#&#8203;56](https://togithub.com/smol-rs/async-broadcast/issues/56))

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-24 23:34:49 -04:00
renovate[bot]
b53c3b84e2 Update Rust crate async-compat to v0.2.4 (#15143)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [async-compat](https://togithub.com/smol-rs/async-compat) |
dependencies | patch | `0.2.1` -> `0.2.4` |

---

### Release Notes

<details>
<summary>smol-rs/async-compat (async-compat)</summary>

###
[`v0.2.4`](https://togithub.com/smol-rs/async-compat/blob/HEAD/CHANGELOG.md#Version-024)

[Compare
Source](https://togithub.com/smol-rs/async-compat/compare/v0.2.3...v0.2.4)

- Derive `Clone` for `Compat`.
([#&#8203;27](https://togithub.com/smol-rs/async-compat/issues/27))
- Rather than spawning our own `tokio` runtime all of the time, reuse an
existing runtime if possible.
([#&#8203;30](https://togithub.com/smol-rs/async-compat/issues/30))

###
[`v0.2.3`](https://togithub.com/smol-rs/async-compat/blob/HEAD/CHANGELOG.md#Version-023)

[Compare
Source](https://togithub.com/smol-rs/async-compat/compare/v0.2.2...v0.2.3)

- Enter the `tokio` context while dropping wrapped `tokio` types.
([#&#8203;22](https://togithub.com/smol-rs/async-compat/issues/22))

###
[`v0.2.2`](https://togithub.com/smol-rs/async-compat/blob/HEAD/CHANGELOG.md#Version-022)

[Compare
Source](https://togithub.com/smol-rs/async-compat/compare/v0.2.1...v0.2.2)

- Add `smol-rs` logo to the docs.
([#&#8203;19](https://togithub.com/smol-rs/async-compat/issues/19))

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-24 23:33:02 -04:00
renovate[bot]
3d6b07d78c Update Rust crate derive_more to v0.99.18 (#15144)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [derive_more](https://togithub.com/JelteF/derive_more) |
workspace.dependencies | patch | `0.99.17` -> `0.99.18` |

---

### Release Notes

<details>
<summary>JelteF/derive_more (derive_more)</summary>

###
[`v0.99.18`](https://togithub.com/JelteF/derive_more/compare/v0.99.17...v0.99.18)

[Compare
Source](https://togithub.com/JelteF/derive_more/compare/v0.99.17...v0.99.18)

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-24 23:31:40 -04:00
Marshall Bowers
4c0d0ad4f4 renovate: Add release notes to PR footer (#15145)
This PR updates the Renovate config to place the "Release Notes" section
in the PR body footer.

Also removed the `cla-signed` label, because the CLA bot just removes
it.

Release Notes:

- N/A
2024-07-24 23:18:33 -04:00
renovate[bot]
55575ca830 Update Rust crate aho-corasick to v1.1.3 (#15137)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [aho-corasick](https://togithub.com/BurntSushi/aho-corasick) |
workspace.dependencies | patch | `1.1.1` -> `1.1.3` |

---

### Release Notes

<details>
<summary>BurntSushi/aho-corasick (aho-corasick)</summary>

###
[`v1.1.3`](https://togithub.com/BurntSushi/aho-corasick/compare/1.1.2...1.1.3)

[Compare
Source](https://togithub.com/BurntSushi/aho-corasick/compare/1.1.2...1.1.3)

###
[`v1.1.2`](https://togithub.com/BurntSushi/aho-corasick/compare/1.1.1...1.1.2)

[Compare
Source](https://togithub.com/BurntSushi/aho-corasick/compare/1.1.1...1.1.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

---

This PR was generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View the
[repository job
log](https://developer.mend.io/github/zed-industries/zed).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy40MzguMCIsInVwZGF0ZWRJblZlciI6IjM3LjQzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJjbGEtc2lnbmVkIl19-->

Release Notes:

- N/A

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-24 23:01:55 -04:00
Marshall Bowers
ea44af4a85 Upgrade anyhow to v1.0.86 (#15140)
This PR upgrades `anyhow` to v1.0.86.

Release Notes:

- N/A
2024-07-24 22:54:02 -04:00
Marshall Bowers
5865c5e80f Update Renovate config (#15141)
This PR applies some further updates to the Renovate config.

We add the https://github.com/zed-industries/zed/labels/cla-signed label
to the PRs so that check passes.

Also seeing if we can add a "Release Notes" sections to Renovate PRs.

Release Notes:

- N/A
2024-07-24 22:51:51 -04:00
renovate[bot]
51f8013616 Configure Renovate (#15132)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

Welcome to [Renovate](https://togithub.com/renovatebot/renovate)! This
is an onboarding PR to help you understand and configure settings before
regular Pull Requests begin.

🚦 To activate Renovate, merge this Pull Request. To disable Renovate,
simply close this Pull Request unmerged.



---
### Detected Package Files

 * `Cargo.toml` (cargo)
 * `crates/activity_indicator/Cargo.toml` (cargo)
 * `crates/anthropic/Cargo.toml` (cargo)
 * `crates/assets/Cargo.toml` (cargo)
 * `crates/assistant/Cargo.toml` (cargo)
 * `crates/assistant_slash_command/Cargo.toml` (cargo)
 * `crates/assistant_tooling/Cargo.toml` (cargo)
 * `crates/audio/Cargo.toml` (cargo)
 * `crates/auto_update/Cargo.toml` (cargo)
 * `crates/breadcrumbs/Cargo.toml` (cargo)
 * `crates/call/Cargo.toml` (cargo)
 * `crates/channel/Cargo.toml` (cargo)
 * `crates/cli/Cargo.toml` (cargo)
 * `crates/client/Cargo.toml` (cargo)
 * `crates/clock/Cargo.toml` (cargo)
 * `crates/collab/Cargo.toml` (cargo)
 * `crates/collab_ui/Cargo.toml` (cargo)
 * `crates/collections/Cargo.toml` (cargo)
 * `crates/command_palette/Cargo.toml` (cargo)
 * `crates/command_palette_hooks/Cargo.toml` (cargo)
 * `crates/completion/Cargo.toml` (cargo)
 * `crates/copilot/Cargo.toml` (cargo)
 * `crates/db/Cargo.toml` (cargo)
 * `crates/dev_server_projects/Cargo.toml` (cargo)
 * `crates/diagnostics/Cargo.toml` (cargo)
 * `crates/editor/Cargo.toml` (cargo)
 * `crates/extension/Cargo.toml` (cargo)
 * `crates/extension_api/Cargo.toml` (cargo)
 * `crates/extension_cli/Cargo.toml` (cargo)
 * `crates/extensions_ui/Cargo.toml` (cargo)
 * `crates/feature_flags/Cargo.toml` (cargo)
 * `crates/feedback/Cargo.toml` (cargo)
 * `crates/file_finder/Cargo.toml` (cargo)
 * `crates/file_icons/Cargo.toml` (cargo)
 * `crates/fs/Cargo.toml` (cargo)
 * `crates/fsevent/Cargo.toml` (cargo)
 * `crates/fuzzy/Cargo.toml` (cargo)
 * `crates/git/Cargo.toml` (cargo)
 * `crates/git_hosting_providers/Cargo.toml` (cargo)
 * `crates/go_to_line/Cargo.toml` (cargo)
 * `crates/google_ai/Cargo.toml` (cargo)
 * `crates/gpui/Cargo.toml` (cargo)
 * `crates/gpui_macros/Cargo.toml` (cargo)
 * `crates/headless/Cargo.toml` (cargo)
 * `crates/html_to_markdown/Cargo.toml` (cargo)
 * `crates/http_client/Cargo.toml` (cargo)
 * `crates/image_viewer/Cargo.toml` (cargo)
 * `crates/indexed_docs/Cargo.toml` (cargo)
 * `crates/inline_completion_button/Cargo.toml` (cargo)
 * `crates/install_cli/Cargo.toml` (cargo)
 * `crates/journal/Cargo.toml` (cargo)
 * `crates/language/Cargo.toml` (cargo)
 * `crates/language_model/Cargo.toml` (cargo)
 * `crates/language_selector/Cargo.toml` (cargo)
 * `crates/language_tools/Cargo.toml` (cargo)
 * `crates/languages/Cargo.toml` (cargo)
 * `crates/live_kit_client/Cargo.toml` (cargo)
 * `crates/live_kit_server/Cargo.toml` (cargo)
 * `crates/lsp/Cargo.toml` (cargo)
 * `crates/markdown/Cargo.toml` (cargo)
 * `crates/markdown_preview/Cargo.toml` (cargo)
 * `crates/media/Cargo.toml` (cargo)
 * `crates/menu/Cargo.toml` (cargo)
 * `crates/multi_buffer/Cargo.toml` (cargo)
 * `crates/node_runtime/Cargo.toml` (cargo)
 * `crates/notifications/Cargo.toml` (cargo)
 * `crates/ollama/Cargo.toml` (cargo)
 * `crates/open_ai/Cargo.toml` (cargo)
 * `crates/outline/Cargo.toml` (cargo)
 * `crates/outline_panel/Cargo.toml` (cargo)
 * `crates/paths/Cargo.toml` (cargo)
 * `crates/picker/Cargo.toml` (cargo)
 * `crates/prettier/Cargo.toml` (cargo)
 * `crates/project/Cargo.toml` (cargo)
 * `crates/project_panel/Cargo.toml` (cargo)
 * `crates/project_symbols/Cargo.toml` (cargo)
 * `crates/proto/Cargo.toml` (cargo)
 * `crates/quick_action_bar/Cargo.toml` (cargo)
 * `crates/recent_projects/Cargo.toml` (cargo)
 * `crates/refineable/Cargo.toml` (cargo)
 * `crates/refineable/derive_refineable/Cargo.toml` (cargo)
 * `crates/release_channel/Cargo.toml` (cargo)
 * `crates/remote/Cargo.toml` (cargo)
 * `crates/remote_server/Cargo.toml` (cargo)
 * `crates/repl/Cargo.toml` (cargo)
 * `crates/rich_text/Cargo.toml` (cargo)
 * `crates/rope/Cargo.toml` (cargo)
 * `crates/rpc/Cargo.toml` (cargo)
 * `crates/search/Cargo.toml` (cargo)
 * `crates/semantic_index/Cargo.toml` (cargo)
 * `crates/semantic_version/Cargo.toml` (cargo)
 * `crates/session/Cargo.toml` (cargo)
 * `crates/settings/Cargo.toml` (cargo)
 * `crates/settings_ui/Cargo.toml` (cargo)
 * `crates/snippet/Cargo.toml` (cargo)
 * `crates/snippet_provider/Cargo.toml` (cargo)
 * `crates/sqlez/Cargo.toml` (cargo)
 * `crates/sqlez_macros/Cargo.toml` (cargo)
 * `crates/story/Cargo.toml` (cargo)
 * `crates/storybook/Cargo.toml` (cargo)
 * `crates/sum_tree/Cargo.toml` (cargo)
 * `crates/supermaven/Cargo.toml` (cargo)
 * `crates/supermaven_api/Cargo.toml` (cargo)
 * `crates/tab_switcher/Cargo.toml` (cargo)
 * `crates/task/Cargo.toml` (cargo)
 * `crates/tasks_ui/Cargo.toml` (cargo)
 * `crates/telemetry_events/Cargo.toml` (cargo)
 * `crates/terminal/Cargo.toml` (cargo)
 * `crates/terminal_view/Cargo.toml` (cargo)
 * `crates/text/Cargo.toml` (cargo)
 * `crates/theme/Cargo.toml` (cargo)
 * `crates/theme_importer/Cargo.toml` (cargo)
 * `crates/theme_selector/Cargo.toml` (cargo)
 * `crates/time_format/Cargo.toml` (cargo)
 * `crates/title_bar/Cargo.toml` (cargo)
 * `crates/ui/Cargo.toml` (cargo)
 * `crates/ui_input/Cargo.toml` (cargo)
 * `crates/util/Cargo.toml` (cargo)
 * `crates/vcs_menu/Cargo.toml` (cargo)
 * `crates/vim/Cargo.toml` (cargo)
 * `crates/welcome/Cargo.toml` (cargo)
 * `crates/workspace/Cargo.toml` (cargo)
 * `crates/worktree/Cargo.toml` (cargo)
 * `crates/zed/Cargo.toml` (cargo)
 * `crates/zed_actions/Cargo.toml` (cargo)
 * `extensions/astro/Cargo.toml` (cargo)
 * `extensions/clojure/Cargo.toml` (cargo)
 * `extensions/csharp/Cargo.toml` (cargo)
 * `extensions/dart/Cargo.toml` (cargo)
 * `extensions/deno/Cargo.toml` (cargo)
 * `extensions/elixir/Cargo.toml` (cargo)
 * `extensions/elm/Cargo.toml` (cargo)
 * `extensions/emmet/Cargo.toml` (cargo)
 * `extensions/erlang/Cargo.toml` (cargo)
 * `extensions/gleam/Cargo.toml` (cargo)
 * `extensions/glsl/Cargo.toml` (cargo)
 * `extensions/haskell/Cargo.toml` (cargo)
 * `extensions/html/Cargo.toml` (cargo)
 * `extensions/lua/Cargo.toml` (cargo)
 * `extensions/ocaml/Cargo.toml` (cargo)
 * `extensions/php/Cargo.toml` (cargo)
 * `extensions/prisma/Cargo.toml` (cargo)
 * `extensions/purescript/Cargo.toml` (cargo)
 * `extensions/ruby/Cargo.toml` (cargo)
 * `extensions/ruff/Cargo.toml` (cargo)
 * `extensions/snippets/Cargo.toml` (cargo)
 * `extensions/svelte/Cargo.toml` (cargo)
 * `extensions/terraform/Cargo.toml` (cargo)
 * `extensions/test-extension/Cargo.toml` (cargo)
 * `extensions/toml/Cargo.toml` (cargo)
 * `extensions/uiua/Cargo.toml` (cargo)
 * `extensions/vue/Cargo.toml` (cargo)
 * `extensions/zig/Cargo.toml` (cargo)
 * `tooling/xtask/Cargo.toml` (cargo)
 * `compose.yml` (docker-compose)
 * `Dockerfile` (dockerfile)
 * `.github/actions/run_tests/action.yml` (github-actions)
 * `.github/workflows/bump_patch_version.yml` (github-actions)
 * `.github/workflows/ci.yml` (github-actions)
 * `.github/workflows/danger.yml` (github-actions)
 * `.github/workflows/deploy_cloudflare.yml` (github-actions)
 * `.github/workflows/deploy_collab.yml` (github-actions)
 * `.github/workflows/publish_extension_cli.yml` (github-actions)
 * `.github/workflows/randomized_tests.yml` (github-actions)
 * `.github/workflows/release_actions.yml` (github-actions)
 * `.github/workflows/release_nightly.yml` (github-actions)
* `.github/workflows/update_all_top_ranking_issues.yml` (github-actions)
* `.github/workflows/update_weekly_top_ranking_issues.yml`
(github-actions)
 * `script/danger/package.json` (npm)
 * `script/update_top_ranking_issues/pyproject.toml` (pep621)
* `script/update_top_ranking_issues/requirements.txt` (pip_requirements)
 * `script/update_top_ranking_issues/pyproject.toml` (poetry)
 * `crates/live_kit_client/LiveKitBridge/Package.swift` (swift)

### Configuration Summary

Based on the default config's presets, Renovate will:

  - Start dependency updates only once this onboarding PR is merged
  - Show all Merge Confidence badges for pull requests.
  - Enable Renovate Dependency Dashboard creation.
- Use semantic commit type `fix` for dependencies and `chore` for all
others if semantic commits are in use.
- Ignore `node_modules`, `bower_components`, `vendor` and various
test/tests directories.
  - Group known monorepo packages together.
  - Use curated list of recommended non-monorepo package groupings.
  - Apply crowd-sourced package replacement rules.
  - Apply crowd-sourced workarounds for known problems with packages.
  - Disable semantic prefixes for commit messages and PR titles.
  - Run Renovate on following schedule: after 3pm on Wednesday

🔡 Do you want to change how Renovate upgrades your dependencies? Add
your custom config to `renovate.json` in this branch. Renovate will
update the Pull Request description the next time it runs.

---

### What to Expect

With your current configuration, Renovate will create 144 Pull Requests:

<details>
<summary>Update Python to v3.12.4</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/python-3.x`
  - Merge into: `main`
- Upgrade [python](https://togithub.com/containerbase/python-prebuild)
to `3.12.4`


</details>

<details>
<summary>Update Rust crate aho-corasick to v1.1.3</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/aho-corasick-1.x-lockfile`
  - Merge into: `main`
- Upgrade [aho-corasick](https://togithub.com/BurntSushi/aho-corasick)
to `1.1.3`


</details>

<details>
<summary>Update Rust crate anyhow to v1.0.86</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/anyhow-1.x-lockfile`
  - Merge into: `main`
  - Upgrade [anyhow](https://togithub.com/dtolnay/anyhow) to `1.0.86`


</details>

<details>
<summary>Update Rust crate async-broadcast to v0.7.1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/async-broadcast-0.x-lockfile`
  - Merge into: `main`
- Upgrade
[async-broadcast](https://togithub.com/smol-rs/async-broadcast) to
`0.7.1`


</details>

<details>
<summary>Update Rust crate async-compat to v0.2.4</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/async-compat-0.x-lockfile`
  - Merge into: `main`
- Upgrade [async-compat](https://togithub.com/smol-rs/async-compat) to
`0.2.4`


</details>

<details>
<summary>Update Rust crate async-compression to v0.4.12</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/async-compression-0.x-lockfile`
  - Merge into: `main`
- Upgrade
[async-compression](https://togithub.com/Nullus157/async-compression) to
`0.4.12`


</details>

<details>
<summary>Update Rust crate async-task to v4.7.1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/async-task-4.x-lockfile`
  - Merge into: `main`
- Upgrade [async-task](https://togithub.com/smol-rs/async-task) to
`4.7.1`


</details>

<details>
<summary>Update Rust crate async-trait to v0.1.81</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/async-trait-0.x-lockfile`
  - Merge into: `main`
- Upgrade [async-trait](https://togithub.com/dtolnay/async-trait) to
`0.1.81`


</details>

<details>
<summary>Update Rust crate backtrace to v0.3.73</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/backtrace-0.x-lockfile`
  - Merge into: `main`
- Upgrade [backtrace](https://togithub.com/rust-lang/backtrace-rs) to
`0.3.73`


</details>

<details>
<summary>Update Rust crate cargo_toml to v0.20.4</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/cargo_toml-0.x-lockfile`
  - Merge into: `main`
- Upgrade [cargo_toml](https://gitlab.com/lib.rs/cargo_toml) to `0.20.4`


</details>

<details>
<summary>Update Rust crate core-graphics to v0.23.2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/core-graphics-0.x-lockfile`
  - Merge into: `main`
- Upgrade [core-graphics](https://togithub.com/servo/core-foundation-rs)
to `0.23.2`


</details>

<details>
<summary>Update Rust crate ctor to v0.2.8</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/ctor-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [ctor](https://togithub.com/mmastrac/rust-ctor) to `0.2.8`


</details>

<details>
<summary>Update Rust crate derive_more to v0.99.18</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/derive_more-0.x-lockfile`
  - Merge into: `main`
- Upgrade [derive_more](https://togithub.com/JelteF/derive_more) to
`0.99.18`


</details>

<details>
<summary>Update Rust crate embed-resource to v2.4.3</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/embed-resource-2.x-lockfile`
  - Merge into: `main`
- Upgrade
[embed-resource](https://togithub.com/nabijaczleweli/rust-embed-resource)
to `2.4.3`


</details>

<details>
<summary>Update Rust crate emojis to v0.6.3</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/emojis-0.x-lockfile`
  - Merge into: `main`
- Upgrade [emojis](https://togithub.com/rossmacarthur/emojis) to `0.6.3`


</details>

<details>
<summary>Update Rust crate etagere to v0.2.13</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/etagere-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [etagere](https://togithub.com/nical/etagere) to `0.2.13`


</details>

<details>
<summary>Update Rust crate futures to v0.3.30</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/rust-futures-monorepo`
  - Merge into: `main`
- Upgrade [futures](https://togithub.com/rust-lang/futures-rs) to
`0.3.30`


</details>

<details>
<summary>Update Rust crate heed to v0.20.3</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/heed-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [heed](https://togithub.com/Kerollmops/heed) to `0.20.3`


</details>

<details>
<summary>Update Rust crate image to v0.25.2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/image-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [image](https://togithub.com/image-rs/image) to `0.25.2`


</details>

<details>
<summary>Update Rust crate ipc-channel to v0.18.2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/ipc-channel-0.x-lockfile`
  - Merge into: `main`
- Upgrade [ipc-channel](https://togithub.com/servo/ipc-channel) to
`0.18.2`


</details>

<details>
<summary>Update Rust crate libc to v0.2.155</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/libc-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [libc](https://togithub.com/rust-lang/libc) to `0.2.155`


</details>

<details>
<summary>Update Rust crate linkme to v0.3.27</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/linkme-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [linkme](https://togithub.com/dtolnay/linkme) to `0.3.27`


</details>

<details>
<summary>Update Rust crate log to v0.4.22</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/log-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [log](https://togithub.com/rust-lang/log) to `0.4.22`


</details>

<details>
<summary>Update Rust crate mimalloc to v0.1.43</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/mimalloc-0.x-lockfile`
  - Merge into: `main`
- Upgrade [mimalloc](https://togithub.com/purpleprotocol/mimalloc_rust)
to `0.1.43`


</details>

<details>
<summary>Update Rust crate oo7 to v0.3.3</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/oo7-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [oo7](https://togithub.com/bilelmoussaoui/oo7) to `0.3.3`


</details>

<details>
<summary>Update Rust crate ordered-float to v2.10.1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/ordered-float-2.x-lockfile`
  - Merge into: `main`
- Upgrade [ordered-float](https://togithub.com/reem/rust-ordered-float)
to `2.10.1`


</details>

<details>
<summary>Update Rust crate palette to v0.7.6</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/palette-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [palette](https://togithub.com/Ogeon/palette) to `0.7.6`


</details>

<details>
<summary>Update Rust crate parking_lot to v0.12.3</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/parking_lot-0.x-lockfile`
  - Merge into: `main`
- Upgrade [parking_lot](https://togithub.com/Amanieu/parking_lot) to
`0.12.3`


</details>

<details>
<summary>Update Rust crate proc-macro2 to v1.0.86</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/proc-macro2-1.x-lockfile`
  - Merge into: `main`
- Upgrade [proc-macro2](https://togithub.com/dtolnay/proc-macro2) to
`1.0.86`


</details>

<details>
<summary>Update Rust crate prometheus to v0.13.4</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/prometheus-0.x-lockfile`
  - Merge into: `main`
- Upgrade [prometheus](https://togithub.com/tikv/rust-prometheus) to
`0.13.4`


</details>

<details>
<summary>Update Rust crate quote to v1.0.36</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/quote-1.x-lockfile`
  - Merge into: `main`
  - Upgrade [quote](https://togithub.com/dtolnay/quote) to `1.0.36`


</details>

<details>
<summary>Update Rust crate raw-window-handle to v0.6.2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/raw-window-handle-0.x-lockfile`
  - Merge into: `main`
- Upgrade
[raw-window-handle](https://togithub.com/rust-windowing/raw-window-handle)
to `0.6.2`


</details>

<details>
<summary>Update Rust crate rustc-demangle to v0.1.24</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/rustc-demangle-0.x-lockfile`
  - Merge into: `main`
- Upgrade
[rustc-demangle](https://togithub.com/rust-lang/rustc-demangle) to
`0.1.24`


</details>

<details>
<summary>Update Rust crate schemars to v0.8.21</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/schemars-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [schemars](https://togithub.com/GREsau/schemars) to `0.8.21`


</details>

<details>
<summary>Update Rust crate sea-orm to v0.12.15</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/sea-orm-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [sea-orm](https://togithub.com/SeaQL/sea-orm) to `0.12.15`


</details>

<details>
<summary>Update Rust crate semver to v1.0.23</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/semver-1.x-lockfile`
  - Merge into: `main`
  - Upgrade [semver](https://togithub.com/dtolnay/semver) to `1.0.23`


</details>

<details>
<summary>Update Rust crate serde_json to v1.0.120</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/serde_json-1.x-lockfile`
  - Merge into: `main`
- Upgrade [serde_json](https://togithub.com/serde-rs/json) to `1.0.120`


</details>

<details>
<summary>Update Rust crate serde_repr to v0.1.19</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/serde_repr-0.x-lockfile`
  - Merge into: `main`
- Upgrade [serde_repr](https://togithub.com/dtolnay/serde-repr) to
`0.1.19`


</details>

<details>
<summary>Update Rust crate sha2 to v0.10.8</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/sha2-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [sha2](https://togithub.com/RustCrypto/hashes) to `0.10.8`


</details>

<details>
<summary>Update Rust crate slotmap to v1.0.7</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/slotmap-1.x-lockfile`
  - Merge into: `main`
  - Upgrade [slotmap](https://togithub.com/orlp/slotmap) to `1.0.7`


</details>

<details>
<summary>Update Rust crate sqlformat to v0.2.4</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/sqlformat-0.x-lockfile`
  - Merge into: `main`
- Upgrade [sqlformat](https://togithub.com/shssoichiro/sqlformat-rs) to
`0.2.4`


</details>

<details>
<summary>Update Rust crate sysinfo to v0.30.13</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/sysinfo-0.x-lockfile`
  - Merge into: `main`
- Upgrade [sysinfo](https://togithub.com/GuillaumeGomez/sysinfo) to
`0.30.13`


</details>

<details>
<summary>Update Rust crate thiserror to v1.0.63</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/thiserror-1.x-lockfile`
  - Merge into: `main`
- Upgrade [thiserror](https://togithub.com/dtolnay/thiserror) to
`1.0.63`


</details>

<details>
<summary>Update Rust crate thread_local to v1.1.8</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/thread_local-1.x-lockfile`
  - Merge into: `main`
- Upgrade [thread_local](https://togithub.com/Amanieu/thread_local-rs)
to `1.1.8`


</details>

<details>
<summary>Update Rust crate toml to v0.8.15</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/toml-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [toml](https://togithub.com/toml-rs/toml) to `0.8.15`


</details>

<details>
<summary>Update Rust crate url to v2.5.2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/url-2.x-lockfile`
  - Merge into: `main`
  - Upgrade [url](https://togithub.com/servo/rust-url) to `2.5.2`


</details>

<details>
<summary>Update Rust crate wayland-backend to v0.3.6</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/wayland-backend-0.x-lockfile`
  - Merge into: `main`
- Upgrade [wayland-backend](https://togithub.com/smithay/wayland-rs) to
`0.3.6`


</details>

<details>
<summary>Update Rust crate wayland-client to v0.31.5</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/wayland-client-0.x-lockfile`
  - Merge into: `main`
- Upgrade [wayland-client](https://togithub.com/smithay/wayland-rs) to
`0.31.5`


</details>

<details>
<summary>Update Rust crate wayland-cursor to v0.31.5</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/wayland-cursor-0.x-lockfile`
  - Merge into: `main`
- Upgrade [wayland-cursor](https://togithub.com/smithay/wayland-rs) to
`0.31.5`


</details>

<details>
<summary>Update Rust crate which to v6.0.1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/which-6.x-lockfile`
  - Merge into: `main`
  - Upgrade [which](https://togithub.com/harryfei/which-rs) to `6.0.1`


</details>

<details>
<summary>Update Rust crate x11rb to v0.13.1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/x11rb-0.x-lockfile`
  - Merge into: `main`
  - Upgrade [x11rb](https://togithub.com/psychon/x11rb) to `0.13.1`


</details>

<details>
<summary>Update Rust crate zed_extension_api to 0.0.6</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/zed_extension_api-0.x`
  - Merge into: `main`
- Upgrade [zed_extension_api](https://togithub.com/zed-industries/zed)
to `0.0.6`


</details>

<details>
<summary>Update serde monorepo to v1.0.204</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/serde-monorepo`
  - Merge into: `main`
  - Upgrade [serde](https://togithub.com/serde-rs/serde) to `1.0.204`
- Upgrade [serde_derive](https://togithub.com/serde-rs/serde) to
`1.0.204`


</details>

<details>
<summary>Update 2428392/gh-truncate-string-action action to
v1.4.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/2428392-gh-truncate-string-action-1.x`
  - Merge into: `main`
- Upgrade
[2428392/gh-truncate-string-action](https://togithub.com/2428392/gh-truncate-string-action)
to `v1.4.0`


</details>

<details>
<summary>Update Rust crate alacritty_terminal to 0.24</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/alacritty_terminal-0.x`
  - Merge into: `main`
- Upgrade [alacritty_terminal](https://togithub.com/alacritty/alacritty)
to `0.24`


</details>

<details>
<summary>Update Rust crate any_vec to 0.14</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/any_vec-0.x`
  - Merge into: `main`
  - Upgrade [any_vec](https://togithub.com/tower120/any_vec) to `0.14`


</details>

<details>
<summary>Update Rust crate async-recursion to v1.1.1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/async-recursion-1.x-lockfile`
  - Merge into: `main`
- Upgrade [async-recursion](https://togithub.com/dcchut/async-recursion)
to `1.1.1`


</details>

<details>
<summary>Update Rust crate async-tungstenite to 0.27</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/async-tungstenite-0.x`
  - Merge into: `main`
- Upgrade
[async-tungstenite](https://togithub.com/sdroege/async-tungstenite) to
`0.27`


</details>

<details>
<summary>Update Rust crate axum to 0.7</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/axum-0.x`
  - Merge into: `main`
  - Upgrade [axum](https://togithub.com/tokio-rs/axum) to `0.7`


</details>

<details>
<summary>Update Rust crate axum-extra to 0.9</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/axum-extra-0.x`
  - Merge into: `main`
  - Upgrade [axum-extra](https://togithub.com/tokio-rs/axum) to `0.9`


</details>

<details>
<summary>Update Rust crate base64 to 0.22</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/base64-0.x`
  - Merge into: `main`
- Upgrade [base64](https://togithub.com/marshallpierce/rust-base64) to
`0.22`


</details>

<details>
<summary>Update Rust crate bindgen to 0.69.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/bindgen-0.x`
  - Merge into: `main`
- Upgrade [bindgen](https://togithub.com/rust-lang/rust-bindgen) to
`0.69.0`


</details>

<details>
<summary>Update Rust crate bytemuck to v1.16.1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/bytemuck-1.x-lockfile`
  - Merge into: `main`
- Upgrade [bytemuck](https://togithub.com/Lokathor/bytemuck) to `1.16.1`


</details>

<details>
<summary>Update Rust crate calloop to 0.14.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/calloop-0.x`
  - Merge into: `main`
  - Upgrade [calloop](https://togithub.com/Smithay/calloop) to `0.14.0`


</details>

<details>
<summary>Update Rust crate cap-std to v3.2.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/cap-std-3.x-lockfile`
  - Merge into: `main`
- Upgrade [cap-std](https://togithub.com/bytecodealliance/cap-std) to
`3.2.0`


</details>

<details>
<summary>Update Rust crate clap to v4.5.10</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/clap-4.x-lockfile`
  - Merge into: `main`
  - Upgrade [clap](https://togithub.com/clap-rs/clap) to `4.5.10`


</details>

<details>
<summary>Update Rust crate clickhouse to 0.12.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/clickhouse-0.x`
  - Merge into: `main`
- Upgrade [clickhouse](https://togithub.com/loyd/clickhouse.rs) to
`0.12.0`


</details>

<details>
<summary>Update Rust crate env_logger to 0.11</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/env_logger-0.x`
  - Merge into: `main`
- Upgrade [env_logger](https://togithub.com/rust-cli/env_logger) to
`0.11`


</details>

<details>
<summary>Update Rust crate fork to 0.2.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/fork-0.x`
  - Merge into: `main`
  - Upgrade [fork](https://togithub.com/immortal/fork) to `0.2.0`


</details>

<details>
<summary>Update Rust crate http to v1.1.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/http-1.x-lockfile`
  - Merge into: `main`
  - Upgrade [http](https://togithub.com/hyperium/http) to `1.1.0`


</details>

<details>
<summary>Update Rust crate itertools to v0.13.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/itertools-0.x`
  - Merge into: `main`
- Upgrade [itertools](https://togithub.com/rust-itertools/itertools) to
`0.13`
- Upgrade [itertools](https://togithub.com/rust-itertools/itertools) to
`0.13.0`


</details>

<details>
<summary>Update Rust crate lazy_static to v1.5.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/lazy_static-1.x-lockfile`
  - Merge into: `main`
- Upgrade
[lazy_static](https://togithub.com/rust-lang-nursery/lazy-static.rs) to
`1.5.0`


</details>

<details>
<summary>Update Rust crate libsqlite3-sys to 0.30</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/libsqlite3-sys-0.x`
  - Merge into: `main`
- Upgrade [libsqlite3-sys](https://togithub.com/rusqlite/rusqlite) to
`0.30`


</details>

<details>
<summary>Update Rust crate nix to 0.29</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/nix-0.x`
  - Merge into: `main`
  - Upgrade [nix](https://togithub.com/nix-rust/nix) to `0.29`


</details>

<details>
<summary>Update Rust crate pulldown-cmark to 0.11.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/pulldown-cmark-0.x`
  - Merge into: `main`
- Upgrade
[pulldown-cmark](https://togithub.com/raphlinus/pulldown-cmark) to
`0.11.0`


</details>

<details>
<summary>Update Rust crate rayon to v1.10.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/rayon-1.x-lockfile`
  - Merge into: `main`
  - Upgrade [rayon](https://togithub.com/rayon-rs/rayon) to `1.10.0`


</details>

<details>
<summary>Update Rust crate reqwest to 0.12</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/reqwest-0.x`
  - Merge into: `main`
- Upgrade [reqwest](https://togithub.com/seanmonstar/reqwest) to `0.12`


</details>

<details>
<summary>Update Rust crate resvg to 0.42.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/resvg-0.x`
  - Merge into: `main`
  - Upgrade [resvg](https://togithub.com/RazrFalcon/resvg) to `0.42.0`


</details>

<details>
<summary>Update Rust crate rodio to 0.19.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/rodio-0.x`
  - Merge into: `main`
  - Upgrade [rodio](https://togithub.com/RustAudio/rodio) to `0.19.0`


</details>

<details>
<summary>Update Rust crate runtimelib to 0.13</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/runtimelib-0.x`
  - Merge into: `main`
  - Upgrade runtimelib to `0.13`


</details>

<details>
<summary>Update Rust crate rusqlite to 0.32.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/rusqlite-0.x`
  - Merge into: `main`
- Upgrade [rusqlite](https://togithub.com/rusqlite/rusqlite) to `0.32.0`


</details>

<details>
<summary>Update Rust crate rust-embed to v8.5.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/rust-embed-8.x-lockfile`
  - Merge into: `main`
- Upgrade [rust-embed](https://togithub.com/pyros2097/rust-embed) to
`8.5.0`


</details>

<details>
<summary>Update Rust crate scrypt to 0.11</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/scrypt-0.x`
  - Merge into: `main`
- Upgrade [scrypt](https://togithub.com/RustCrypto/password-hashes) to
`0.11`


</details>

<details>
<summary>Update Rust crate serde_json_lenient to 0.2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/serde_json_lenient-0.x`
  - Merge into: `main`
- Upgrade
[serde_json_lenient](https://togithub.com/google/serde_json_lenient) to
`0.2`


</details>

<details>
<summary>Update Rust crate smallvec to v1.13.2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/smallvec-1.x-lockfile`
  - Merge into: `main`
- Upgrade [smallvec](https://togithub.com/servo/rust-smallvec) to
`1.13.2`


</details>

<details>
<summary>Update Rust crate sqlx to 0.8</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/sqlx-0.x`
  - Merge into: `main`
  - Upgrade [sqlx](https://togithub.com/launchbadge/sqlx) to `0.8`


</details>

<details>
<summary>Update Rust crate strum to 0.26.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/strum-monorepo`
  - Merge into: `main`
  - Upgrade [strum](https://togithub.com/Peternator7/strum) to `0.26.0`


</details>

<details>
<summary>Update Rust crate subtle to v2.6.1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/subtle-2.x-lockfile`
  - Merge into: `main`
- Upgrade [subtle](https://togithub.com/dalek-cryptography/subtle) to
`2.6.1`


</details>

<details>
<summary>Update Rust crate taffy to 0.5.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/taffy-0.x`
  - Merge into: `main`
  - Upgrade [taffy](https://togithub.com/DioxusLabs/taffy) to `0.5.0`


</details>

<details>
<summary>Update Rust crate tempfile to v3.10.1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/tempfile-3.x-lockfile`
  - Merge into: `main`
- Upgrade [tempfile](https://togithub.com/Stebalien/tempfile) to
`3.10.1`


</details>

<details>
<summary>Update Rust crate tiny_http to 0.12</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/tiny_http-0.x`
  - Merge into: `main`
- Upgrade [tiny_http](https://togithub.com/tiny-http/tiny-http) to
`0.12`


</details>

<details>
<summary>Update Rust crate tokio to v1.39.1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/tokio-1.x-lockfile`
  - Merge into: `main`
  - Upgrade [tokio](https://togithub.com/tokio-rs/tokio) to `1.39.1`


</details>

<details>
<summary>Update Rust crate tower-http to 0.5.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/tower-http-0.x`
  - Merge into: `main`
- Upgrade [tower-http](https://togithub.com/tower-rs/tower-http) to
`0.5.0`


</details>

<details>
<summary>Update Rust crate tree-sitter-embedded-template to
0.21.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/tree-sitter-embedded-template-0.x`
  - Merge into: `main`
- Upgrade
[tree-sitter-embedded-template](https://togithub.com/tree-sitter/tree-sitter-embedded-template)
to `0.21.0`


</details>

<details>
<summary>Update Rust crate unicode-segmentation to v1.11.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/unicode-segmentation-1.x-lockfile`
  - Merge into: `main`
- Upgrade
[unicode-segmentation](https://togithub.com/unicode-rs/unicode-segmentation)
to `1.11.0`


</details>

<details>
<summary>Update Rust crate unindent to 0.2.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/unindent-0.x`
  - Merge into: `main`
  - Upgrade [unindent](https://togithub.com/dtolnay/indoc) to `0.2.0`


</details>

<details>
<summary>Update Rust crate usvg to 0.42.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/usvg-0.x`
  - Merge into: `main`
  - Upgrade [usvg](https://togithub.com/RazrFalcon/resvg) to `0.42.0`


</details>

<details>
<summary>Update Rust crate uuid to v1.10.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/uuid-1.x-lockfile`
  - Merge into: `main`
  - Upgrade [uuid](https://togithub.com/uuid-rs/uuid) to `1.10.0`


</details>

<details>
<summary>Update Rust crate waker-fn to v1.2.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/waker-fn-1.x-lockfile`
  - Merge into: `main`
  - Upgrade [waker-fn](https://togithub.com/smol-rs/waker-fn) to `1.2.0`


</details>

<details>
<summary>Update Rust crate wasm-encoder to 0.214</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/wasm-encoder-0.x`
  - Merge into: `main`
- Upgrade
[wasm-encoder](https://togithub.com/bytecodealliance/wasm-tools) to
`0.214`


</details>

<details>
<summary>Update Rust crate wasmparser to 0.214</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/wasmparser-0.x`
  - Merge into: `main`
- Upgrade [wasmparser](https://togithub.com/bytecodealliance/wasm-tools)
to `0.214`


</details>

<details>
<summary>Update Rust crate wayland-protocols to 0.32.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/wayland-protocols-0.x`
  - Merge into: `main`
- Upgrade [wayland-protocols](https://togithub.com/smithay/wayland-rs)
to `0.32.0`


</details>

<details>
<summary>Update Rust crate wayland-protocols-plasma to 0.3.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/wayland-protocols-plasma-0.x`
  - Merge into: `main`
- Upgrade
[wayland-protocols-plasma](https://togithub.com/smithay/wayland-rs) to
`0.3.0`


</details>

<details>
<summary>Update Rust crate windows to 0.58</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/windows-0.x`
  - Merge into: `main`
- Upgrade [windows](https://togithub.com/microsoft/windows-rs) to `0.58`


</details>

<details>
<summary>Update Rust crate windows-core to 0.58</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/windows-core-0.x`
  - Merge into: `main`
- Upgrade [windows-core](https://togithub.com/microsoft/windows-rs) to
`0.58`


</details>

<details>
<summary>Update Rust crate wit-bindgen to 0.28</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/wit-bindgen-0.x`
  - Merge into: `main`
- Upgrade [wit-bindgen](https://togithub.com/bytecodealliance/wasi-rs)
to `0.28`


</details>

<details>
<summary>Update Rust crate wit-component to 0.214</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/wit-component-0.x`
  - Merge into: `main`
- Upgrade
[wit-component](https://togithub.com/bytecodealliance/wasm-tools) to
`0.214`


</details>

<details>
<summary>Update Rust crate zstd to 0.13</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/zstd-0.x`
  - Merge into: `main`
  - Upgrade [zstd](https://togithub.com/gyscos/zstd-rs) to `0.13`


</details>

<details>
<summary>Update aws-sdk-rust monorepo</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/aws-sdk-rust-monorepo`
  - Merge into: `main`
- Upgrade [aws-config](https://togithub.com/smithy-lang/smithy-rs) to
`1.5.4`
- Upgrade [aws-sdk-s3](https://togithub.com/awslabs/aws-sdk-rust) to
`1.42.0`


</details>

<details>
<summary>Update dependency PyGithub to v1.59.1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/pygithub-1.x`
  - Merge into: `main`
- Upgrade [PyGithub](https://togithub.com/pygithub/pygithub) to `1.59.1`


</details>

<details>
<summary>Update dependency livekit/client-sdk-swift to v1.1.6</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/livekit-client-sdk-swift-1.x`
  - Merge into: `main`
- Upgrade
[livekit/client-sdk-swift](https://togithub.com/livekit/client-sdk-swift)
to `1.1.6`


</details>

<details>
<summary>Update dependency mypy to v1.11.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/mypy-1.x`
  - Merge into: `main`
  - Upgrade [mypy](https://togithub.com/python/mypy) to `1.11.0`
  - Upgrade [mypy](https://togithub.com/python/mypy) to `==1.11.0`


</details>

<details>
<summary>Update dependency pytz to v2022.7.1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/pytz-2022.x`
  - Merge into: `main`
  - Upgrade pytz to `2022.7.1`
  - Upgrade pytz to `==2022.7.1`


</details>

<details>
<summary>Update dependency ruff to v0.5.4</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/ruff-0.x`
  - Merge into: `main`
  - Upgrade [ruff](https://togithub.com/astral-sh/ruff) to `==0.5.4`


</details>

<details>
<summary>Update dependency typer to v0.12.3</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/typer-0.x`
  - Merge into: `main`
  - Upgrade [typer](https://togithub.com/tiangolo/typer) to `0.12.3`
  - Upgrade [typer](https://togithub.com/tiangolo/typer) to `==0.12.3`


</details>

<details>
<summary>Update dependency types-pytz to v2023.4.0.20240130</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/types-pytz-2023.x`
  - Merge into: `main`
- Upgrade [types-pytz](https://togithub.com/python/typeshed) to
`2023.4.0.20240130`
- Upgrade [types-pytz](https://togithub.com/python/typeshed) to
`==2023.4.0.20240130`


</details>

<details>
<summary>Update docker/dockerfile Docker tag to v1.9</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/docker-dockerfile-1.x`
  - Merge into: `main`
  - Upgrade docker/dockerfile to `1.9`


</details>

<details>
<summary>Update tokio-prost monorepo to 0.13</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/tokio-prost-monorepo`
  - Merge into: `main`
  - Upgrade [prost](https://togithub.com/tokio-rs/prost) to `0.13`
  - Upgrade [prost-build](https://togithub.com/tokio-rs/prost) to `0.13`
  - Upgrade [prost-types](https://togithub.com/tokio-rs/prost) to `0.13`


</details>

<details>
<summary>Update tsickert/discord-webhook action to v5.5.0</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/tsickert-discord-webhook-5.x`
  - Merge into: `main`
- Upgrade
[tsickert/discord-webhook](https://togithub.com/tsickert/discord-webhook)
to `v5.5.0`


</details>

<details>
<summary>Update Rust crate async-fs to v2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/async-fs-2.x`
  - Merge into: `main`
  - Upgrade [async-fs](https://togithub.com/smol-rs/async-fs) to `2.0`


</details>

<details>
<summary>Update Rust crate async-recursion to v1</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/async-recursion-1.x`
  - Merge into: `main`
- Upgrade [async-recursion](https://togithub.com/dcchut/async-recursion)
to `1.0`


</details>

<details>
<summary>Update Rust crate dashmap to v6</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/dashmap-6.x`
  - Merge into: `main`
  - Upgrade [dashmap](https://togithub.com/xacrimon/dashmap) to `6.0`
  - Upgrade [dashmap](https://togithub.com/xacrimon/dashmap) to `6.0.0`


</details>

<details>
<summary>Update Rust crate dirs to v5</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/dirs-5.x`
  - Merge into: `main`
  - Upgrade [dirs](https://togithub.com/soc/dirs-rs) to `5.0`


</details>

<details>
<summary>Update Rust crate fsevent-sys to v4</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/fsevent-sys-4.x`
  - Merge into: `main`
- Upgrade [fsevent-sys](https://togithub.com/octplane/fsevent-rust) to
`4.0.0`


</details>

<details>
<summary>Update Rust crate futures-lite to v2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/futures-lite-2.x`
  - Merge into: `main`
- Upgrade [futures-lite](https://togithub.com/smol-rs/futures-lite) to
`2.0`


</details>

<details>
<summary>Update Rust crate indexmap to v2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/indexmap-2.x`
  - Merge into: `main`
- Upgrade [indexmap](https://togithub.com/indexmap-rs/indexmap) to
`2.0.0`


</details>

<details>
<summary>Update Rust crate indoc to v2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/indoc-2.x`
  - Merge into: `main`
  - Upgrade [indoc](https://togithub.com/dtolnay/indoc) to `2`


</details>

<details>
<summary>Update Rust crate ordered-float to v4</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/ordered-float-4.x`
  - Merge into: `main`
- Upgrade [ordered-float](https://togithub.com/reem/rust-ordered-float)
to `4.0.0`


</details>

<details>
<summary>Update Rust crate rustc-hash to v2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/rustc-hash-2.x`
  - Merge into: `main`
- Upgrade [rustc-hash](https://togithub.com/rust-lang/rustc-hash) to
`2.0`


</details>

<details>
<summary>Update Rust crate shellexpand to v3</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/shellexpand-3.x`
  - Merge into: `main`
- Upgrade [shellexpand](https://gitlab.com/ijackson/rust-shellexpand) to
`3.0.0`


</details>

<details>
<summary>Update Rust crate similar to v2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/similar-2.x`
  - Merge into: `main`
  - Upgrade [similar](https://togithub.com/mitsuhiko/similar) to `2.0`


</details>

<details>
<summary>Update Rust crate smol to v2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/smol-2.x`
  - Merge into: `main`
  - Upgrade [smol](https://togithub.com/smol-rs/smol) to `2.0`


</details>

<details>
<summary>Update Rust crate syn to v2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/syn-2.x`
  - Merge into: `main`
  - Upgrade [syn](https://togithub.com/dtolnay/syn) to `2.0`
  - Upgrade [syn](https://togithub.com/dtolnay/syn) to `2.0.0`


</details>

<details>
<summary>Update Rust crate wasmtime to v23</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/wasmtime-23.x`
  - Merge into: `main`
- Upgrade [wasmtime](https://togithub.com/bytecodealliance/wasmtime) to
`23.0.0`


</details>

<details>
<summary>Update Rust crate wasmtime-wasi to v23</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/wasmtime-wasi-23.x`
  - Merge into: `main`
- Upgrade
[wasmtime-wasi](https://togithub.com/bytecodealliance/wasmtime) to
`23.0.0`


</details>

<details>
<summary>Update actions/checkout action to v4</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/actions-checkout-4.x`
  - Merge into: `main`
- Upgrade [actions/checkout](https://togithub.com/actions/checkout) to
`v4`


</details>

<details>
<summary>Update dependency PyGithub to v2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/pygithub-2.x`
  - Merge into: `main`
- Upgrade [PyGithub](https://togithub.com/pygithub/pygithub) to `2.3.0`


</details>

<details>
<summary>Update dependency danger to v12</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/danger-12.x`
  - Merge into: `main`
  - Upgrade [danger](https://togithub.com/danger/danger-js) to `12.3.3`


</details>

<details>
<summary>Update dependency livekit/client-sdk-swift to v2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/livekit-client-sdk-swift-2.x`
  - Merge into: `main`
- Upgrade
[livekit/client-sdk-swift](https://togithub.com/livekit/client-sdk-swift)
to `2.0.12`


</details>

<details>
<summary>Update dependency pytz to v2024</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/pytz-2024.x`
  - Merge into: `main`
  - Upgrade pytz to `2024.1`
  - Upgrade pytz to `==2024.1`


</details>

<details>
<summary>Update dependency types-pytz to v2024</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/types-pytz-2024.x`
  - Merge into: `main`
- Upgrade [types-pytz](https://togithub.com/python/typeshed) to
`2024.1.0.20240417`
- Upgrade [types-pytz](https://togithub.com/python/typeshed) to
`==2024.1.0.20240417`


</details>

<details>
<summary>Update postgres Docker tag to v16</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/postgres-16.x`
  - Merge into: `main`
  - Upgrade postgres to `16`


</details>

<details>
<summary>Update softprops/action-gh-release action to v2</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/softprops-action-gh-release-2.x`
  - Merge into: `main`
- Upgrade
[softprops/action-gh-release](https://togithub.com/softprops/action-gh-release)
to `v2`


</details>

<details>
<summary>Update tsickert/discord-webhook action to v6</summary>

  - Schedule: ["after 3pm on Wednesday"]
  - Branch name: `renovate/tsickert-discord-webhook-6.x`
  - Merge into: `main`
- Upgrade
[tsickert/discord-webhook](https://togithub.com/tsickert/discord-webhook)
to `v6.0.0`


</details>



🚸 Branch creation will be limited to maximum 2 per hour, so it doesn't
swamp any CI resources or overwhelm the project. See docs for
`prhourlylimit` for details.


---

 Got questions? Check out Renovate's
[Docs](https://docs.renovatebot.com/), particularly the Getting Started
section.
If you need any further assistance then you can also [request help
here](https://togithub.com/renovatebot/renovate/discussions).


---

This PR was generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View the
[repository job
log](https://developer.mend.io/github/zed-industries/zed).


<!--renovate-config-hash:1752dbf5c31d5751ca59e575a43754358e807680713ab4daa60a32ec782882a0-->

Release Notes:

- N/A

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-24 22:32:58 -04:00
pantheraleo-7
b9570218b6 Remove !jupyter context for cmd-enter (ctrl-enter for linux) key binding (#15133)
Since zed has done away with `cmd-enter` binding for `repl::Run`
[#15026](https://github.com/zed-industries/zed/pull/15026), I think this
is no longer needed.

Release Notes:

- N/A
2024-07-24 19:02:24 -07:00
Marshall Bowers
fb4d77c008 Pin a specific version of typos in CI (#15128)
This PR makes it so we pin a specific version of `typos` in CI, rather
than just relying on whatever is already installed or what the latest
version is.

Release Notes:

- N/A
2024-07-24 19:03:07 -04:00
Max Brunsfeld
b14bb6bda4 Make git blame for SSH remote projects (#15106)
This also refactors the BufferStore + WorktreeStore interfaces to make
them cleaner, more fully encapsulating the RPC aspects of their
functionality.

Release Notes:

- N/A
2024-07-24 15:50:29 -07:00
Marshall Bowers
8501ae6a19 Make context and dropdown menus scrollable (#15127)
This PR makes context and dropdown menus scrollable, so that they don't
become totally unusable when they overflow the window.

Release Notes:

- N/A
2024-07-24 18:43:01 -04:00
Piotr Osiewicz
4e88a08ed4 chore: Some more dependency bumps (#15125)
Release Notes:

- N/A
2024-07-25 00:36:50 +02:00
Marshall Bowers
659f34bf21 settings_ui: Add UI and buffer font family controls (#15124)
This PR adds settings controls for changing the UI and buffer font
families.

Release Notes:

- N/A
2024-07-24 18:01:35 -04:00
Fernando Tagawa
001376fd6d cpp: Improve syntax highlighting (#13922)
* Added some missing operators, delimeters and keywords
* Highlight destructor, and operator overload as `@function`
* Moved `(field_identifier)` to the top, as it was highlighting methods
as `@property`

There are still some problems with something like `n1::n2::foo(...)`,
`foo` is not properly highlighted as a function

Release Notes:

- Improved C++ syntax highlighting
2024-07-25 00:55:21 +03:00
Marshall Bowers
298ca5ff1b Prefer .map for conditionals with else conditions (#15118)
This PR updates instances where we were using `.when_else` and
`.when_else_some` to use `.map` with a conditional inside.

This allows us to avoid reinventing Rust's syntax for conditionals and
(IMO) makes the code easier to read.

Release Notes:

- N/A
2024-07-24 17:09:07 -04:00
Kirill Bulatov
596ee58be8 Bump tree-sitter and related core language parser libraries (#14986)
Closes https://github.com/zed-industries/zed/issues/4565

To fix issues with code blocks' parsing in Markdown, a
tree-sitter-markdown library update is needed.
But `tree_sitter::language` is used in many places within core Zed,
which forced more library updates.

Release Notes:

- Updated tree-sitter parsers for core languages

---------

Co-authored-by: Max Brunsfeld <max@zed.dev>
Co-authored-by: Piotr Osiewicz <piotr@zed.dev>
2024-07-24 23:38:21 +03:00
Kirill Bulatov
fd4a4127eb Use proper names for sorting path entries (#15116)
Closes https://github.com/zed-industries/zed/issues/12470

Release Notes:

- Fixed project panel not showing properly file entries for directories
with dots in their names
([#12470](https://github.com/zed-industries/zed/issues/12470))
2024-07-24 23:36:13 +03:00
Marshall Bowers
0297a42735 Use US English spelling of "behavior" and "customize" (#15117)
This PR fixes some instances that weren't using US English spellings of
"behavior" and "customize".

Release Notes:

- N/A
2024-07-24 16:36:01 -04:00
Vitaly Slobodin
9c9a0bd24f lsp: Check if "Goto Definition" supported before request (#15111)
This is related to #15023 where we have the running Rubocop LSP that
provides diagnostics and formatting capabilities. Rubocop LSP sends its
capabilities
back to Zed without support for "textDocument/definition" request, Zed
actually does not check that and sends a request to Rubocop that results
in the server error "Unsupported method: textDocument/definition".

The fix here is related to
https://github.com/zed-industries/zed/pull/14666

Release Notes:

- N/A
2024-07-24 23:28:23 +03:00
Marshall Bowers
740c444089 settings_ui: Add theme settings controls (#15115)
This PR adds settings controls for the theme settings.

Release Notes:

- N/A
2024-07-24 16:25:52 -04:00
Piotr Osiewicz
325e6b9fef editor: Improve performance of buffer/project search (#15109)
Fixes #15102
Release Notes:

- Improved performance of project and buffer search when there are many
matches.
2024-07-24 21:04:07 +02:00
Danilo Leal
65c63defcc Ensure quick action bar icon buttons are a square (#15092)
As the title says! Also decreased a bit the gap between them so that's
consistent with other similar icon button stacks. I wish they could be
bigger buttons but the icons would need to be refined further for that,
as each has been drawn with a different dimension/bounding-box. Maybe in
the near future :)

---

Release Notes:

- N/A
2024-07-24 15:40:55 -03:00
Marshall Bowers
274e56b086 settings_ui: Add UI and buffer font weight controls (#15104)
This PR adds settings controls for the UI and buffer font weight
settings.

It also does some work around grouping the settings into related
sections.

Release Notes:

- N/A
2024-07-24 14:09:13 -04:00
Tom Anderson
7fb906d774 vim: Prevent overflowing integer when pushing count digit (#15079)
Minor bug where the command count overflows when enough digits are
entered. This swaps out simple multiplication/addition for their checked
counter parts, falling back to the previous value in case of overflow.

Release Notes:

- N/A
2024-07-24 11:54:59 -06:00
Conrad Irwin
d107d22c2d Fix cmd-alt-g b for git blame (#15103)
Broken by #14942 as the matching Pane binding for toggle regex now takes
precedence. cmd-alt-x still works for that.

Release Notes:

- N/A
2024-07-24 11:44:39 -06:00
Kyle Kelley
23dac9cfce docs: Show how to use the percent format to denote cells for the Zed REPL (#15099)
Release Notes:

- N/A
2024-07-24 10:31:25 -07:00
Joseph T Lyons
777ddefa73 Fix contributor-scraping code 2024-07-24 12:34:42 -04:00
Joseph T Lyons
77a2f2490e v0.147.x dev 2024-07-24 12:08:26 -04:00
Kyle Kelley
846fc67519 repl: Show executing status at the bottom of the outputs while waiting for execution to finish (#15096)
![image](https://github.com/user-attachments/assets/3b43f9d9-6456-42e2-a76a-43887d63309e)

Release Notes:

- N/A
2024-07-24 09:04:41 -07:00
Kyle Kelley
0843da0f81 repl: Incorporate moving down to next cell in jupytext mode (#15094)
`jupytext_snippets` now returns back both the jupytext snippets and the
`Point` of the next jupytext snippet.

Release Notes:

- N/A
2024-07-24 08:53:10 -07:00
Kyle Kelley
9e528dbb16 repl: Simplify error output formatting (#15088)
Release Notes:

- N/A
2024-07-24 08:38:04 -07:00
Conrad Irwin
c6d6c44810 Revert "Try blade#144 (#15036)" (#15095)
This reverts commit d034d73af9.

Release Notes:

- N/A
2024-07-24 09:36:59 -06:00
Marshall Bowers
9de6d318e9 feature_flags: Add support for flags that aren't auto-enabled for staff (#15093)
This PR adds support for defining feature flags that aren't auto-enabled
for Zed staff.

This will be useful in situations where we want to land a feature behind
a feature flag, but only want to ship it to certain staff members (e.g.,
the members currently working on it) initially.

Release Notes:

- N/A
2024-07-24 11:23:50 -04:00
Marshall Bowers
d540c95c81 settings_ui: Add prototype for settings controls (#15090)
This PR adds a prototype for controlling settings via the settings UI.

Still staff-shipped.

Release Notes:

- N/A
2024-07-24 10:36:27 -04:00
Peter Tripp
f7d6818c97 Sublime swap lines (#15089)
Improved SublimeText keymap (Mac & Linux).

- Add bind for MoveLineUp/Down (`ctrl-shift-up` on linux and `cmd-ctrl-up` on MacOS).

Co-authored-by: unixtensor <brandon@rhpidfyre.io>
2024-07-24 10:35:53 -04:00
Stanislav Alekseev
06d3dc010c Fix rendering issue with vtsls (#15087)
Before:
<img width="697" alt="Screenshot_2024-07-20_at_20 47 41"
src="https://github.com/user-attachments/assets/bc6db18e-77b9-400b-a917-2545269b7d1b">
After:
<img width="707" alt="Screenshot_2024-07-23_at_22 09 27"
src="https://github.com/user-attachments/assets/a0f6ebe5-fba0-4cc1-8d8b-b1982e2adf95">

Release Notes:

- Fixed multiline details provided by vtsls breaking rendering in
autocompletions
2024-07-24 17:30:44 +03:00
Kirill Bulatov
64b15d3a0e Display blocks over the git hunks (#15083)
Before:

![before](https://github.com/user-attachments/assets/614bbafd-f344-4c86-bd62-fff5f1ab143a)

After:

![after](https://github.com/user-attachments/assets/40a72a46-9fc4-4f94-9090-fb389f824a8d)


Release Notes:

- N/A
2024-07-24 07:23:20 -07:00
Piotr Osiewicz
23c5fc1b03 chore: Use explicit if let instead of for .. in option (#15086)
Fixes one clippy violation from 1.80 (that's due for release tomorrow).

Release Notes:

- N/A
2024-07-24 16:19:29 +02:00
Kirill Bulatov
c43eafbf9a Update the test code to newer realities (#15084)
Follow-up of https://github.com/zed-industries/zed/pull/14886

We do not require the branch to be up-to-date with `main` before
merging, and in 4 days some related test code got reworked so that there
were no conflicts and it slipped.

Release Notes:

- N/A
2024-07-24 16:51:18 +03:00
Danilo Leal
62a890fb7f Increase size of icon buttons within the inline editor (#15051)
This PR increases the size of the the icon buttons within the inline
editors, both within the buffer and on the terminal. I also added
properties to make sure they always render as a square, as well as
tweaking the stop icon SVG, adding an alternative sparkle icon that fit
the same grid as the close (14x14) icon, and adding a bit more right
padding on the buffer's case so it doesn't collide with the scrollbar.
End result is that they have a bit of an easier target space area and
normalized sizes.

---

Release Notes:

- N/A
2024-07-24 10:39:05 -03:00
Artur Rodrigues
5ad3c6b6dc inline_assistant: Respect tabs when selection first row is not indented (#14886)
When using the inline assistant with a language such as Go that uses
tabs, if the user selects a block of text that is correctly formatted
and where the first line has no indentation, the `suggested_line_indent`
variable ends up with `IndentSize { len: 0, kind: Space }`. That's
because `suggested_line_indent` current relies on
`BufferSnapshot::suggested_indents` suggestion for the first line on the
selection, but since it is already correctly indented, there are no
suggestions and `MultiBufferSnapshot::indent_size_for_line` is used
instead.


2d96bba61f/crates/assistant/src/inline_assistant.rs (L2124-L2128)

In this patch, we also take a look at the rest of the selection and
detect tabs. If one is encountered, we assume that tabs should always be
used. I suppose this isn't perfect, especially if the original file had
a mix of spaces and tabs, however it seems better than the status quo.

I considered using `BufferSnapshot::language_indent_size_at`, but I
imagine tabs should be preserved even when a specific language isn't
being used.

See screenshot below of the original prompt with this patch.

Tests:

* New unit test
* I've also manually tested with a few other cases: selection where all
lines are indented and file that only use spaces.

Release Notes:

- Fixed 'inline_assistant: tabs are overwritten with space characters
when first line in selection has no indentation'
([#14885](https://github.com/zed-industries/zed/issues/14885)).

<img width="942" alt="image"
src="https://github.com/user-attachments/assets/f2c5d7e9-e8bc-400b-bd6f-09e4a89d22c1">
2024-07-24 16:31:16 +03:00
schdaniel20
e004a573fd Improve project panel settings documentation (#15075)
Improved Project Panel Settings Documentation: The documentation for the
project panel settings has been enhanced for clarity and
comprehensiveness, providing better guidance and examples for users.
Default values have been aligned with project_panel_settings.rs.

Release Notes:

- N/A
2024-07-24 16:16:22 +03:00
Kirill Bulatov
babf8923ee Do not show signature help when editor is not focused (#15080)
Show the help again, if it was not dismissed and the editor regains
focus.

Release Notes:

- N/A
2024-07-24 14:37:35 +03:00
Remco Smits
1a421db92d Make clippy happier than ever 2024-07-24 13:05:14 +02:00
Kirill Bulatov
faf5ab7873 Fix a typo in the remote development docs (#15078)
Uses https://github.com/zed-industries/zed/pull/15015 

Release Notes:

- N/A
2024-07-24 13:53:49 +03:00
Remco Smits
9c60884771 Merge branch 'main' into debugger 2024-07-24 12:53:44 +02:00
Danilo Leal
912b396e58 Adjust model selector popover design (#15056)
This PR mostly refines the model selector popover design by formatting
the models names' and adjusting spacing/alignment in the list-related
items. The list component changes could've been made in a separate PR
but it was also very practical to do it here as I was already
in-context. Either way, I'm happy to separate if that's better!

One thing I couldn't necessarily figure out, though, is why the order
changed (e.g., Anthropic at last ). I wonder if that was because of the
separator logic somehow? I'd love guidance here—new to Rust!

| Before | After |
|--------|--------|
| <img width="228" alt="Screenshot 2024-07-23 at 21 02 33"
src="https://github.com/user-attachments/assets/3372c6c9-08dc-4d71-9265-26f015e2dbc2">
| <img width="228" alt="Screenshot 2024-07-23 at 21 01 45"
src="https://github.com/user-attachments/assets/624cc7db-a3d9-48e3-99d7-c29829501130">
|

---

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
2024-07-24 12:24:54 +02:00
Remco Smits
ee3323d12a Make clippy happy again 2024-07-24 12:14:18 +02:00
Nathan Sobo
87d93033d1 Support Jupytext-style line comments for REPL evaluation ranges (#15073)
This adds support for detecting line comments in the
[Jupytext](https://jupytext.readthedocs.io/) format. When line comments
such as `# %%` is present, invoking `repl: run` will evaluate the code
between these line comments as a unit.

/cc @rgbkrk 

```py
# %%
# This is my first block
print(1)
print(2)

# %%
# This is my second block
print(3)
```

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
2024-07-24 11:53:58 +02:00
Remco Smits
b88fd3e0c5 Make sure we read events as early as possible
We have to read them as early as possible to make sure we support more debug adapters. Some of them wait before you handled the initialized event. That is also why I moved the launch/attach request to be directly after the initialize request. For example the xdebug adapter sends the initialized event only when you send the launch request, so before if we send the breakpoints and configuration is done requests the adapter would stop and all channels were closed.
2024-07-24 11:51:03 +02:00
Bennet Bo Fenner
af4b9805c9 assistant: Fix issues when configuring different providers (#15072)
Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
2024-07-24 11:21:31 +02:00
Thorsten Ball
ba6c36f370 repl: Fix repl-over-selection not being Vim-line-wise aware (#15068)
Release Notes:

- N/A
2024-07-24 10:06:38 +02:00
Thorsten Ball
7644605f0f docs: Fix default binding for REPL (#15065)
Release Notes:

- N/A
2024-07-24 09:50:23 +02:00
Conrad Irwin
b4a8f14a76 linux: Detect shift better (#15013)
Release Notes:

- linux: Fixed typing shortcuts like ctrl-/ on some systems
2024-07-23 21:05:11 -06:00
Marshall Bowers
c84da37030 rpc: Add support for OAEP-based encryption format (#15058)
This PR adds support for a new encryption format for exchanging access
tokens during the authentication flow.

The new format uses Optimal Asymmetric Encryption Padding (OAEP) instead
of PKCS#1 v1.5, which is known to be vulnerable to side-channel attacks.

**Note: We are not yet encrypting access tokens using the new format, as
this is a breaking change between the client and the server. This PR
only adds support for it, and makes it so the client and server can
decrypt either format moving forward.**

This required bumping the RSA key size from 1024 bits to 2048 bits. This
is necessary to be able to encode the access token into the ciphertext
when using OAEP.

This also follows OWASP recommendations:

> If ECC is not available and RSA must be used, then ensure that the key
is at least 2048 bits.
>
> —
[source](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#algorithms)

Release Notes:

- N/A
2024-07-23 21:25:25 -04:00
Marshall Bowers
edf7f6defe Upgrade rsa to v0.9.6 (#15055)
This PR upgrades the `rsa` crate to v0.9.6.

The version we were using was rather old, and for something
security-sensitive we should be using a recent version.

No behavioral changes have been made, just updates to account for
changes in the crate's API.

Release Notes:

- N/A
2024-07-23 20:11:48 -04:00
Marshall Bowers
1307a80e07 rpc: Add regression tests for encoding/decoding public keys (#15054)
This PR adds some tests to ensure we don't regress in our public key
encoding/decoding capabilities when making changes in this area.

Release Notes:

- N/A
2024-07-23 19:58:47 -04:00
Kyle Kelley
9d11a6ff78 repl: Detect Conda and Python environments with their own kernel specs (#15047)
Detect kernels in conda and python environments.

Release Notes:

- N/A
2024-07-23 15:23:24 -07:00
Marshall Bowers
6769e55ce0 Revert "chore: Bump async-tungstenite to 0.23 (and tungstenite to 0.20.1) (#15039)" (#15048)
This reverts commit 4d65f7eea3.

Reverting because it causes auth with collab to break.

Release Notes:

- N/A
2024-07-23 18:22:37 -04:00
Mikayla Maki
855048041d Update http crate name (#15041)
Release Notes:

- N/A
2024-07-23 15:01:05 -07:00
Marshall Bowers
d36ebc8c74 Add global Fs instance (#15044)
This PR adds a global `Fs` instance to the app context.

This will make it easier to access the filesystem in some cases instead
of having to thread it around.

Release Notes:

- N/A
2024-07-23 17:59:55 -04:00
Kyle Kelley
5062bf0b4d repl: Pad the table rows with a fraction of the line height (#15042)
Compute the final height using the number of rows and the constant
fraction.

Ensures we don't accidentally overlap lines below table output.

<img width="663" alt="image"
src="https://github.com/user-attachments/assets/fe24b08d-2271-4dcc-88c7-8702ba4c68b0">


Release Notes:

- N/A
2024-07-23 14:42:58 -07:00
Piotr Osiewicz
4d65f7eea3 chore: Bump async-tungstenite to 0.23 (and tungstenite to 0.20.1) (#15039)
Release Notes:

- N/A
2024-07-23 23:18:00 +02:00
Conrad Irwin
7ae305ac0d Make vim::test_remap less flaky on linux (#15040)
Release Notes:

- N/A
2024-07-23 15:16:45 -06:00
Piotr Osiewicz
ba4ff1df59 chore: Remove clap3 dependency by disabling default features of cbindgen (#15037)
cbindgen pulled that in, but we don't really need it (Plus it pulls in a
dep with an advisory)

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-23 22:49:49 +02:00
Conrad Irwin
d034d73af9 Try blade#144 (#15036)
This pulls in https://github.com/kvark/blade#144 to see if it results in
fewer bad GPU configurations selected

Release Notes:

- linux: Improved graphics card detection
2024-07-23 14:37:22 -06:00
Max Brunsfeld
5f7881fc1e Improve ssh remote error handling and logging (#15035)
Release Notes:

- N/A
2024-07-23 13:29:56 -07:00
Conrad Irwin
b0c525af5f inotify alert (#15027)
Release Notes:

- linux: Show an error and troubleshooting steps for inotify limits
(#10310)
2024-07-23 14:21:56 -06:00
Conrad Irwin
41a3e78b1e Don't try to connect to X11/Wayland when headless (#15028)
Release Notes:

- remote development: Stopped logging every 16ms when connected to a
remote server with a badly configured X server.
2024-07-23 14:11:47 -06:00
Stanislav Alekseev
5021397c01 Fix diagnostic popups flickering when moving cursor in the boundaries of the symbol (#14870)
This PR just uses ranges returned by an LSP to work, the subsequent PR
would focus on trying to fall back onto tree-sitter in case of info
hovers. I'm also unsure if I'm supposed to use `local_diagnostic` or
`primary_diagnostic` when both are available
Release Notes:

- Fix diagnostic popups flickering when moving cursor in the boundaries
of the symbol

Before:


https://github.com/user-attachments/assets/4905a7e5-c333-453b-b204-264b3ef79586

After:


https://github.com/user-attachments/assets/c742c424-fb20-450d-8848-baaf1937dd47
2024-07-23 14:10:19 -06:00
Kirill Bulatov
b2b9d4ccb6 Extend task templates with shell and hide fields to use custom shells and custom close behavior (#15031) 2024-07-23 22:58:36 +03:00
Marshall Bowers
4a43084cb7 Bump wasmtime and wasmtime-wasi to v19.0.2 (#15033)
This PR bumps `wasmtime` and `wasmtime-wasi` to v19.0.2 for some bug
fixes.

https://github.com/bytecodealliance/wasmtime/releases/tag/v19.0.2

Release Notes:

- N/A
2024-07-23 15:55:15 -04:00
Piotr Osiewicz
fa76d8edcf chore: Bump dependencies (#15029)
Release Notes:


- N/A
2024-07-23 21:38:47 +02:00
Marshall Bowers
5f8e799d60 repl: Fix a small typo in a variable name (#15030)
This PR fixes a small typo in a variable name.

Release Notes:

- N/A
2024-07-23 15:09:40 -04:00
Max Brunsfeld
38e3182bef Handle buffer diff base updates and file renames properly for SSH projects (#14989)
Release Notes:

- N/A

---------

Co-authored-by: Conrad <conrad@zed.dev>
2024-07-23 11:32:37 -07:00
Kyle Kelley
ec093c390f repl: Change keybinding to ctrl-shift-enter for repl::Run (#15026)
... on all platforms.

`ctrl-shift-enter` for the repl, `cmd-enter` for the assistant. People
can override this behavior as they desire in their own keymaps.

Release Notes:

- N/A
2024-07-23 11:23:06 -07:00
Piotr Osiewicz
3d1bf09299 Allow user to use multiple formatters (#14846)
Fixes #4822
- [x] Release note
- [ ] Surface formatting errors via a toast
- [x] Doc updates
- [x] Have "language-server" accept an optional name of the server.

Release Notes:

- `format` and `format_on_save` now accept an array of formatting
actions to run.
- `language_server` formatter option now accepts the name of a language
server to use (e.g. `{"language_server": {"name: "ruff"}}`); when not
specified, a primary language server is used.

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-07-23 20:05:09 +02:00
Kyle Kelley
53b711c2b4 repl: Make the terminal background transparent (#15022)
Keeps the background the same as the output area background by making
the terminal background be `Hsla::transparent_black()`.

Release Notes:

- N/A

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
2024-07-23 11:03:22 -07:00
Bennet Bo Fenner
d0f52e90e6 assistant: Overhaul provider infrastructure (#14929)
<img width="624" alt="image"
src="https://github.com/user-attachments/assets/f492b0bd-14c3-49e2-b2ff-dc78e52b0815">

- [x] Correctly set custom model token count
- [x] How to count tokens for Gemini models?
- [x] Feature flag zed.dev provider
- [x] Figure out how to configure custom models
- [ ] Update docs

Release Notes:

- Added support for quickly switching between multiple language model
providers in the assistant panel

---------

Co-authored-by: Antonio <antonio@zed.dev>
2024-07-23 19:48:41 +02:00
Thorsten Ball
17ef9a367f zed: Add ability to restore last session w/ multiple windows (#14965)
This adds the ability for Zed to restore multiple windows after
restarting. It's now the default behavior.

Release Notes:

- Added ability to restore all windows that were open when Zed was quit.
Previously only the last used workspace was restored. This is now the
default behavior. To get back the old behavior, add the following to
your settings: `{"restore_on_startup": "last_workspace"}` (Part of
[#4985](https://github.com/zed-industries/zed/issues/4985) and
[#4683](https://github.com/zed-industries/zed/issues/4683))

Demo:



https://github.com/user-attachments/assets/57a375ec-0c6a-4724-97c4-3fea8f18bc2d

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-23 19:44:02 +02:00
Antonio Scandurra
53f828df7d Avoid inserting extra newlines when evaluating code (#15018)
When the evaluation range ends at the start of a line, back it up to the
end of the previous line. This avoids inserting extra newlines below the
evaluation range when they already exist.

Release Notes:

- N/A

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-23 19:03:15 +02:00
Nathan Sobo
d9a00b6f8b Update setting name in REPL docs (#15017)
cc @rgbkrk @iamnbutler 

Release Notes:

- N/A
2024-07-23 09:53:46 -07:00
Marshall Bowers
7d0386eff9 settings_ui: Add placeholder view (#15019)
This PR adds a placeholder view for the settings UI. It does not contain
any functionality, as of yet.

This view is staff-shipped behind a feature flag.

Release Notes:

- N/A
2024-07-23 12:50:11 -04:00
Conrad Irwin
bdf1d4edea linux: Better GPU debugging (#14706)
Release Notes:

- linux: Added GPU information to `editor: Copy System Specs to
Clipboard`
- linux: Show a prominant warning before running under llvmpipe and
similar.
2024-07-23 09:56:45 -06:00
Marshall Bowers
c262c81e52 repl: Filter commands out of command palette when REPL is disabled (#15016)
This PR makes it so the `repl: ` commands don't appear in the command
palette when the REPL feature is disabled.

Release Notes:

- N/A
2024-07-23 11:49:56 -04:00
Kirill Bulatov
a5cb66f0e1 Allow to regenerate a summary of the assistant context (#14964)
Both manual and LLM-through ways are supported:


https://github.com/user-attachments/assets/afb0d2b3-9a9b-4f78-a909-1e663e686323


Release Notes:

- Improved assistant panel summarization usability
2024-07-23 17:51:49 +03:00
Fabian
a0d687c24a astro: Ensure Typescript is present (#14849)
The current Astro Extension fails to load properly if it can't find a
`tsserver.js` file in the current workspaces' `node_modules` folder.
This happens pretty frequently, either if `typescript` is not installed
in the project (which it isn't by default), or if `node_modules` is not
in the workspace root.

This PR adds a fallback method of installing `typescript` alongside the
extensions' language server if it is not found in the workspaces'
`node_modules`, as well as correctly setting the `tsdk` path in the
initialization options.

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-23 10:39:25 -04:00
Marshall Bowers
5f57efb266 astro: Align version numbers in extension.toml and Cargo.toml (#15011)
This PR aligns the version numbers in `extension.toml` and `Cargo.toml`
for the Astro extension, as they had gotten out-of-sync.

Release Notes:

- N/A
2024-07-23 10:28:09 -04:00
Marshall Bowers
6398b45084 astro: Upgrade zed_extension_api to v0.0.6 (#15010)
This PR upgrades the Astro extension to use v0.0.6 of the
`zed_extension_api`.

Release Notes:

- N/A
2024-07-23 10:12:40 -04:00
Antonio Scandurra
728650f94a Fix interaction with Anthropic models when using it via zed.dev (#15009)
Release Notes:

- N/A

---------

Co-authored-by: Bennet <bennet@zed.dev>
2024-07-23 15:47:38 +02:00
Remco Smits
2e10853b34 Fix sending incorrect start request id 2024-07-23 14:45:04 +02:00
Antonio Scandurra
dde9d37cf9 Remove completion dependency from collab (#15006)
This was causing CI to fail when trying to deploy collab.

Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-07-23 11:44:07 +02:00
Piotr Osiewicz
5d77a7dc6c Ruff: Do not pass --preview flag, respect binary settings (#15001)
Bumps version to 0.0.2 as well.

Release Notes:

- N/A
2024-07-23 09:57:42 +02:00
Benjamin Westphal
1fae99a7c4 vim: Add motion support for toggle comments (#14919)
### Summary

This PR adds support for count and object motions to the toggle comments
action in Vim mode. The relevant issue is
[#14337](https://github.com/zed-industries/zed/issues/14337).

For example, `2 g c j` will toggle comments three lines downward. `g c g
g` will toggle comments from the current cursor position up to the start
of the file.

Notably missing from this PR are `g c b` (toggle comments for the
current block) as well as `g c p` (toggle comments for the current
paragraph). These seem to be non-standard.

The new module `normal/toggle_comments.rs` has been copied almost
verbatim from `normal/indent.rs`. Maybe that ought to be abstracted over
but I feel I lack the overview.

Release Notes:

- vim: Added support for count and object motion to the toggle comments
action ([#14337](https://github.com/zed-industries/zed/issues/14337)).
2024-07-22 21:22:10 -06:00
CharlesChen0823
eb210ca248 linux: Fix crash in Wayland when dragging and dropping a tab not belonging to Zed (#14995)
close #14189 

Release Notes:

- N/A
2024-07-22 21:20:53 -06:00
Joseph T Lyons
ddea18d546 Add snippets category to docs sidebar 2024-07-22 21:21:11 -04:00
Joseph T. Lyons
b85dba106b Add minimal snippets documentation (#14992)
Release Notes:

- N/A
2024-07-22 21:18:39 -04:00
Nate Butler
4ba430b16c repl: Design tweaks (#14988)
- Add spinner to "Executing..."
- Update Queued label to match others.

Release Notes:

- N/A
2024-07-22 19:05:36 -04:00
Marshall Bowers
fe1f55cbfd repl: Iterate on design of REPL sessions view (#14987)
This PR iterates on the design of the REPL sessions view.

We now use the same component for both available kernels and running
ones to provide some consistency between the two modes:

<img width="1208" alt="Screenshot 2024-07-22 at 6 49 08 PM"
src="https://github.com/user-attachments/assets/8b5c3600-e438-49fa-8484-cefabf4b44f1">

<img width="1208" alt="Screenshot 2024-07-22 at 6 49 14 PM"
src="https://github.com/user-attachments/assets/5125e9b3-6465-4d1e-9036-e6ca270dedcb">

Release Notes:

- N/A
2024-07-22 19:02:11 -04:00
Kyle Kelley
01392c1329 repl: Enable jupyter by default, allow disabling (#14985)
Enables the jupyter feature by default, which is shown only when we have
a kernelspec or know that we (can) support it well (Python,
Deno/TypeScript).

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-22 17:30:21 -04:00
Marshall Bowers
a9397834eb quick_action_bar: Add menu entry to view REPL sessions (#14984)
This PR adds a new menu entry in the REPL item in the quick action bar
to open up the REPL sessions view:

<img width="232" alt="Screenshot 2024-07-22 at 4 54 36 PM"
src="https://github.com/user-attachments/assets/acc60a25-7722-4331-9b80-fab9cca65842">

This makes this more discoverable than having to know that the command
exists.

Release Notes:

- N/A
2024-07-22 17:05:05 -04:00
Marshall Bowers
4227a3d3e0 repl: Remove unused repl_panel::ToggleFocus action (#14983)
This PR removes the `repl_panel::ToggleFocus` action, as we don't need
it anymore.

Release Notes:

- N/A
2024-07-22 16:45:04 -04:00
Marshall Bowers
2a69420c42 repl: Do some cleanup (#14982)
This PR cleans up the REPL implementation a bit.

Release Notes:

- N/A
2024-07-22 16:39:32 -04:00
Marshall Bowers
d8a42bbf63 repl: Replace REPL panel with sessions view (#14981)
This PR removes the REPL panel and replaces it with a new sessions view
that gets displayed in its own pane.

The sessions view can be opened with the `repl: sessions` command (we
can adjust the name, as needed).

There was a rather in-depth refactoring needed to extricate the various
REPL functionality on the editor from the `RuntimePanel`.

<img width="1136" alt="Screenshot 2024-07-22 at 4 12 12 PM"
src="https://github.com/user-attachments/assets/ac0da351-778e-4200-b08c-39f9e77d78bf">

<img width="1136" alt="Screenshot 2024-07-22 at 4 12 17 PM"
src="https://github.com/user-attachments/assets/6ca53476-6ac4-4f8b-afc8-f7863f7065c7">

Release Notes:

- N/A
2024-07-22 16:22:50 -04:00
Joseph T. Lyons
8f20ea1093 Add a section about navigating within multibuffers (#14979)
Release Notes:

- N/A
2024-07-22 15:10:02 -04:00
Kevin Wang
a20e92a8c1 Truncate line when accepting inline suggestions for Supermaven (#13884)
Configures inline completions to delete the remaining text on the given
line. This doesn't affect the github copilot inline completion provider
since it seems to only generate suggestions if the cursor is at the end
of the line but fixes the usability issues related to Supermaven.




https://github.com/user-attachments/assets/1b8bc9a3-4666-4665-a436-96e4beee01bb





Release Notes:

- Fixed https://github.com/zed-industries/zed/issues/13039

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-07-22 11:59:38 -07:00
Csaba Hoch
c703e20a06 docs: Fix ctrl-x meaning in vim mode (#14968)
Release Notes:

- N/A
2024-07-22 12:52:35 -06:00
Conrad Irwin
a955968de3 Don't panic on GPU hang (#14974)
Fixes: #12766
Fixes: #14022

Release Notes:

- linux: Fix panic when GPU is temporarily unavailable.
2024-07-22 12:21:28 -06:00
Kirill Bulatov
f597c29432 Position X for deleted hunks better (#14973)
Release Notes:

- N/A
2024-07-22 20:24:24 +03:00
Rashid Almheiri
a5a7a835fe Set the default Starlark LSP for zaucy/zed-starlark (#14972)
given zaucy/zed-starlark#4 was merged, zed-starlark now has multiple
LSPs and requires additional configuration which isn't available
directly for extensions.

cc @zaucy 

Release Notes:

- N/A
2024-07-22 12:58:28 -04:00
Marshall Bowers
28baa56e3d repl: Factor out ReplStore (#14970)
This PR factors a `ReplStore` out of the `RuntimePanel`.

Since we're planning to remove the `RuntimePanel` and replace it with an
ephemeral tab that can be opened, we need the kernel specifications and
sessions to have somewhere long-lived that they can reside in.

Release Notes:

- N/A
2024-07-22 12:46:33 -04:00
Conrad Irwin
2e23527e09 Refactor key dispatch (#14942)
Simplify key dispatch code.

Previously we would maintain a cache of key matchers for each context
that
would store the pending input. For the last while we've also stored the
typed prefix on the window. This is redundant, we only need one copy, so
now
it's just stored on the window, which lets us avoid the boilerplate of
keeping
all the matchers in sync.

This stops us from losing multikey bindings when the context on a node
changes
(#11009) (though we still interrupt multikey bindings if the focus
changes).

While in the code, I fixed up a few other things with multi-key bindings
that
were causing problems:

Previously we assumed that all multi-key bindings took precedence over
any
single-key binding, now this is done such that if a user binds a
single-key
binding, it will take precedence over all system-defined multi-key
bindings
(irrespective of the depth in the context tree). This was a common cause
of
confusion for new users trying to bind to `cmd-k` or `ctrl-w` in vim
mode
(#13543).

Previously after a pending multi-key keystroke failed to match, we would
drop
the prefix if it was an input event. Now we correctly replay it
(#14725).

Release Notes:

- Fixed multi-key shortcuts not working across completion menu changes
([#11009](https://github.com/zed-industries/zed/issues/11009))
- Fixed multi-key shortcuts discarding earlier input
([#14445](https://github.com/zed-industries/zed/pull/14445))
- vim: Fixed `jk` binding preventing you from repeating `j`
([#14725](https://github.com/zed-industries/zed/issues/14725))
- vim: Fixed `escape` in normal mode to also clear the selected
register.
- Fixed key maps so user-defined mappings take precedence over builtin
multi-key mappings
([#13543](https://github.com/zed-industries/zed/issues/13543))
- Fixed a bug where overridden shortcuts would still show in the Command
Palette
2024-07-22 10:46:16 -06:00
Piotr Osiewicz
865904a0c9 lsp: Pass back diagnostic .data when querying code actions for it (#14962)
Per the LSP spec, we should pass .data field of diagnostics into code
action request:
```
	/**
	 * A data entry field that is preserved between a
	 * `textDocument/publishDiagnostics` notification and
	 * `textDocument/codeAction` request. *
	 * @since 3.16.0 */ data?: LSPAny;
```


Release Notes:

- Fixed rare cases where a code action triggered by diagnostic may not
be available for use.
2024-07-22 17:49:11 +02:00
张小白
10d2353e07 windows: Treat settings.json as JSONC (#14944)
Before this PR, comments in `settings.json` are marked with red lines,
indicating that `"comments are not allowed in JSON."`

![Screenshot 2024-07-22
153951](https://github.com/user-attachments/assets/fbb631e8-43cf-4473-97c0-50c83c4d6ab1)


After this PR, this issue is resolved.

![Screenshot 2024-07-22
153527](https://github.com/user-attachments/assets/ee0f7877-c623-4caa-94cd-97e82f9b8945)

Release Notes:

- N/A
2024-07-22 10:46:52 -04:00
Floyd Wang
1ea363b020 Fix typo in font-weight setting story (#14958)
Release Notes:

- N/A
2024-07-22 07:49:27 -04:00
Antonio Scandurra
0155435142 Allow using a custom model when using zed.dev (#14933)
Release Notes:

- N/A
2024-07-22 12:25:53 +02:00
YeonGyu-Kim
a334c69e05 Add instructions for configuring linting in the Python documentation using Ruff extension (#14896)
Added documentation for #14198

I also suggest replacing format guides from `black` to `ruff` to unify
the tooling in the document.

Ruff is now widely used in the Python community, including
[fastapi](cd6e9db065/pyproject.toml (L213)).
It's compatible with black but a lot faster.

Release Notes:

- N/A
2024-07-22 11:59:42 +02:00
Kirill Bulatov
31d283932c Allow to input spaces in the outline panel filter input (#14951)
Release Notes:

- Fixed outline panel's filter not accepting spaces
2024-07-22 12:02:25 +03:00
Luke Naylor
0ef19dedd2 Correct escaping in snippets (#14912)
## Release Notes:

- Fixed issue with backslashes not appearing in snippets
([#14721](https://github.com/zed-industries/zed/issues/14721)),
motivated by a snippet provided by the latex LSP
([texlab](https://github.com/latex-lsp/texlab)) not working as intended
in Zed ([extension
issue](https://github.com/rzukic/zed-latex/issues/5)).

[Screencast from 2024-07-21
14-57-19.webm](https://github.com/user-attachments/assets/3c95a987-16e5-4132-8c96-15553966d4ac)

## Fix details:

Only $, }, \ can be escaped by a backslash as per [LSP spec (under
grammar
section)](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/\#snippet_syntax).
Technically, commas and pipes can also be escaped only in "choice"
tabstops but it does not look like they are implemented in Zed yet.

## Additional tests added for cases currently not covered:
- backslash not being used to escape anything (so just a normal
backslash)
- backslash escaping a backslash (so that the second does not escape
what follows it)

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2024-07-22 00:57:34 +02:00
Marshall Bowers
83f6a7f228 assistant: Use square buttons for the inline assist model selector (#14928)
This PR updates the model selector buttons in the inline assistant to
use `IconButtonShape::Square`.

Release Notes:

- N/A
2024-07-21 13:59:21 -04:00
Marshall Bowers
2d96bba61f assistant: Respect ui_font_weight setting for inline assist in the terminal (#14924)
This PR updates the terminal inline assist to respect the
`ui_font_weight` setting.

Release Notes:

- N/A
2024-07-21 13:09:25 -04:00
Marshall Bowers
54039162ac erlang: Add support for installing elp language server (#14923)
This PR updates the Erlang extension with support for installing the
[Erlang Language
Platform](https://github.com/WhatsApp/erlang-language-platform) (`elp`)
language server from the GitHub Release assets.

Release Notes:

- N/A
2024-07-21 12:42:21 -04:00
Marshall Bowers
45b45155d4 Treat tsconfig.json as JSONC (#14920)
This PR updates the default settings to treat `tsconfig.json` files as
JSONC.

Resolves https://github.com/zed-industries/zed/issues/14906.

Release Notes:

- TypeScript's `tsconfig.json` files are now treated as JSONC.
2024-07-21 12:17:16 -04:00
Ryan Hawkins
a4baba7edd Add button to copy SHA from Git blame (#14883)
The git blame dialog doesn't give the user a way to quickly copy the SHA
of the associated commit for a line. Adding an option for users to
quickly access this SHA is helpful for user's to do any more git-fu they
might need, such as viewing the full changes themselves within git,
checking the commit out, bisecting off the commit, etc.

This is also very handy for user's of self-hosted git providers.
Determining what provider a self-hosted repository is using could be
quite difficult and this presents an easy option to allow users to look
up more about a commit without having to memorize the short SHA.

Release Notes:

- Added a button to copy the SHA from a Git blame entry.

<img width="1552" alt="A screenshot showing the new copy SHA button
within the Zed editor's inline blame "
src="https://github.com/user-attachments/assets/9365950d-3a3f-4c11-b119-ab02654f5669">

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-21 12:02:16 -04:00
Marshall Bowers
cb2c334358 Use defaults for unchanged TextStyle fields (#14918)
This PR updates a number of spots where we were setting all of the
`TextStyle` fields even if we were not changing the values from the
defaults.

We now use `..Default::default()`.

Release Notes:

- N/A
2024-07-21 11:55:45 -04:00
Max Brunsfeld
e8bcc412b5 Fix usability issues with ssh connection modal (#14917)
Release Notes:

- N/A
2024-07-21 08:43:59 -07:00
Fabian Bergström
7b88fc5cda erlang: Add Erlang Language Platform support (#14879)
Added support for the [Erlang Language
Platform](https://whatsapp.github.io/erlang-language-platform/) language
server to the Erlang extension.

Release Notes:

- N/A

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-21 11:38:24 -04:00
Marshall Bowers
2f3df9fd93 erlang: Update structure to accommodate multiple language servers (#14915)
This PR updates the structure of the Erlang extension to accommodate
multiple language servers.

Release Notes:

- N/A
2024-07-21 11:21:52 -04:00
Marshall Bowers
7d063aa10b erlang: Upgrade zed_extension_api to v0.0.6 (#14914)
This PR upgrades the Erlang extension to use v0.0.6 of the
`zed_extension_api`.

Release Notes:

- N/A
2024-07-21 10:59:05 -04:00
张小白
9c26d07f7a Ensure ExtensionBuilder respects the proxy settings (#14899)
Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-21 10:46:43 -04:00
Marshall Bowers
0a9d50bf01 http: Refactor construction of HTTP clients with a proxy (#14911)
This PR refactors the `http` crate to expose a better way of
constructing an `HttpClient` that contains a proxy.

Release Notes:

- N/A
2024-07-21 10:15:38 -04:00
Marshall Bowers
c7331b41e9 gpui: Include image URI in ImageCacheError::BadStatus (#14910)
This PR updates the `ImageCacheError::BadStatus` variant to include the
URI of the image that failed to load.

This helps contextualize the resulting error logs.

Release Notes:

- N/A
2024-07-21 10:13:31 -04:00
Mag Mell
70f7f2d2da theme_importer: Output logs to stderr (#14890)
Will allow writing directly to a file without logging via `cargo run -p
theme_importer -- /path/to/file`

Release Notes:

- N/A
2024-07-21 08:53:37 -04:00
Kyle Kelley
cd9b25d827 repl: Increase accuracy of error output line height (#14880) 2024-07-20 15:59:54 -07:00
Kyle Kelley
781633fb1a repl: Ensure that the output's computed line height is at least 1 (#14877) 2024-07-20 15:59:28 -07:00
Kyle Kelley
6dfb0a4a70 repl: Push button to clear outputs (#14873) 2024-07-20 11:32:03 -07:00
claytonrcarter
a1670551bf Fix description of -l flag in bundle-mac (#14864)
This removes mention of "copy bundle to `/Applications`" from the help
text for `bundle-mac` because, as far as I can tell, the `-l` flag only
controls the build, not the copy/install. (The copy/install is
controlled by using the `-i` flag in conjunction with `-l`.)


Release Notes:

- N/A
2024-07-20 19:16:54 +03:00
Piotr Osiewicz
71cbfc65b1 Ruff: pass initialization_options from settings (#14866)
No version bump, as the extension is not out yet.
Release Notes:

- N/A
2024-07-20 16:09:53 +02:00
Piotr Osiewicz
1218a846c1 extensions: Add Ruff extension (#14198)
Release Notes:

- Added extension for [Ruff](https://docs.astral.sh/ruff/), an extremely fast Python linter and code formatter, written in Rust.
2024-07-20 15:18:02 +02:00
versecafe
48c253feb8 Expand terminal menu actions (#14828)
<img width="422" alt="image"
src="https://github.com/user-attachments/assets/73195894-81a7-4b8e-b5cf-ae60bf5b1fb9">

Release Notes:

- Added `Copy`, `Paste`, `Select All`, & `New Terminal` into terminal's context menu
2024-07-20 12:20:53 +03:00
Kevin Wang
46b7fa9bcb Add Sign Out link for Supermaven (#14834)
Adds a menu item to sign out from a linked Supermaven account.


![image](https://github.com/user-attachments/assets/6af3c39f-9ca4-4ac2-a5c0-c7cb3c3aaac2)


Release Notes:

- Added the ability to sign out of a Supermaven account ([#12715(https://github.com/zed-industries/zed/issues/12715))
2024-07-20 03:53:24 +03:00
Max Brunsfeld
022e662815 Start work on showing progress when initializing ssh remoting 2024-07-19 17:25:28 -07:00
Max Brunsfeld
d89e2592a1 Fix file name conflict when downloading app update (#14847)
This fixes broken auto-updates on nightly. Unfortunately, nightly users
will need to re-download.

Release Notes:

- N/A
2024-07-19 16:33:41 -07:00
Fabian
a072caab0b node_runtime: Bump downloaded Node.js version to Current (Jod) (#14687)
This PR bumps the hard-coded Node.js version from v18.x (Hydrogen), which was LTS until October 2023, to v22.x (Jod) which will be the next LTS release in October 2024.

Release Notes:

- Updated Zed's node version (v18.x -> v22.x)
2024-07-20 01:18:56 +03:00
Max Brunsfeld
fbe30c6f2e Fixes for SSH remoting infrastructure (#14844)
* Fixed mis-named macOS remote server archives in actions and packaging
scripts
* Fixed an issue with the ask pass script on linux
* Download nightly versions of remote servers in dev mode (not stable)

Release Notes:

- N/A
2024-07-19 15:08:10 -07:00
Marshall Bowers
a5b8094082 editor: Implement From instead of Into for converting BlockIds to ElementIds (#14839)
This PR replaces the `Into<ElementId> for BlockId` implementation with
`From<BlockId> for ElementId`.

This keeps in line with Rust's guidance for preferring implementing
`From`, and gives us more flexibility when converting.

Release Notes:

- N/A
2024-07-19 16:24:12 -04:00
Conrad Irwin
d2efa12e16 vim: Fix gv after actions (#14829)
Fixes: #13720

Co-Authored-By: <tobbe@tlundberg.com>



Release Notes:

- vim: Fixed `gv` after `y`, `d`, etc.
([#13760](https://github.com/zed-industries/zed/issues/13760)).
2024-07-19 13:26:55 -06:00
Marshall Bowers
5e635b8914 ui: Remove absolute positioning for tab slots (#14836)
This PR reworks the `Tab` component to not use absolute positioning in
order to position the tab slots.

This should make any further adjustments we want to make to the spacing
easier to do.

Release Notes:

- N/A
2024-07-19 15:11:27 -04:00
Jason Lee
0a02691778 Fix tooltips sometimes continuously displaying when the button is selected (#14832)
Release Notes:

- Fixed sometime tooltip will continuously display when the button is
selected.

---

@mrnugget The #13857 This change has led into a bug, the selected item
before tooltip will continuous display if there are no other tooltips.



https://github.com/user-attachments/assets/06b4a9a4-dede-4c18-b020-e20b6090341f
2024-07-19 12:49:08 -06:00
Conrad Irwin
1dc4d4200f Add command aliases (#14826)
Co-Authored-By: <tobbe@tlundberg.com>

Release Notes:

- Added `"command_aliases"` setting to let you abbreviate commands
2024-07-19 12:48:48 -06:00
Max Brunsfeld
b22718e643 Fix log file path for dsymutil in bundle-mac 2024-07-19 11:35:31 -07:00
Max Brunsfeld
bc16c2f85d Fix error in bundle-mac 2024-07-19 11:22:46 -07:00
Max Brunsfeld
1805986d82 Suppress noisy output from dsymutil in bundle-mac 2024-07-19 11:22:46 -07:00
Marshall Bowers
ee0dfe9c44 elixir: Make start_lexical.sh executable (#14831)
This PR fixes an issue in the Lexical language server installation where
the `start_lexical.sh` script was not being made executable when
installed from GitHub.

Release Notes:

- N/A
2024-07-19 14:17:04 -04:00
Max Brunsfeld
f4074d784c Remove spurious self-hosted label for bundle-linux-arm job 2024-07-19 11:08:20 -07:00
Max Brunsfeld
e58db43ed8 Remove stray step from release nightly workflow 2024-07-19 10:49:34 -07:00
Richard Feldman
ec487d8f64 Extract completion provider crate (#14823)
We will soon need `semantic_index` to be able to use
`CompletionProvider`. This is currently impossible due to a cyclic crate
dependency, because `CompletionProvider` lives in the `assistant` crate,
which depends on `semantic_index`.

This PR breaks the dependency cycle by extracting two crates out of
`assistant`: `language_model` and `completion`.

Only one piece of logic changed: [this
code](922fcaf5a6 (diff-3857b3707687a4d585f1200eec4c34a7a079eae8d303b4ce5b4fce46234ace9fR61-R69)).
* As of https://github.com/zed-industries/zed/pull/13276, whenever we
ask a given completion provider for its available models, OpenAI
providers would go and ask the global assistant settings whether the
user had configured an `available_models` setting, and if so, return
that.
* This PR changes it so that instead of eagerly asking the assistant
settings for this info (the new crate must not depend on `assistant`, or
else the dependency cycle would be back), OpenAI completion providers
now store the user-configured settings as part of their struct, and
whenever the settings change, we update the provider.

In theory, this change should not change user-visible behavior...but
since it's the only change in this large PR that's more than just moving
code around, I'm mentioning it here in case there's an unexpected
regression in practice! (cc @amtoaer in case you'd like to try out this
branch and verify that the feature is still working the way you expect.)

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-19 13:35:34 -04:00
Max Brunsfeld
b9a53ffa0b Add the ability to edit remote directories over SSH (#14530)
This is a first step towards allowing you to edit remote projects
directly over SSH. We'll start with a pretty bare-bones feature set, and
incrementally add further features.

### Todo

Distribution
* [x] Build nightly releases of `zed-remote-server` binaries
    * [x] linux (arm + x86)
    * [x] mac (arm + x86)
* [x] Build stable + preview releases of `zed-remote-server`
* [x] download and cache remote server binaries as needed when opening
ssh project
* [x] ensure server has the latest version of the binary


Auth
* [x] allow specifying password at the command line
* [x] auth via ssh keys
* [x] UI password prompt

Features
* [x] upload remote server binary to server automatically
* [x] opening directories
* [x] tracking file system updates
* [x] opening, editing, saving buffers
* [ ] file operations (rename, delete, create)
* [ ] git diffs
* [ ] project search

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2024-07-19 10:27:26 -07:00
Kyle Kelley
7733bf686b Repl reorder keybinding (#14824)
Ensures that the assistant keybinding for cmd-enter takes precedence
over `repl::Run`.

On Linux, `ctrl-enter` (the equivalent), issues `repl::Run` when in a
jupyter context.

Release Notes:

- N/A
2024-07-19 10:05:08 -07:00
Marshall Bowers
edddc68da6 client: Remove leftover http.rs file (#14822)
This PR removes an empty `http.rs` file that was leftover from a
previous PR.

Release Notes:

- N/A
2024-07-19 12:22:31 -04:00
Thorsten Ball
5de5d5bc56 go: Fix quoting of targeting expression for non-fish shells (#14821)
This fixes #14818.

The change in #14055 broke the tasks in `zsh` (and I suspect in `bash`,
`sh` too), because what was executed was NOT

    $ go test . -run '^TestThis$'

but instead this:

    $ go test . -run \'^TestThis$\'

And in `zsh` this means that `'` is part of the argument passed to `go`,
which means the targeting string is wrong.

Since the problem in `fish` doesn't seem to be the `^` but the `$`, we
can only escape that, which makes the escaped string work in `zsh` and
`fish` and `bash` (in which I've tested this change here)

Release Notes:

- go: Fix running single tests by changing the quoted expression in the
`go test` command to work again in `bash`, `zsh`, etc.
([#14818](https://github.com/zed-industries/zed/issues/14818))
2024-07-19 18:05:31 +02:00
Kyle Kelley
5467e18a5b repl: Refactor editor registration (#14819)
Cleans up action registration with the editors and also fixes a major
bug where only one workspace's panel was getting session info (due to my
not understanding that `cx.observe_new_views` is for the whole app).

Release Notes:

- N/A

Co-authored-by: Conrad <conrad@zed.dev>
2024-07-19 08:58:57 -07:00
Antonio Scandurra
4c7f1032a4 Allow an initial prompt to be associated with inline assist (#14816)
Release Notes:

- Added the ability to create custom inline assist bindings that
pre-fill a prompt from your keymap, e.g.:
    ```json
    [
        {
            "context": "Editor && mode == full",
            "bindings": {
                "ctrl-shift-enter": [
                    "assistant::InlineAssist",
                    { "prompt": "Build a snake game" }
                ]
            }
        }
    ]
    ```

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-19 17:13:48 +02:00
Antonio Scandurra
d61eaea4b9 Avoid losing focus when block decorations go offscreen (#14815)
Release Notes:

- Fixed a bug that caused focus to be lost when renames and inline
assists were scrolled offscreen.

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-19 17:04:18 +02:00
Richard Feldman
f5d50f2b1e Delete obsolete and unused remote_projects.rs (#14811)
See
https://github.com/zed-industries/zed/pull/11301#issuecomment-2234239630
for context

Release Notes:

- N/A
2024-07-19 08:50:49 -06:00
Kyle Kelley
be4b19babb repl: Create action to refresh kernelspecs (#14786)
Adds a command to refresh kernelspecs. Also added the kernelspecs to the
runtime panel when none are running. That's just for now until we move
out of the panel completely.

Release Notes:

- N/A
2024-07-19 07:42:18 -07:00
Marshall Bowers
272be98ec8 php: Bump to v0.1.2 (#14808)
This PR bumps the PHP extension to v0.1.2.

Changes:

- #14806

Release Notes:

- N/A
2024-07-19 09:13:23 -04:00
Marshall Bowers
e3d5eff858 Use ui::prelude::* in a few more spots (#14807)
This PR updates a couple files to make use of the `ui::prelude::*`
import.

Release Notes:

- N/A
2024-07-19 08:57:51 -04:00
Marshall Bowers
9cb17ac630 php: Respect LSP settings for Intelephense (#14806)
This PR updates the PHP extension with support for reading LSP settings
when using Intelephense as the language server.

Addresses #4258.

Release Notes:

- N/A
2024-07-19 08:51:44 -04:00
Jason Lee
fb541accb2 gpui: Update Menu name to use SharedString type to support more types (#14791)
Release Notes:

- N/A
2024-07-19 08:51:31 -04:00
Marshall Bowers
836f623800 Update icon positioning in tabs (#14804)
This PR updates the icon positioning in tabs to make give them even
spacing on either side.

Without file icons:

<img width="901" alt="Screenshot 2024-07-19 at 7 55 39 AM"
src="https://github.com/user-attachments/assets/89cc80cd-1323-424e-90a5-79d8586e8725">

With file icons:

<img width="956" alt="Screenshot 2024-07-19 at 7 55 52 AM"
src="https://github.com/user-attachments/assets/c5b47b4e-e6c3-4dbd-aeb3-fb09a0032105">

Release Notes:

- N/A
2024-07-19 08:04:57 -04:00
Kirill Bulatov
18c2e8f6ca Rework mouse handling of git hunks diff (#14727)
Closes https://github.com/zed-industries/zed/issues/12404

![Screenshot 2024-07-18 at 14 02
31](https://github.com/user-attachments/assets/a8addd22-0ed9-4f4b-852a-f347314c27ce)

![Screenshot 2024-07-18 at 14 02
43](https://github.com/user-attachments/assets/0daaed10-b9f3-4d4b-b8d7-189aa7e013b9)

Video:


https://github.com/user-attachments/assets/58e62527-da75-4017-a43e-a37803bd7b49


* now shows a context menu on left click instead of expanding the hunk
diff
* hunk diffs can be toggled with a single cmd-click still
* adds a X mark into gutter for every hunk expanded
* makes `editor::ToggleDiffHunk` to work inside the deleted hunk editors

Additionally, changes the way editor context menus behave when the
editor is scrolled — right click and diff hunks context menu now will
stick to the place it was invoked at, instead of staying onscreen at the
same pixel positions.

Release Notes:

- Improved the way git hunks diff can be toggled with mouse
([#12404](https://github.com/zed-industries/zed/issues/12404))

---------

Co-authored-by: Nate Butler <nate@zed.dev>
Co-authored-by: Conrad Irwin <conrad@zed.dev>
2024-07-19 13:48:04 +03:00
0x2CA
bf4645b1fe Fix vim <CTRL-J> slines downward linewise, Not Join Lines (#14796)
https://vimhelp.org/motion.txt.html#CTRL-J

Release Notes:

- vim: Added `ctrl-j` as a new binding that's equivalent to `j`.
2024-07-19 11:52:37 +02:00
Antonio Scandurra
4d177918c1 Start on adding support for editing via the assistant panel (#14795)
Note that this shouldn't have any visible user-facing behavior yet. The
feature is incomplete but we wanna merge early to avoid a long-running
branch.

Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-19 11:13:15 +02:00
Conrad Irwin
87457f9ae8 Fix off-by-one errors in syntax highlighting (#14780)
In the case that a line ended with a 0-length run, we would get our
highlights offset by one position.

Release Notes:

- Fixed syntax highlights being offset from syntax in diagnostics
popovers.
2024-07-18 22:37:30 -06:00
Vishal Bhavsar
be45f32753 vim: Fix 'Y' to yank to end of line (#14783)
Instead of yanking the entire line.

Release Notes:

- vim: Updated `Y` to yank to end of line (like neovim)
https://github.com/zed-industries/zed/issues/14771
2024-07-18 22:34:40 -06:00
versecafe
18b5a87298 Add gpt-4o-mini as an available model (#14770)
Release Notes:

- Fixes #14769
2024-07-18 22:32:56 -06:00
Kyle Kelley
48211e8ce2 repl: Check process status and propagate to output (#14782)
<img width="582" alt="image"
src="https://github.com/user-attachments/assets/14bd321d-f5fc-4cc0-9386-f435423057ad">

Release Notes:

- N/A
2024-07-18 19:35:05 -07:00
Conrad Irwin
5008a388e6 Use -f in ssh remoting too (#14773)
Release Notes:

- N/A
2024-07-18 19:24:59 -06:00
versecafe
3c417864e6 typescript: Highlight using keyword (#14772)
Release Notes:

- Added syntax highlighting for the `using` keyword in TypeScript
([#14762](https://github.com/zed-industries/zed/issues/14762)).
2024-07-18 17:33:13 -04:00
Marshall Bowers
ad3055076d assistant: Allow /docs to perform JIT indexing when run (#14768)
This PR updates the `/docs` slash command with the ability to
just-in-time index a package when there are not yet any results in the
index.

When running a `/docs` slash command, we fist check to see if there are
any results in the index that would match the search.

If there are, we go ahead and return them, as we do today.

However, if there are not yet any results we kick off an indexing task
as part of the command execution to fetch the results.

Release Notes:

- N/A
2024-07-18 17:01:48 -04:00
Peter Tripp
7c63f26aa9 Add duplicate and support as core github issue labels (#14735) 2024-07-18 16:21:30 -04:00
Conrad Irwin
f15a441c9d Fix video rendering on docs (#14764)
Release Notes:

- N/A
2024-07-18 13:34:25 -06:00
Kyle Kelley
cb217381ce repl: Scroll down after running code (#14759)
Go to the next line after running code. Allows for fluid coding and
running.


https://github.com/user-attachments/assets/d11ac05d-7801-4191-b275-3b20302e54c5

Release Notes:

- N/A
2024-07-18 11:22:43 -07:00
Kevin Wang
862f5a0561 Handle supermaven account status messages (#14749)
Sets the account status state to allow the Supermaven button to move out
of the "Supermaven is initializing..." state. We also need to add the
ability to sign out and change tiers but I will do that in a separate
PR.

Release Notes:

- Improved Supermaven status messages
([#12715](https://github.com/zed-industries/zed/issues/12715)).
2024-07-18 11:22:30 -07:00
Hans
7d30175527 vim: Add repeat support for add_surround (#14746)
Fix: #14242

At present, we don't trigger the method `start_recording(cx)` to start
recording when typing add_surround, so it can't be repeated well, delete
and change actually don't increase, but when entering `d` and `c`, it
happens that these two operations are recorded, I think maybe we also
need to call `start_recording(cx)` when calling delete_surround and
change_surround, otherwise when the user modifies their shortcuts, these
two functions may not be supported by repeat

Release Notes:

- N/A
2024-07-18 11:54:59 -06:00
Hans
d044685706 vim: Adjust surrounding_markers method (#14752)
At present, when calculating some ranges, we take the `tuple_windows` to
iterate forward, which will cause some problems when the cursor is being
front, because `tuple_windows` iteration cannot iterate to the very
beginning, so there will be some cases that cannot be calculated, adjust
this method, and now it can calculate more perfectly, and the execution
speed is about the same

Release Notes:

- N/A
2024-07-18 11:53:51 -06:00
Joseph T. Lyons
0496d0db9a Add multibuffer edit video (#14757)
Release Notes:

- N/A
2024-07-18 13:45:06 -04:00
Marshall Bowers
c19e71f51e languages: Remove lingering Elm file (#14754)
This PR removes a leftover file from when Elm support was done natively.

Elm was extracted into an extension in #10432.

Release Notes:

- N/A
2024-07-18 12:41:18 -04:00
Marshall Bowers
f7ff9b0811 vue: Bump to v0.1.0 (#14748)
This PR bumps the Vue extension to v0.1.0.

Changes:

- https://github.com/zed-industries/zed/pull/14747

Release Notes:

- N/A
2024-07-18 11:39:30 -04:00
Marshall Bowers
be1ff8aee9 vue: Install a global version of TypeScript if not present in the project (#14747)
This PR updates the Vue extension with support for installing and using
its own copy of TypeScript if it can't find one in the project.

The way we resolve `typescript` is as follows:

- We check the project's `package.json` for `typescript` in either the
`devDependencies` or `dependencies`
- If found, we set the `typescript.tsdk` to
`node_modules/typescript/lib` to use the project's copy of TypeScript
- If not found, we install the latest version of `typescript` (if not
already downloaded) to the extension's `package.json` and use that
version for `typescript.tsdk`

This should resolve instances where Vue projects that do not have an
explicit `typescript` dependency—such as those using Vue with plain
JavaScript—fail to load the language server due to TypeScript not being
found.

Release Notes:

- N/A
2024-07-18 11:32:46 -04:00
Thorsten Ball
3a83fecea9 gpui: Prefer removable over integrated Metal devices (#14744)
For context, see:

-
https://github.com/zed-industries/zed/issues/5124#issuecomment-2227743811
-
https://github.com/zed-industries/zed/pull/14738#issuecomment-2236613976

Short version: on Intel MacBooks it's better to prefer integrated
(`is_low_poer()`) GPUs, except when a user has an eGPU plugged-in, in
which case they very likely want to prefer that.

Before this change, we'd always prefer the integrated GPU, even if an
eGPU was available.

Now, with this change, if a user has

- eGPU
- integrated GPU
- discrete GPU

We'd first prefer eGPU, then integrated, then discrete.



Release Notes:

- Changed preference for GPUs on macOS so that eGPUs are now preferred
over integrated ones (and both of which are preferred over discrete
GPUs) on Intel Macs.
2024-07-18 16:52:38 +02:00
CharlesChen0823
bac4a0428d project_panel: Select the newly created file when copy/pasting a file (#14705)
Closes: #14361 

Release Notes:

- Improved project panel to select newly created file on copy/paste ([#14361](https://github.com/zed-industries/zed/issues/14361))
2024-07-18 17:26:33 +03:00
Kirill Bulatov
ed3d3dc690 Improve same line diagnostic rendering (#14741)
Follow-up of https://github.com/zed-industries/zed/pull/14515

Fixed certain visual artifacts, related to multi line diagnostics and
block toggle rendering.
Also enabled diagnostics toolbar controls for the experimental view too.


Release Notes:

- N/A
2024-07-18 17:22:38 +03:00
Marshall Bowers
24d9374744 Add text_color helper for tab contents (#14737)
This PR adds a `text_color` method to `TabContentParams` to more easily
compute the text color to be used for tab contents.

This consolidates a number of conditionals that were scattered all over
the place to give us a singular source of truth for these colors.

Release Notes:

- N/A
2024-07-18 09:27:05 -04:00
Marshall Bowers
5d751f232c collab_ui: Refine channel tab appearance (#14736)
This PR refines the appearance of the channel tabs.

We now display the channel icon in the tab's icon slot. We also now
adjust the icon based on whether the channel is public or members-only
(the same we do in the channel list):

<img width="214" alt="Screenshot 2024-07-18 at 9 02 00 AM"
src="https://github.com/user-attachments/assets/973d83c5-f045-4282-a43a-18e12ce93f78">

The `read-only` and `disconnected` states are now also shown in a
visually different style than the channel name:

<img width="247" alt="Screenshot 2024-07-18 at 9 01 13 AM"
src="https://github.com/user-attachments/assets/359f61cf-3b80-4a3f-8948-d705f6c24695">

Release Notes:

- Refined the appearance of channel tabs.
2024-07-18 09:18:09 -04:00
Piotr Osiewicz
3fb3148995 lsp: Use CompletionTriggerKind::TRIGGER_CHARACTER only for characters allowlisted by the server (#14734)
Fixes #13823


Release Notes:

- N/A
2024-07-18 14:53:31 +02:00
Remco Smits
d08e28f4e0 Clean up show continue/pause buttons 2024-07-17 15:43:16 +02:00
Remco Smits
ef67321ff2 Implement restart program 2024-07-17 14:41:22 +02:00
Remco Smits
4c777ad140 Fix deserialize error when debug adapter send empty {} body but expected type () 2024-07-17 14:09:56 +02:00
Remco Smits
923ae5473a Fix that we change the thread state status to running
Not all the debug adapters send the continue event so we couldn't determine that the thread state status was `running`. So we have to determine it our selfs if the thread state status did not change, if so we change the status to `running`.

This allows us to show the pause button when the thread state status is running, so you can pause the programs execution.
2024-07-17 10:21:39 +02:00
Remco Smits
a48166e5a0 Add method to get the capabilities from the client 2024-07-16 23:07:43 +02:00
Remco Smits
ef098c028b Show/disable actions buttons based on thread state status 2024-07-16 23:07:29 +02:00
Remco Smits
7ce1b8dc76 Update the thread state status after continue request if continue event was not send 2024-07-16 23:06:39 +02:00
Remco Smits
e350417a33 Add debug pause icon 2024-07-16 22:55:37 +02:00
Remco Smits
ea9e0755df Implement stop action 2024-07-16 21:51:46 +02:00
Remco Smits
6aced1b3aa Add tooltip for tab content to indicate the thread status 2024-07-16 21:29:47 +02:00
Remco Smits
99e01fc608 Clippyyyyy 2024-07-16 21:28:52 +02:00
Remco Smits
fe899c9164 Change that current thread state does not return option 2024-07-16 21:28:36 +02:00
Remco Smits
f8b9937e51 Handle exited event 2024-07-16 21:19:20 +02:00
Remco Smits
dc5928374e Fix we did not notify after stack frame list reset was doen
This fixes an issue if you step debug the same thread, the stack frame list was not up to date.
2024-07-16 21:18:41 +02:00
Remco Smits
0deb3cc606 Move handle initialized event to own method 2024-07-16 21:17:05 +02:00
Remco Smits
ba4a70d7ae Make TCP connect delay configurable 2024-07-16 20:56:24 +02:00
Remco Smits
65a790e4ca Make double click on pane action optional 2024-07-16 20:55:55 +02:00
Remco Smits
b6e677eb06 Wip configure host for tcp debug adapter
Co-Authored-By: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
2024-07-15 18:44:21 +02:00
Anthony Eid
ffa0609f8d Implement stdio debugger connection type (#5)
* Add support for DAP to use std for communication

* Add more descriptive error logs for DAP

* Implement handler for continued event

* Add PR feedback to handle_continued_event function
2024-07-15 18:12:05 +02:00
Remco Smits
77a314350f Fix autocompletion for connection type
before `s_t_d_i_o` after `stdio`

Co-Authored-By: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
2024-07-15 18:03:44 +02:00
Remco Smits
737b03c928 Refactor go_to_stack_frame & clear_hightlights 2024-07-14 16:44:47 +02:00
Remco Smits
1b42dd5865 Add client id to thread tab title 2024-07-14 15:48:43 +02:00
Remco Smits
c9074b1c25 Wire through debugger id for initialize request 2024-07-14 15:19:05 +02:00
Remco Smits
00379280f3 Don't store request type on client(its already in the config) 2024-07-14 15:18:37 +02:00
Remco Smits
4e2d0351cc Rename thread state -> thread states 2024-07-14 15:18:07 +02:00
Remco Smits
a583efd9b9 Give clippy the best day of his life 2024-07-12 22:34:42 +02:00
Remco Smits
6237c29a42 Merge branch 'main' into debugger 2024-07-12 22:21:46 +02:00
Remco Smits
9ea9b41e73 Merge main into debugger
Co-Authored-By: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
2024-07-12 22:18:39 +02:00
Remco Smits
8d99f9b7d2 Fix send all breakpoints when toggling breakpoints
We now send all the breakpoints for the current buffer when toggling one breakpoint.

Co-Authored-By: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
2024-07-12 22:00:58 +02:00
Remco Smits
014ffbce2e Send the initial breakpoints from the project
We now send the initial breakpoints that we created before the debug adapter was running.

Co-Authored-By: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
2024-07-12 21:11:38 +02:00
Remco Smits
68dd3c90c2 Fix we never created the first pane OOPS
Co-Authored-By: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
2024-07-12 21:10:39 +02:00
Remco Smits
4a6f6151f0 Remove editor highlights when debugger terminated event was received 2024-07-11 20:59:02 +02:00
Remco Smits
f108d4c705 Replace find method with any
Because we don't use the result of the find so any would be better in this case
2024-07-11 20:54:41 +02:00
Remco Smits
5ab95f1e1a Remove unused method 2024-07-11 20:51:56 +02:00
Remco Smits
49da08ffa4 Make goto stack trace frame work again after stopped event 2024-07-11 20:44:55 +02:00
Remco Smits
f287c897a4 Remove commented out code 2024-07-11 19:13:48 +02:00
Remco Smits
d15ff2d06f Don't spawn task for empty tasks vec 2024-07-11 19:13:26 +02:00
Remco Smits
648daa3237 WIP start adding support for debugging multiple threads at the same time
Start showing all the threads as tabs so you can debug multiple threads at the same time.
2024-07-10 22:31:49 +02:00
Remco Smits
33e127de09 Merge pull request #4 from Anthony-Eid/debugger
Clean up debugger Inventory code
2024-07-09 16:25:14 +02:00
Anthony Eid
5a9b279039 Merge with remco-debugger branch 2024-07-08 19:55:11 -04:00
Remco Smits
cce58570dc Remove unneeded clone 2024-07-07 20:52:40 +02:00
Remco Smits
361bbec3a0 Make some more room to show stack trace 2024-07-07 20:37:33 +02:00
Remco Smits
a87409813c Fix that thread is not always known in stopped event
This fixes an issue that not all debuggers send the `thread` event and we assumed that we always know the thread inside the handling code of the `stopped` event.
2024-07-07 20:24:33 +02:00
Remco Smits
da84aa1ac2 Fix panic with line - 1 and column - 1 2024-07-07 20:21:38 +02:00
Remco Smits
817760688a Don't support removing current thread_id 2024-07-07 17:32:29 +02:00
Remco Smits
13e56010c1 Add status types for thread state so we can disable buttons on this status 2024-07-07 17:27:40 +02:00
Remco Smits
b827a35e44 Make clippy's day again with using async mutex 2024-07-05 19:26:12 +02:00
Remco Smits
9006e8fdff Make clippy's day 2024-07-05 19:19:00 +02:00
Remco Smits
ce8ec033f4 Fix clippy 2024-07-05 19:16:38 +02:00
Remco Smits
9678cc9bc3 Remove editor highlights 2024-07-05 19:15:16 +02:00
Remco Smits
e87c4ddadc Merge branch 'main' into debugger 2024-07-05 17:02:58 +02:00
Remco Smits
3f8581a2fb Merge branch 'main' into debugger 2024-07-05 16:54:35 +02:00
Remco Smits
00c5b83384 Update handle terminated event to restart debugger 2024-07-05 16:50:43 +02:00
Remco Smits
4aedc1cd0b Save request type on client 2024-07-05 16:44:27 +02:00
Remco Smits
d238675c1a Wip support for determing to use launch or attach request 2024-07-05 16:31:07 +02:00
Remco Smits
dcf6f6ca30 Implement handle terminated event 2024-07-05 15:50:11 +02:00
Remco Smits
f4606bd951 Don't go to stack frame if event is not ment for current client 2024-07-05 15:11:26 +02:00
Remco Smits
12bef0830a Allow clicking on stack frames 2024-07-05 14:25:01 +02:00
Remco Smits
953a2b376c Make sure we don't resume other threads with action for one thread 2024-07-05 00:08:54 +02:00
Remco Smits
ac3b9f7a4c Wip show current file & column 2024-07-04 23:57:30 +02:00
Remco Smits
ef5990d427 Move ToggleBreakpoint action to editor instead of workspace 2024-07-04 14:52:50 +02:00
Remco Smits
23a81d5d70 Send all stack frame, scopes, and variable requests parallel
This will improve performance for loading large stack frames.
2024-07-04 13:58:12 +02:00
Remco Smits
515122c54d Clean up debug panel code
For now we only support debugger to be positioned on the bottom.
2024-07-03 23:20:39 +02:00
Remco Smits
f4eacca987 Fix 2 TODO's for multiple client support 2024-07-03 15:48:47 +02:00
Remco Smits
003fb7c81e Move thread state to the client
This allows us to show the current line and is closer for multi client/adapter support
2024-07-03 14:53:22 +02:00
Remco Smits
7d2f63ebbd Split event handling code to separate functions 2024-07-03 12:49:49 +02:00
Remco Smits
93e0bbb833 Use parking lot mutex instead of sync mutex 2024-07-03 10:40:02 +02:00
Remco Smits
8c5f6a0be7 Make clippppy more happy 2024-07-03 10:12:07 +02:00
Remco Smits
d6cafb8315 Fix typo 2024-07-03 09:45:54 +02:00
Remco Smits
a24b76b30b Merge branch 'main' into debugger 2024-07-03 09:38:56 +02:00
Remco Smits
11d74ea4ec Add tooltip for stack trace debug items
Co-Authored-By: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
2024-07-02 19:00:35 +02:00
Remco Smits
93f4775cf6 Properly fix race condition of receive events and handling responses
Co-Authored-By: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
2024-07-02 19:00:00 +02:00
Remco Smits
a93913b9a3 Swap handle events with handle request
This is a temporary fix, so we don't crash when race condition occurs and the event is send earlier than request was finished. The debug adapter was still `starting` meaning the task was still pending.
2024-07-02 16:51:30 +02:00
Remco Smits
08afbc6b58 Update capabilities 2024-07-02 15:09:44 +02:00
Remco Smits
b869465f00 Fix send configuration done request when initialized event was received.
This fixes an issue that the debugger adapter was notified to early. So it had in correct breakpoint information and would always stop on the first line without given a stack trace
2024-07-02 15:09:26 +02:00
Remco Smits
2debea8115 Wire through transport type from config 2024-07-02 15:06:09 +02:00
Remco Smits
cae295ff65 Fix send correct line to set breakpoint 2024-07-02 15:02:59 +02:00
Remco Smits
c51206e980 Wire through launch config values to launch request 2024-07-02 10:59:02 +02:00
Remco Smits
1baa5aea94 Fix missing match arg for Initialized event 2024-07-02 10:57:49 +02:00
Remco Smits
3e022a5565 Change error message when non-success response came back 2024-07-02 10:46:52 +02:00
Remco Smits
3dd769be94 Fix panic denormalizing Terminated event without body 2024-07-02 10:46:12 +02:00
Remco Smits
7936a4bee3 Fix panic when denormalizing Initialized event with body 2024-07-02 10:45:41 +02:00
Remco Smits
09aabe481c Undo wrong request value 2024-07-01 16:09:06 +02:00
Remco Smits
2ea1e4fa85 Add debug panel actions 2024-07-01 12:45:08 +02:00
Remco Smits
8699dad0e3 Add restart & pause method & log request errors 2024-07-01 12:43:57 +02:00
Remco Smits
47a5f0c620 Start wiring through custom launch data 2024-06-29 16:47:51 +02:00
Remco Smits
854ff68bac Clean up transport code 2024-06-29 16:38:43 +02:00
Remco Smits
01d384e676 Add missing event body mapping 2024-06-29 16:36:32 +02:00
Anthony Eid
ddd893a795 Clean up debugger inventory files/code 2024-06-28 00:52:15 -04:00
Remco Smits
3d7cd5dac7 Remove todo and change casing 2024-06-27 22:00:01 +02:00
Remco Smits
a6fdfb5191 Merge pull request #3 from Anthony-Eid/debugger
Debugger Tasks
2024-06-27 20:35:10 +02:00
Anthony Eid
73a68d560f Change abs_path to path in project update worktree settings 2024-06-27 14:31:50 -04:00
Anthony Eid
b4eeb25f55 Merge branch 'remco-debugger' into debugger 2024-06-27 14:15:03 -04:00
Remco Smits
fc991ab273 Merge branch 'main' into debugger 2024-06-27 20:12:43 +02:00
Remco Smits
ab58d14559 Wip display scopes & variables 2024-06-27 20:05:02 +02:00
Anthony Eid
3a0b311378 Get start debugger task to run DAP on confirm 2024-06-27 13:23:31 -04:00
Anthony Eid
b81065fe63 Get debug tasks to show in start debugger menu 2024-06-27 13:23:31 -04:00
Anthony Eid
a67f28dba2 Get Start Debugger action to open task menu 2024-06-27 13:23:31 -04:00
Remco Smits
99b2472e83 Merge pull request #2 from d1y/debugger
debug add icon
2024-06-27 18:40:37 +02:00
Remco Smits
be45d5aa73 Merge branch 'debugger' into debugger 2024-06-27 18:24:00 +02:00
Remco Smits
0508df9e7b Remove commit hash use repo url only 2024-06-26 18:20:18 +02:00
Remco Smits
153efab377 Migrate to dap-types crate 2024-06-26 17:58:48 +02:00
Remco Smits
8015fb70e3 Remove unused mut self 2024-06-26 11:44:43 +02:00
Remco Smits
79d23aa4fe Start adding support for multiple threads + beginning of stack frame UI 2024-06-25 21:38:01 +02:00
Remco Smits
d5dae425fc Allow stepping to next line 2024-06-25 21:35:47 +02:00
d1y
d9e09c4a66 add debug icon 2024-06-26 03:31:19 +08:00
Remco Smits
331625e876 Change hardcoded set breakpoint to TODO 2024-06-25 21:07:01 +02:00
Remco Smits
61949fb348 Add enum for SteppingGranularity 2024-06-25 21:06:33 +02:00
Remco Smits
c7f4e09496 Merge pull request #1 from Anthony-Eid/debugger
Set up ground work for debugger config files
2024-06-25 09:27:58 +02:00
Anthony Eid
5442e116ce Get debugger inventory to add configs from .zed/debug.json 2024-06-25 02:45:46 -04:00
Anthony Eid
11a4fc8b02 Start work on debugger inventory
The debugger config inventory is used to track config files for the debugger. Currently, only .zed/debug.json is set up to work, but it's still untested.
2024-06-25 02:13:52 -04:00
Remco Smits
d303ebd46e Wip add select launch config modal
Co-Authored-By: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
2024-06-24 21:00:09 +02:00
Remco Smits
e1de8dc50e Remove unused code 2024-06-24 18:47:17 +02:00
Remco Smits
5fe110c1dd Rename play to continue debug 2024-06-24 00:07:43 +02:00
Remco Smits
89b203d03a Move current thread to debug panel instead of client 2024-06-23 23:52:10 +02:00
Remco Smits
0f4f8abbaa Add start debugger command 2024-06-23 23:51:53 +02:00
Remco Smits
0d97e9e579 Send debug adapter event through project and listen in debug panel 2024-06-23 14:27:07 +02:00
Remco Smits
14b913fb4b Make request return result of struct instead of Value 2024-06-23 11:21:15 +02:00
Remco Smits
9f1cd2bdb5 Merge branch 'main' into debugger 2024-06-23 10:52:52 +02:00
Remco Smits
547c40e332 change duration 2024-06-23 10:42:34 +02:00
Remco Smits
9cff6d5aa5 Wire through project path
Co-Authored-By: Bennet Bo Fenner <53836821+bennetbo@users.noreply.github.com>
2024-06-23 01:53:41 +02:00
Remco Smits
7e438bc1f3 Wip breakpoints
Co-Authored-By: Bennet Bo Fenner <53836821+bennetbo@users.noreply.github.com>
2024-06-23 00:36:31 +02:00
Remco Smits
944a52ce91 Wip break points
Co-Authored-By: Bennet Bo Fenner <53836821+bennetbo@users.noreply.github.com>
2024-06-22 23:47:18 +02:00
Remco Smits
95a814ed41 Wip breakpoints
Co-Authored-By: Bennet Bo Fenner <53836821+bennetbo@users.noreply.github.com>
2024-06-22 22:55:39 +02:00
Remco Smits
6b9295b6c4 wip write through buttons
Co-Authored-By: Bennet Bo Fenner <53836821+bennetbo@users.noreply.github.com>
2024-06-22 21:17:43 +02:00
Remco Smits
1128fce61a make thread id public 2024-06-22 20:22:32 +02:00
Remco Smits
7c355fdb0f clean up 2024-06-22 20:22:23 +02:00
Remco Smits
0e2a0b9edc Remove deps 2024-06-22 20:22:12 +02:00
Remco Smits
c130f9c2f2 Wip 2024-06-10 21:41:36 +02:00
Remco Smits
08300c6e90 Wip debugger client 2024-06-10 20:38:00 +02:00
Remco Smits
7b71119094 Move to imports 2024-06-09 13:01:46 +02:00
Remco Smits
c18db76862 Merge main 2024-06-09 12:27:47 +02:00
Remco Smits
c0dd152509 Merge branch 'main' into debugger 2024-06-09 12:27:39 +02:00
Remco Smits
f402a4e5ce WIP 2024-04-10 15:57:29 +02:00
477 changed files with 22863 additions and 10164 deletions

View File

@@ -10,7 +10,7 @@ runs:
cargo install cargo-nextest
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4
with:
node-version: "18"

View File

@@ -19,7 +19,7 @@ jobs:
- test
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
ref: ${{ github.event.inputs.branch }}
ssh-key: ${{ secrets.ZED_BOT_DEPLOY_KEY }}

View File

@@ -30,7 +30,7 @@ jobs:
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
fetch-depth: 0
@@ -40,10 +40,15 @@ jobs:
- name: Check spelling
run: |
if ! which typos > /dev/null; then
cargo install typos-cli
if ! cargo install --list | grep "typos-cli v$TYPOS_CLI_VERSION" > /dev/null; then
echo "Installing typos-cli@$TYPOS_CLI_VERSION..."
cargo install "typos-cli@$TYPOS_CLI_VERSION"
else
echo "typos-cli@$TYPOS_CLI_VERSION is already installed."
fi
typos
env:
TYPOS_CLI_VERSION: "1.23.3"
- name: Run style checks
uses: ./.github/actions/check_style
@@ -85,7 +90,7 @@ jobs:
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
@@ -112,7 +117,7 @@ jobs:
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
@@ -132,12 +137,12 @@ jobs:
runs-on: hosted-windows-1
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
- name: Cache dependencies
uses: swatinem/rust-cache@v2
uses: swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
@@ -165,12 +170,12 @@ jobs:
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
steps:
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4
with:
node-version: "18"
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
# We need to fetch more than one commit so that `script/draft-release-notes`
# is able to diff between the current and previous tag.
@@ -225,32 +230,34 @@ jobs:
mv target/x86_64-apple-darwin/release/Zed.dmg target/x86_64-apple-darwin/release/Zed-x86_64.dmg
- name: Upload app bundle (universal) to workflow run if main branch or specific label
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # 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 }}.dmg
path: target/release/Zed.dmg
- name: Upload app bundle (aarch64) to workflow run if main branch or specific label
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # 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@v4
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # 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
path: target/x86_64-apple-darwin/release/Zed-x86_64.dmg
- uses: softprops/action-gh-release@v1
- uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
name: Upload app bundle to release
if: ${{ env.RELEASE_CHANNEL == 'preview' || env.RELEASE_CHANNEL == 'stable' }}
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
files: |
target/zed-remote-server-macos-x86_64.gz
target/zed-remote-server-macos-aarch64.gz
target/aarch64-apple-darwin/release/Zed-aarch64.dmg
target/x86_64-apple-darwin/release/Zed-x86_64.dmg
target/release/Zed.dmg
@@ -273,7 +280,7 @@ jobs:
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
@@ -311,18 +318,20 @@ jobs:
run: script/bundle-linux
- name: Upload Linux bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # 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 app bundle to release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
files: target/release/zed-linux-x86_64.tar.gz
files: |
target/zed-remote-server-linux-x86_64.gz
target/release/zed-linux-x86_64.tar.gz
body: ""
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -338,11 +347,11 @@ jobs:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
- name: "Setup jq"
uses: dcarbone/install-jq-action@v2
uses: dcarbone/install-jq-action@8867ddb4788346d7c22b72ea2e2ffe4d514c7bcb # v2
- name: Set up Clang
run: |
@@ -350,7 +359,7 @@ jobs:
sudo apt-get install -y llvm-10 clang-10 build-essential cmake pkg-config libasound2-dev libfontconfig-dev libwayland-dev libxkbcommon-x11-dev libssl-dev libsqlite3-dev libzstd-dev libvulkan1 libgit2-dev
echo "/usr/lib/llvm-10/bin" >> $GITHUB_PATH
- uses: rui314/setup-mold@v1
- uses: rui314/setup-mold@2e332a0b602c2fc65d2d3995941b1b29a5f554a0 # v1
with:
mold-version: 2.32.0
@@ -393,19 +402,21 @@ jobs:
run: script/bundle-linux
- name: Upload Linux bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # 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 app bundle to release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
if: ${{ env.RELEASE_CHANNEL == 'preview' || env.RELEASE_CHANNEL == 'stable' }}
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
files: target/release/zed-linux-aarch64.tar.gz
files: |
target/zed-remote-server-linux-aarch64.gz
target/release/zed-linux-aarch64.tar.gz
body: ""
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -14,14 +14,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- uses: pnpm/action-setup@v3
with:
version: 9
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4
with:
node-version: "20"
cache: "pnpm"

View File

@@ -12,12 +12,12 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
- name: Setup mdBook
uses: peaceiris/actions-mdbook@v2
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08 # v2
with:
mdbook-version: "0.4.37"
@@ -28,28 +28,28 @@ jobs:
mdbook build ./docs --dest-dir=../target/deploy/docs/
- name: Deploy Docs
uses: cloudflare/wrangler-action@v3
uses: cloudflare/wrangler-action@f84a562284fc78278ff9052435d9526f9c718361 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy target/deploy --project-name=docs
- name: Deploy Install
uses: cloudflare/wrangler-action@v3
uses: cloudflare/wrangler-action@f84a562284fc78278ff9052435d9526f9c718361 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: r2 object put -f script/install.sh zed-open-source-website-assets/install.sh
- name: Deploy Docs Workers
uses: cloudflare/wrangler-action@v3
uses: cloudflare/wrangler-action@f84a562284fc78278ff9052435d9526f9c718361 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy .cloudflare/docs-proxy/src/worker.js
- name: Deploy Install Workers
uses: cloudflare/wrangler-action@v3
uses: cloudflare/wrangler-action@f84a562284fc78278ff9052435d9526f9c718361 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

View File

@@ -18,7 +18,7 @@ jobs:
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
fetch-depth: 0
@@ -37,7 +37,7 @@ jobs:
needs: style
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
fetch-depth: 0
@@ -71,7 +71,7 @@ jobs:
run: doctl registry login
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false

View File

@@ -16,12 +16,12 @@ jobs:
- ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
- name: Cache dependencies
uses: swatinem/rust-cache@v2
uses: swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}

View File

@@ -23,12 +23,12 @@ jobs:
- randomized-tests
steps:
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4
with:
node-version: "18"
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false

View File

@@ -16,7 +16,7 @@ jobs:
fi
echo "::set-output name=URL::$URL"
- name: Get content
uses: 2428392/gh-truncate-string-action@v1.3.0
uses: 2428392/gh-truncate-string-action@67b1b814955634208b103cff064be3cb1c7a19be # v1.3.0
id: get-content
with:
stringToTruncate: |
@@ -26,7 +26,7 @@ jobs:
maxLength: 2000
truncationSymbol: "..."
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v5.3.0
uses: tsickert/discord-webhook@c840d45a03a323fbc3f7507ac7769dbd91bfb164 # v5.3.0
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
content: ${{ steps.get-content.outputs.string }}

View File

@@ -23,7 +23,7 @@ jobs:
- test
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
fetch-depth: 0
@@ -33,6 +33,7 @@ jobs:
- name: Run clippy
run: ./script/clippy
tests:
timeout-minutes: 60
name: Run tests
@@ -43,7 +44,7 @@ jobs:
needs: style
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
@@ -68,12 +69,12 @@ jobs:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
steps:
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4
with:
node-version: "18"
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
@@ -107,7 +108,7 @@ jobs:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
@@ -132,7 +133,6 @@ jobs:
name: Create a Linux *.tar.gz bundle for ARM
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- hosted-linux-arm-1
needs: tests
env:
@@ -141,12 +141,12 @@ jobs:
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
clean: false
- name: "Setup jq"
uses: dcarbone/install-jq-action@v2
uses: dcarbone/install-jq-action@8867ddb4788346d7c22b72ea2e2ffe4d514c7bcb # v2
- name: Set up Clang
run: |
@@ -154,7 +154,7 @@ jobs:
sudo apt-get install -y llvm-10 clang-10 build-essential cmake pkg-config libasound2-dev libfontconfig-dev libwayland-dev libxkbcommon-x11-dev libssl-dev libsqlite3-dev libzstd-dev libvulkan1 libgit2-dev
echo "/usr/lib/llvm-10/bin" >> $GITHUB_PATH
- uses: rui314/setup-mold@v1
- uses: rui314/setup-mold@2e332a0b602c2fc65d2d3995941b1b29a5f554a0 # v1
with:
mold-version: 2.32.0

View File

@@ -8,8 +8,8 @@ jobs:
runs-on: ubuntu-latest
if: github.repository_owner == 'zed-industries'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5
with:
python-version: "3.11"
architecture: "x64"

View File

@@ -8,8 +8,8 @@ jobs:
runs-on: ubuntu-latest
if: github.repository_owner == 'zed-industries'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5
with:
python-version: "3.11"
architecture: "x64"

1
.gitignore vendored
View File

@@ -29,3 +29,4 @@ DerivedData/
.vscode
.wrangler
.flatpak-builder
.zed/debug.json

1132
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,10 @@ members = [
"crates/collections",
"crates/command_palette",
"crates/command_palette_hooks",
"crates/completion",
"crates/copilot",
"crates/dap",
"crates/debugger_ui",
"crates/db",
"crates/dev_server_projects",
"crates/diagnostics",
@@ -43,13 +46,14 @@ members = [
"crates/gpui_macros",
"crates/headless",
"crates/html_to_markdown",
"crates/http",
"crates/http_client",
"crates/image_viewer",
"crates/indexed_docs",
"crates/inline_completion_button",
"crates/install_cli",
"crates/journal",
"crates/language",
"crates/language_model",
"crates/language_selector",
"crates/language_tools",
"crates/languages",
@@ -79,6 +83,8 @@ members = [
"crates/refineable",
"crates/refineable/derive_refineable",
"crates/release_channel",
"crates/remote",
"crates/remote_server",
"crates/repl",
"crates/rich_text",
"crates/rope",
@@ -86,7 +92,9 @@ members = [
"crates/search",
"crates/semantic_index",
"crates/semantic_version",
"crates/session",
"crates/settings",
"crates/settings_ui",
"crates/snippet",
"crates/snippet_provider",
"crates/sqlez",
@@ -137,6 +145,7 @@ members = [
"extensions/php",
"extensions/prisma",
"extensions/purescript",
"extensions/ruff",
"extensions/ruby",
"extensions/snippets",
"extensions/svelte",
@@ -154,6 +163,7 @@ resolver = "2"
[workspace.dependencies]
activity_indicator = { path = "crates/activity_indicator" }
aho-corasick = "1.1"
ai = { path = "crates/ai" }
anthropic = { path = "crates/anthropic" }
assets = { path = "crates/assets" }
@@ -173,8 +183,11 @@ collab_ui = { path = "crates/collab_ui" }
collections = { path = "crates/collections" }
command_palette = { path = "crates/command_palette" }
command_palette_hooks = { path = "crates/command_palette_hooks" }
completion = { path = "crates/completion" }
copilot = { path = "crates/copilot" }
dap = { path = "crates/dap" }
db = { path = "crates/db" }
debugger_ui = { path = "crates/debugger_ui" }
dev_server_projects = { path = "crates/dev_server_projects" }
diagnostics = { path = "crates/diagnostics" }
editor = { path = "crates/editor" }
@@ -195,13 +208,14 @@ gpui = { path = "crates/gpui" }
gpui_macros = { path = "crates/gpui_macros" }
headless = { path = "crates/headless" }
html_to_markdown = { path = "crates/html_to_markdown" }
http = { path = "crates/http" }
http_client = { path = "crates/http_client" }
image_viewer = { path = "crates/image_viewer" }
indexed_docs = { path = "crates/indexed_docs" }
inline_completion_button = { path = "crates/inline_completion_button" }
install_cli = { path = "crates/install_cli" }
journal = { path = "crates/journal" }
language = { path = "crates/language" }
language_model = { path = "crates/language_model" }
language_selector = { path = "crates/language_selector" }
language_tools = { path = "crates/language_tools" }
languages = { path = "crates/languages" }
@@ -231,6 +245,8 @@ proto = { path = "crates/proto" }
quick_action_bar = { path = "crates/quick_action_bar" }
recent_projects = { path = "crates/recent_projects" }
release_channel = { path = "crates/release_channel" }
remote = { path = "crates/remote" }
remote_server = { path = "crates/remote_server" }
repl = { path = "crates/repl" }
rich_text = { path = "crates/rich_text" }
rope = { path = "crates/rope" }
@@ -238,7 +254,9 @@ rpc = { path = "crates/rpc" }
search = { path = "crates/search" }
semantic_index = { path = "crates/semantic_index" }
semantic_version = { path = "crates/semantic_version" }
session = { path = "crates/session" }
settings = { path = "crates/settings" }
settings_ui = { path = "crates/settings_ui" }
snippet = { path = "crates/snippet" }
snippet_provider = { path = "crates/snippet_provider" }
sqlez = { path = "crates/sqlez" }
@@ -272,22 +290,24 @@ zed = { path = "crates/zed" }
zed_actions = { path = "crates/zed_actions" }
alacritty_terminal = "0.23"
any_vec = "0.13"
anyhow = "1.0.57"
any_vec = "0.14"
anyhow = "1.0.86"
ashpd = "0.9.1"
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
async-dispatcher = { version = "0.1" }
async-fs = "1.6"
async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553" }
async-recursion = "1.0.0"
async-tar = "0.4.2"
async-trait = "0.1"
async-tungstenite = { version = "0.16" }
async-watch = "0.3.1"
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
base64 = "0.13"
bitflags = "2.6.0"
blade-graphics = { git = "https://github.com/zed-industries/blade", rev = "a477c2008db27db0b9f745715e119b3ee7ab7818" }
blade-macros = { git = "https://github.com/zed-industries/blade", rev = "a477c2008db27db0b9f745715e119b3ee7ab7818" }
blade-util = { git = "https://github.com/zed-industries/blade", rev = "a477c2008db27db0b9f745715e119b3ee7ab7818" }
blade-graphics = { git = "https://github.com/zed-industries/blade", rev = "7e497c534d5d4a30c18d9eb182cf39eaf0aaa25e" }
blade-macros = { git = "https://github.com/zed-industries/blade", rev = "7e497c534d5d4a30c18d9eb182cf39eaf0aaa25e" }
blade-util = { git = "https://github.com/zed-industries/blade", rev = "7e497c534d5d4a30c18d9eb182cf39eaf0aaa25e" }
cap-std = "3.0"
cargo_toml = "0.20"
chrono = { version = "0.4", features = ["serde"] }
@@ -301,7 +321,7 @@ dashmap = "5.5.3"
derive_more = "0.99.17"
dirs = "4.0"
emojis = "0.6.1"
env_logger = "0.9"
env_logger = "0.10"
exec = "0.3.1"
fork = "0.1.23"
futures = "0.3"
@@ -324,7 +344,7 @@ itertools = "0.11.0"
lazy_static = "1.4.0"
libc = "0.2"
linkify = "0.10.0"
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
markup5ever_rcdom = "0.3.0"
nanoid = "0.4"
nix = "0.28"
@@ -345,12 +365,14 @@ rand = "0.8.5"
refineable = { path = "./crates/refineable" }
regex = "1.5"
repair_json = "0.1.0"
rsa = "0.9.6"
runtimelib = { version = "0.12", default-features = false, features = [
"async-dispatcher-runtime",
] }
rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
rustc-demangle = "0.1.23"
rust-embed = { version = "8.4", features = ["include-exclude"] }
schemars = "0.8"
schemars = {version = "0.8", features = ["impl_json_schema"]}
semver = "1.0"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
@@ -381,32 +403,32 @@ time = { version = "0.3", features = [
"serde-well-known",
"formatting",
] }
tiny_http = "0.8"
toml = "0.8"
tokio = { version = "1", features = ["full"] }
tower-http = "0.4.4"
tree-sitter = { version = "0.20", features = ["wasm"] }
tree-sitter-bash = "0.20.5"
tree-sitter-c = "0.20.1"
tree-sitter-cpp = "0.20.5"
tree-sitter-css = "0.20"
tree-sitter-elixir = "0.1.1"
tree-sitter = { version = "0.22", features = ["wasm"] }
tree-sitter-bash = "0.21"
tree-sitter-c = "0.21"
tree-sitter-cpp = "0.22"
tree-sitter-css = "0.21"
tree-sitter-elixir = "0.2"
tree-sitter-embedded-template = "0.20.0"
tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "b82ab803d887002a0af11f6ce63d72884580bf33" }
tree-sitter-gomod = "1.0.1"
tree-sitter-gowork = { git = "https://github.com/d1y/tree-sitter-go-work" }
rustc-demangle = "0.1.23"
tree-sitter-heex = { git = "https://github.com/phoenixframework/tree-sitter-heex", rev = "2e1348c3cf2c9323e87c2744796cf3f3868aa82a" }
tree-sitter-html = "0.19.0"
tree-sitter-jsdoc = { git = "https://github.com/tree-sitter/tree-sitter-jsdoc", rev = "6a6cf9e7341af32d8e2b2e24a37fbfebefc3dc55" }
tree-sitter-json = "0.20.2"
tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
tree-sitter-proto = { git = "https://github.com/rewinfrey/tree-sitter-proto", rev = "36d54f288aee112f13a67b550ad32634d0c2cb52" }
tree-sitter-python = "0.20.2"
tree-sitter-regex = "0.20.0"
tree-sitter-ruby = "0.20.0"
tree-sitter-rust = "0.20.3"
tree-sitter-typescript = "0.20.5"
tree-sitter-yaml = "0.0.1"
tree-sitter-go = "0.21"
tree-sitter-go-mod = { git = "https://github.com/SomeoneToIgnore/tree-sitter-go-mod", rev = "8c1f54f12bb4c846336b634bc817645d6f35d641", package = "tree-sitter-gomod"}
tree-sitter-gowork = { git = "https://github.com/d1y/tree-sitter-go-work", rev = "dcbabff454703c3a4bc98a23cf8778d4be46fd22" }
tree-sitter-heex = { git = "https://github.com/phoenixframework/tree-sitter-heex", rev = "6dd0303acf7138dd2b9b432a229e16539581c701" }
tree-sitter-html = "0.20"
tree-sitter-jsdoc = "0.21"
tree-sitter-json = "0.21"
tree-sitter-md = { git = "https://github.com/zed-industries/tree-sitter-markdown", rev = "e3855e37f8f2c71aa7513c18a9c95fb7461b1b10" }
protols-tree-sitter-proto = "0.2"
tree-sitter-python = "0.21"
tree-sitter-regex = "0.21"
tree-sitter-ruby = "0.21"
tree-sitter-rust = "0.21"
tree-sitter-typescript = "0.21"
tree-sitter-yaml = "0.6"
unindent = "0.1.7"
unicase = "2.6"
unicode-segmentation = "1.10"
@@ -414,14 +436,14 @@ url = "2.2"
uuid = { version = "1.1.2", features = ["v4", "v5", "serde"] }
wasmparser = "0.201"
wasm-encoder = "0.201"
wasmtime = { version = "19.0.0", default-features = false, features = [
wasmtime = { version = "19.0.2", default-features = false, features = [
"async",
"demangle",
"runtime",
"cranelift",
"component-model",
] }
wasmtime-wasi = "19.0.0"
wasmtime-wasi = "19.0.2"
which = "6.0.0"
wit-component = "0.201"
sys-locale = "0.3.1"
@@ -466,11 +488,6 @@ features = [
"Win32_UI_WindowsAndMessaging",
]
[patch.crates-io]
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "7b4894ba2ae81b988846676f54c0988d4027ef4f" }
# Workaround for a broken nightly build of gpui: See #7644 and revisit once 0.5.3 is released.
pathfinder_simd = { git = "https://github.com/servo/pathfinder.git", rev = "4968e819c0d9b015437ffc694511e175801a17c7" }
[profile.dev]
split-debuginfo = "unpacked"
debug = "limited"

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M2.5 2H4v12H2.5V2zm4.936.39L6.25 3v10l1.186.61 7-5V7.39l-7-5zM12.71 8l-4.96 3.543V4.457L12.71 8z"/></svg>

After

Width:  |  Height:  |  Size: 257 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M4.5 3H6v10H4.5V3zm7 0v10H10V3h1.5z"/></svg>

After

Width:  |  Height:  |  Size: 156 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.75 8a4.5 4.5 0 0 1-8.61 1.834l-1.391.565A6.001 6.001 0 0 0 14.25 8 6 6 0 0 0 3.5 4.334V2.5H2v4l.75.75h3.5v-1.5H4.352A4.5 4.5 0 0 1 12.75 8z"/></svg>

After

Width:  |  Height:  |  Size: 304 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 9.532h.542l3.905-3.905-1.061-1.06-2.637 2.61V1H7.251v6.177l-2.637-2.61-1.061 1.06 3.905 3.905H8zm1.956 3.481a2 2 0 1 1-4 0 2 2 0 0 1 4 0z"/></svg>

After

Width:  |  Height:  |  Size: 301 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 1h-.542L3.553 4.905l1.061 1.06 2.637-2.61v6.177h1.498V3.355l2.637 2.61 1.061-1.06L8.542 1H8zm1.956 12.013a2 2 0 1 1-4 0 2 2 0 0 1 4 0z"/></svg>

After

Width:  |  Height:  |  Size: 298 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M14.25 5.75v-4h-1.5v2.542c-1.145-1.359-2.911-2.209-4.84-2.209-3.177 0-5.92 2.307-6.16 5.398l-.02.269h1.501l.022-.226c.212-2.195 2.202-3.94 4.656-3.94 1.736 0 3.244.875 4.05 2.166h-2.83v1.5h4.163l.962-.975V5.75h-.004zM8 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></svg>

After

Width:  |  Height:  |  Size: 411 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M13 1.99976L14 2.99976V12.9998L13 13.9998H3L2 12.9998L2 2.99976L3 1.99976H13ZM12.7461 3.25057L3.25469 3.25057L3.25469 12.7504H12.7461V3.25057Z"/></svg>

After

Width:  |  Height:  |  Size: 303 B

View File

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 6C5.69062 6.30938 4.56159 6.55977 3.51192 6.73263C3.27345 6.7719 3.27345 7.2281 3.51192 7.26737C4.56159 7.44023 5.69062 7.69062 6 8C6.30938 8.30938 6.55977 9.43841 6.73263 10.4881C6.7719 10.7266 7.2281 10.7266 7.26737 10.4881C7.44023 9.43841 7.69062 8.30938 8 8C8.30938 7.69062 9.43841 7.44023 10.4881 7.26737C10.7266 7.2281 10.7266 6.7719 10.4881 6.73263C9.43841 6.55977 8.30938 6.30938 8 6C7.69062 5.69062 7.44023 4.56159 7.26737 3.51192C7.2281 3.27345 6.7719 3.27345 6.73263 3.51192C6.55977 4.56159 6.30938 5.69062 6 6Z" stroke="black" stroke-width="1.25" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 700 B

View File

@@ -1,3 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.88889 1H2.11111C1.49746 1 1 1.49746 1 2.11111V9.88889C1 10.5025 1.49746 11 2.11111 11H9.88889C10.5025 11 11 10.5025 11 9.88889V2.11111C11 1.49746 10.5025 1 9.88889 1Z" stroke="#C56757" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 9.8V4.2C4 4.08954 4.08954 4 4.2 4H9.8C9.91046 4 10 4.08954 10 4.2V9.8C10 9.91046 9.91046 10 9.8 10H4.2C4.08954 10 4 9.91046 4 9.8Z" stroke="#C56757" stroke-width="1.25" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 369 B

After

Width:  |  Height:  |  Size: 310 B

View File

@@ -106,8 +106,8 @@
"bindings": {
"enter": "editor::Newline",
"shift-enter": "editor::Newline",
"ctrl-shift-enter": "editor::NewlineBelow",
"ctrl-enter": "editor::NewlineAbove",
"ctrl-shift-enter": "editor::NewlineBelow",
"alt-z": "editor::ToggleSoftWrap",
"ctrl-f": "buffer_search::Deploy",
"ctrl-h": ["buffer_search::Deploy", { "replace_enabled": true }],
@@ -477,6 +477,12 @@
"ctrl-enter": "assistant::InlineAssist"
}
},
{
"context": "Editor && jupyter && !ContextEditor",
"bindings": {
"ctrl-shift-enter": "repl::Run"
}
},
{
"context": "ContextEditor > Editor",
"bindings": {
@@ -595,6 +601,7 @@
"ctrl-alt-space": "terminal::ShowCharacterPalette",
"shift-ctrl-c": "terminal::Copy",
"ctrl-insert": "terminal::Copy",
"ctrl-a": "editor::SelectAll",
"shift-ctrl-v": "terminal::Paste",
"shift-insert": "terminal::Paste",
"ctrl-enter": "assistant::InlineAssist",

View File

@@ -135,6 +135,7 @@
"bindings": {
"enter": "editor::Newline",
"shift-enter": "editor::Newline",
"cmd-enter": "editor::NewlineBelow",
"cmd-shift-enter": "editor::NewlineAbove",
"alt-z": "editor::ToggleSoftWrap",
"cmd-f": "buffer_search::Deploy",
@@ -146,12 +147,6 @@
"cmd-alt-e": "editor::SelectEnclosingSymbol"
}
},
{
"context": "Editor && mode == full && !jupyter",
"bindings": {
"cmd-enter": "editor::NewlineBelow"
}
},
{
"context": "Editor && mode == full && inline_completion",
"bindings": {
@@ -180,6 +175,12 @@
"cmd-c": "markdown::Copy"
}
},
{
"context": "Editor && jupyter && !ContextEditor",
"bindings": {
"ctrl-shift-enter": "repl::Run"
}
},
{
"context": "AssistantPanel",
"bindings": {
@@ -292,7 +293,6 @@
"alt-cmd-c": "search::ToggleCaseSensitive",
"alt-cmd-w": "search::ToggleWholeWord",
"alt-cmd-f": "project_search::ToggleFilters",
"alt-cmd-g": "search::ToggleRegex",
"alt-cmd-x": "search::ToggleRegex"
}
},
@@ -570,12 +570,6 @@
"space": "project_panel::Open"
}
},
{
"context": "Editor && jupyter && !ContextEditor",
"bindings": {
"cmd-enter": "repl::Run"
}
},
{
"context": "CollabPanel && not_editing",
"bindings": {
@@ -628,6 +622,7 @@
"ctrl-cmd-space": "terminal::ShowCharacterPalette",
"cmd-c": "terminal::Copy",
"cmd-v": "terminal::Paste",
"cmd-a": "editor::SelectAll",
"cmd-k": "terminal::Clear",
"ctrl-enter": "assistant::InlineAssist",
// Some nice conveniences

View File

@@ -12,8 +12,8 @@
{
"context": "Editor",
"bindings": {
"ctrl-shift-up": "editor::AddSelectionAbove",
"ctrl-shift-down": "editor::AddSelectionBelow",
"ctrl-shift-up": "editor::MoveLineUp",
"ctrl-shift-down": "editor::MoveLineDown",
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
"ctrl-shift-l": "editor::SplitSelectionIntoLines",
"ctrl-shift-a": "editor::SelectLargerSyntaxNode",

View File

@@ -14,6 +14,8 @@
"bindings": {
"ctrl-shift-up": "editor::AddSelectionAbove",
"ctrl-shift-down": "editor::AddSelectionBelow",
"cmd-ctrl-up": "editor::MoveLineUp",
"cmd-ctrl-down": "editor::MoveLineDown",
"cmd-shift-space": "editor::SelectAll",
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
"cmd-shift-l": "editor::SplitSelectionIntoLines",

View File

@@ -10,6 +10,7 @@
"backspace": "vim::Backspace",
"j": "vim::Down",
"down": "vim::Down",
"ctrl-j": "vim::Down",
"enter": "vim::NextLineStart",
"ctrl-m": "vim::NextLineStart",
"+": "vim::NextLineStart",
@@ -215,7 +216,7 @@
"shift-d": "vim::DeleteToEndOfLine",
"shift-j": "vim::JoinLines",
"y": ["vim::PushOperator", "Yank"],
"shift-y": "vim::YankLine",
"shift-y": "vim::YankToEndOfLine",
"i": "vim::InsertBefore",
"shift-i": "vim::InsertFirstNonWhitespace",
"a": "vim::InsertAfter",
@@ -252,7 +253,7 @@
"[ d": "editor::GoToPrevDiagnostic",
"] c": "editor::GoToHunk",
"[ c": "editor::GoToPrevHunk",
"g c c": "vim::ToggleComments"
"g c": ["vim::PushOperator", "ToggleComments"]
}
},
{
@@ -433,6 +434,12 @@
"<": "vim::CurrentLine"
}
},
{
"context": "vim_operator == gc",
"bindings": {
"c": "vim::CurrentLine"
}
},
{
"context": "BufferSearchBar && !in_replace",
"bindings": {

View File

@@ -0,0 +1,241 @@
Your task is to map a step from the conversation above to operations on symbols inside the provided source files.
Guidelines:
- There's no need to describe *what* to do, just *where* to do it.
- If creating a file, assume any subsequent updates are included at the time of creation.
- Don't create and then update a file.
- We'll create it in one shot.
- Prefer updating symbols lower in the syntax tree if possible.
- Never include operations on a parent symbol and one of its children in the same <operations> block.
- Never nest an operation with another operation or include CDATA or other content. All operations are leaf nodes.
- Include a description attribute for each operation with a brief, one-line description of the change to perform.
- Descriptions are required for all operations except delete.
- When generating multiple operations, ensure the descriptions are specific to each individual operation.
- Avoid referring to the location in the description. Focus on the change to be made, not the location where it's made. That's implicit with the symbol you provide.
- Don't generate multiple operations at the same location. Instead, combine them together in a single operation with a succinct combined description.
The available operation types are:
1. <update>: Modify an existing symbol in a file.
2. <create_file>: Create a new file.
3. <insert_sibling_after>: Add a new symbol as sibling after an existing symbol in a file.
4. <append_child>: Add a new symbol as the last child of an existing symbol in a file.
5. <prepend_child>: Add a new symbol as the first child of an existing symbol in a file.
6. <delete>: Remove an existing symbol from a file. The `description` attribute is invalid for delete, but required for other ops.
All operations *require* a path.
Operations that *require* a symbol: <update>, <insert_sibling_after>, <delete>
Operations that don't allow a symbol: <create>
Operations that have an *optional* symbol: <prepend_child>, <append_child>
Example 1:
User:
```rs src/rectangle.rs
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
}
```
Symbols for src/rectangle.rs:
- struct Rectangle
- impl Rectangle
- impl Rectangle fn new
<step>Add new methods 'calculate_area' and 'calculate_perimeter' to the Rectangle struct</step>
<step>Implement the 'Display' trait for the Rectangle struct</step>
What are the operations for the step: <step>Add a new method 'calculate_area' to the Rectangle struct</step>
Assistant (wrong):
<operations>
<append_child path="src/shapes.rs" symbol="impl Rectangle" description="Add calculate_area method" />
<append_child path="src/shapes.rs" symbol="impl Rectangle" description="Add calculate_perimeter method" />
</operations>
This demonstrates what NOT to do. NEVER append multiple children at the same location.
Assistant (corrected):
<operations>
<append_child path="src/shapes.rs" symbol="impl Rectangle" description="Add calculate area and perimeter methods" />
</operations>
User:
What are the operations for the step: <step>Implement the 'Display' trait for the Rectangle struct</step>
Assistant:
<operations>
<insert_sibling_after path="src/shapes.rs" symbol="impl Rectangle" description="Implement Display trait for Rectangle"/>
</operations>
Example 2:
User:
```rs src/user.rs
struct User {
pub name: String,
age: u32,
email: String,
}
impl User {
fn new(name: String, age: u32, email: String) -> Self {
User { name, age, email }
}
pub fn print_info(&self) {
println!("Name: {}, Age: {}, Email: {}", self.name, self.age, self.email);
}
}
```
Symbols for src/user.rs:
- struct User
- struct User pub name
- struct User age
- struct User email
- impl User
- impl User fn new
- impl User pub fn print_info
<step>Update the 'print_info' method to use formatted output</step>
<step>Remove the 'email' field from the User struct</step>
What are the operations for the step: <step>Update the 'print_info' method to use formatted output</step>
Assistant:
<operations>
<update path="src/user.rs" symbol="impl User fn print_info" description="Use formatted output" />
</operations>
User:
What are the operations for the step: <step>Remove the 'email' field from the User struct</step>
Assistant:
<operations>
<delete path="src/user.rs" symbol="struct User email" description="Remove the email field" />
</operations>
Example 3:
User:
```rs src/vehicle.rs
struct Vehicle {
make: String,
model: String,
year: u32,
}
impl Vehicle {
fn new(make: String, model: String, year: u32) -> Self {
Vehicle { make, model, year }
}
fn print_year(&self) {
println!("Year: {}", self.year);
}
}
```
Symbols for src/vehicle.rs:
- struct Vehicle
- struct Vehicle make
- struct Vehicle model
- struct Vehicle year
- impl Vehicle
- impl Vehicle fn new
- impl Vehicle fn print_year
<step>Add a 'use std::fmt;' statement at the beginning of the file</step>
<step>Add a new method 'start_engine' in the Vehicle impl block</step>
What are the operations for the step: <step>Add a 'use std::fmt;' statement at the beginning of the file</step>
Assistant:
<operations>
<prepend_child path="src/vehicle.rs" description="Add 'use std::fmt' statement" />
</operations>
User:
What are the operations for the step: <step>Add a new method 'start_engine' in the Vehicle impl block</step>
Assistant:
<operations>
<insert_sibling_after path="src/vehicle.rs" symbol="impl Vehicle fn new" description="Add start_engine method"/>
</operations>
Example 4:
User:
```rs src/employee.rs
struct Employee {
name: String,
position: String,
salary: u32,
department: String,
}
impl Employee {
fn new(name: String, position: String, salary: u32, department: String) -> Self {
Employee { name, position, salary, department }
}
fn print_details(&self) {
println!("Name: {}, Position: {}, Salary: {}, Department: {}",
self.name, self.position, self.salary, self.department);
}
fn give_raise(&mut self, amount: u32) {
self.salary += amount;
}
}
```
Symbols for src/employee.rs:
- struct Employee
- struct Employee name
- struct Employee position
- struct Employee salary
- struct Employee department
- impl Employee
- impl Employee fn new
- impl Employee fn print_details
- impl Employee fn give_raise
<step>Make salary an f32</step>
What are the operations for the step: <step>Make salary an f32</step>
A (wrong):
<operations>
<update path="src/employee.rs" symbol="struct Employee" description="Change the type of salary to an f32" />
<update path="src/employee.rs" symbol="struct Employee salary" description="Change the type to an f32" />
</operations>
This example demonstrates what not to do. `struct Employee salary` is a child of `struct Employee`.
A (corrected):
<operations>
<update path="src/employee.rs" symbol="struct Employee salary" description="Change the type to an f32" />
</operations>
User:
What are the correct operations for the step: <step>Remove the 'department' field and update the 'print_details' method</step>
A:
<operations>
<delete path="src/employee.rs" symbol="struct Employee department" />
<update path="src/employee.rs" symbol="impl Employee fn print_details" description="Don't print the 'department' field" />
</operations>
Now generate the operations for the following step.
Output only valid XML containing valid operations with their required attributes.
NEVER output code or any other text inside <operation> tags. If you do, you will replaced with another model.
Your response *MUST* begin with <operations> and end with </operations>:

View File

@@ -82,7 +82,7 @@
// Whether to confirm before quitting Zed.
"confirm_quit": false,
// Whether to restore last closed project when fresh Zed instance is opened.
"restore_on_startup": "last_workspace",
"restore_on_startup": "last_session",
// Size of the drop target in the editor.
"drop_target_size": 0.2,
// Whether the window should be closed when using 'close active item' on a window with no tabs.
@@ -375,7 +375,7 @@
},
"assistant": {
// Version of this setting.
"version": "1",
"version": "2",
// Whether the assistant is enabled.
"enabled": true,
// Whether to show the assistant panel button in the status bar.
@@ -386,17 +386,12 @@
"default_width": 640,
// Default height when the assistant is docked to the bottom.
"default_height": 320,
// AI provider.
"provider": {
"name": "openai",
// The default model to use when creating new contexts. This
// setting can take three values:
//
// 1. "gpt-3.5-turbo"
// 2. "gpt-4"
// 3. "gpt-4-turbo-preview"
// 4. "gpt-4o"
"default_model": "gpt-4o"
// The default model to use when creating new contexts.
"default_model": {
// The provider to use.
"provider": "openai",
// The model to use.
"model": "gpt-4o"
}
},
// Whether the screen sharing icon is shown in the os status bar.
@@ -704,7 +699,7 @@
//
"file_types": {
"JSON": ["flake.lock"],
"JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json"]
"JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json", "**/Zed/**/*.json", "tsconfig.json"]
},
// The extensions that Zed should automatically install on startup.
//
@@ -742,6 +737,9 @@
"Elixir": {
"language_servers": ["elixir-ls", "!next-ls", "!lexical", "..."]
},
"Erlang": {
"language_servers": ["erlang-ls", "!elp", "..."]
},
"Go": {
"code_actions_on_format": {
"source.organizeImports": true
@@ -811,6 +809,9 @@
"plugins": ["prettier-plugin-sql"]
}
},
"Starlark": {
"language_servers": ["starpls", "!buck2-lsp", "..."]
},
"Svelte": {
"prettier": {
"allowed": true,
@@ -851,6 +852,18 @@
}
}
},
// Different settings for specific language models.
"language_models": {
"anthropic": {
"api_url": "https://api.anthropic.com"
},
"openai": {
"api_url": "https://api.openai.com/v1"
},
"ollama": {
"api_url": "http://localhost:11434"
}
},
// Zed's Prettier integration settings.
// Allows to enable/disable formatting with Prettier
// and configure default Prettier, used when no project-level Prettier installation is found.
@@ -883,6 +896,15 @@
// }
// }
},
// Jupyter settings
"jupyter": {
"enabled": true
// Specify the language name as the key and the kernel name as the value.
// "kernel_selections": {
// "python": "conda-base"
// "typescript": "deno"
// }
},
// Vim settings
"vim": {
"use_system_clipboard": "always",
@@ -935,5 +957,13 @@
// Examples:
// - "proxy": "socks5://localhost:10808"
// - "proxy": "http://127.0.0.1:10809"
"proxy": null
"proxy": null,
// Set to configure aliases for the command palette.
// When typing a query which is a key of this object, the value will be used instead.
//
// Examples:
// {
// "W": "workspace::Save"
// }
"command_aliases": {}
}

View File

@@ -1,5 +1,5 @@
// Folder-specific settings
//
// For a full list of overridable settings, and general information on folder-specific settings,
// see the documentation: https://zed.dev/docs/configuring-zed#folder-specific-settings
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
{}

View File

@@ -17,6 +17,27 @@
// What to do with the terminal pane and tab, after the command was started:
// * `always` — always show the terminal pane, add and focus the corresponding task's tab in it (default)
// * `never` — avoid changing current terminal pane focus, but still add/reuse the task's tab there
"reveal": "always"
"reveal": "always",
// What to do with the terminal pane and tab, after the command had finished:
// * `never` — Do nothing when the command finishes (default)
// * `always` — always hide the terminal tab, hide the pane also if it was the last tab in it
// * `on_success` — hide the terminal tab on task success only, otherwise behaves similar to `always`
"hide": "never",
// Which shell to use when running a task inside the terminal.
// May take 3 values:
// 1. (default) Use the system's default terminal configuration in /etc/passwd
// "shell": "system"
// 2. A program:
// "shell": {
// "program": "sh"
// }
// 3. A program with arguments:
// "shell": {
// "with_arguments": {
// "program": "/bin/bash",
// "arguments": ["--login"]
// }
// }
"shell": "system"
}
]

View File

@@ -107,6 +107,7 @@ impl ActivityIndicator {
Editor::for_buffer(buffer, Some(project.clone()), cx)
})),
None,
true,
cx,
);
})?;

View File

@@ -18,7 +18,7 @@ path = "src/anthropic.rs"
[dependencies]
anyhow.workspace = true
futures.workspace = true
http.workspace = true
http_client.workspace = true
isahc.workspace = true
schemars = { workspace = true, optional = true }
serde.workspace = true

View File

@@ -1,6 +1,6 @@
use anyhow::{anyhow, Result};
use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, StreamExt};
use http::{AsyncBody, HttpClient, Method, Request as HttpRequest};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
use isahc::config::Configurable;
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, time::Duration};
@@ -20,6 +20,8 @@ pub enum Model {
Claude3Sonnet,
#[serde(alias = "claude-3-haiku", rename = "claude-3-haiku-20240307")]
Claude3Haiku,
#[serde(rename = "custom")]
Custom { name: String, max_tokens: usize },
}
impl Model {
@@ -33,30 +35,38 @@ impl Model {
} else if id.starts_with("claude-3-haiku") {
Ok(Self::Claude3Haiku)
} else {
Err(anyhow!("Invalid model id: {}", id))
Err(anyhow!("invalid model id"))
}
}
pub fn id(&self) -> &'static str {
pub fn id(&self) -> &str {
match self {
Model::Claude3_5Sonnet => "claude-3-5-sonnet-20240620",
Model::Claude3Opus => "claude-3-opus-20240229",
Model::Claude3Sonnet => "claude-3-sonnet-20240229",
Model::Claude3Haiku => "claude-3-opus-20240307",
Self::Custom { name, .. } => name,
}
}
pub fn display_name(&self) -> &'static str {
pub fn display_name(&self) -> &str {
match self {
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
Self::Claude3Opus => "Claude 3 Opus",
Self::Claude3Sonnet => "Claude 3 Sonnet",
Self::Claude3Haiku => "Claude 3 Haiku",
Self::Custom { name, .. } => name,
}
}
pub fn max_token_count(&self) -> usize {
200_000
match self {
Self::Claude3_5Sonnet
| Self::Claude3Opus
| Self::Claude3Sonnet
| Self::Claude3Haiku => 200_000,
Self::Custom { max_tokens, .. } => *max_tokens,
}
}
}
@@ -90,6 +100,7 @@ impl From<Role> for String {
#[derive(Debug, Serialize)]
pub struct Request {
#[serde(serialize_with = "serialize_request_model")]
pub model: Model,
pub messages: Vec<RequestMessage>,
pub stream: bool,
@@ -97,6 +108,13 @@ pub struct Request {
pub max_tokens: u32,
}
fn serialize_request_model<S>(model: &Model, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&model.id())
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct RequestMessage {
pub role: Role,

View File

@@ -23,15 +23,16 @@ test-support = [
[dependencies]
anthropic = { workspace = true, features = ["schemars"] }
anyhow.workspace = true
assets.workspace = true
assistant_slash_command.workspace = true
async-watch.workspace = true
breadcrumbs.workspace = true
cargo_toml.workspace = true
chrono.workspace = true
client.workspace = true
clock.workspace = true
collections.workspace = true
command_palette_hooks.workspace = true
completion.workspace = true
editor.workspace = true
feature_flags.workspace = true
fs.workspace = true
@@ -40,10 +41,11 @@ fuzzy.workspace = true
gpui.workspace = true
heed.workspace = true
html_to_markdown.workspace = true
http.workspace = true
http_client.workspace = true
indexed_docs.workspace = true
indoc.workspace = true
language.workspace = true
language_model.workspace = true
log.workspace = true
menu.workspace = true
multi_buffer.workspace = true
@@ -63,21 +65,20 @@ serde_json.workspace = true
settings.workspace = true
similar.workspace = true
smol.workspace = true
strsim = "0.11"
strum.workspace = true
telemetry_events.workspace = true
terminal.workspace = true
terminal_view.workspace = true
theme.workspace = true
tiktoken-rs.workspace = true
toml.workspace = true
ui.workspace = true
util.workspace = true
uuid.workspace = true
workspace.workspace = true
picker.workspace = true
roxmltree = "0.20.0"
[dev-dependencies]
completion = { workspace = true, features = ["test-support"] }
ctor.workspace = true
editor = { workspace = true, features = ["test-support"] }
env_logger.workspace = true

View File

@@ -1,42 +1,40 @@
pub mod assistant_panel;
pub mod assistant_settings;
mod completion_provider;
mod context;
pub mod context_store;
mod inline_assistant;
mod model_selector;
mod prompt_library;
mod prompts;
mod search;
mod slash_command;
mod streaming_diff;
mod terminal_inline_assistant;
pub use assistant_panel::{AssistantPanel, AssistantPanelEvent};
use assistant_settings::{AnthropicModel, AssistantSettings, CloudModel, OllamaModel, OpenAiModel};
use assistant_settings::AssistantSettings;
use assistant_slash_command::SlashCommandRegistry;
use client::{proto, Client};
use command_palette_hooks::CommandPaletteFilter;
pub use completion_provider::*;
use completion::LanguageModelCompletionProvider;
pub use context::*;
pub use context_store::*;
use fs::Fs;
use gpui::{actions, AppContext, Global, SharedString, UpdateGlobal};
use gpui::{actions, impl_actions, AppContext, Global, SharedString, UpdateGlobal};
use indexed_docs::IndexedDocsRegistry;
pub(crate) use inline_assistant::*;
use language_model::{
LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
};
pub(crate) use model_selector::*;
use semantic_index::{CloudEmbeddingProvider, SemanticIndex};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
use settings::{update_settings_file, Settings, SettingsStore};
use slash_command::{
active_command, default_command, diagnostics_command, docs_command, fetch_command,
file_command, now_command, project_command, prompt_command, search_command, symbols_command,
tabs_command, term_command,
};
use std::{
fmt::{self, Display},
sync::Arc,
};
use std::sync::Arc;
pub(crate) use streaming_diff::*;
actions!(
@@ -49,16 +47,22 @@ actions!(
InsertIntoEditor,
ToggleFocus,
ResetKey,
InlineAssist,
InsertActivePrompt,
DeployHistory,
DeployPromptLibrary,
ApplyEdit,
ConfirmCommand,
ToggleModelSelector
ToggleModelSelector,
DebugEditSteps
]
);
#[derive(Clone, Default, Deserialize, PartialEq)]
pub struct InlineAssist {
prompt: Option<String>,
}
impl_actions!(assistant, [InlineAssist]);
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct MessageId(clock::Lamport);
@@ -68,166 +72,6 @@ impl MessageId {
}
}
#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Role {
User,
Assistant,
System,
}
impl Role {
pub fn from_proto(role: i32) -> Role {
match proto::LanguageModelRole::from_i32(role) {
Some(proto::LanguageModelRole::LanguageModelUser) => Role::User,
Some(proto::LanguageModelRole::LanguageModelAssistant) => Role::Assistant,
Some(proto::LanguageModelRole::LanguageModelSystem) => Role::System,
Some(proto::LanguageModelRole::LanguageModelTool) => Role::System,
None => Role::User,
}
}
pub fn to_proto(&self) -> proto::LanguageModelRole {
match self {
Role::User => proto::LanguageModelRole::LanguageModelUser,
Role::Assistant => proto::LanguageModelRole::LanguageModelAssistant,
Role::System => proto::LanguageModelRole::LanguageModelSystem,
}
}
pub fn cycle(self) -> Role {
match self {
Role::User => Role::Assistant,
Role::Assistant => Role::System,
Role::System => Role::User,
}
}
}
impl Display for Role {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Role::User => write!(f, "user"),
Role::Assistant => write!(f, "assistant"),
Role::System => write!(f, "system"),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum LanguageModel {
Cloud(CloudModel),
OpenAi(OpenAiModel),
Anthropic(AnthropicModel),
Ollama(OllamaModel),
}
impl Default for LanguageModel {
fn default() -> Self {
LanguageModel::Cloud(CloudModel::default())
}
}
impl LanguageModel {
pub fn telemetry_id(&self) -> String {
match self {
LanguageModel::OpenAi(model) => format!("openai/{}", model.id()),
LanguageModel::Anthropic(model) => format!("anthropic/{}", model.id()),
LanguageModel::Cloud(model) => format!("zed.dev/{}", model.id()),
LanguageModel::Ollama(model) => format!("ollama/{}", model.id()),
}
}
pub fn display_name(&self) -> String {
match self {
LanguageModel::OpenAi(model) => model.display_name().into(),
LanguageModel::Anthropic(model) => model.display_name().into(),
LanguageModel::Cloud(model) => model.display_name().into(),
LanguageModel::Ollama(model) => model.display_name().into(),
}
}
pub fn max_token_count(&self) -> usize {
match self {
LanguageModel::OpenAi(model) => model.max_token_count(),
LanguageModel::Anthropic(model) => model.max_token_count(),
LanguageModel::Cloud(model) => model.max_token_count(),
LanguageModel::Ollama(model) => model.max_token_count(),
}
}
pub fn id(&self) -> &str {
match self {
LanguageModel::OpenAi(model) => model.id(),
LanguageModel::Anthropic(model) => model.id(),
LanguageModel::Cloud(model) => model.id(),
LanguageModel::Ollama(model) => model.id(),
}
}
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct LanguageModelRequestMessage {
pub role: Role,
pub content: String,
}
impl LanguageModelRequestMessage {
pub fn to_proto(&self) -> proto::LanguageModelRequestMessage {
proto::LanguageModelRequestMessage {
role: self.role.to_proto() as i32,
content: self.content.clone(),
tool_calls: Vec::new(),
tool_call_id: None,
}
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct LanguageModelRequest {
pub model: LanguageModel,
pub messages: Vec<LanguageModelRequestMessage>,
pub stop: Vec<String>,
pub temperature: f32,
}
impl LanguageModelRequest {
pub fn to_proto(&self) -> proto::CompleteWithLanguageModel {
proto::CompleteWithLanguageModel {
model: self.model.id().to_string(),
messages: self.messages.iter().map(|m| m.to_proto()).collect(),
stop: self.stop.clone(),
temperature: self.temperature,
tool_choice: None,
tools: Vec::new(),
}
}
/// Before we send the request to the server, we can perform fixups on it appropriate to the model.
pub fn preprocess(&mut self) {
match &self.model {
LanguageModel::OpenAi(_) => {}
LanguageModel::Anthropic(_) => {}
LanguageModel::Ollama(_) => {}
LanguageModel::Cloud(model) => match model {
CloudModel::Claude3Opus
| CloudModel::Claude3Sonnet
| CloudModel::Claude3Haiku
| CloudModel::Claude3_5Sonnet => {
preprocess_anthropic_request(self);
}
_ => {}
},
}
}
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct LanguageModelResponseMessage {
pub role: Option<Role>,
pub content: Option<String>,
}
#[derive(Deserialize, Debug)]
pub struct LanguageModelUsage {
pub prompt_tokens: u32,
@@ -321,6 +165,16 @@ pub fn init(fs: Arc<dyn Fs>, client: Arc<Client>, cx: &mut AppContext) {
cx.set_global(Assistant::default());
AssistantSettings::register(cx);
// TODO: remove this when 0.148.0 is released.
if AssistantSettings::get_global(cx).using_outdated_settings_version {
update_settings_file::<AssistantSettings>(fs.clone(), cx, {
let fs = fs.clone();
|content, cx| {
content.update_file(fs, cx);
}
});
}
cx.spawn(|mut cx| {
let client = client.clone();
async move {
@@ -338,7 +192,7 @@ pub fn init(fs: Arc<dyn Fs>, client: Arc<Client>, cx: &mut AppContext) {
context_store::init(&client);
prompt_library::init(cx);
completion_provider::init(client.clone(), cx);
init_completion_provider(cx);
assistant_slash_command::init(cx);
register_slash_commands(cx);
assistant_panel::init(cx);
@@ -363,6 +217,38 @@ pub fn init(fs: Arc<dyn Fs>, client: Arc<Client>, cx: &mut AppContext) {
.detach();
}
fn init_completion_provider(cx: &mut AppContext) {
completion::init(cx);
update_active_language_model_from_settings(cx);
cx.observe_global::<SettingsStore>(update_active_language_model_from_settings)
.detach();
cx.observe(&LanguageModelRegistry::global(cx), |_, cx| {
update_active_language_model_from_settings(cx)
})
.detach();
}
fn update_active_language_model_from_settings(cx: &mut AppContext) {
let settings = AssistantSettings::get_global(cx);
let provider_name = LanguageModelProviderId::from(settings.default_model.provider.clone());
let model_id = LanguageModelId::from(settings.default_model.model.clone());
let Some(provider) = LanguageModelRegistry::global(cx)
.read(cx)
.provider(&provider_name)
else {
return;
};
let models = provider.provided_models(cx);
if let Some(model) = models.iter().find(|model| model.id() == model_id).cloned() {
LanguageModelCompletionProvider::global(cx).update(cx, |completion_provider, cx| {
completion_provider.set_active_model(model, cx);
});
}
}
fn register_slash_commands(cx: &mut AppContext) {
let slash_command_registry = SlashCommandRegistry::global(cx);
slash_command_registry.register_command(file_command::FileSlashCommand, true);

File diff suppressed because it is too large Load Diff

View File

@@ -1,162 +1,14 @@
use std::fmt;
use std::sync::Arc;
use crate::{preprocess_anthropic_request, LanguageModel, LanguageModelRequest};
pub use anthropic::Model as AnthropicModel;
use gpui::Pixels;
pub use ollama::Model as OllamaModel;
pub use open_ai::Model as OpenAiModel;
use schemars::{
schema::{InstanceType, Metadata, Schema, SchemaObject},
JsonSchema,
};
use serde::{
de::{self, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use settings::{Settings, SettingsSources};
use strum::{EnumIter, IntoEnumIterator};
#[derive(Clone, Debug, Default, PartialEq, EnumIter)]
pub enum CloudModel {
Gpt3Point5Turbo,
Gpt4,
Gpt4Turbo,
#[default]
Gpt4Omni,
Claude3_5Sonnet,
Claude3Opus,
Claude3Sonnet,
Claude3Haiku,
Gemini15Pro,
Gemini15Flash,
Custom(String),
}
impl Serialize for CloudModel {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.id())
}
}
impl<'de> Deserialize<'de> for CloudModel {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct ZedDotDevModelVisitor;
impl<'de> Visitor<'de> for ZedDotDevModelVisitor {
type Value = CloudModel;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string for a ZedDotDevModel variant or a custom model")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let model = CloudModel::iter()
.find(|model| model.id() == value)
.unwrap_or_else(|| CloudModel::Custom(value.to_string()));
Ok(model)
}
}
deserializer.deserialize_str(ZedDotDevModelVisitor)
}
}
impl JsonSchema for CloudModel {
fn schema_name() -> String {
"ZedDotDevModel".to_owned()
}
fn json_schema(_generator: &mut schemars::gen::SchemaGenerator) -> Schema {
let variants = CloudModel::iter()
.filter_map(|model| {
let id = model.id();
if id.is_empty() {
None
} else {
Some(id.to_string())
}
})
.collect::<Vec<_>>();
Schema::Object(SchemaObject {
instance_type: Some(InstanceType::String.into()),
enum_values: Some(variants.iter().map(|s| s.clone().into()).collect()),
metadata: Some(Box::new(Metadata {
title: Some("ZedDotDevModel".to_owned()),
default: Some(CloudModel::default().id().into()),
examples: variants.into_iter().map(Into::into).collect(),
..Default::default()
})),
..Default::default()
})
}
}
impl CloudModel {
pub fn id(&self) -> &str {
match self {
Self::Gpt3Point5Turbo => "gpt-3.5-turbo",
Self::Gpt4 => "gpt-4",
Self::Gpt4Turbo => "gpt-4-turbo-preview",
Self::Gpt4Omni => "gpt-4o",
Self::Claude3_5Sonnet => "claude-3-5-sonnet",
Self::Claude3Opus => "claude-3-opus",
Self::Claude3Sonnet => "claude-3-sonnet",
Self::Claude3Haiku => "claude-3-haiku",
Self::Gemini15Pro => "gemini-1.5-pro",
Self::Gemini15Flash => "gemini-1.5-flash",
Self::Custom(id) => id,
}
}
pub fn display_name(&self) -> &str {
match self {
Self::Gpt3Point5Turbo => "GPT 3.5 Turbo",
Self::Gpt4 => "GPT 4",
Self::Gpt4Turbo => "GPT 4 Turbo",
Self::Gpt4Omni => "GPT 4 Omni",
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
Self::Claude3Opus => "Claude 3 Opus",
Self::Claude3Sonnet => "Claude 3 Sonnet",
Self::Claude3Haiku => "Claude 3 Haiku",
Self::Gemini15Pro => "Gemini 1.5 Pro",
Self::Gemini15Flash => "Gemini 1.5 Flash",
Self::Custom(id) => id.as_str(),
}
}
pub fn max_token_count(&self) -> usize {
match self {
Self::Gpt3Point5Turbo => 2048,
Self::Gpt4 => 4096,
Self::Gpt4Turbo | Self::Gpt4Omni => 128000,
Self::Claude3_5Sonnet
| Self::Claude3Opus
| Self::Claude3Sonnet
| Self::Claude3Haiku => 200000,
Self::Gemini15Pro => 128000,
Self::Gemini15Flash => 32000,
Self::Custom(_) => 4096, // TODO: Make this configurable
}
}
pub fn preprocess_request(&self, request: &mut LanguageModelRequest) {
match self {
Self::Claude3Opus | Self::Claude3Sonnet | Self::Claude3Haiku => {
preprocess_anthropic_request(request)
}
_ => {}
}
}
}
use anthropic::Model as AnthropicModel;
use fs::Fs;
use gpui::{AppContext, Pixels};
use language_model::{settings::AllLanguageModelSettings, CloudModel, LanguageModel};
use ollama::Model as OllamaModel;
use open_ai::Model as OpenAiModel;
use schemars::{schema::Schema, JsonSchema};
use serde::{Deserialize, Serialize};
use settings::{update_settings_file, Settings, SettingsSources};
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
@@ -167,43 +19,9 @@ pub enum AssistantDockPosition {
Bottom,
}
#[derive(Debug, PartialEq)]
pub enum AssistantProvider {
ZedDotDev {
model: CloudModel,
},
OpenAi {
model: OpenAiModel,
api_url: String,
low_speed_timeout_in_seconds: Option<u64>,
available_models: Vec<OpenAiModel>,
},
Anthropic {
model: AnthropicModel,
api_url: String,
low_speed_timeout_in_seconds: Option<u64>,
},
Ollama {
model: OllamaModel,
api_url: String,
low_speed_timeout_in_seconds: Option<u64>,
},
}
impl Default for AssistantProvider {
fn default() -> Self {
Self::OpenAi {
model: OpenAiModel::default(),
api_url: open_ai::OPEN_AI_API_URL.into(),
low_speed_timeout_in_seconds: None,
available_models: Default::default(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
#[serde(tag = "name", rename_all = "snake_case")]
pub enum AssistantProviderContent {
pub enum AssistantProviderContentV1 {
#[serde(rename = "zed.dev")]
ZedDotDev { default_model: Option<CloudModel> },
#[serde(rename = "openai")]
@@ -234,7 +52,8 @@ pub struct AssistantSettings {
pub dock: AssistantDockPosition,
pub default_width: Pixels,
pub default_height: Pixels,
pub provider: AssistantProvider,
pub default_model: AssistantDefaultModel,
pub using_outdated_settings_version: bool,
}
/// Assistant panel settings
@@ -266,34 +85,142 @@ impl Default for AssistantSettingsContent {
}
impl AssistantSettingsContent {
fn upgrade(&self) -> AssistantSettingsContentV1 {
pub fn is_version_outdated(&self) -> bool {
match self {
AssistantSettingsContent::Versioned(settings) => match settings {
VersionedAssistantSettingsContent::V1(settings) => settings.clone(),
VersionedAssistantSettingsContent::V1(_) => true,
VersionedAssistantSettingsContent::V2(_) => false,
},
AssistantSettingsContent::Legacy(settings) => AssistantSettingsContentV1 {
AssistantSettingsContent::Legacy(_) => true,
}
}
pub fn update_file(&mut self, fs: Arc<dyn Fs>, cx: &AppContext) {
if let AssistantSettingsContent::Versioned(settings) = self {
if let VersionedAssistantSettingsContent::V1(settings) = settings {
if let Some(provider) = settings.provider.clone() {
match provider {
AssistantProviderContentV1::Anthropic {
api_url,
low_speed_timeout_in_seconds,
..
} => update_settings_file::<AllLanguageModelSettings>(
fs,
cx,
move |content, _| {
if content.anthropic.is_none() {
content.anthropic =
Some(language_model::settings::AnthropicSettingsContent {
api_url,
low_speed_timeout_in_seconds,
..Default::default()
});
}
},
),
AssistantProviderContentV1::Ollama {
api_url,
low_speed_timeout_in_seconds,
..
} => update_settings_file::<AllLanguageModelSettings>(
fs,
cx,
move |content, _| {
if content.ollama.is_none() {
content.ollama =
Some(language_model::settings::OllamaSettingsContent {
api_url,
low_speed_timeout_in_seconds,
});
}
},
),
AssistantProviderContentV1::OpenAi {
api_url,
low_speed_timeout_in_seconds,
available_models,
..
} => update_settings_file::<AllLanguageModelSettings>(
fs,
cx,
move |content, _| {
if content.openai.is_none() {
content.openai =
Some(language_model::settings::OpenAiSettingsContent {
api_url,
low_speed_timeout_in_seconds,
available_models,
});
}
},
),
_ => {}
}
}
}
}
*self = AssistantSettingsContent::Versioned(VersionedAssistantSettingsContent::V2(
self.upgrade(),
));
}
fn upgrade(&self) -> AssistantSettingsContentV2 {
match self {
AssistantSettingsContent::Versioned(settings) => match settings {
VersionedAssistantSettingsContent::V1(settings) => AssistantSettingsContentV2 {
enabled: settings.enabled,
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_width,
default_model: settings
.provider
.clone()
.and_then(|provider| match provider {
AssistantProviderContentV1::ZedDotDev { default_model } => {
default_model.map(|model| AssistantDefaultModel {
provider: "zed.dev".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::OpenAi { default_model, .. } => {
default_model.map(|model| AssistantDefaultModel {
provider: "openai".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::Anthropic { default_model, .. } => {
default_model.map(|model| AssistantDefaultModel {
provider: "anthropic".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::Ollama { default_model, .. } => {
default_model.map(|model| AssistantDefaultModel {
provider: "ollama".to_string(),
model: model.id().to_string(),
})
}
}),
},
VersionedAssistantSettingsContent::V2(settings) => settings.clone(),
},
AssistantSettingsContent::Legacy(settings) => AssistantSettingsContentV2 {
enabled: None,
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_height,
provider: if let Some(open_ai_api_url) = settings.openai_api_url.as_ref() {
Some(AssistantProviderContent::OpenAi {
default_model: settings.default_open_ai_model.clone(),
api_url: Some(open_ai_api_url.clone()),
low_speed_timeout_in_seconds: None,
available_models: Some(Default::default()),
})
} else {
settings.default_open_ai_model.clone().map(|open_ai_model| {
AssistantProviderContent::OpenAi {
default_model: Some(open_ai_model),
api_url: None,
low_speed_timeout_in_seconds: None,
available_models: Some(Default::default()),
}
})
},
default_model: Some(AssistantDefaultModel {
provider: "openai".to_string(),
model: settings
.default_open_ai_model
.clone()
.unwrap_or_default()
.id()
.to_string(),
}),
},
}
}
@@ -304,6 +231,9 @@ impl AssistantSettingsContent {
VersionedAssistantSettingsContent::V1(settings) => {
settings.dock = Some(dock);
}
VersionedAssistantSettingsContent::V2(settings) => {
settings.dock = Some(dock);
}
},
AssistantSettingsContent::Legacy(settings) => {
settings.dock = Some(dock);
@@ -311,74 +241,78 @@ impl AssistantSettingsContent {
}
}
pub fn set_model(&mut self, new_model: LanguageModel) {
pub fn set_model(&mut self, language_model: Arc<dyn LanguageModel>) {
let model = language_model.id().0.to_string();
let provider = language_model.provider_id().0.to_string();
match self {
AssistantSettingsContent::Versioned(settings) => match settings {
VersionedAssistantSettingsContent::V1(settings) => match &mut settings.provider {
Some(AssistantProviderContent::ZedDotDev {
default_model: model,
}) => {
if let LanguageModel::Cloud(new_model) = new_model {
*model = Some(new_model);
}
VersionedAssistantSettingsContent::V1(settings) => match provider.as_ref() {
"zed.dev" => {
settings.provider = Some(AssistantProviderContentV1::ZedDotDev {
default_model: CloudModel::from_id(&model).ok(),
});
}
Some(AssistantProviderContent::OpenAi {
default_model: model,
..
}) => {
if let LanguageModel::OpenAi(new_model) = new_model {
*model = Some(new_model);
}
"anthropic" => {
let (api_url, low_speed_timeout_in_seconds) = match &settings.provider {
Some(AssistantProviderContentV1::Anthropic {
api_url,
low_speed_timeout_in_seconds,
..
}) => (api_url.clone(), *low_speed_timeout_in_seconds),
_ => (None, None),
};
settings.provider = Some(AssistantProviderContentV1::Anthropic {
default_model: AnthropicModel::from_id(&model).ok(),
api_url,
low_speed_timeout_in_seconds,
});
}
Some(AssistantProviderContent::Anthropic {
default_model: model,
..
}) => {
if let LanguageModel::Anthropic(new_model) = new_model {
*model = Some(new_model);
}
"ollama" => {
let (api_url, low_speed_timeout_in_seconds) = match &settings.provider {
Some(AssistantProviderContentV1::Ollama {
api_url,
low_speed_timeout_in_seconds,
..
}) => (api_url.clone(), *low_speed_timeout_in_seconds),
_ => (None, None),
};
settings.provider = Some(AssistantProviderContentV1::Ollama {
default_model: Some(ollama::Model::new(&model)),
api_url,
low_speed_timeout_in_seconds,
});
}
Some(AssistantProviderContent::Ollama {
default_model: model,
..
}) => {
if let LanguageModel::Ollama(new_model) = new_model {
*model = Some(new_model);
}
"openai" => {
let (api_url, low_speed_timeout_in_seconds, available_models) =
match &settings.provider {
Some(AssistantProviderContentV1::OpenAi {
api_url,
low_speed_timeout_in_seconds,
available_models,
..
}) => (
api_url.clone(),
*low_speed_timeout_in_seconds,
available_models.clone(),
),
_ => (None, None, None),
};
settings.provider = Some(AssistantProviderContentV1::OpenAi {
default_model: open_ai::Model::from_id(&model).ok(),
api_url,
low_speed_timeout_in_seconds,
available_models,
});
}
provider => match new_model {
LanguageModel::Cloud(model) => {
*provider = Some(AssistantProviderContent::ZedDotDev {
default_model: Some(model),
})
}
LanguageModel::OpenAi(model) => {
*provider = Some(AssistantProviderContent::OpenAi {
default_model: Some(model),
api_url: None,
low_speed_timeout_in_seconds: None,
available_models: Some(Default::default()),
})
}
LanguageModel::Anthropic(model) => {
*provider = Some(AssistantProviderContent::Anthropic {
default_model: Some(model),
api_url: None,
low_speed_timeout_in_seconds: None,
})
}
LanguageModel::Ollama(model) => {
*provider = Some(AssistantProviderContent::Ollama {
default_model: Some(model),
api_url: None,
low_speed_timeout_in_seconds: None,
})
}
},
_ => {}
},
VersionedAssistantSettingsContent::V2(settings) => {
settings.default_model = Some(AssistantDefaultModel { provider, model });
}
},
AssistantSettingsContent::Legacy(settings) => {
if let LanguageModel::OpenAi(model) = new_model {
if let Ok(model) = open_ai::Model::from_id(&language_model.id().0) {
settings.default_open_ai_model = Some(model);
}
}
@@ -391,21 +325,78 @@ impl AssistantSettingsContent {
pub enum VersionedAssistantSettingsContent {
#[serde(rename = "1")]
V1(AssistantSettingsContentV1),
#[serde(rename = "2")]
V2(AssistantSettingsContentV2),
}
impl Default for VersionedAssistantSettingsContent {
fn default() -> Self {
Self::V1(AssistantSettingsContentV1 {
Self::V2(AssistantSettingsContentV2 {
enabled: None,
button: None,
dock: None,
default_width: None,
default_height: None,
provider: None,
default_model: None,
})
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
pub struct AssistantSettingsContentV2 {
/// Whether the Assistant is enabled.
///
/// Default: true
enabled: Option<bool>,
/// Whether to show the assistant panel button in the status bar.
///
/// Default: true
button: Option<bool>,
/// Where to dock the assistant.
///
/// Default: right
dock: Option<AssistantDockPosition>,
/// Default width in pixels when the assistant is docked to the left or right.
///
/// Default: 640
default_width: Option<f32>,
/// Default height in pixels when the assistant is docked to the bottom.
///
/// Default: 320
default_height: Option<f32>,
/// The default model to use when creating new contexts.
default_model: Option<AssistantDefaultModel>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
pub struct AssistantDefaultModel {
#[schemars(schema_with = "providers_schema")]
pub provider: String,
pub model: String,
}
fn providers_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
schemars::schema::SchemaObject {
enum_values: Some(vec![
"anthropic".into(),
"ollama".into(),
"openai".into(),
"zed.dev".into(),
]),
..Default::default()
}
.into()
}
impl Default for AssistantDefaultModel {
fn default() -> Self {
Self {
provider: "openai".to_string(),
model: "gpt-4".to_string(),
}
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
pub struct AssistantSettingsContentV1 {
/// Whether the Assistant is enabled.
@@ -432,7 +423,7 @@ pub struct AssistantSettingsContentV1 {
///
/// This can either be the internal `zed.dev` service or an external `openai` service,
/// each with their respective default models and configurations.
provider: Option<AssistantProviderContent>,
provider: Option<AssistantProviderContentV1>,
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
@@ -475,6 +466,10 @@ impl Settings for AssistantSettings {
let mut settings = AssistantSettings::default();
for value in sources.defaults_and_customizations() {
if value.is_version_outdated() {
settings.using_outdated_settings_version = true;
}
let value = value.upgrade();
merge(&mut settings.enabled, value.enabled);
merge(&mut settings.button, value.button);
@@ -487,123 +482,10 @@ impl Settings for AssistantSettings {
&mut settings.default_height,
value.default_height.map(Into::into),
);
if let Some(provider) = value.provider.clone() {
match (&mut settings.provider, provider) {
(
AssistantProvider::ZedDotDev { model },
AssistantProviderContent::ZedDotDev {
default_model: model_override,
},
) => {
merge(model, model_override);
}
(
AssistantProvider::OpenAi {
model,
api_url,
low_speed_timeout_in_seconds,
available_models,
},
AssistantProviderContent::OpenAi {
default_model: model_override,
api_url: api_url_override,
low_speed_timeout_in_seconds: low_speed_timeout_in_seconds_override,
available_models: available_models_override,
},
) => {
merge(model, model_override);
merge(api_url, api_url_override);
merge(available_models, available_models_override);
if let Some(low_speed_timeout_in_seconds_override) =
low_speed_timeout_in_seconds_override
{
*low_speed_timeout_in_seconds =
Some(low_speed_timeout_in_seconds_override);
}
}
(
AssistantProvider::Ollama {
model,
api_url,
low_speed_timeout_in_seconds,
},
AssistantProviderContent::Ollama {
default_model: model_override,
api_url: api_url_override,
low_speed_timeout_in_seconds: low_speed_timeout_in_seconds_override,
},
) => {
merge(model, model_override);
merge(api_url, api_url_override);
if let Some(low_speed_timeout_in_seconds_override) =
low_speed_timeout_in_seconds_override
{
*low_speed_timeout_in_seconds =
Some(low_speed_timeout_in_seconds_override);
}
}
(
AssistantProvider::Anthropic {
model,
api_url,
low_speed_timeout_in_seconds,
},
AssistantProviderContent::Anthropic {
default_model: model_override,
api_url: api_url_override,
low_speed_timeout_in_seconds: low_speed_timeout_in_seconds_override,
},
) => {
merge(model, model_override);
merge(api_url, api_url_override);
if let Some(low_speed_timeout_in_seconds_override) =
low_speed_timeout_in_seconds_override
{
*low_speed_timeout_in_seconds =
Some(low_speed_timeout_in_seconds_override);
}
}
(provider, provider_override) => {
*provider = match provider_override {
AssistantProviderContent::ZedDotDev {
default_model: model,
} => AssistantProvider::ZedDotDev {
model: model.unwrap_or_default(),
},
AssistantProviderContent::OpenAi {
default_model: model,
api_url,
low_speed_timeout_in_seconds,
available_models,
} => AssistantProvider::OpenAi {
model: model.unwrap_or_default(),
api_url: api_url.unwrap_or_else(|| open_ai::OPEN_AI_API_URL.into()),
low_speed_timeout_in_seconds,
available_models: available_models.unwrap_or_default(),
},
AssistantProviderContent::Anthropic {
default_model: model,
api_url,
low_speed_timeout_in_seconds,
} => AssistantProvider::Anthropic {
model: model.unwrap_or_default(),
api_url: api_url
.unwrap_or_else(|| anthropic::ANTHROPIC_API_URL.into()),
low_speed_timeout_in_seconds,
},
AssistantProviderContent::Ollama {
default_model: model,
api_url,
low_speed_timeout_in_seconds,
} => AssistantProvider::Ollama {
model: model.unwrap_or_default(),
api_url: api_url.unwrap_or_else(|| ollama::OLLAMA_API_URL.into()),
low_speed_timeout_in_seconds,
},
};
}
}
}
merge(
&mut settings.default_model,
value.default_model.map(Into::into),
);
}
Ok(settings)
@@ -616,96 +498,103 @@ fn merge<T>(target: &mut T, value: Option<T>) {
}
}
#[cfg(test)]
mod tests {
use gpui::{AppContext, UpdateGlobal};
use settings::SettingsStore;
// #[cfg(test)]
// mod tests {
// use gpui::{AppContext, UpdateGlobal};
// use settings::SettingsStore;
use super::*;
// use super::*;
#[gpui::test]
fn test_deserialize_assistant_settings(cx: &mut AppContext) {
let store = settings::SettingsStore::test(cx);
cx.set_global(store);
// #[gpui::test]
// fn test_deserialize_assistant_settings(cx: &mut AppContext) {
// let store = settings::SettingsStore::test(cx);
// cx.set_global(store);
// Settings default to gpt-4-turbo.
AssistantSettings::register(cx);
assert_eq!(
AssistantSettings::get_global(cx).provider,
AssistantProvider::OpenAi {
model: OpenAiModel::FourOmni,
api_url: open_ai::OPEN_AI_API_URL.into(),
low_speed_timeout_in_seconds: None,
available_models: Default::default(),
}
);
// // Settings default to gpt-4-turbo.
// AssistantSettings::register(cx);
// assert_eq!(
// AssistantSettings::get_global(cx).provider,
// AssistantProvider::OpenAi {
// model: OpenAiModel::FourOmni,
// api_url: open_ai::OPEN_AI_API_URL.into(),
// low_speed_timeout_in_seconds: None,
// available_models: Default::default(),
// }
// );
// Ensure backward-compatibility.
SettingsStore::update_global(cx, |store, cx| {
store
.set_user_settings(
r#"{
"assistant": {
"openai_api_url": "test-url",
}
}"#,
cx,
)
.unwrap();
});
assert_eq!(
AssistantSettings::get_global(cx).provider,
AssistantProvider::OpenAi {
model: OpenAiModel::FourOmni,
api_url: "test-url".into(),
low_speed_timeout_in_seconds: None,
available_models: Default::default(),
}
);
SettingsStore::update_global(cx, |store, cx| {
store
.set_user_settings(
r#"{
"assistant": {
"default_open_ai_model": "gpt-4-0613"
}
}"#,
cx,
)
.unwrap();
});
assert_eq!(
AssistantSettings::get_global(cx).provider,
AssistantProvider::OpenAi {
model: OpenAiModel::Four,
api_url: open_ai::OPEN_AI_API_URL.into(),
low_speed_timeout_in_seconds: None,
available_models: Default::default(),
}
);
// // Ensure backward-compatibility.
// SettingsStore::update_global(cx, |store, cx| {
// store
// .set_user_settings(
// r#"{
// "assistant": {
// "openai_api_url": "test-url",
// }
// }"#,
// cx,
// )
// .unwrap();
// });
// assert_eq!(
// AssistantSettings::get_global(cx).provider,
// AssistantProvider::OpenAi {
// model: OpenAiModel::FourOmni,
// api_url: "test-url".into(),
// low_speed_timeout_in_seconds: None,
// available_models: Default::default(),
// }
// );
// SettingsStore::update_global(cx, |store, cx| {
// store
// .set_user_settings(
// r#"{
// "assistant": {
// "default_open_ai_model": "gpt-4-0613"
// }
// }"#,
// cx,
// )
// .unwrap();
// });
// assert_eq!(
// AssistantSettings::get_global(cx).provider,
// AssistantProvider::OpenAi {
// model: OpenAiModel::Four,
// api_url: open_ai::OPEN_AI_API_URL.into(),
// low_speed_timeout_in_seconds: None,
// available_models: Default::default(),
// }
// );
// The new version supports setting a custom model when using zed.dev.
SettingsStore::update_global(cx, |store, cx| {
store
.set_user_settings(
r#"{
"assistant": {
"version": "1",
"provider": {
"name": "zed.dev",
"default_model": "custom"
}
}
}"#,
cx,
)
.unwrap();
});
assert_eq!(
AssistantSettings::get_global(cx).provider,
AssistantProvider::ZedDotDev {
model: CloudModel::Custom("custom".into())
}
);
}
}
// // The new version supports setting a custom model when using zed.dev.
// SettingsStore::update_global(cx, |store, cx| {
// store
// .set_user_settings(
// r#"{
// "assistant": {
// "version": "1",
// "provider": {
// "name": "zed.dev",
// "default_model": {
// "custom": {
// "name": "custom-provider"
// }
// }
// }
// }
// }"#,
// cx,
// )
// .unwrap();
// });
// assert_eq!(
// AssistantSettings::get_global(cx).provider,
// AssistantProvider::ZedDotDev {
// model: CloudModel::Custom {
// name: "custom-provider".into(),
// max_tokens: None
// }
// }
// );
// }
// }

View File

@@ -1,373 +0,0 @@
mod anthropic;
mod cloud;
#[cfg(any(test, feature = "test-support"))]
mod fake;
mod ollama;
mod open_ai;
pub use anthropic::*;
pub use cloud::*;
#[cfg(any(test, feature = "test-support"))]
pub use fake::*;
pub use ollama::*;
pub use open_ai::*;
use parking_lot::RwLock;
use smol::lock::{Semaphore, SemaphoreGuardArc};
use crate::{
assistant_settings::{AssistantProvider, AssistantSettings},
LanguageModel, LanguageModelRequest,
};
use anyhow::Result;
use client::Client;
use futures::{future::BoxFuture, stream::BoxStream};
use gpui::{AnyView, AppContext, BorrowAppContext, Task, WindowContext};
use settings::{Settings, SettingsStore};
use std::time::Duration;
use std::{any::Any, sync::Arc};
/// Choose which model to use for openai provider.
/// If the model is not available, try to use the first available model, or fallback to the original model.
fn choose_openai_model(
model: &::open_ai::Model,
available_models: &[::open_ai::Model],
) -> ::open_ai::Model {
available_models
.iter()
.find(|&m| m == model)
.or_else(|| available_models.first())
.unwrap_or_else(|| model)
.clone()
}
pub fn init(client: Arc<Client>, cx: &mut AppContext) {
let provider = create_provider_from_settings(client.clone(), 0, cx);
cx.set_global(CompletionProvider::new(provider, Some(client)));
let mut settings_version = 0;
cx.observe_global::<SettingsStore>(move |cx| {
settings_version += 1;
cx.update_global::<CompletionProvider, _>(|provider, cx| {
provider.update_settings(settings_version, cx);
})
})
.detach();
}
pub struct CompletionResponse {
pub inner: BoxFuture<'static, Result<BoxStream<'static, Result<String>>>>,
_lock: SemaphoreGuardArc,
}
pub trait LanguageModelCompletionProvider: Send + Sync {
fn available_models(&self, cx: &AppContext) -> Vec<LanguageModel>;
fn settings_version(&self) -> usize;
fn is_authenticated(&self) -> bool;
fn authenticate(&self, cx: &AppContext) -> Task<Result<()>>;
fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView;
fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>>;
fn model(&self) -> LanguageModel;
fn count_tokens(
&self,
request: LanguageModelRequest,
cx: &AppContext,
) -> BoxFuture<'static, Result<usize>>;
fn complete(
&self,
request: LanguageModelRequest,
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>>;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
const MAX_CONCURRENT_COMPLETION_REQUESTS: usize = 4;
pub struct CompletionProvider {
provider: Arc<RwLock<dyn LanguageModelCompletionProvider>>,
client: Option<Arc<Client>>,
request_limiter: Arc<Semaphore>,
}
impl CompletionProvider {
pub fn new(
provider: Arc<RwLock<dyn LanguageModelCompletionProvider>>,
client: Option<Arc<Client>>,
) -> Self {
Self {
provider,
client,
request_limiter: Arc::new(Semaphore::new(MAX_CONCURRENT_COMPLETION_REQUESTS)),
}
}
pub fn available_models(&self, cx: &AppContext) -> Vec<LanguageModel> {
self.provider.read().available_models(cx)
}
pub fn settings_version(&self) -> usize {
self.provider.read().settings_version()
}
pub fn is_authenticated(&self) -> bool {
self.provider.read().is_authenticated()
}
pub fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
self.provider.read().authenticate(cx)
}
pub fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
self.provider.read().authentication_prompt(cx)
}
pub fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>> {
self.provider.read().reset_credentials(cx)
}
pub fn model(&self) -> LanguageModel {
self.provider.read().model()
}
pub fn count_tokens(
&self,
request: LanguageModelRequest,
cx: &AppContext,
) -> BoxFuture<'static, Result<usize>> {
self.provider.read().count_tokens(request, cx)
}
pub fn complete(
&self,
request: LanguageModelRequest,
cx: &AppContext,
) -> Task<CompletionResponse> {
let rate_limiter = self.request_limiter.clone();
let provider = self.provider.clone();
cx.background_executor().spawn(async move {
let lock = rate_limiter.acquire_arc().await;
let response = provider.read().complete(request);
CompletionResponse {
inner: response,
_lock: lock,
}
})
}
}
impl gpui::Global for CompletionProvider {}
impl CompletionProvider {
pub fn global(cx: &AppContext) -> &Self {
cx.global::<Self>()
}
pub fn update_current_as<R, T: LanguageModelCompletionProvider + 'static>(
&mut self,
update: impl FnOnce(&mut T) -> R,
) -> Option<R> {
let mut provider = self.provider.write();
if let Some(provider) = provider.as_any_mut().downcast_mut::<T>() {
Some(update(provider))
} else {
None
}
}
pub fn update_settings(&mut self, version: usize, cx: &mut AppContext) {
let updated = match &AssistantSettings::get_global(cx).provider {
AssistantProvider::ZedDotDev { model } => self
.update_current_as::<_, CloudCompletionProvider>(|provider| {
provider.update(model.clone(), version);
}),
AssistantProvider::OpenAi {
model,
api_url,
low_speed_timeout_in_seconds,
available_models,
} => self.update_current_as::<_, OpenAiCompletionProvider>(|provider| {
provider.update(
choose_openai_model(&model, &available_models),
api_url.clone(),
low_speed_timeout_in_seconds.map(Duration::from_secs),
version,
);
}),
AssistantProvider::Anthropic {
model,
api_url,
low_speed_timeout_in_seconds,
} => self.update_current_as::<_, AnthropicCompletionProvider>(|provider| {
provider.update(
model.clone(),
api_url.clone(),
low_speed_timeout_in_seconds.map(Duration::from_secs),
version,
);
}),
AssistantProvider::Ollama {
model,
api_url,
low_speed_timeout_in_seconds,
} => self.update_current_as::<_, OllamaCompletionProvider>(|provider| {
provider.update(
model.clone(),
api_url.clone(),
low_speed_timeout_in_seconds.map(Duration::from_secs),
version,
cx,
);
}),
};
// Previously configured provider was changed to another one
if updated.is_none() {
if let Some(client) = self.client.clone() {
self.provider = create_provider_from_settings(client, version, cx);
} else {
log::warn!("completion provider cannot be created because client is not set");
}
}
}
}
fn create_provider_from_settings(
client: Arc<Client>,
settings_version: usize,
cx: &mut AppContext,
) -> Arc<RwLock<dyn LanguageModelCompletionProvider>> {
match &AssistantSettings::get_global(cx).provider {
AssistantProvider::ZedDotDev { model } => Arc::new(RwLock::new(
CloudCompletionProvider::new(model.clone(), client.clone(), settings_version, cx),
)),
AssistantProvider::OpenAi {
model,
api_url,
low_speed_timeout_in_seconds,
available_models,
} => Arc::new(RwLock::new(OpenAiCompletionProvider::new(
choose_openai_model(&model, &available_models),
api_url.clone(),
client.http_client(),
low_speed_timeout_in_seconds.map(Duration::from_secs),
settings_version,
))),
AssistantProvider::Anthropic {
model,
api_url,
low_speed_timeout_in_seconds,
} => Arc::new(RwLock::new(AnthropicCompletionProvider::new(
model.clone(),
api_url.clone(),
client.http_client(),
low_speed_timeout_in_seconds.map(Duration::from_secs),
settings_version,
))),
AssistantProvider::Ollama {
model,
api_url,
low_speed_timeout_in_seconds,
} => Arc::new(RwLock::new(OllamaCompletionProvider::new(
model.clone(),
api_url.clone(),
client.http_client(),
low_speed_timeout_in_seconds.map(Duration::from_secs),
settings_version,
cx,
))),
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use gpui::AppContext;
use parking_lot::RwLock;
use settings::SettingsStore;
use smol::stream::StreamExt;
use crate::{
completion_provider::MAX_CONCURRENT_COMPLETION_REQUESTS, CompletionProvider,
FakeCompletionProvider, LanguageModelRequest,
};
#[gpui::test]
fn test_rate_limiting(cx: &mut AppContext) {
SettingsStore::test(cx);
let fake_provider = FakeCompletionProvider::setup_test(cx);
let provider = CompletionProvider::new(Arc::new(RwLock::new(fake_provider.clone())), None);
// Enqueue some requests
for i in 0..MAX_CONCURRENT_COMPLETION_REQUESTS * 2 {
let response = provider.complete(
LanguageModelRequest {
temperature: i as f32 / 10.0,
..Default::default()
},
cx,
);
cx.background_executor()
.spawn(async move {
let response = response.await;
let mut stream = response.inner.await.unwrap();
while let Some(message) = stream.next().await {
message.unwrap();
}
})
.detach();
}
cx.background_executor().run_until_parked();
assert_eq!(
fake_provider.completion_count(),
MAX_CONCURRENT_COMPLETION_REQUESTS
);
// Get the first completion request that is in flight and mark it as completed.
let completion = fake_provider
.running_completions()
.into_iter()
.next()
.unwrap();
fake_provider.finish_completion(&completion);
// Ensure that the number of in-flight completion requests is reduced.
assert_eq!(
fake_provider.completion_count(),
MAX_CONCURRENT_COMPLETION_REQUESTS - 1
);
cx.background_executor().run_until_parked();
// Ensure that another completion request was allowed to acquire the lock.
assert_eq!(
fake_provider.completion_count(),
MAX_CONCURRENT_COMPLETION_REQUESTS
);
// Mark all completion requests as finished that are in flight.
for request in fake_provider.running_completions() {
fake_provider.finish_completion(&request);
}
assert_eq!(fake_provider.completion_count(), 0);
// Wait until the background tasks acquire the lock again.
cx.background_executor().run_until_parked();
assert_eq!(
fake_provider.completion_count(),
MAX_CONCURRENT_COMPLETION_REQUESTS - 1
);
// Finish all remaining completion requests.
for request in fake_provider.running_completions() {
fake_provider.finish_completion(&request);
}
cx.background_executor().run_until_parked();
assert_eq!(fake_provider.completion_count(), 0);
}
}

View File

@@ -1,208 +0,0 @@
use crate::{
assistant_settings::CloudModel, count_open_ai_tokens, CompletionProvider, LanguageModel,
LanguageModelCompletionProvider, LanguageModelRequest,
};
use anyhow::{anyhow, Result};
use client::{proto, Client};
use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt, TryFutureExt};
use gpui::{AnyView, AppContext, Task};
use std::{future, sync::Arc};
use strum::IntoEnumIterator;
use ui::prelude::*;
pub struct CloudCompletionProvider {
client: Arc<Client>,
model: CloudModel,
settings_version: usize,
status: client::Status,
_maintain_client_status: Task<()>,
}
impl CloudCompletionProvider {
pub fn new(
model: CloudModel,
client: Arc<Client>,
settings_version: usize,
cx: &mut AppContext,
) -> Self {
let mut status_rx = client.status();
let status = *status_rx.borrow();
let maintain_client_status = cx.spawn(|mut cx| async move {
while let Some(status) = status_rx.next().await {
let _ = cx.update_global::<CompletionProvider, _>(|provider, _cx| {
provider.update_current_as::<_, Self>(|provider| {
provider.status = status;
});
});
}
});
Self {
client,
model,
settings_version,
status,
_maintain_client_status: maintain_client_status,
}
}
pub fn update(&mut self, model: CloudModel, settings_version: usize) {
self.model = model;
self.settings_version = settings_version;
}
}
impl LanguageModelCompletionProvider for CloudCompletionProvider {
fn available_models(&self, _cx: &AppContext) -> Vec<LanguageModel> {
let mut custom_model = if let CloudModel::Custom(custom_model) = self.model.clone() {
Some(custom_model)
} else {
None
};
CloudModel::iter()
.filter_map(move |model| {
if let CloudModel::Custom(_) = model {
Some(CloudModel::Custom(custom_model.take()?))
} else {
Some(model)
}
})
.map(LanguageModel::Cloud)
.collect()
}
fn settings_version(&self) -> usize {
self.settings_version
}
fn is_authenticated(&self) -> bool {
self.status.is_connected()
}
fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
let client = self.client.clone();
cx.spawn(move |cx| async move { client.authenticate_and_connect(true, &cx).await })
}
fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
cx.new_view(|_cx| AuthenticationPrompt).into()
}
fn reset_credentials(&self, _cx: &AppContext) -> Task<Result<()>> {
Task::ready(Ok(()))
}
fn model(&self) -> LanguageModel {
LanguageModel::Cloud(self.model.clone())
}
fn count_tokens(
&self,
request: LanguageModelRequest,
cx: &AppContext,
) -> BoxFuture<'static, Result<usize>> {
match request.model {
LanguageModel::Cloud(CloudModel::Gpt4)
| LanguageModel::Cloud(CloudModel::Gpt4Turbo)
| LanguageModel::Cloud(CloudModel::Gpt4Omni)
| LanguageModel::Cloud(CloudModel::Gpt3Point5Turbo) => {
count_open_ai_tokens(request, cx.background_executor())
}
LanguageModel::Cloud(
CloudModel::Claude3_5Sonnet
| CloudModel::Claude3Opus
| CloudModel::Claude3Sonnet
| CloudModel::Claude3Haiku,
) => {
// Can't find a tokenizer for Claude 3, so for now just use the same as OpenAI's as an approximation.
count_open_ai_tokens(request, cx.background_executor())
}
LanguageModel::Cloud(CloudModel::Custom(model)) => {
let request = self.client.request(proto::CountTokensWithLanguageModel {
model,
messages: request
.messages
.iter()
.map(|message| message.to_proto())
.collect(),
});
async move {
let response = request.await?;
Ok(response.token_count as usize)
}
.boxed()
}
_ => future::ready(Err(anyhow!("invalid model"))).boxed(),
}
}
fn complete(
&self,
mut request: LanguageModelRequest,
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
request.preprocess();
let request = proto::CompleteWithLanguageModel {
model: request.model.id().to_string(),
messages: request
.messages
.iter()
.map(|message| message.to_proto())
.collect(),
stop: request.stop,
temperature: request.temperature,
tools: Vec::new(),
tool_choice: None,
};
self.client
.request_stream(request)
.map_ok(|stream| {
stream
.filter_map(|response| async move {
match response {
Ok(mut response) => Some(Ok(response.choices.pop()?.delta?.content?)),
Err(error) => Some(Err(error)),
}
})
.boxed()
})
.boxed()
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
struct AuthenticationPrompt;
impl Render for AuthenticationPrompt {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
const LABEL: &str = "Generate and analyze code with language models. You can dialog with the assistant in this panel or transform code inline.";
v_flex().gap_6().p_4().child(Label::new(LABEL)).child(
v_flex()
.gap_2()
.child(
Button::new("sign_in", "Sign in")
.icon_color(Color::Muted)
.icon(IconName::Github)
.icon_position(IconPosition::Start)
.style(ButtonStyle::Filled)
.full_width()
.on_click(|_, cx| {
CompletionProvider::global(cx)
.authenticate(cx)
.detach_and_log_err(cx);
}),
)
.child(
div().flex().w_full().items_center().child(
Label::new("Sign in to enable collaboration.")
.color(Color::Muted)
.size(LabelSize::Small),
),
),
)
}
}

View File

@@ -1,106 +0,0 @@
use anyhow::Result;
use collections::HashMap;
use futures::{channel::mpsc, future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
use gpui::{AnyView, AppContext, Task};
use std::sync::Arc;
use ui::WindowContext;
use crate::{LanguageModel, LanguageModelCompletionProvider, LanguageModelRequest};
#[derive(Clone, Default)]
pub struct FakeCompletionProvider {
current_completion_txs: Arc<parking_lot::Mutex<HashMap<String, mpsc::UnboundedSender<String>>>>,
}
impl FakeCompletionProvider {
pub fn setup_test(cx: &mut AppContext) -> Self {
use crate::CompletionProvider;
use parking_lot::RwLock;
let this = Self::default();
let provider = CompletionProvider::new(Arc::new(RwLock::new(this.clone())), None);
cx.set_global(provider);
this
}
pub fn running_completions(&self) -> Vec<LanguageModelRequest> {
self.current_completion_txs
.lock()
.keys()
.map(|k| serde_json::from_str(k).unwrap())
.collect()
}
pub fn completion_count(&self) -> usize {
self.current_completion_txs.lock().len()
}
pub fn send_completion(&self, request: &LanguageModelRequest, chunk: String) {
let json = serde_json::to_string(request).unwrap();
self.current_completion_txs
.lock()
.get(&json)
.unwrap()
.unbounded_send(chunk)
.unwrap();
}
pub fn finish_completion(&self, request: &LanguageModelRequest) {
self.current_completion_txs
.lock()
.remove(&serde_json::to_string(request).unwrap());
}
}
impl LanguageModelCompletionProvider for FakeCompletionProvider {
fn available_models(&self, _cx: &AppContext) -> Vec<LanguageModel> {
vec![LanguageModel::default()]
}
fn settings_version(&self) -> usize {
0
}
fn is_authenticated(&self) -> bool {
true
}
fn authenticate(&self, _cx: &AppContext) -> Task<Result<()>> {
Task::ready(Ok(()))
}
fn authentication_prompt(&self, _cx: &mut WindowContext) -> AnyView {
unimplemented!()
}
fn reset_credentials(&self, _cx: &AppContext) -> Task<Result<()>> {
Task::ready(Ok(()))
}
fn model(&self) -> LanguageModel {
LanguageModel::default()
}
fn count_tokens(
&self,
_request: LanguageModelRequest,
_cx: &AppContext,
) -> BoxFuture<'static, Result<usize>> {
futures::future::ready(Ok(0)).boxed()
}
fn complete(
&self,
_request: LanguageModelRequest,
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
let (tx, rx) = mpsc::unbounded();
self.current_completion_txs
.lock()
.insert(serde_json::to_string(&_request).unwrap(), tx);
async move { Ok(rx.map(Ok).boxed()) }.boxed()
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
use crate::{
assistant_settings::AssistantSettings, humanize_token_count, prompts::generate_content_prompt,
AssistantPanel, AssistantPanelEvent, CompletionProvider, Hunk, LanguageModelRequest,
LanguageModelRequestMessage, Role, StreamingDiff,
humanize_token_count, prompts::generate_content_prompt, AssistantPanel, AssistantPanelEvent,
Hunk, LanguageModelCompletionProvider, ModelSelector, StreamingDiff,
};
use anyhow::{anyhow, Context as _, Result};
use client::telemetry::Telemetry;
@@ -9,27 +8,36 @@ use collections::{hash_map, HashMap, HashSet, VecDeque};
use editor::{
actions::{MoveDown, MoveUp, SelectAll},
display_map::{
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock,
BlockContext, BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
ToDisplayPoint,
},
Anchor, AnchorRangeExt, Editor, EditorElement, EditorEvent, EditorMode, EditorStyle,
ExcerptRange, GutterDimensions, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint,
};
use fs::Fs;
use futures::{channel::mpsc, SinkExt, Stream, StreamExt};
use gpui::{
point, AppContext, EventEmitter, FocusHandle, FocusableView, FontStyle, Global, HighlightStyle,
Model, ModelContext, Subscription, Task, TextStyle, UpdateGlobal, View, ViewContext, WeakView,
WhiteSpace, WindowContext,
use futures::{
channel::mpsc,
future::LocalBoxFuture,
stream::{self, BoxStream},
SinkExt, Stream, StreamExt,
};
use language::{Buffer, Point, Selection, TransactionId};
use gpui::{
point, AppContext, EventEmitter, FocusHandle, FocusableView, Global, HighlightStyle, Model,
ModelContext, Subscription, Task, TextStyle, UpdateGlobal, View, ViewContext, WeakView,
WindowContext,
};
use language::{Buffer, IndentKind, Point, Selection, TransactionId};
use language_model::{LanguageModelRequest, LanguageModelRequestMessage, Role};
use multi_buffer::MultiBufferRow;
use parking_lot::Mutex;
use rope::Rope;
use settings::{update_settings_file, Settings};
use settings::Settings;
use similar::TextDiff;
use smol::future::FutureExt;
use std::{
cmp, mem,
cmp,
future::Future,
mem,
ops::{Range, RangeInclusive},
pin::Pin,
sync::Arc,
@@ -37,7 +45,7 @@ use std::{
time::{Duration, Instant},
};
use theme::ThemeSettings;
use ui::{prelude::*, ContextMenu, PopoverMenu, Tooltip};
use ui::{prelude::*, IconButtonShape, Tooltip};
use util::RangeExt;
use workspace::{notifications::NotificationId, Toast, Workspace};
@@ -79,6 +87,7 @@ impl InlineAssistant {
editor: &View<Editor>,
workspace: Option<WeakView<Workspace>>,
assistant_panel: Option<&View<AssistantPanel>>,
initial_prompt: Option<String>,
cx: &mut WindowContext,
) {
let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx);
@@ -130,11 +139,11 @@ impl InlineAssistant {
}
let assist_group_id = self.next_assist_group_id.post_inc();
let prompt_buffer = cx.new_model(|cx| Buffer::local("", cx));
let prompt_buffer =
cx.new_model(|cx| Buffer::local(initial_prompt.unwrap_or_default(), cx));
let prompt_buffer = cx.new_model(|cx| MultiBuffer::singleton(prompt_buffer, cx));
let mut assists = Vec::new();
let mut assist_blocks = Vec::new();
let mut assist_to_focus = None;
for range in codegen_ranges {
let assist_id = self.next_assist_id.post_inc();
@@ -142,6 +151,7 @@ impl InlineAssistant {
Codegen::new(
editor.read(cx).buffer().clone(),
range.clone(),
None,
self.telemetry.clone(),
cx,
)
@@ -174,42 +184,18 @@ impl InlineAssistant {
}
}
assist_blocks.push(BlockProperties {
style: BlockStyle::Sticky,
position: range.start,
height: prompt_editor.read(cx).height_in_lines,
render: build_assist_editor_renderer(&prompt_editor),
disposition: BlockDisposition::Above,
});
assist_blocks.push(BlockProperties {
style: BlockStyle::Sticky,
position: range.end,
height: 1,
render: Box::new(|cx| {
v_flex()
.h_full()
.w_full()
.border_t_1()
.border_color(cx.theme().status().info_border)
.into_any_element()
}),
disposition: BlockDisposition::Below,
});
assists.push((assist_id, prompt_editor));
}
let [prompt_block_id, end_block_id] =
self.insert_assist_blocks(editor, &range, &prompt_editor, cx);
let assist_block_ids = editor.update(cx, |editor, cx| {
editor.insert_blocks(assist_blocks, None, cx)
});
assists.push((assist_id, prompt_editor, prompt_block_id, end_block_id));
}
let editor_assists = self
.assists_by_editor
.entry(editor.downgrade())
.or_insert_with(|| EditorInlineAssists::new(&editor, cx));
let mut assist_group = InlineAssistGroup::new();
for ((assist_id, prompt_editor), block_ids) in
assists.into_iter().zip(assist_block_ids.chunks_exact(2))
{
for (assist_id, prompt_editor, prompt_block_id, end_block_id) in assists {
self.assists.insert(
assist_id,
InlineAssist::new(
@@ -218,8 +204,8 @@ impl InlineAssistant {
assistant_panel.is_some(),
editor,
&prompt_editor,
block_ids[0],
block_ids[1],
prompt_block_id,
end_block_id,
prompt_editor.read(cx).codegen.clone(),
workspace.clone(),
cx,
@@ -235,6 +221,128 @@ impl InlineAssistant {
}
}
#[allow(clippy::too_many_arguments)]
pub fn suggest_assist(
&mut self,
editor: &View<Editor>,
mut range: Range<Anchor>,
initial_prompt: String,
initial_insertion: Option<String>,
workspace: Option<WeakView<Workspace>>,
assistant_panel: Option<&View<AssistantPanel>>,
cx: &mut WindowContext,
) -> InlineAssistId {
let assist_group_id = self.next_assist_group_id.post_inc();
let prompt_buffer = cx.new_model(|cx| Buffer::local(&initial_prompt, cx));
let prompt_buffer = cx.new_model(|cx| MultiBuffer::singleton(prompt_buffer, cx));
let assist_id = self.next_assist_id.post_inc();
let buffer = editor.read(cx).buffer().clone();
let prepend_transaction_id = initial_insertion.and_then(|initial_insertion| {
buffer.update(cx, |buffer, cx| {
buffer.start_transaction(cx);
buffer.edit([(range.start..range.start, initial_insertion)], None, cx);
buffer.end_transaction(cx)
})
});
range.start = range.start.bias_left(&buffer.read(cx).read(cx));
range.end = range.end.bias_right(&buffer.read(cx).read(cx));
let codegen = cx.new_model(|cx| {
Codegen::new(
editor.read(cx).buffer().clone(),
range.clone(),
prepend_transaction_id,
self.telemetry.clone(),
cx,
)
});
let gutter_dimensions = Arc::new(Mutex::new(GutterDimensions::default()));
let prompt_editor = cx.new_view(|cx| {
PromptEditor::new(
assist_id,
gutter_dimensions.clone(),
self.prompt_history.clone(),
prompt_buffer.clone(),
codegen.clone(),
editor,
assistant_panel,
workspace.clone(),
self.fs.clone(),
cx,
)
});
let [prompt_block_id, end_block_id] =
self.insert_assist_blocks(editor, &range, &prompt_editor, cx);
let editor_assists = self
.assists_by_editor
.entry(editor.downgrade())
.or_insert_with(|| EditorInlineAssists::new(&editor, cx));
let mut assist_group = InlineAssistGroup::new();
self.assists.insert(
assist_id,
InlineAssist::new(
assist_id,
assist_group_id,
assistant_panel.is_some(),
editor,
&prompt_editor,
prompt_block_id,
end_block_id,
prompt_editor.read(cx).codegen.clone(),
workspace.clone(),
cx,
),
);
assist_group.assist_ids.push(assist_id);
editor_assists.assist_ids.push(assist_id);
self.assist_groups.insert(assist_group_id, assist_group);
assist_id
}
fn insert_assist_blocks(
&self,
editor: &View<Editor>,
range: &Range<Anchor>,
prompt_editor: &View<PromptEditor>,
cx: &mut WindowContext,
) -> [CustomBlockId; 2] {
let assist_blocks = vec![
BlockProperties {
style: BlockStyle::Sticky,
position: range.start,
height: prompt_editor.read(cx).height_in_lines,
render: build_assist_editor_renderer(prompt_editor),
disposition: BlockDisposition::Above,
},
BlockProperties {
style: BlockStyle::Sticky,
position: range.end,
height: 1,
render: Box::new(|cx| {
v_flex()
.h_full()
.w_full()
.border_t_1()
.border_color(cx.theme().status().info_border)
.into_any_element()
}),
disposition: BlockDisposition::Below,
},
];
editor.update(cx, |editor, cx| {
let block_ids = editor.insert_blocks(assist_blocks, None, cx);
[block_ids[0], block_ids[1]]
})
}
fn handle_prompt_editor_focus_in(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
let assist = &self.assists[&assist_id];
let Some(decorations) = assist.decorations.as_ref() else {
@@ -379,6 +487,14 @@ impl InlineAssistant {
cx.propagate();
}
fn handle_editor_release(&mut self, editor: WeakView<Editor>, cx: &mut WindowContext) {
if let Some(editor_assists) = self.assists_by_editor.get_mut(&editor) {
for assist_id in editor_assists.assist_ids.clone() {
self.finish_assist(assist_id, true, cx);
}
}
}
fn handle_editor_change(&mut self, editor: View<Editor>, cx: &mut WindowContext) {
let Some(editor_assists) = self.assists_by_editor.get(&editor.downgrade()) else {
return;
@@ -698,7 +814,7 @@ impl InlineAssistant {
assist_group.assist_ids.clone()
}
fn start_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
pub fn start_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
assist
} else {
@@ -727,16 +843,32 @@ impl InlineAssistant {
self.prompt_history.pop_front();
}
assist.codegen.update(cx, |codegen, cx| codegen.undo(cx));
let codegen = assist.codegen.clone();
let request = self.request_for_inline_assist(assist_id, cx);
cx.spawn(|mut cx| async move {
let request = request.await?;
codegen.update(&mut cx, |codegen, cx| codegen.start(request, cx))?;
anyhow::Ok(())
})
.detach_and_log_err(cx);
let telemetry_id = LanguageModelCompletionProvider::read_global(cx)
.active_model()
.map(|m| m.telemetry_id())
.unwrap_or_default();
let chunks: LocalBoxFuture<Result<BoxStream<Result<String>>>> =
if user_prompt.trim().to_lowercase() == "delete" {
async { Ok(stream::empty().boxed()) }.boxed_local()
} else {
let request = self.request_for_inline_assist(assist_id, cx);
let mut cx = cx.to_async();
async move {
let request = request.await?;
let chunks = cx
.update(|cx| {
LanguageModelCompletionProvider::read_global(cx)
.stream_completion(request, cx)
})?
.await?;
Ok(chunks.boxed())
}
.boxed_local()
};
codegen.update(cx, |codegen, cx| {
codegen.start(telemetry_id, chunks, cx);
});
}
fn request_for_inline_assist(
@@ -745,8 +877,8 @@ impl InlineAssistant {
cx: &mut WindowContext,
) -> Task<Result<LanguageModelRequest>> {
cx.spawn(|mut cx| async move {
let (user_prompt, context_request, project_name, buffer, range, model) = cx
.read_global(|this: &InlineAssistant, cx: &WindowContext| {
let (user_prompt, context_request, project_name, buffer, range) =
cx.read_global(|this: &InlineAssistant, cx: &WindowContext| {
let assist = this.assists.get(&assist_id).context("invalid assist")?;
let decorations = assist.decorations.as_ref().context("invalid assist")?;
let editor = assist.editor.upgrade().context("invalid assist")?;
@@ -780,15 +912,7 @@ impl InlineAssistant {
});
let buffer = editor.read(cx).buffer().read(cx).snapshot(cx);
let range = assist.codegen.read(cx).range.clone();
let model = CompletionProvider::global(cx).model();
anyhow::Ok((
user_prompt,
context_request,
project_name,
buffer,
range,
model,
))
anyhow::Ok((user_prompt, context_request, project_name, buffer, range))
})??;
let language = buffer.language_at(range.start);
@@ -847,7 +971,6 @@ impl InlineAssistant {
});
Ok(LanguageModelRequest {
model,
messages,
stop: vec!["|END|>".to_string()],
temperature,
@@ -855,7 +978,7 @@ impl InlineAssistant {
})
}
fn stop_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
pub fn stop_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
let assist = if let Some(assist) = self.assists.get_mut(&assist_id) {
assist
} else {
@@ -1074,6 +1197,14 @@ impl EditorInlineAssists {
}
}),
_subscriptions: vec![
cx.observe_release(editor, {
let editor = editor.downgrade();
|_, cx| {
InlineAssistant::update_global(cx, |this, cx| {
this.handle_editor_release(editor, cx);
})
}
}),
cx.observe(editor, move |editor, cx| {
InlineAssistant::update_global(cx, |this, cx| {
this.handle_editor_change(editor, cx)
@@ -1138,7 +1269,7 @@ fn build_assist_editor_renderer(editor: &View<PromptEditor>) -> RenderBlock {
}
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
struct InlineAssistId(usize);
pub struct InlineAssistId(usize);
impl InlineAssistId {
fn post_inc(&mut self) -> InlineAssistId {
@@ -1192,22 +1323,19 @@ impl EventEmitter<PromptEditorEvent> for PromptEditor {}
impl Render for PromptEditor {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let gutter_dimensions = *self.gutter_dimensions.lock();
let fs = self.fs.clone();
let buttons = match &self.codegen.read(cx).status {
CodegenStatus::Idle => {
vec![
IconButton::new("cancel", IconName::Close)
.icon_color(Color::Muted)
.size(ButtonSize::None)
.shape(IconButtonShape::Square)
.tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx))
.on_click(
cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
),
IconButton::new("start", IconName::Sparkle)
IconButton::new("start", IconName::SparkleAlt)
.icon_color(Color::Muted)
.size(ButtonSize::None)
.icon_size(IconSize::XSmall)
.shape(IconButtonShape::Square)
.tooltip(|cx| Tooltip::for_action("Transform", &menu::Confirm, cx))
.on_click(
cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::StartRequested)),
@@ -1218,15 +1346,14 @@ impl Render for PromptEditor {
vec![
IconButton::new("cancel", IconName::Close)
.icon_color(Color::Muted)
.size(ButtonSize::None)
.shape(IconButtonShape::Square)
.tooltip(|cx| Tooltip::text("Cancel Assist", cx))
.on_click(
cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
),
IconButton::new("stop", IconName::Stop)
.icon_color(Color::Error)
.size(ButtonSize::None)
.icon_size(IconSize::XSmall)
.shape(IconButtonShape::Square)
.tooltip(|cx| {
Tooltip::with_meta(
"Interrupt Transformation",
@@ -1244,7 +1371,7 @@ impl Render for PromptEditor {
vec![
IconButton::new("cancel", IconName::Close)
.icon_color(Color::Muted)
.size(ButtonSize::None)
.shape(IconButtonShape::Square)
.tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx))
.on_click(
cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
@@ -1252,8 +1379,7 @@ impl Render for PromptEditor {
if self.edited_since_done {
IconButton::new("restart", IconName::RotateCw)
.icon_color(Color::Info)
.icon_size(IconSize::XSmall)
.size(ButtonSize::None)
.shape(IconButtonShape::Square)
.tooltip(|cx| {
Tooltip::with_meta(
"Restart Transformation",
@@ -1268,7 +1394,7 @@ impl Render for PromptEditor {
} else {
IconButton::new("confirm", IconName::Check)
.icon_color(Color::Info)
.size(ButtonSize::None)
.shape(IconButtonShape::Square)
.tooltip(|cx| Tooltip::for_action("Confirm Assist", &menu::Confirm, cx))
.on_click(cx.listener(|_, _, cx| {
cx.emit(PromptEditorEvent::ConfirmRequested);
@@ -1294,59 +1420,27 @@ impl Render for PromptEditor {
.w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0))
.justify_center()
.gap_2()
.child(
PopoverMenu::new("model-switcher")
.menu(move |cx| {
ContextMenu::build(cx, |mut menu, cx| {
for model in CompletionProvider::global(cx).available_models(cx)
{
menu = menu.custom_entry(
{
let model = model.clone();
move |_| {
Label::new(model.display_name())
.into_any_element()
}
},
{
let fs = fs.clone();
let model = model.clone();
move |cx| {
let model = model.clone();
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings| settings.set_model(model),
);
}
},
);
}
menu
})
.into()
})
.trigger(
IconButton::new("context", IconName::Settings)
.size(ButtonSize::None)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.tooltip(move |cx| {
Tooltip::with_meta(
format!(
"Using {}",
CompletionProvider::global(cx)
.model()
.display_name()
),
None,
"Click to Change Model",
cx,
)
}),
)
.anchor(gpui::AnchorCorner::BottomRight),
)
.child(ModelSelector::new(
self.fs.clone(),
IconButton::new("context", IconName::Settings)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.tooltip(move |cx| {
Tooltip::with_meta(
format!(
"Using {}",
LanguageModelCompletionProvider::read_global(cx)
.active_model()
.map(|model| model.name().0)
.unwrap_or_else(|| "No model selected".into()),
),
None,
"Change Model",
cx,
)
}),
))
.children(
if let CodegenStatus::Error(error) = &self.codegen.read(cx).status {
let error_message = SharedString::from(error.to_string());
@@ -1369,7 +1463,7 @@ impl Render for PromptEditor {
.child(
h_flex()
.gap_2()
.pr_4()
.pr_6()
.children(self.render_token_count(cx))
.children(buttons),
)
@@ -1535,7 +1629,9 @@ impl PromptEditor {
.await?;
let token_count = cx
.update(|cx| CompletionProvider::global(cx).count_tokens(request, cx))?
.update(|cx| {
LanguageModelCompletionProvider::read_global(cx).count_tokens(request, cx)
})?
.await?;
this.update(&mut cx, |this, cx| {
this.token_count = Some(token_count);
@@ -1663,7 +1759,7 @@ impl PromptEditor {
}
fn render_token_count(&self, cx: &mut ViewContext<Self>) -> Option<impl IntoElement> {
let model = CompletionProvider::global(cx).model();
let model = LanguageModelCompletionProvider::read_global(cx).active_model()?;
let token_count = self.token_count?;
let max_token_count = model.max_token_count();
@@ -1731,12 +1827,8 @@ impl PromptEditor {
font_features: settings.ui_font.features.clone(),
font_size: rems(0.875).into(),
font_weight: settings.ui_font.weight,
font_style: FontStyle::Normal,
line_height: relative(1.3),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
..Default::default()
};
EditorElement::new(
&self.editor,
@@ -1768,8 +1860,8 @@ impl InlineAssist {
include_context: bool,
editor: &View<Editor>,
prompt_editor: &View<PromptEditor>,
prompt_block_id: BlockId,
end_block_id: BlockId,
prompt_block_id: CustomBlockId,
end_block_id: CustomBlockId,
codegen: Model<Codegen>,
workspace: Option<WeakView<Workspace>>,
cx: &mut WindowContext,
@@ -1863,10 +1955,10 @@ impl InlineAssist {
}
struct InlineAssistDecorations {
prompt_block_id: BlockId,
prompt_block_id: CustomBlockId,
prompt_editor: View<PromptEditor>,
removed_line_block_ids: HashSet<BlockId>,
end_block_id: BlockId,
removed_line_block_ids: HashSet<CustomBlockId>,
end_block_id: CustomBlockId,
}
#[derive(Debug)]
@@ -1882,7 +1974,8 @@ pub struct Codegen {
range: Range<Anchor>,
edit_position: Anchor,
last_equal_ranges: Vec<Range<Anchor>>,
transaction_id: Option<TransactionId>,
prepend_transaction_id: Option<TransactionId>,
generation_transaction_id: Option<TransactionId>,
status: CodegenStatus,
generation: Task<()>,
diff: Diff,
@@ -1911,6 +2004,7 @@ impl Codegen {
pub fn new(
buffer: Model<MultiBuffer>,
range: Range<Anchor>,
prepend_transaction_id: Option<TransactionId>,
telemetry: Option<Arc<Telemetry>>,
cx: &mut ModelContext<Self>,
) -> Self {
@@ -1943,7 +2037,8 @@ impl Codegen {
range,
snapshot,
last_equal_ranges: Default::default(),
transaction_id: Default::default(),
prepend_transaction_id,
generation_transaction_id: None,
status: CodegenStatus::Idle,
generation: Task::ready(()),
diff: Diff::default(),
@@ -1959,8 +2054,13 @@ impl Codegen {
cx: &mut ModelContext<Self>,
) {
if let multi_buffer::Event::TransactionUndone { transaction_id } = event {
if self.transaction_id == Some(*transaction_id) {
self.transaction_id = None;
if self.generation_transaction_id == Some(*transaction_id) {
self.generation_transaction_id = None;
self.generation = Task::ready(());
cx.emit(CodegenEvent::Undone);
} else if self.prepend_transaction_id == Some(*transaction_id) {
self.prepend_transaction_id = None;
self.generation_transaction_id = None;
self.generation = Task::ready(());
cx.emit(CodegenEvent::Undone);
}
@@ -1971,7 +2071,12 @@ impl Codegen {
&self.last_equal_ranges
}
pub fn start(&mut self, prompt: LanguageModelRequest, cx: &mut ModelContext<Self>) {
pub fn start(
&mut self,
telemetry_id: String,
stream: impl 'static + Future<Output = Result<BoxStream<'static, Result<String>>>>,
cx: &mut ModelContext<Self>,
) {
let range = self.range.clone();
let snapshot = self.snapshot.clone();
let selected_text = snapshot
@@ -1979,21 +2084,37 @@ impl Codegen {
.collect::<Rope>();
let selection_start = range.start.to_point(&snapshot);
let suggested_line_indent = snapshot
.suggested_indents(selection_start.row..selection_start.row + 1, cx)
// Start with the indentation of the first line in the selection
let mut suggested_line_indent = snapshot
.suggested_indents(selection_start.row..=selection_start.row, cx)
.into_values()
.next()
.unwrap_or_else(|| snapshot.indent_size_for_line(MultiBufferRow(selection_start.row)));
let model_telemetry_id = prompt.model.telemetry_id();
let response = CompletionProvider::global(cx).complete(prompt, cx);
// If the first line in the selection does not have indentation, check the following lines
if suggested_line_indent.len == 0 && suggested_line_indent.kind == IndentKind::Space {
for row in selection_start.row..=range.end.to_point(&snapshot).row {
let line_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
// Prefer tabs if a line in the selection uses tabs as indentation
if line_indent.kind == IndentKind::Tab {
suggested_line_indent.kind = IndentKind::Tab;
break;
}
}
}
let telemetry = self.telemetry.clone();
self.edit_position = range.start;
self.diff = Diff::default();
self.status = CodegenStatus::Pending;
if let Some(transaction_id) = self.generation_transaction_id.take() {
self.buffer
.update(cx, |buffer, cx| buffer.undo_transaction(transaction_id, cx));
}
self.generation = cx.spawn(|this, mut cx| {
async move {
let response = response.await;
let chunks = stream.await;
let generate = async {
let mut edit_start = range.start.to_offset(&snapshot);
@@ -2003,7 +2124,7 @@ impl Codegen {
let mut response_latency = None;
let request_start = Instant::now();
let diff = async {
let chunks = StripInvalidSpans::new(response.inner.await?);
let chunks = StripInvalidSpans::new(chunks?);
futures::pin_mut!(chunks);
let mut diff = StreamingDiff::new(selected_text.to_string());
@@ -2086,7 +2207,7 @@ impl Codegen {
telemetry.report_assistant_event(
None,
telemetry_events::AssistantKind::Inline,
model_telemetry_id,
telemetry_id,
response_latency,
error_message,
);
@@ -2136,7 +2257,7 @@ impl Codegen {
});
if let Some(transaction) = transaction {
if let Some(first_transaction) = this.transaction_id {
if let Some(first_transaction) = this.generation_transaction_id {
// Group all assistant edits into the first transaction.
this.buffer.update(cx, |buffer, cx| {
buffer.merge_transactions(
@@ -2146,7 +2267,7 @@ impl Codegen {
)
});
} else {
this.transaction_id = Some(transaction);
this.generation_transaction_id = Some(transaction);
this.buffer.update(cx, |buffer, cx| {
buffer.finalize_last_transaction(cx)
});
@@ -2189,7 +2310,12 @@ impl Codegen {
}
pub fn undo(&mut self, cx: &mut ModelContext<Self>) {
if let Some(transaction_id) = self.transaction_id.take() {
if let Some(transaction_id) = self.prepend_transaction_id.take() {
self.buffer
.update(cx, |buffer, cx| buffer.undo_transaction(transaction_id, cx));
}
if let Some(transaction_id) = self.generation_transaction_id.take() {
self.buffer
.update(cx, |buffer, cx| buffer.undo_transaction(transaction_id, cx));
}
@@ -2451,10 +2577,6 @@ fn merge_ranges(ranges: &mut Vec<Range<Anchor>>, buffer: &MultiBufferSnapshot) {
#[cfg(test)]
mod tests {
use std::sync::Arc;
use crate::FakeCompletionProvider;
use super::*;
use futures::stream::{self};
use gpui::{Context, TestAppContext};
@@ -2463,9 +2585,11 @@ mod tests {
language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, LanguageMatcher,
Point,
};
use language_model::LanguageModelRegistry;
use rand::prelude::*;
use serde::Serialize;
use settings::SettingsStore;
use std::{future, sync::Arc};
#[derive(Serialize)]
pub struct DummyCompletionRequest {
@@ -2475,7 +2599,8 @@ mod tests {
#[gpui::test(iterations = 10)]
async fn test_transform_autoindent(cx: &mut TestAppContext, mut rng: StdRng) {
cx.set_global(cx.update(SettingsStore::test));
let provider = cx.update(|cx| FakeCompletionProvider::setup_test(cx));
cx.update(language_model::LanguageModelRegistry::test);
cx.update(completion::LanguageModelCompletionProvider::test);
cx.update(language_settings::init);
let text = indoc! {"
@@ -2493,14 +2618,17 @@ mod tests {
let snapshot = buffer.snapshot(cx);
snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(4, 5))
});
let codegen = cx.new_model(|cx| Codegen::new(buffer.clone(), range, None, cx));
let codegen = cx.new_model(|cx| Codegen::new(buffer.clone(), range, None, None, cx));
let (chunks_tx, chunks_rx) = mpsc::unbounded();
codegen.update(cx, |codegen, cx| {
codegen.start(LanguageModelRequest::default(), cx)
codegen.start(
String::new(),
future::ready(Ok(chunks_rx.map(|chunk| Ok(chunk)).boxed())),
cx,
)
});
cx.background_executor.run_until_parked();
let mut new_text = concat!(
" let mut x = 0;\n",
" while x < 10 {\n",
@@ -2511,11 +2639,11 @@ mod tests {
let max_len = cmp::min(new_text.len(), 10);
let len = rng.gen_range(1..=max_len);
let (chunk, suffix) = new_text.split_at(len);
provider.send_completion(&LanguageModelRequest::default(), chunk.into());
chunks_tx.unbounded_send(chunk.to_string()).unwrap();
new_text = suffix;
cx.background_executor.run_until_parked();
}
provider.finish_completion(&LanguageModelRequest::default());
drop(chunks_tx);
cx.background_executor.run_until_parked();
assert_eq!(
@@ -2536,7 +2664,6 @@ mod tests {
cx: &mut TestAppContext,
mut rng: StdRng,
) {
let provider = cx.update(|cx| FakeCompletionProvider::setup_test(cx));
cx.set_global(cx.update(SettingsStore::test));
cx.update(language_settings::init);
@@ -2552,10 +2679,16 @@ mod tests {
let snapshot = buffer.snapshot(cx);
snapshot.anchor_before(Point::new(1, 6))..snapshot.anchor_after(Point::new(1, 6))
});
let codegen = cx.new_model(|cx| Codegen::new(buffer.clone(), range, None, cx));
let codegen = cx.new_model(|cx| Codegen::new(buffer.clone(), range, None, None, cx));
let request = LanguageModelRequest::default();
codegen.update(cx, |codegen, cx| codegen.start(request, cx));
let (chunks_tx, chunks_rx) = mpsc::unbounded();
codegen.update(cx, |codegen, cx| {
codegen.start(
String::new(),
future::ready(Ok(chunks_rx.map(|chunk| Ok(chunk)).boxed())),
cx,
)
});
cx.background_executor.run_until_parked();
@@ -2569,11 +2702,11 @@ mod tests {
let max_len = cmp::min(new_text.len(), 10);
let len = rng.gen_range(1..=max_len);
let (chunk, suffix) = new_text.split_at(len);
provider.send_completion(&LanguageModelRequest::default(), chunk.into());
chunks_tx.unbounded_send(chunk.to_string()).unwrap();
new_text = suffix;
cx.background_executor.run_until_parked();
}
provider.finish_completion(&LanguageModelRequest::default());
drop(chunks_tx);
cx.background_executor.run_until_parked();
assert_eq!(
@@ -2594,7 +2727,8 @@ mod tests {
cx: &mut TestAppContext,
mut rng: StdRng,
) {
let provider = cx.update(|cx| FakeCompletionProvider::setup_test(cx));
cx.update(LanguageModelRegistry::test);
cx.update(completion::LanguageModelCompletionProvider::test);
cx.set_global(cx.update(SettingsStore::test));
cx.update(language_settings::init);
@@ -2610,10 +2744,16 @@ mod tests {
let snapshot = buffer.snapshot(cx);
snapshot.anchor_before(Point::new(1, 2))..snapshot.anchor_after(Point::new(1, 2))
});
let codegen = cx.new_model(|cx| Codegen::new(buffer.clone(), range, None, cx));
let codegen = cx.new_model(|cx| Codegen::new(buffer.clone(), range, None, None, cx));
let request = LanguageModelRequest::default();
codegen.update(cx, |codegen, cx| codegen.start(request, cx));
let (chunks_tx, chunks_rx) = mpsc::unbounded();
codegen.update(cx, |codegen, cx| {
codegen.start(
String::new(),
future::ready(Ok(chunks_rx.map(|chunk| Ok(chunk)).boxed())),
cx,
)
});
cx.background_executor.run_until_parked();
@@ -2627,11 +2767,11 @@ mod tests {
let max_len = cmp::min(new_text.len(), 10);
let len = rng.gen_range(1..=max_len);
let (chunk, suffix) = new_text.split_at(len);
provider.send_completion(&LanguageModelRequest::default(), chunk.into());
chunks_tx.unbounded_send(chunk.to_string()).unwrap();
new_text = suffix;
cx.background_executor.run_until_parked();
}
provider.finish_completion(&LanguageModelRequest::default());
drop(chunks_tx);
cx.background_executor.run_until_parked();
assert_eq!(
@@ -2647,6 +2787,62 @@ mod tests {
);
}
#[gpui::test(iterations = 10)]
async fn test_autoindent_respects_tabs_in_selection(cx: &mut TestAppContext) {
cx.update(LanguageModelRegistry::test);
cx.update(completion::LanguageModelCompletionProvider::test);
cx.set_global(cx.update(SettingsStore::test));
cx.update(language_settings::init);
let text = indoc! {"
func main() {
\tx := 0
\tfor i := 0; i < 10; i++ {
\t\tx++
\t}
}
"};
let buffer = cx.new_model(|cx| Buffer::local(text, cx));
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
let range = buffer.read_with(cx, |buffer, cx| {
let snapshot = buffer.snapshot(cx);
snapshot.anchor_before(Point::new(0, 0))..snapshot.anchor_after(Point::new(4, 2))
});
let codegen = cx.new_model(|cx| Codegen::new(buffer.clone(), range, None, None, cx));
let (chunks_tx, chunks_rx) = mpsc::unbounded();
codegen.update(cx, |codegen, cx| {
codegen.start(
String::new(),
future::ready(Ok(chunks_rx.map(|chunk| Ok(chunk)).boxed())),
cx,
)
});
let new_text = concat!(
"func main() {\n",
"\tx := 0\n",
"\tfor x < 10 {\n",
"\t\tx++\n",
"\t}", //
);
chunks_tx.unbounded_send(new_text.to_string()).unwrap();
drop(chunks_tx);
cx.background_executor.run_until_parked();
assert_eq!(
buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx).text()),
indoc! {"
func main() {
\tx := 0
\tfor x < 10 {
\t\tx++
\t}
}
"}
);
}
#[gpui::test]
async fn test_strip_invalid_spans_from_codeblock() {
assert_chunks("Lorem ipsum dolor", "Lorem ipsum dolor").await;

View File

@@ -1,82 +1,128 @@
use std::sync::Arc;
use crate::{assistant_settings::AssistantSettings, CompletionProvider, ToggleModelSelector};
use crate::{assistant_settings::AssistantSettings, LanguageModelCompletionProvider};
use fs::Fs;
use language_model::LanguageModelRegistry;
use settings::update_settings_file;
use ui::{prelude::*, ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
use ui::{prelude::*, ContextMenu, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
#[derive(IntoElement)]
pub struct ModelSelector {
handle: PopoverMenuHandle<ContextMenu>,
pub struct ModelSelector<T: PopoverTrigger> {
handle: Option<PopoverMenuHandle<ContextMenu>>,
fs: Arc<dyn Fs>,
trigger: T,
}
impl ModelSelector {
pub fn new(handle: PopoverMenuHandle<ContextMenu>, fs: Arc<dyn Fs>) -> Self {
ModelSelector { handle, fs }
impl<T: PopoverTrigger> ModelSelector<T> {
pub fn new(fs: Arc<dyn Fs>, trigger: T) -> Self {
ModelSelector {
handle: None,
fs,
trigger,
}
}
pub fn with_handle(mut self, handle: PopoverMenuHandle<ContextMenu>) -> Self {
self.handle = Some(handle);
self
}
}
impl RenderOnce for ModelSelector {
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
PopoverMenu::new("model-switcher")
.with_handle(self.handle)
.menu(move |cx| {
ContextMenu::build(cx, |mut menu, cx| {
for model in CompletionProvider::global(cx).available_models(cx) {
impl<T: PopoverTrigger> RenderOnce for ModelSelector<T> {
fn render(self, _: &mut WindowContext) -> impl IntoElement {
let mut menu = PopoverMenu::new("model-switcher");
if let Some(handle) = self.handle {
menu = menu.with_handle(handle);
}
menu.menu(move |cx| {
ContextMenu::build(cx, |mut menu, cx| {
for (index, provider) in LanguageModelRegistry::global(cx)
.read(cx)
.providers()
.enumerate()
{
if index > 0 {
menu = menu.separator();
}
menu = menu.header(provider.name().0);
let available_models = provider.provided_models(cx);
if available_models.is_empty() {
menu = menu.custom_entry(
{
let model = model.clone();
move |_| Label::new(model.display_name()).into_any_element()
move |_| {
h_flex()
.w_full()
.gap_1()
.child(Icon::new(IconName::Settings))
.child(Label::new("Configure"))
.into_any()
}
},
{
let fs = self.fs.clone();
let model = model.clone();
let provider = provider.id();
move |cx| {
let model = model.clone();
update_settings_file::<AssistantSettings>(
fs.clone(),
LanguageModelCompletionProvider::global(cx).update(
cx,
move |settings| settings.set_model(model),
|completion_provider, cx| {
completion_provider
.set_active_provider(provider.clone(), cx)
},
);
}
},
);
}
menu
})
.into()
})
.trigger(
ButtonLike::new("active-model")
.style(ButtonStyle::Subtle)
.child(
h_flex()
.w_full()
.gap_0p5()
.child(
div()
.overflow_x_hidden()
.flex_grow()
.whitespace_nowrap()
.child(
Label::new(
CompletionProvider::global(cx).model().display_name(),
let selected_model = LanguageModelCompletionProvider::read_global(cx)
.active_model()
.map(|m| m.id());
let selected_provider = LanguageModelCompletionProvider::read_global(cx)
.active_provider()
.map(|m| m.id());
for available_model in available_models {
menu = menu.custom_entry(
{
let id = available_model.id();
let provider_id = available_model.provider_id();
let model_name = available_model.name().0.clone();
let selected_model = selected_model.clone();
let selected_provider = selected_provider.clone();
move |_| {
h_flex()
.w_full()
.justify_between()
.child(Label::new(model_name.clone()))
.when(
selected_model.as_ref() == Some(&id)
&& selected_provider.as_ref() == Some(&provider_id),
|this| this.child(Icon::new(IconName::Check)),
)
.size(LabelSize::Small)
.color(Color::Muted),
),
)
.child(
Icon::new(IconName::ChevronDown)
.color(Color::Muted)
.size(IconSize::XSmall),
),
)
.tooltip(move |cx| {
Tooltip::for_action("Change Model", &ToggleModelSelector, cx)
}),
)
.attach(gpui::AnchorCorner::BottomLeft)
.into_any()
}
},
{
let fs = self.fs.clone();
let model = available_model.clone();
move |cx| {
let model = model.clone();
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings, _| settings.set_model(model),
);
}
},
);
}
}
menu
})
.into()
})
.trigger(self.trigger)
.attach(gpui::AnchorCorner::BottomLeft)
}
}

View File

@@ -1,8 +1,9 @@
use crate::{
slash_command::SlashCommandCompletionProvider, AssistantPanel, CompletionProvider,
InlineAssist, InlineAssistant, LanguageModelRequest, LanguageModelRequestMessage, Role,
slash_command::SlashCommandCompletionProvider, AssistantPanel, InlineAssist, InlineAssistant,
LanguageModelCompletionProvider,
};
use anyhow::{anyhow, Result};
use assets::Assets;
use chrono::{DateTime, Utc};
use collections::{HashMap, HashSet};
use editor::{actions::Tab, CurrentLineHighlight, Editor, EditorElement, EditorEvent, EditorStyle};
@@ -12,12 +13,13 @@ use futures::{
};
use fuzzy::StringMatchCandidate;
use gpui::{
actions, point, size, transparent_black, AppContext, BackgroundExecutor, Bounds, EventEmitter,
Global, HighlightStyle, PromptLevel, ReadGlobal, Subscription, Task, TextStyle,
actions, point, size, transparent_black, AppContext, AssetSource, BackgroundExecutor, Bounds,
EventEmitter, Global, HighlightStyle, PromptLevel, ReadGlobal, Subscription, Task, TextStyle,
TitlebarOptions, UpdateGlobal, View, WindowBounds, WindowHandle, WindowOptions,
};
use heed::{types::SerdeBincode, Database, RoTxn};
use language::{language_settings::SoftWrap, Buffer, LanguageRegistry};
use language_model::{LanguageModelRequest, LanguageModelRequestMessage, Role};
use parking_lot::RwLock;
use picker::{Picker, PickerDelegate};
use rope::Rope;
@@ -627,17 +629,18 @@ impl PromptLibrary {
self.picker.update(cx, |picker, cx| picker.focus(cx));
}
pub fn inline_assist(&mut self, _: &InlineAssist, cx: &mut ViewContext<Self>) {
pub fn inline_assist(&mut self, action: &InlineAssist, cx: &mut ViewContext<Self>) {
let Some(active_prompt_id) = self.active_prompt_id else {
cx.propagate();
return;
};
let prompt_editor = &self.prompt_editors[&active_prompt_id].body_editor;
let provider = CompletionProvider::global(cx);
if provider.is_authenticated() {
let provider = LanguageModelCompletionProvider::read_global(cx);
let initial_prompt = action.prompt.clone();
if provider.is_authenticated(cx) {
InlineAssistant::update_global(cx, |assistant, cx| {
assistant.assist(&prompt_editor, None, None, cx)
assistant.assist(&prompt_editor, None, None, initial_prompt, cx)
})
} else {
for window in cx.windows() {
@@ -733,11 +736,8 @@ impl PromptLibrary {
cx.background_executor().timer(DEBOUNCE_TIMEOUT).await;
let token_count = cx
.update(|cx| {
let provider = CompletionProvider::global(cx);
let model = provider.model();
provider.count_tokens(
LanguageModelCompletionProvider::read_global(cx).count_tokens(
LanguageModelRequest {
model,
messages: vec![LanguageModelRequestMessage {
role: Role::System,
content: body.to_string(),
@@ -803,7 +803,7 @@ impl PromptLibrary {
let prompt_metadata = self.store.metadata(prompt_id)?;
let prompt_editor = &self.prompt_editors[&prompt_id];
let focus_handle = prompt_editor.body_editor.focus_handle(cx);
let current_model = CompletionProvider::global(cx).model();
let current_model = LanguageModelCompletionProvider::read_global(cx).active_model();
let settings = ThemeSettings::get_global(cx);
Some(
@@ -914,7 +914,11 @@ impl PromptLibrary {
format!(
"Model: {}",
current_model
.display_name()
.as_ref()
.map(|model| model
.name()
.0)
.unwrap_or_default()
),
cx,
)
@@ -1296,6 +1300,17 @@ impl PromptStore {
fn first(&self) -> Option<PromptMetadata> {
self.metadata_cache.read().metadata.first().cloned()
}
pub fn operations_prompt(&self) -> String {
String::from_utf8(
Assets
.load("prompts/operations.md")
.unwrap()
.unwrap()
.to_vec(),
)
.unwrap()
}
}
/// Wraps a shared future to a prompt store so it can be assigned as a context global.

View File

@@ -1,171 +0,0 @@
use language::Rope;
use std::ops::Range;
/// Search the given buffer for the given substring, ignoring any differences
/// in line indentation between the query and the buffer.
///
/// Returns a vector of ranges of byte offsets in the buffer corresponding
/// to the entire lines of the buffer.
pub fn fuzzy_search_lines(haystack: &Rope, needle: &str) -> Option<Range<usize>> {
const SIMILARITY_THRESHOLD: f64 = 0.8;
let mut best_match: Option<(Range<usize>, f64)> = None; // (range, score)
let mut haystack_lines = haystack.chunks().lines();
let mut haystack_line_start = 0;
while let Some(mut haystack_line) = haystack_lines.next() {
let next_haystack_line_start = haystack_line_start + haystack_line.len() + 1;
let mut advanced_to_next_haystack_line = false;
let mut matched = true;
let match_start = haystack_line_start;
let mut match_end = next_haystack_line_start;
let mut match_score = 0.0;
let mut needle_lines = needle.lines().peekable();
while let Some(needle_line) = needle_lines.next() {
let similarity = line_similarity(haystack_line, needle_line);
if similarity >= SIMILARITY_THRESHOLD {
match_end = haystack_lines.offset();
match_score += similarity;
if needle_lines.peek().is_some() {
if let Some(next_haystack_line) = haystack_lines.next() {
advanced_to_next_haystack_line = true;
haystack_line = next_haystack_line;
} else {
matched = false;
break;
}
} else {
break;
}
} else {
matched = false;
break;
}
}
if matched
&& best_match
.as_ref()
.map(|(_, best_score)| match_score > *best_score)
.unwrap_or(true)
{
best_match = Some((match_start..match_end, match_score));
}
if advanced_to_next_haystack_line {
haystack_lines.seek(next_haystack_line_start);
}
haystack_line_start = next_haystack_line_start;
}
best_match.map(|(range, _)| range)
}
/// Calculates the similarity between two lines, ignoring leading and trailing whitespace,
/// using the Jaro-Winkler distance.
///
/// Returns a value between 0.0 and 1.0, where 1.0 indicates an exact match.
fn line_similarity(line1: &str, line2: &str) -> f64 {
strsim::jaro_winkler(line1.trim(), line2.trim())
}
#[cfg(test)]
mod test {
use super::*;
use gpui::{AppContext, Context as _};
use language::Buffer;
use unindent::Unindent as _;
use util::test::marked_text_ranges;
#[gpui::test]
fn test_fuzzy_search_lines(cx: &mut AppContext) {
let (text, expected_ranges) = marked_text_ranges(
&r#"
fn main() {
if a() {
assert_eq!(
1 + 2,
does_not_match,
);
}
println!("hi");
assert_eq!(
1 + 2,
3,
); // this last line does not match
« assert_eq!(
1 + 2,
3,
);
»
« assert_eq!(
"something",
"else",
);
»
}
"#
.unindent(),
false,
);
let buffer = cx.new_model(|cx| Buffer::local(&text, cx));
let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
let actual_range = fuzzy_search_lines(
snapshot.as_rope(),
&"
assert_eq!(
1 + 2,
3,
);
"
.unindent(),
)
.unwrap();
assert_eq!(actual_range, expected_ranges[0]);
let actual_range = fuzzy_search_lines(
snapshot.as_rope(),
&"
assert_eq!(
1 + 2,
3,
);
"
.unindent(),
)
.unwrap();
assert_eq!(actual_range, expected_ranges[0]);
let actual_range = fuzzy_search_lines(
snapshot.as_rope(),
&"
asst_eq!(
\"something\",
\"els\"
)
"
.unindent(),
)
.unwrap();
assert_eq!(actual_range, expected_ranges[1]);
let actual_range = fuzzy_search_lines(
snapshot.as_rope(),
&"
assert_eq!(
2 + 1,
3,
);
"
.unindent(),
);
assert_eq!(actual_range, None);
}
}

View File

@@ -284,7 +284,7 @@ fn collect_diagnostics(
PathBuf::try_from(path)
.ok()
.and_then(|path| {
project.read(cx).worktrees().find_map(|worktree| {
project.read(cx).worktrees(cx).find_map(|worktree| {
let worktree = worktree.read(cx);
let worktree_root_path = Path::new(worktree.root_name());
let relative_path = path.strip_prefix(worktree_root_path).ok()?;

View File

@@ -1,12 +1,13 @@
use std::path::Path;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::time::Duration;
use anyhow::{anyhow, bail, Result};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
};
use gpui::{AppContext, Model, Task, WeakView};
use gpui::{AppContext, BackgroundExecutor, Model, Task, WeakView};
use indexed_docs::{
DocsDotRsProvider, IndexedDocsRegistry, IndexedDocsStore, LocalRustdocProvider, PackageName,
ProviderId,
@@ -23,7 +24,7 @@ impl DocsSlashCommand {
pub const NAME: &'static str = "docs";
fn path_to_cargo_toml(project: Model<Project>, cx: &mut AppContext) -> Option<Arc<Path>> {
let worktree = project.read(cx).worktrees().next()?;
let worktree = project.read(cx).worktrees(cx).next()?;
let worktree = worktree.read(cx);
let entry = worktree.entry_for_path("Cargo.toml")?;
let path = ProjectPath {
@@ -90,6 +91,55 @@ impl DocsSlashCommand {
}
}
}
/// Runs just-in-time indexing for a given package, in case the slash command
/// is run without any entries existing in the index.
fn run_just_in_time_indexing(
store: Arc<IndexedDocsStore>,
key: String,
package: PackageName,
executor: BackgroundExecutor,
) -> Task<()> {
executor.clone().spawn(async move {
let (prefix, needs_full_index) = if let Some((prefix, _)) = key.split_once('*') {
// If we have a wildcard in the search, we want to wait until
// we've completely finished indexing so we get a full set of
// results for the wildcard.
(prefix.to_string(), true)
} else {
(key, false)
};
// If we already have some entries, we assume that we've indexed the package before
// and don't need to do it again.
let has_any_entries = store
.any_with_prefix(prefix.clone())
.await
.unwrap_or_default();
if has_any_entries {
return ();
};
let index_task = store.clone().index(package.clone());
if needs_full_index {
_ = index_task.await;
} else {
loop {
executor.timer(Duration::from_millis(200)).await;
if store
.any_with_prefix(prefix.clone())
.await
.unwrap_or_default()
|| !store.is_indexing(&package)
{
break;
}
}
}
})
}
}
impl SlashCommand for DocsSlashCommand {
@@ -200,13 +250,14 @@ impl SlashCommand for DocsSlashCommand {
};
let args = DocsSlashCommandArgs::parse(argument);
let executor = cx.background_executor().clone();
let task = cx.background_executor().spawn({
let store = args
.provider()
.ok_or_else(|| anyhow!("no docs provider specified"))
.and_then(|provider| IndexedDocsStore::try_global(provider, cx));
async move {
let (provider, key) = match args {
let (provider, key) = match args.clone() {
DocsSlashCommandArgs::NoProvider => bail!("no docs provider specified"),
DocsSlashCommandArgs::SearchPackageDocs {
provider, package, ..
@@ -219,6 +270,12 @@ impl SlashCommand for DocsSlashCommand {
};
let store = store?;
if let Some(package) = args.package() {
Self::run_just_in_time_indexing(store.clone(), key.clone(), package, executor)
.await;
}
let (text, ranges) = if let Some((prefix, _)) = key.split_once('*') {
let docs = store.load_many_by_prefix(prefix.to_string()).await?;
@@ -269,7 +326,7 @@ fn is_item_path_delimiter(char: char) -> bool {
!char.is_alphanumeric() && char != '-' && char != '_'
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub(crate) enum DocsSlashCommandArgs {
NoProvider,
SearchPackageDocs {

View File

@@ -10,7 +10,7 @@ use assistant_slash_command::{
use futures::AsyncReadExt;
use gpui::{AppContext, Task, WeakView};
use html_to_markdown::{convert_html_to_markdown, markdown, TagHandler};
use http::{AsyncBody, HttpClient, HttpClientWithUrl};
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
use language::LspAdapterDelegate;
use ui::prelude::*;
use workspace::Workspace;

View File

@@ -188,7 +188,7 @@ fn collect_files(
let project_handle = project.downgrade();
let snapshots = project
.read(cx)
.worktrees()
.worktrees(cx)
.map(|worktree| worktree.read(cx).snapshot())
.collect::<Vec<_>>();
cx.spawn(|mut cx| async move {

View File

@@ -75,7 +75,7 @@ impl ProjectSlashCommand {
}
fn path_to_cargo_toml(project: Model<Project>, cx: &mut AppContext) -> Option<Arc<Path>> {
let worktree = project.read(cx).worktrees().next()?;
let worktree = project.read(cx).worktrees(cx).next()?;
let worktree = worktree.read(cx);
let entry = worktree.entry_for_path("Cargo.toml")?;
let path = ProjectPath {

View File

@@ -1,7 +1,6 @@
use crate::{
assistant_settings::AssistantSettings, humanize_token_count,
prompts::generate_terminal_assistant_prompt, AssistantPanel, AssistantPanelEvent,
CompletionProvider, LanguageModelRequest, LanguageModelRequestMessage, Role,
humanize_token_count, prompts::generate_terminal_assistant_prompt, AssistantPanel,
AssistantPanelEvent, LanguageModelCompletionProvider, ModelSelector,
};
use anyhow::{Context as _, Result};
use client::telemetry::Telemetry;
@@ -13,11 +12,12 @@ use editor::{
use fs::Fs;
use futures::{channel::mpsc, SinkExt, StreamExt};
use gpui::{
AppContext, Context, EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight, Global,
Model, ModelContext, Subscription, Task, TextStyle, UpdateGlobal, View, WeakView, WhiteSpace,
AppContext, Context, EventEmitter, FocusHandle, FocusableView, Global, Model, ModelContext,
Subscription, Task, TextStyle, UpdateGlobal, View, WeakView,
};
use language::Buffer;
use settings::{update_settings_file, Settings};
use language_model::{LanguageModelRequest, LanguageModelRequestMessage, Role};
use settings::Settings;
use std::{
cmp,
sync::Arc,
@@ -26,7 +26,7 @@ use std::{
use terminal::Terminal;
use terminal_view::TerminalView;
use theme::ThemeSettings;
use ui::{prelude::*, ContextMenu, PopoverMenu, Tooltip};
use ui::{prelude::*, IconButtonShape, Tooltip};
use util::ResultExt;
use workspace::{notifications::NotificationId, Toast, Workspace};
@@ -73,11 +73,13 @@ impl TerminalInlineAssistant {
terminal_view: &View<TerminalView>,
workspace: Option<WeakView<Workspace>>,
assistant_panel: Option<&View<AssistantPanel>>,
initial_prompt: Option<String>,
cx: &mut WindowContext,
) {
let terminal = terminal_view.read(cx).terminal().clone();
let assist_id = self.next_assist_id.post_inc();
let prompt_buffer = cx.new_model(|cx| Buffer::local("", cx));
let prompt_buffer =
cx.new_model(|cx| Buffer::local(initial_prompt.unwrap_or_default(), cx));
let prompt_buffer = cx.new_model(|cx| MultiBuffer::singleton(prompt_buffer, cx));
let codegen = cx.new_model(|_| Codegen::new(terminal, self.telemetry.clone()));
@@ -212,8 +214,6 @@ impl TerminalInlineAssistant {
) -> Result<LanguageModelRequest> {
let assist = self.assists.get(&assist_id).context("invalid assist")?;
let model = CompletionProvider::global(cx).model();
let shell = std::env::var("SHELL").ok();
let working_directory = assist
.terminal
@@ -265,7 +265,6 @@ impl TerminalInlineAssistant {
});
Ok(LanguageModelRequest {
model,
messages,
stop: Vec::new(),
temperature: 1.0,
@@ -448,22 +447,19 @@ impl EventEmitter<PromptEditorEvent> for PromptEditor {}
impl Render for PromptEditor {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let fs = self.fs.clone();
let buttons = match &self.codegen.read(cx).status {
CodegenStatus::Idle => {
vec![
IconButton::new("cancel", IconName::Close)
.icon_color(Color::Muted)
.size(ButtonSize::None)
.shape(IconButtonShape::Square)
.tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx))
.on_click(
cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
),
IconButton::new("start", IconName::Sparkle)
IconButton::new("start", IconName::SparkleAlt)
.icon_color(Color::Muted)
.size(ButtonSize::None)
.icon_size(IconSize::XSmall)
.shape(IconButtonShape::Square)
.tooltip(|cx| Tooltip::for_action("Generate", &menu::Confirm, cx))
.on_click(
cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::StartRequested)),
@@ -474,15 +470,14 @@ impl Render for PromptEditor {
vec![
IconButton::new("cancel", IconName::Close)
.icon_color(Color::Muted)
.size(ButtonSize::None)
.shape(IconButtonShape::Square)
.tooltip(|cx| Tooltip::text("Cancel Assist", cx))
.on_click(
cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
),
IconButton::new("stop", IconName::Stop)
.icon_color(Color::Error)
.size(ButtonSize::None)
.icon_size(IconSize::XSmall)
.shape(IconButtonShape::Square)
.tooltip(|cx| {
Tooltip::with_meta(
"Interrupt Generation",
@@ -500,7 +495,7 @@ impl Render for PromptEditor {
vec![
IconButton::new("cancel", IconName::Close)
.icon_color(Color::Muted)
.size(ButtonSize::None)
.shape(IconButtonShape::Square)
.tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx))
.on_click(
cx.listener(|_, _, cx| cx.emit(PromptEditorEvent::CancelRequested)),
@@ -508,8 +503,7 @@ impl Render for PromptEditor {
if self.edited_since_done {
IconButton::new("restart", IconName::RotateCw)
.icon_color(Color::Info)
.icon_size(IconSize::XSmall)
.size(ButtonSize::None)
.shape(IconButtonShape::Square)
.tooltip(|cx| {
Tooltip::with_meta(
"Restart Generation",
@@ -524,7 +518,7 @@ impl Render for PromptEditor {
} else {
IconButton::new("confirm", IconName::Play)
.icon_color(Color::Info)
.size(ButtonSize::None)
.shape(IconButtonShape::Square)
.tooltip(|cx| {
Tooltip::for_action("Execute generated command", &menu::Confirm, cx)
})
@@ -552,59 +546,27 @@ impl Render for PromptEditor {
.w_12()
.justify_center()
.gap_2()
.child(
PopoverMenu::new("model-switcher")
.menu(move |cx| {
ContextMenu::build(cx, |mut menu, cx| {
for model in CompletionProvider::global(cx).available_models(cx)
{
menu = menu.custom_entry(
{
let model = model.clone();
move |_| {
Label::new(model.display_name())
.into_any_element()
}
},
{
let fs = fs.clone();
let model = model.clone();
move |cx| {
let model = model.clone();
update_settings_file::<AssistantSettings>(
fs.clone(),
cx,
move |settings| settings.set_model(model),
);
}
},
);
}
menu
})
.into()
})
.trigger(
IconButton::new("context", IconName::Settings)
.size(ButtonSize::None)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.tooltip(move |cx| {
Tooltip::with_meta(
format!(
"Using {}",
CompletionProvider::global(cx)
.model()
.display_name()
),
None,
"Click to Change Model",
cx,
)
}),
)
.anchor(gpui::AnchorCorner::BottomRight),
)
.child(ModelSelector::new(
self.fs.clone(),
IconButton::new("context", IconName::Settings)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.tooltip(move |cx| {
Tooltip::with_meta(
format!(
"Using {}",
LanguageModelCompletionProvider::read_global(cx)
.active_model()
.map(|model| model.name().0)
.unwrap_or_else(|| "No model selected".into()),
),
None,
"Change Model",
cx,
)
}),
))
.children(
if let CodegenStatus::Error(error) = &self.codegen.read(cx).status {
let error_message = SharedString::from(error.to_string());
@@ -746,7 +708,9 @@ impl PromptEditor {
})??;
let token_count = cx
.update(|cx| CompletionProvider::global(cx).count_tokens(request, cx))?
.update(|cx| {
LanguageModelCompletionProvider::read_global(cx).count_tokens(request, cx)
})?
.await?;
this.update(&mut cx, |this, cx| {
this.token_count = Some(token_count);
@@ -876,7 +840,7 @@ impl PromptEditor {
}
fn render_token_count(&self, cx: &mut ViewContext<Self>) -> Option<impl IntoElement> {
let model = CompletionProvider::global(cx).model();
let model = LanguageModelCompletionProvider::read_global(cx).active_model()?;
let token_count = self.token_count?;
let max_token_count = model.max_token_count();
@@ -943,13 +907,9 @@ impl PromptEditor {
font_family: settings.ui_font.family.clone(),
font_features: settings.ui_font.features.clone(),
font_size: rems(0.875).into(),
font_weight: FontWeight::NORMAL,
font_style: FontStyle::Normal,
font_weight: settings.ui_font.weight,
line_height: relative(1.3),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
..Default::default()
};
EditorElement::new(
&self.editor,
@@ -1025,8 +985,12 @@ impl Codegen {
self.transaction = Some(TerminalTransaction::start(self.terminal.clone()));
let telemetry = self.telemetry.clone();
let model_telemetry_id = prompt.model.telemetry_id();
let response = CompletionProvider::global(cx).complete(prompt, cx);
let model_telemetry_id = LanguageModelCompletionProvider::read_global(cx)
.active_model()
.map(|m| m.telemetry_id())
.unwrap_or_default();
let response =
LanguageModelCompletionProvider::read_global(cx).stream_completion(prompt, cx);
self.generation = cx.spawn(|this, mut cx| async move {
let response = response.await;
@@ -1037,8 +1001,8 @@ impl Codegen {
let mut response_latency = None;
let request_start = Instant::now();
let task = async {
let mut response = response.inner.await?;
while let Some(chunk) = response.next().await {
let mut chunks = response?;
while let Some(chunk) = chunks.next().await {
if response_latency.is_none() {
response_latency = Some(request_start.elapsed());
}

View File

@@ -222,7 +222,7 @@ mod tests {
let worktree_ids = project.read_with(cx, |project, cx| {
project
.worktrees()
.worktrees(cx)
.map(|worktree| worktree.read(cx).id())
.collect::<Vec<_>>()
});

View File

@@ -18,11 +18,12 @@ client.workspace = true
db.workspace = true
editor.workspace = true
gpui.workspace = true
http.workspace = true
http_client.workspace = true
isahc.workspace = true
log.workspace = true
markdown_preview.workspace = true
menu.workspace = true
paths.workspace = true
release_channel.workspace = true
schemars.workspace = true
serde.workspace = true

View File

@@ -20,7 +20,7 @@ use smol::{fs, io::AsyncReadExt};
use settings::{Settings, SettingsSources, SettingsStore};
use smol::{fs::File, process::Command};
use http::{HttpClient, HttpClientWithUrl};
use http_client::{HttpClient, HttpClientWithUrl};
use release_channel::{AppCommitSha, AppVersion, ReleaseChannel};
use std::{
env::{
@@ -28,7 +28,7 @@ use std::{
consts::{ARCH, OS},
},
ffi::OsString,
path::PathBuf,
path::{Path, PathBuf},
sync::Arc,
time::Duration,
};
@@ -288,7 +288,12 @@ fn view_release_notes_locally(workspace: &mut Workspace, cx: &mut ViewContext<Wo
Some(tab_description),
cx,
);
workspace.add_item_to_active_pane(Box::new(view.clone()), None, cx);
workspace.add_item_to_active_pane(
Box::new(view.clone()),
None,
true,
cx,
);
cx.notify();
})
.log_err();
@@ -354,7 +359,6 @@ impl AutoUpdater {
return;
}
self.status = AutoUpdateStatus::Checking;
cx.notify();
self.pending_poll = Some(cx.spawn(|this, mut cx| async move {
@@ -380,29 +384,65 @@ impl AutoUpdater {
cx.notify();
}
async fn update(this: Model<Self>, mut cx: AsyncAppContext) -> Result<()> {
let (client, current_version) = this.read_with(&cx, |this, _| {
(this.http_client.clone(), this.current_version)
})?;
pub async fn get_latest_remote_server_release(
os: &str,
arch: &str,
mut release_channel: ReleaseChannel,
cx: &mut AsyncAppContext,
) -> Result<PathBuf> {
let this = cx.update(|cx| {
cx.default_global::<GlobalAutoUpdate>()
.0
.clone()
.ok_or_else(|| anyhow!("auto-update not initialized"))
})??;
let asset = match OS {
"linux" => format!("zed-linux-{}.tar.gz", ARCH),
"macos" => "Zed.dmg".into(),
_ => return Err(anyhow!("auto-update not supported for OS {:?}", OS)),
};
if release_channel == ReleaseChannel::Dev {
release_channel = ReleaseChannel::Nightly;
}
let release = Self::get_latest_release(
&this,
"zed-remote-server",
os,
arch,
Some(release_channel),
cx,
)
.await?;
let servers_dir = paths::remote_servers_dir();
let channel_dir = servers_dir.join(release_channel.dev_name());
let platform_dir = channel_dir.join(format!("{}-{}", os, arch));
let version_path = platform_dir.join(format!("{}.gz", release.version));
smol::fs::create_dir_all(&platform_dir).await.ok();
let client = this.read_with(cx, |this, _| this.http_client.clone())?;
if smol::fs::metadata(&version_path).await.is_err() {
log::info!("downloading zed-remote-server {os} {arch}");
download_remote_server_binary(&version_path, release, client, cx).await?;
}
Ok(version_path)
}
async fn get_latest_release(
this: &Model<Self>,
asset: &str,
os: &str,
arch: &str,
release_channel: Option<ReleaseChannel>,
cx: &mut AsyncAppContext,
) -> Result<JsonRelease> {
let client = this.read_with(cx, |this, _| this.http_client.clone())?;
let mut url_string = client.build_url(&format!(
"/api/releases/latest?asset={}&os={}&arch={}",
asset, OS, ARCH
asset, os, arch
));
cx.update(|cx| {
if let Some(param) = ReleaseChannel::try_global(cx)
.and_then(|release_channel| release_channel.release_query_param())
{
url_string += "&";
url_string += param;
}
})?;
if let Some(param) = release_channel.and_then(|c| c.release_query_param()) {
url_string += "&";
url_string += param;
}
let mut response = client.get(&url_string, Default::default(), true).await?;
@@ -413,8 +453,34 @@ impl AutoUpdater {
.await
.context("error reading release")?;
let release: JsonRelease =
serde_json::from_slice(body.as_slice()).context("error deserializing release")?;
if !response.status().is_success() {
Err(anyhow!(
"failed to fetch release: {:?}",
String::from_utf8_lossy(&body),
))?;
}
serde_json::from_slice(body.as_slice()).with_context(|| {
format!(
"error deserializing release {:?}",
String::from_utf8_lossy(&body),
)
})
}
async fn update(this: Model<Self>, mut cx: AsyncAppContext) -> Result<()> {
let (client, current_version, release_channel) = this.update(&mut cx, |this, cx| {
this.status = AutoUpdateStatus::Checking;
cx.notify();
(
this.http_client.clone(),
this.current_version,
ReleaseChannel::try_global(cx),
)
})?;
let release =
Self::get_latest_release(&this, "zed", OS, ARCH, release_channel, &mut cx).await?;
let should_download = match *RELEASE_CHANNEL {
ReleaseChannel::Nightly => cx
@@ -441,7 +507,14 @@ impl AutoUpdater {
let temp_dir = tempfile::Builder::new()
.prefix("zed-auto-update")
.tempdir()?;
let downloaded_asset = download_release(&temp_dir, release, &asset, client, &cx).await?;
let filename = match OS {
"macos" => Ok("Zed.dmg"),
"linux" => Ok("zed.tar.gz"),
_ => Err(anyhow!("not supported: {:?}", OS)),
}?;
let downloaded_asset = temp_dir.path().join(filename);
download_release(&downloaded_asset, release, client, &cx).await?;
this.update(&mut cx, |this, cx| {
this.status = AutoUpdateStatus::Installing;
@@ -495,14 +568,38 @@ impl AutoUpdater {
}
}
async fn download_release(
temp_dir: &tempfile::TempDir,
async fn download_remote_server_binary(
target_path: &PathBuf,
release: JsonRelease,
target_filename: &str,
client: Arc<HttpClientWithUrl>,
cx: &AsyncAppContext,
) -> Result<PathBuf> {
let target_path = temp_dir.path().join(target_filename);
) -> Result<()> {
let mut target_file = File::create(&target_path).await?;
let (installation_id, release_channel, telemetry) = cx.update(|cx| {
let installation_id = Client::global(cx).telemetry().installation_id();
let release_channel =
ReleaseChannel::try_global(cx).map(|release_channel| release_channel.display_name());
let telemetry = TelemetrySettings::get_global(cx).metrics;
(installation_id, release_channel, telemetry)
})?;
let request_body = AsyncBody::from(serde_json::to_string(&UpdateRequestBody {
installation_id,
release_channel,
telemetry,
})?);
let mut response = client.get(&release.url, request_body, true).await?;
smol::io::copy(response.body_mut(), &mut target_file).await?;
Ok(())
}
async fn download_release(
target_path: &Path,
release: JsonRelease,
client: Arc<HttpClientWithUrl>,
cx: &AsyncAppContext,
) -> Result<()> {
let mut target_file = File::create(&target_path).await?;
let (installation_id, release_channel, telemetry) = cx.update(|cx| {
@@ -524,7 +621,7 @@ async fn download_release(
smol::io::copy(response.body_mut(), &mut target_file).await?;
log::info!("downloaded update. path:{:?}", target_path);
Ok(target_path)
Ok(())
}
async fn install_release_linux(

View File

@@ -51,4 +51,4 @@ language = { workspace = true, features = ["test-support"] }
live_kit_client = { workspace = true, features = ["test-support"] }
project = { workspace = true, features = ["test-support"] }
util = { workspace = true, features = ["test-support"] }
http = { workspace = true, features = ["test-support"] }
http_client = { workspace = true, features = ["test-support"] }

View File

@@ -526,7 +526,7 @@ impl Room {
rejoined_projects.push(proto::RejoinProject {
id: project_id,
worktrees: project
.worktrees()
.worktrees(cx)
.map(|worktree| {
let worktree = worktree.read(cx);
proto::RejoinWorktree {

View File

@@ -40,4 +40,4 @@ rpc = { workspace = true, features = ["test-support"] }
client = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
util = { workspace = true, features = ["test-support"] }
http = { workspace = true, features = ["test-support"] }
http_client = { workspace = true, features = ["test-support"] }

View File

@@ -4,7 +4,7 @@ use super::*;
use client::{test::FakeServer, Client, UserStore};
use clock::FakeSystemClock;
use gpui::{AppContext, Context, Model, SemanticVersion, TestAppContext};
use http::FakeHttpClient;
use http_client::FakeHttpClient;
use rpc::proto::{self};
use settings::SettingsStore;

View File

@@ -129,6 +129,7 @@ fn main() -> Result<()> {
|| path.starts_with("http://")
|| path.starts_with("https://")
|| path.starts_with("file://")
|| path.starts_with("ssh://")
{
urls.push(path.to_string());
} else {

View File

@@ -18,7 +18,7 @@ test-support = ["clock/test-support", "collections/test-support", "gpui/test-sup
[dependencies]
anyhow.workspace = true
async-recursion = "0.3"
async-tungstenite = { version = "0.16", features = ["async-std", "async-native-tls"] }
async-tungstenite = { workspace = true, features = ["async-std", "async-native-tls"] }
chrono = { workspace = true, features = ["serde"] }
clock.workspace = true
collections.workspace = true
@@ -26,7 +26,7 @@ feature_flags.workspace = true
fs.workspace = true
futures.workspace = true
gpui.workspace = true
http.workspace = true
http_client.workspace = true
lazy_static.workspace = true
log.workspace = true
once_cell.workspace = true
@@ -60,12 +60,11 @@ gpui = { workspace = true, features = ["test-support"] }
rpc = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
util = { workspace = true, features = ["test-support"] }
http = { workspace = true, features = ["test-support"] }
http_client = { workspace = true, features = ["test-support"] }
[target.'cfg(target_os = "windows")'.dependencies]
windows.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
cocoa.workspace = true
isahc = { workspace = true, features = ["static-curl"] }
async-native-tls = { version = "0.5.0", features = ["vendored"] }

View File

@@ -20,7 +20,7 @@ use futures::{
use gpui::{
actions, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Global, Model, Task, WeakModel,
};
use http::{HttpClient, HttpClientWithUrl};
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
use lazy_static::lazy_static;
use parking_lot::RwLock;
use postage::watch;
@@ -233,7 +233,7 @@ pub enum EstablishConnectionError {
#[error("{0}")]
Other(#[from] anyhow::Error),
#[error("{0}")]
Http(#[from] http::Error),
Http(#[from] http_client::Error),
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
@@ -1351,7 +1351,7 @@ impl Client {
let mut url = self.rpc_url(http.clone(), None).await?;
url.set_path("/user");
url.set_query(Some(&format!("github_login={login}")));
let request = Request::get(url.as_str())
let request: http_client::Request<AsyncBody> = Request::get(url.as_str())
.header("Authorization", format!("token {api_token}"))
.body("".into())?;
@@ -1410,7 +1410,7 @@ impl Client {
self.peer.send(self.connection_id()?, message)
}
fn send_dynamic(&self, envelope: proto::Envelope) -> Result<()> {
pub fn send_dynamic(&self, envelope: proto::Envelope) -> Result<()> {
let connection_id = self.connection_id()?;
self.peer.send_dynamic(connection_id, envelope)
}
@@ -1783,7 +1783,7 @@ mod tests {
use clock::FakeSystemClock;
use gpui::{BackgroundExecutor, Context, TestAppContext};
use http::FakeHttpClient;
use http_client::FakeHttpClient;
use parking_lot::Mutex;
use proto::TypedEnvelope;
use settings::SettingsStore;

View File

@@ -6,7 +6,7 @@ use clock::SystemClock;
use collections::{HashMap, HashSet};
use futures::Future;
use gpui::{AppContext, BackgroundExecutor, Task};
use http::{self, HttpClient, HttpClientWithUrl, Method};
use http_client::{self, HttpClient, HttpClientWithUrl, Method};
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use release_channel::ReleaseChannel;
@@ -632,7 +632,7 @@ impl Telemetry {
let checksum = calculate_json_checksum(&json_bytes).unwrap_or("".to_string());
let request = http::Request::builder()
let request = http_client::Request::builder()
.method(Method::POST)
.uri(
this.http_client
@@ -661,7 +661,7 @@ mod tests {
use chrono::TimeZone;
use clock::FakeSystemClock;
use gpui::TestAppContext;
use http::FakeHttpClient;
use http_client::FakeHttpClient;
#[gpui::test]
fn test_telemetry_flush_on_max_queue_size(cx: &mut TestAppContext) {

View File

@@ -20,7 +20,7 @@ test-support = ["sqlite"]
[dependencies]
anthropic.workspace = true
anyhow.workspace = true
async-tungstenite = "0.16"
async-tungstenite.workspace = true
aws-config = { version = "1.1.5" }
aws-sdk-s3 = { version = "1.15.0" }
axum = { version = "0.6", features = ["json", "headers", "ws"] }
@@ -35,7 +35,7 @@ envy = "0.4.2"
futures.workspace = true
google_ai.workspace = true
hex.workspace = true
http.workspace = true
http_client.workspace = true
live_kit_server.workspace = true
log.workspace = true
nanoid.workspace = true
@@ -79,6 +79,7 @@ channel.workspace = true
client = { workspace = true, features = ["test-support"] }
collab_ui = { workspace = true, features = ["test-support"] }
collections = { workspace = true, features = ["test-support"] }
completion = { workspace = true, features = ["test-support"] }
ctor.workspace = true
editor = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
@@ -89,6 +90,7 @@ git_hosting_providers.workspace = true
gpui = { workspace = true, features = ["test-support"] }
indoc.workspace = true
language = { workspace = true, features = ["test-support"] }
language_model = { workspace = true, features = ["test-support"] }
live_kit_client = { workspace = true, features = ["test-support"] }
lsp = { workspace = true, features = ["test-support"] }
menu.workspace = true
@@ -99,10 +101,13 @@ pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
recent_projects = { workspace = true }
release_channel.workspace = true
remote = { workspace = true, features = ["test-support"] }
remote_server.workspace = true
dev_server_projects.workspace = true
rpc = { workspace = true, features = ["test-support"] }
sea-orm = { version = "0.12.x", features = ["sqlx-sqlite"] }
serde_json.workspace = true
session = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
sqlx = { version = "0.7", features = ["sqlite"] }
theme.workspace = true

View File

@@ -164,10 +164,21 @@ pub fn hash_access_token(token: &str) -> String {
/// Encrypts the given access token with the given public key to avoid leaking it on the way
/// to the client.
pub fn encrypt_access_token(access_token: &str, public_key: String) -> Result<String> {
use rpc::auth::EncryptionFormat;
/// The encryption format to use for the access token.
///
/// Currently we're using the original encryption format to avoid
/// breaking compatibility with older clients.
///
/// Once enough clients are capable of decrypting the newer encryption
/// format we can start encrypting with `EncryptionFormat::V1`.
const ENCRYPTION_FORMAT: EncryptionFormat = EncryptionFormat::V0;
let native_app_public_key =
rpc::auth::PublicKey::try_from(public_key).context("failed to parse app public key")?;
let encrypted_access_token = native_app_public_key
.encrypt_string(access_token)
.encrypt_string(access_token, ENCRYPTION_FORMAT)
.context("failed to encrypt access token with public key")?;
Ok(encrypted_access_token)
}

View File

@@ -42,7 +42,7 @@ use futures::{
stream::FuturesUnordered,
FutureExt, SinkExt, StreamExt, TryStreamExt,
};
use http::IsahcHttpClient;
use http_client::IsahcHttpClient;
use prometheus::{register_int_gauge, IntGauge};
use rpc::{
proto::{
@@ -4514,7 +4514,7 @@ impl RateLimit for CompleteWithLanguageModelRateLimit {
}
async fn complete_with_language_model(
request: proto::CompleteWithLanguageModel,
mut request: proto::CompleteWithLanguageModel,
response: StreamingResponse<proto::CompleteWithLanguageModel>,
session: Session,
open_ai_api_key: Option<Arc<str>>,
@@ -4530,18 +4530,43 @@ async fn complete_with_language_model(
.check::<CompleteWithLanguageModelRateLimit>(session.user_id())
.await?;
if request.model.starts_with("gpt") {
let api_key =
open_ai_api_key.ok_or_else(|| anyhow!("no OpenAI API key configured on the server"))?;
complete_with_open_ai(request, response, session, api_key).await?;
} else if request.model.starts_with("gemini") {
let api_key = google_ai_api_key
.ok_or_else(|| anyhow!("no Google AI API key configured on the server"))?;
complete_with_google_ai(request, response, session, api_key).await?;
} else if request.model.starts_with("claude") {
let api_key = anthropic_api_key
.ok_or_else(|| anyhow!("no Anthropic AI API key configured on the server"))?;
complete_with_anthropic(request, response, session, api_key).await?;
let mut provider_and_model = request.model.split('/');
let (provider, model) = match (
provider_and_model.next().unwrap(),
provider_and_model.next(),
) {
(provider, Some(model)) => (provider, model),
(model, None) => {
if model.starts_with("gpt") {
("openai", model)
} else if model.starts_with("gemini") {
("google", model)
} else if model.starts_with("claude") {
("anthropic", model)
} else {
("unknown", model)
}
}
};
let provider = provider.to_string();
request.model = model.to_string();
match provider.as_str() {
"openai" => {
let api_key = open_ai_api_key.context("no OpenAI API key configured on the server")?;
complete_with_open_ai(request, response, session, api_key).await?;
}
"anthropic" => {
let api_key =
anthropic_api_key.context("no Anthropic AI API key configured on the server")?;
complete_with_anthropic(request, response, session, api_key).await?;
}
"google" => {
let api_key =
google_ai_api_key.context("no Google AI API key configured on the server")?;
complete_with_google_ai(request, response, session, api_key).await?;
}
provider => return Err(anyhow!("unknown provider {:?}", provider))?,
}
Ok(())

View File

@@ -16,6 +16,7 @@ mod notification_tests;
mod random_channel_buffer_tests;
mod random_project_collaboration_tests;
mod randomized_test_helpers;
mod remote_editing_collaboration_tests;
mod test_server;
use language::{tree_sitter_rust, Language, LanguageConfig, LanguageMatcher};

View File

@@ -52,7 +52,7 @@ async fn test_channel_guests(
assert!(project_b.read_with(cx_b, |project, _| project.is_read_only()));
assert!(project_b
.update(cx_b, |project, cx| {
let worktree_id = project.worktrees().next().unwrap().read(cx).id();
let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
project.create_entry((worktree_id, "b.txt"), false, cx)
})
.await

View File

@@ -76,7 +76,7 @@ async fn test_host_disconnect(
let active_call_a = cx_a.read(ActiveCall::global);
let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
let worktree_a = project_a.read_with(cx_a, |project, _| project.worktrees().next().unwrap());
let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
let project_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
.await
@@ -1144,7 +1144,7 @@ async fn test_share_project(
});
project_b.read_with(cx_b, |project, cx| {
let worktree = project.worktrees().next().unwrap().read(cx);
let worktree = project.worktrees(cx).next().unwrap().read(cx);
assert_eq!(
worktree.paths().map(AsRef::as_ref).collect::<Vec<_>>(),
[
@@ -1158,7 +1158,7 @@ async fn test_share_project(
project_b
.update(cx_b, |project, cx| {
let worktree = project.worktrees().next().unwrap();
let worktree = project.worktrees(cx).next().unwrap();
let entry = worktree.read(cx).entry_for_path("ignored-dir").unwrap();
project.expand_entry(worktree_id, entry.id, cx).unwrap()
})
@@ -1166,7 +1166,7 @@ async fn test_share_project(
.unwrap();
project_b.read_with(cx_b, |project, cx| {
let worktree = project.worktrees().next().unwrap().read(cx);
let worktree = project.worktrees(cx).next().unwrap().read(cx);
assert_eq!(
worktree.paths().map(AsRef::as_ref).collect::<Vec<_>>(),
[

View File

@@ -266,7 +266,7 @@ async fn test_basic_following(
// When client A activates a different editor, client B does so as well.
workspace_a.update(cx_a, |workspace, cx| {
workspace.activate_item(&editor_a1, cx)
workspace.activate_item(&editor_a1, true, true, cx)
});
executor.run_until_parked();
workspace_b.update(cx_b, |workspace, cx| {
@@ -311,7 +311,7 @@ async fn test_basic_following(
let editor = cx.new_view(|cx| {
Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), true, cx)
});
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, cx);
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
editor
});
executor.run_until_parked();
@@ -401,7 +401,7 @@ async fn test_basic_following(
workspace.unfollow(peer_id_a, cx).unwrap()
});
workspace_a.update(cx_a, |workspace, cx| {
workspace.activate_item(&editor_a2, cx)
workspace.activate_item(&editor_a2, true, true, cx)
});
executor.run_until_parked();
assert_eq!(
@@ -466,7 +466,7 @@ async fn test_basic_following(
// Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
workspace_b.update(cx_b, |workspace, cx| {
workspace.activate_item(&multibuffer_editor_b, cx)
workspace.activate_item(&multibuffer_editor_b, true, true, cx)
});
executor.run_until_parked();
workspace_a.update(cx_a, |workspace, cx| {

View File

@@ -18,7 +18,9 @@ use gpui::{
TestAppContext, UpdateGlobal,
};
use language::{
language_settings::{AllLanguageSettings, Formatter, PrettierSettings},
language_settings::{
AllLanguageSettings, Formatter, FormatterList, PrettierSettings, SelectedFormatter,
},
tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, LanguageConfig,
LanguageMatcher, LineEnding, OffsetRangeExt, Point, Rope,
};
@@ -1375,7 +1377,7 @@ async fn test_unshare_project(
.await
.unwrap();
let worktree_a = project_a.read_with(cx_a, |project, _| project.worktrees().next().unwrap());
let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
executor.run_until_parked();
@@ -1503,7 +1505,8 @@ async fn test_project_reconnect(
let (project_a1, _) = client_a.build_local_project("/root-1/dir1", cx_a).await;
let (project_a2, _) = client_a.build_local_project("/root-2", cx_a).await;
let (project_a3, _) = client_a.build_local_project("/root-3", cx_a).await;
let worktree_a1 = project_a1.read_with(cx_a, |project, _| project.worktrees().next().unwrap());
let worktree_a1 =
project_a1.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
let project1_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a1.clone(), cx))
.await
@@ -2306,7 +2309,7 @@ async fn test_propagate_saves_and_fs_changes(
.await;
let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
let worktree_a = project_a.read_with(cx_a, |p, _| p.worktrees().next().unwrap());
let worktree_a = project_a.read_with(cx_a, |p, cx| p.worktrees(cx).next().unwrap());
let project_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
.await
@@ -2316,9 +2319,9 @@ async fn test_propagate_saves_and_fs_changes(
let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
let project_c = client_c.build_dev_server_project(project_id, cx_c).await;
let worktree_b = project_b.read_with(cx_b, |p, _| p.worktrees().next().unwrap());
let worktree_b = project_b.read_with(cx_b, |p, cx| p.worktrees(cx).next().unwrap());
let worktree_c = project_c.read_with(cx_c, |p, _| p.worktrees().next().unwrap());
let worktree_c = project_c.read_with(cx_c, |p, cx| p.worktrees(cx).next().unwrap());
// Open and edit a buffer as both guests B and C.
let buffer_b = project_b
@@ -3020,8 +3023,8 @@ async fn test_fs_operations(
.unwrap();
let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
let worktree_a = project_a.read_with(cx_a, |project, _| project.worktrees().next().unwrap());
let worktree_b = project_b.read_with(cx_b, |project, _| project.worktrees().next().unwrap());
let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
let entry = project_b
.update(cx_b, |project, cx| {
@@ -3321,7 +3324,7 @@ async fn test_local_settings(
// As client B, join that project and observe the local settings.
let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
let worktree_b = project_b.read_with(cx_b, |project, _| project.worktrees().next().unwrap());
let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
executor.run_until_parked();
cx_b.read(|cx| {
let store = cx.global::<SettingsStore>();
@@ -3733,7 +3736,7 @@ async fn test_leaving_project(
// Client B opens a buffer.
let buffer_b1 = project_b1
.update(cx_b, |project, cx| {
let worktree_id = project.worktrees().next().unwrap().read(cx).id();
let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
project.open_buffer((worktree_id, "a.txt"), cx)
})
.await
@@ -3771,7 +3774,7 @@ async fn test_leaving_project(
let buffer_b2 = project_b2
.update(cx_b, |project, cx| {
let worktree_id = project.worktrees().next().unwrap().read(cx).id();
let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
project.open_buffer((worktree_id, "a.txt"), cx)
})
.await
@@ -4409,10 +4412,13 @@ async fn test_formatting_buffer(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
file.defaults.formatter = Some(Formatter::External {
command: "awk".into(),
arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
});
file.defaults.formatter = Some(SelectedFormatter::List(FormatterList(
vec![Formatter::External {
command: "awk".into(),
arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
}]
.into(),
)));
});
});
});
@@ -4493,7 +4499,7 @@ async fn test_prettier_formatting_buffer(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
file.defaults.formatter = Some(Formatter::Auto);
file.defaults.formatter = Some(SelectedFormatter::Auto);
file.defaults.prettier = Some(PrettierSettings {
allowed: true,
..PrettierSettings::default()
@@ -4504,7 +4510,9 @@ async fn test_prettier_formatting_buffer(
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
file.defaults.formatter = Some(Formatter::LanguageServer);
file.defaults.formatter = Some(SelectedFormatter::List(FormatterList(
vec![Formatter::LanguageServer { name: None }].into(),
)));
file.defaults.prettier = Some(PrettierSettings {
allowed: true,
..PrettierSettings::default()
@@ -4620,7 +4628,7 @@ async fn test_definition(
.unwrap();
cx_b.read(|cx| {
assert_eq!(definitions_1.len(), 1);
assert_eq!(project_b.read(cx).worktrees().count(), 2);
assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
let target_buffer = definitions_1[0].target.buffer.read(cx);
assert_eq!(
target_buffer.text(),
@@ -4649,7 +4657,7 @@ async fn test_definition(
.unwrap();
cx_b.read(|cx| {
assert_eq!(definitions_2.len(), 1);
assert_eq!(project_b.read(cx).worktrees().count(), 2);
assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
let target_buffer = definitions_2[0].target.buffer.read(cx);
assert_eq!(
target_buffer.text(),
@@ -4807,7 +4815,7 @@ async fn test_references(
assert!(status.pending_work.is_empty());
assert_eq!(references.len(), 3);
assert_eq!(project.worktrees().count(), 2);
assert_eq!(project.worktrees(cx).count(), 2);
let two_buffer = references[0].buffer.read(cx);
let three_buffer = references[2].buffer.read(cx);
@@ -6192,7 +6200,7 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
let project = workspace.update(cx, |workspace, _| workspace.project().clone());
let worktree_id = project.update(cx, |project, cx| {
project.worktrees().next().unwrap().read(cx).id()
project.worktrees(cx).next().unwrap().read(cx).id()
});
let path_1 = ProjectPath {

View File

@@ -301,7 +301,7 @@ impl RandomizedTest for ProjectCollaborationTest {
let is_local = project.read_with(cx, |project, _| project.is_local());
let worktree = project.read_with(cx, |project, cx| {
project
.worktrees()
.worktrees(cx)
.filter(|worktree| {
let worktree = worktree.read(cx);
worktree.is_visible()
@@ -423,7 +423,7 @@ impl RandomizedTest for ProjectCollaborationTest {
81.. => {
let worktree = project.read_with(cx, |project, cx| {
project
.worktrees()
.worktrees(cx)
.filter(|worktree| {
let worktree = worktree.read(cx);
worktree.is_visible()
@@ -1172,7 +1172,7 @@ impl RandomizedTest for ProjectCollaborationTest {
let host_worktree_snapshots =
host_project.read_with(host_cx, |host_project, cx| {
host_project
.worktrees()
.worktrees(cx)
.map(|worktree| {
let worktree = worktree.read(cx);
(worktree.id(), worktree.snapshot())
@@ -1180,7 +1180,7 @@ impl RandomizedTest for ProjectCollaborationTest {
.collect::<BTreeMap<_, _>>()
});
let guest_worktree_snapshots = guest_project
.worktrees()
.worktrees(cx)
.map(|worktree| {
let worktree = worktree.read(cx);
(worktree.id(), worktree.snapshot())
@@ -1538,7 +1538,7 @@ fn project_path_for_full_path(
let root_name = components.next().unwrap().as_os_str().to_str().unwrap();
let path = components.as_path().into();
let worktree_id = project.read_with(cx, |project, cx| {
project.worktrees().find_map(|worktree| {
project.worktrees(cx).find_map(|worktree| {
let worktree = worktree.read(cx);
if worktree.root_name() == root_name {
Some(worktree.id())

View File

@@ -0,0 +1,102 @@
use crate::tests::TestServer;
use call::ActiveCall;
use fs::{FakeFs, Fs as _};
use gpui::{Context as _, TestAppContext};
use remote::SshSession;
use remote_server::HeadlessProject;
use serde_json::json;
use std::{path::Path, sync::Arc};
#[gpui::test]
async fn test_sharing_an_ssh_remote_project(
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
server_cx: &mut TestAppContext,
) {
let executor = cx_a.executor();
let mut server = TestServer::start(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;
server
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
.await;
// Set up project on remote FS
let (client_ssh, server_ssh) = SshSession::fake(cx_a, server_cx);
let remote_fs = FakeFs::new(server_cx.executor());
remote_fs
.insert_tree(
"/code",
json!({
"project1": {
"README.md": "# project 1",
"src": {
"lib.rs": "fn one() -> usize { 1 }"
}
},
"project2": {
"README.md": "# project 2",
},
}),
)
.await;
// User A connects to the remote project via SSH.
server_cx.update(HeadlessProject::init);
let _headless_project =
server_cx.new_model(|cx| HeadlessProject::new(server_ssh, remote_fs.clone(), cx));
let (project_a, worktree_id) = client_a
.build_ssh_project("/code/project1", client_ssh, cx_a)
.await;
// User A shares the remote project.
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();
// User B joins the project.
let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
let worktree_b = project_b
.update(cx_b, |project, cx| project.worktree_for_id(worktree_id, cx))
.unwrap();
executor.run_until_parked();
worktree_b.update(cx_b, |worktree, _cx| {
assert_eq!(
worktree.paths().map(Arc::as_ref).collect::<Vec<_>>(),
vec![
Path::new("README.md"),
Path::new("src"),
Path::new("src/lib.rs"),
]
);
});
// User B can open buffers in the remote project.
let buffer_b = project_b
.update(cx_b, |project, cx| {
project.open_buffer((worktree_id, "src/lib.rs"), cx)
})
.await
.unwrap();
buffer_b.update(cx_b, |buffer, cx| {
assert_eq!(buffer.text(), "fn one() -> usize { 1 }");
let ix = buffer.text().find('1').unwrap();
buffer.edit([(ix..ix + 1, "100")], None, cx);
});
project_b
.update(cx_b, |project, cx| project.save_buffer(buffer_b, cx))
.await
.unwrap();
assert_eq!(
remote_fs
.load("/code/project1/src/lib.rs".as_ref())
.await
.unwrap(),
"fn one() -> usize { 100 }"
);
}

View File

@@ -19,18 +19,20 @@ use fs::FakeFs;
use futures::{channel::oneshot, StreamExt as _};
use git::GitHostingProviderRegistry;
use gpui::{BackgroundExecutor, Context, Model, Task, TestAppContext, View, VisualTestContext};
use http::FakeHttpClient;
use http_client::FakeHttpClient;
use language::LanguageRegistry;
use node_runtime::FakeNodeRuntime;
use notifications::NotificationStore;
use parking_lot::Mutex;
use project::{Project, WorktreeId};
use remote::SshSession;
use rpc::{
proto::{self, ChannelRole},
RECEIVE_TIMEOUT,
};
use semantic_version::SemanticVersion;
use serde_json::json;
use session::Session;
use settings::SettingsStore;
use std::{
cell::{Ref, RefCell, RefMut},
@@ -155,6 +157,8 @@ impl TestServer {
}
pub async fn create_client(&mut self, cx: &mut TestAppContext, name: &str) -> TestClient {
let fs = FakeFs::new(cx.executor());
cx.update(|cx| {
if cx.has_global::<SettingsStore>() {
panic!("Same cx used to create two test clients")
@@ -263,7 +267,6 @@ impl TestServer {
git_hosting_provider_registry
.register_hosting_provider(Arc::new(git_hosting_providers::Github));
let fs = FakeFs::new(cx.executor());
let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx));
let language_registry = Arc::new(LanguageRegistry::test(cx.executor()));
@@ -275,6 +278,7 @@ impl TestServer {
fs: fs.clone(),
build_window_options: |_, _| Default::default(),
node_runtime: FakeNodeRuntime::new(),
session: Session::test(),
});
let os_keymap = "keymaps/default-macos.json";
@@ -294,7 +298,8 @@ impl TestServer {
menu::init();
dev_server_projects::init(client.clone(), cx);
settings::KeymapFile::load_asset(os_keymap, cx).unwrap();
assistant::FakeCompletionProvider::setup_test(cx);
language_model::LanguageModelRegistry::test(cx);
completion::init(cx);
assistant::context_store::init(&client);
});
@@ -402,6 +407,7 @@ impl TestServer {
fs: fs.clone(),
build_window_options: |_, _| Default::default(),
node_runtime: FakeNodeRuntime::new(),
session: Session::test(),
});
cx.update(|cx| {
@@ -814,6 +820,30 @@ impl TestClient {
(project, worktree.read_with(cx, |tree, _| tree.id()))
}
pub async fn build_ssh_project(
&self,
root_path: impl AsRef<Path>,
ssh: Arc<SshSession>,
cx: &mut TestAppContext,
) -> (Model<Project>, WorktreeId) {
let project = cx.update(|cx| {
Project::ssh(
ssh,
self.client().clone(),
self.app_state.node_runtime.clone(),
self.app_state.user_store.clone(),
self.app_state.languages.clone(),
self.app_state.fs.clone(),
cx,
)
});
let (worktree, _) = project
.update(cx, |p, cx| p.find_or_create_worktree(root_path, true, cx))
.await
.unwrap();
(project, worktree.read_with(cx, |tree, _| tree.id()))
}
pub async fn build_test_project(&self, cx: &mut TestAppContext) -> Model<Project> {
self.fs()
.insert_tree(

View File

@@ -25,7 +25,7 @@ test-support = [
"settings/test-support",
"util/test-support",
"workspace/test-support",
"http/test-support",
"http_client/test-support",
]
[dependencies]
@@ -78,7 +78,7 @@ pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
rpc = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
tree-sitter-markdown.workspace = true
tree-sitter-md.workspace = true
util = { workspace = true, features = ["test-support"] }
http = { workspace = true, features = ["test-support"] }
http_client = { workspace = true, features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }

View File

@@ -16,12 +16,14 @@ use gpui::{
WindowContext,
};
use project::Project;
use rpc::proto::ChannelVisibility;
use std::{
any::{Any, TypeId},
sync::Arc,
};
use ui::prelude::*;
use util::ResultExt;
use workspace::item::TabContentParams;
use workspace::{item::Dedup, notifications::NotificationId};
use workspace::{
item::{FollowableItem, Item, ItemEvent, ItemHandle},
@@ -385,20 +387,47 @@ impl Item for ChannelView {
}
}
fn tab_content_text(&self, cx: &WindowContext) -> Option<SharedString> {
let label = if let Some(channel) = self.channel(cx) {
match (
fn tab_icon(&self, cx: &WindowContext) -> Option<Icon> {
let channel = self.channel(cx)?;
let icon = match channel.visibility {
ChannelVisibility::Public => IconName::Public,
ChannelVisibility::Members => IconName::Hash,
};
Some(Icon::new(icon))
}
fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> gpui::AnyElement {
let (channel_name, status) = if let Some(channel) = self.channel(cx) {
let status = match (
self.channel_buffer.read(cx).buffer().read(cx).read_only(),
self.channel_buffer.read(cx).is_connected(),
) {
(false, true) => format!("#{}", channel.name),
(true, true) => format!("#{} (read-only)", channel.name),
(_, false) => format!("#{} (disconnected)", channel.name),
}
(false, true) => None,
(true, true) => Some("read-only"),
(_, false) => Some("disconnected"),
};
(channel.name.clone(), status)
} else {
"channel notes (disconnected)".to_string()
("<unknown>".into(), Some("disconnected"))
};
Some(label.into())
h_flex()
.gap_2()
.child(
Label::new(channel_name)
.color(params.text_color())
.italic(params.preview),
)
.when_some(status, |element, status| {
element.child(
Label::new(status)
.size(LabelSize::XSmall)
.color(Color::Muted),
)
})
.into_any_element()
}
fn telemetry_event_text(&self) -> Option<&'static str> {

View File

@@ -1107,9 +1107,11 @@ impl Panel for ChatPanel {
}
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
settings::update_settings_file::<ChatPanelSettings>(self.fs.clone(), cx, move |settings| {
settings.dock = Some(position)
});
settings::update_settings_file::<ChatPanelSettings>(
self.fs.clone(),
cx,
move |settings, _| settings.dock = Some(position),
);
}
fn size(&self, cx: &gpui::WindowContext) -> Pixels {

View File

@@ -6,7 +6,7 @@ use editor::{AnchorRangeExt, CompletionProvider, Editor, EditorElement, EditorSt
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
AsyncWindowContext, FocusableView, FontStyle, FontWeight, HighlightStyle, IntoElement, Model,
Render, Task, TextStyle, View, ViewContext, WeakView, WhiteSpace,
Render, Task, TextStyle, View, ViewContext, WeakView,
};
use language::{
language_settings::SoftWrap, Anchor, Buffer, BufferSnapshot, CodeLabel, LanguageRegistry,
@@ -537,10 +537,7 @@ impl Render for MessageEditor {
font_weight: settings.ui_font.weight,
font_style: FontStyle::Normal,
line_height: relative(1.3),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
..Default::default()
};
div()

View File

@@ -16,7 +16,7 @@ use gpui::{
EventEmitter, FocusHandle, FocusableView, FontStyle, InteractiveElement, IntoElement,
ListOffset, ListState, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel,
Render, SharedString, Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext,
WeakView, WhiteSpace,
WeakView,
};
use menu::{Cancel, Confirm, SecondaryConfirm, SelectNext, SelectPrev};
use project::{Fs, Project};
@@ -2194,10 +2194,7 @@ impl CollabPanel {
font_weight: settings.ui_font.weight,
font_style: FontStyle::Normal,
line_height: relative(1.3),
background_color: None,
underline: None,
strikethrough: None,
white_space: WhiteSpace::Normal,
..Default::default()
};
EditorElement::new(
@@ -2809,7 +2806,7 @@ impl Panel for CollabPanel {
settings::update_settings_file::<CollaborationPanelSettings>(
self.fs.clone(),
cx,
move |settings| settings.dock = Some(position),
move |settings, _| settings.dock = Some(position),
);
}

View File

@@ -5,7 +5,6 @@ use gpui::{
};
use picker::{Picker, PickerDelegate};
use std::sync::Arc;
use theme::ActiveTheme as _;
use ui::{prelude::*, Avatar, ListItem, ListItemSpacing};
use util::{ResultExt as _, TryFutureExt};
use workspace::ModalView;

View File

@@ -672,7 +672,7 @@ impl Panel for NotificationPanel {
settings::update_settings_file::<NotificationPanelSettings>(
self.fs.clone(),
cx,
move |settings| settings.dock = Some(position),
move |settings, _| settings.dock = Some(position),
);
}

View File

@@ -17,9 +17,10 @@ use gpui::{
use picker::{Picker, PickerDelegate};
use postage::{sink::Sink, stream::Stream};
use settings::Settings;
use ui::{h_flex, prelude::*, v_flex, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing};
use util::ResultExt;
use workspace::{ModalView, Workspace};
use workspace::{ModalView, Workspace, WorkspaceSettings};
use zed_actions::OpenZedUrl;
actions!(command_palette, [Toggle]);
@@ -248,9 +249,13 @@ impl PickerDelegate for CommandPaletteDelegate {
fn update_matches(
&mut self,
query: String,
mut query: String,
cx: &mut ViewContext<Picker<Self>>,
) -> gpui::Task<()> {
let settings = WorkspaceSettings::get_global(cx);
if let Some(alias) = settings.command_aliases.get(&query) {
query = alias.to_string();
}
let (mut tx, mut rx) = postage::dispatch::channel(1);
let task = cx.background_executor().spawn({
let mut commands = self.all_commands.clone();
@@ -477,7 +482,7 @@ mod tests {
});
workspace.update(cx, |workspace, cx| {
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, cx);
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
editor.update(cx, |editor, cx| editor.focus(cx))
});

View File

@@ -0,0 +1,43 @@
[package]
name = "completion"
version = "0.1.0"
edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/completion.rs"
doctest = false
[features]
test-support = [
"editor/test-support",
"language/test-support",
"language_model/test-support",
"project/test-support",
"text/test-support",
]
[dependencies]
anyhow.workspace = true
futures.workspace = true
gpui.workspace = true
language_model.workspace = true
serde.workspace = true
settings.workspace = true
smol.workspace = true
ui.workspace = true
[dev-dependencies]
ctor.workspace = true
editor = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
language = { workspace = true, features = ["test-support"] }
project = { workspace = true, features = ["test-support"] }
language_model = { workspace = true, features = ["test-support"] }
rand.workspace = true
text = { workspace = true, features = ["test-support"] }
unindent.workspace = true

View File

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

View File

@@ -0,0 +1,286 @@
use anyhow::{anyhow, Result};
use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
use gpui::{AppContext, Global, Model, ModelContext, Task};
use language_model::{
LanguageModel, LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry,
LanguageModelRequest,
};
use smol::lock::{Semaphore, SemaphoreGuardArc};
use std::{pin::Pin, sync::Arc, task::Poll};
use ui::Context;
pub fn init(cx: &mut AppContext) {
let completion_provider = cx.new_model(|cx| LanguageModelCompletionProvider::new(cx));
cx.set_global(GlobalLanguageModelCompletionProvider(completion_provider));
}
struct GlobalLanguageModelCompletionProvider(Model<LanguageModelCompletionProvider>);
impl Global for GlobalLanguageModelCompletionProvider {}
pub struct LanguageModelCompletionProvider {
active_provider: Option<Arc<dyn LanguageModelProvider>>,
active_model: Option<Arc<dyn LanguageModel>>,
request_limiter: Arc<Semaphore>,
}
const MAX_CONCURRENT_COMPLETION_REQUESTS: usize = 4;
pub struct LanguageModelCompletionResponse {
pub inner: BoxStream<'static, Result<String>>,
_lock: SemaphoreGuardArc,
}
impl futures::Stream for LanguageModelCompletionResponse {
type Item = Result<String>;
fn poll_next(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Option<Self::Item>> {
Pin::new(&mut self.inner).poll_next(cx)
}
}
impl LanguageModelCompletionProvider {
pub fn global(cx: &AppContext) -> Model<Self> {
cx.global::<GlobalLanguageModelCompletionProvider>()
.0
.clone()
}
pub fn read_global(cx: &AppContext) -> &Self {
cx.global::<GlobalLanguageModelCompletionProvider>()
.0
.read(cx)
}
#[cfg(any(test, feature = "test-support"))]
pub fn test(cx: &mut AppContext) {
let provider = cx.new_model(|cx| {
let mut this = Self::new(cx);
let available_model = LanguageModelRegistry::read_global(cx)
.available_models(cx)
.first()
.unwrap()
.clone();
this.set_active_model(available_model, cx);
this
});
cx.set_global(GlobalLanguageModelCompletionProvider(provider));
}
pub fn new(cx: &mut ModelContext<Self>) -> Self {
cx.observe(&LanguageModelRegistry::global(cx), |_, _, cx| {
cx.notify();
})
.detach();
Self {
active_provider: None,
active_model: None,
request_limiter: Arc::new(Semaphore::new(MAX_CONCURRENT_COMPLETION_REQUESTS)),
}
}
pub fn active_provider(&self) -> Option<Arc<dyn LanguageModelProvider>> {
self.active_provider.clone()
}
pub fn set_active_provider(
&mut self,
provider_id: LanguageModelProviderId,
cx: &mut ModelContext<Self>,
) {
self.active_provider = LanguageModelRegistry::read_global(cx).provider(&provider_id);
self.active_model = None;
cx.notify();
}
pub fn active_model(&self) -> Option<Arc<dyn LanguageModel>> {
self.active_model.clone()
}
pub fn set_active_model(&mut self, model: Arc<dyn LanguageModel>, cx: &mut ModelContext<Self>) {
if self.active_model.as_ref().map_or(false, |m| {
m.id() == model.id() && m.provider_id() == model.provider_id()
}) {
return;
}
self.active_provider =
LanguageModelRegistry::read_global(cx).provider(&model.provider_id());
self.active_model = Some(model.clone());
if let Some(provider) = self.active_provider.as_ref() {
provider.load_model(model, cx);
}
cx.notify();
}
pub fn is_authenticated(&self, cx: &AppContext) -> bool {
self.active_provider
.as_ref()
.map_or(false, |provider| provider.is_authenticated(cx))
}
pub fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
self.active_provider
.as_ref()
.map_or(Task::ready(Ok(())), |provider| provider.authenticate(cx))
}
pub fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>> {
self.active_provider
.as_ref()
.map_or(Task::ready(Ok(())), |provider| {
provider.reset_credentials(cx)
})
}
pub fn count_tokens(
&self,
request: LanguageModelRequest,
cx: &AppContext,
) -> BoxFuture<'static, Result<usize>> {
if let Some(model) = self.active_model() {
model.count_tokens(request, cx)
} else {
std::future::ready(Err(anyhow!("No active model set"))).boxed()
}
}
pub fn stream_completion(
&self,
request: LanguageModelRequest,
cx: &AppContext,
) -> Task<Result<LanguageModelCompletionResponse>> {
if let Some(language_model) = self.active_model() {
let rate_limiter = self.request_limiter.clone();
cx.spawn(|cx| async move {
let lock = rate_limiter.acquire_arc().await;
let response = language_model.stream_completion(request, &cx).await?;
Ok(LanguageModelCompletionResponse {
inner: response,
_lock: lock,
})
})
} else {
Task::ready(Err(anyhow!("No active model set")))
}
}
pub fn complete(&self, request: LanguageModelRequest, cx: &AppContext) -> Task<Result<String>> {
let response = self.stream_completion(request, cx);
cx.foreground_executor().spawn(async move {
let mut chunks = response.await?;
let mut completion = String::new();
while let Some(chunk) = chunks.next().await {
let chunk = chunk?;
completion.push_str(&chunk);
}
Ok(completion)
})
}
}
#[cfg(test)]
mod tests {
use futures::StreamExt;
use gpui::AppContext;
use settings::SettingsStore;
use ui::Context;
use crate::{
LanguageModelCompletionProvider, LanguageModelRequest, MAX_CONCURRENT_COMPLETION_REQUESTS,
};
use language_model::LanguageModelRegistry;
#[gpui::test]
fn test_rate_limiting(cx: &mut AppContext) {
SettingsStore::test(cx);
let fake_provider = LanguageModelRegistry::test(cx);
let model = LanguageModelRegistry::read_global(cx)
.available_models(cx)
.first()
.cloned()
.unwrap();
let provider = cx.new_model(|cx| {
let mut provider = LanguageModelCompletionProvider::new(cx);
provider.set_active_model(model.clone(), cx);
provider
});
let fake_model = fake_provider.test_model();
// Enqueue some requests
for i in 0..MAX_CONCURRENT_COMPLETION_REQUESTS * 2 {
let response = provider.read(cx).stream_completion(
LanguageModelRequest {
temperature: i as f32 / 10.0,
..Default::default()
},
cx,
);
cx.background_executor()
.spawn(async move {
let mut stream = response.await.unwrap();
while let Some(message) = stream.next().await {
message.unwrap();
}
})
.detach();
}
cx.background_executor().run_until_parked();
assert_eq!(
fake_model.completion_count(),
MAX_CONCURRENT_COMPLETION_REQUESTS
);
// Get the first completion request that is in flight and mark it as completed.
let completion = fake_model.pending_completions().into_iter().next().unwrap();
fake_model.finish_completion(&completion);
// Ensure that the number of in-flight completion requests is reduced.
assert_eq!(
fake_model.completion_count(),
MAX_CONCURRENT_COMPLETION_REQUESTS - 1
);
cx.background_executor().run_until_parked();
// Ensure that another completion request was allowed to acquire the lock.
assert_eq!(
fake_model.completion_count(),
MAX_CONCURRENT_COMPLETION_REQUESTS
);
// Mark all completion requests as finished that are in flight.
for request in fake_model.pending_completions() {
fake_model.finish_completion(&request);
}
assert_eq!(fake_model.completion_count(), 0);
// Wait until the background tasks acquire the lock again.
cx.background_executor().run_until_parked();
assert_eq!(
fake_model.completion_count(),
MAX_CONCURRENT_COMPLETION_REQUESTS - 1
);
// Finish all remaining completion requests.
for request in fake_model.pending_completions() {
fake_model.finish_completion(&request);
}
cx.background_executor().run_until_parked();
assert_eq!(fake_model.completion_count(), 0);
}
}

View File

@@ -32,7 +32,7 @@ command_palette_hooks.workspace = true
editor.workspace = true
futures.workspace = true
gpui.workspace = true
http.workspace = true
http_client.workspace = true
language.workspace = true
lsp.workspace = true
menu.workspace = true
@@ -65,4 +65,4 @@ rpc = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
theme = { workspace = true, features = ["test-support"] }
util = { workspace = true, features = ["test-support"] }
http = { workspace = true, features = ["test-support"] }
http_client = { workspace = true, features = ["test-support"] }

View File

@@ -12,8 +12,8 @@ use gpui::{
actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Global, Model,
ModelContext, Task, WeakModel,
};
use http::github::latest_github_release;
use http::HttpClient;
use http_client::github::latest_github_release;
use http_client::HttpClient;
use language::{
language_settings::{all_language_settings, language_settings, InlineCompletionProvider},
point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16,
@@ -393,7 +393,7 @@ impl Copilot {
Default::default(),
cx.to_async(),
);
let http = http::FakeHttpClient::create(|_| async { unreachable!() });
let http = http_client::FakeHttpClient::create(|_| async { unreachable!() });
let node_runtime = FakeNodeRuntime::new();
let this = cx.new_model(|cx| Self {
server_id: LanguageServerId(0),
@@ -1236,7 +1236,7 @@ mod tests {
unimplemented!()
}
fn to_proto(&self) -> rpc::proto::File {
fn to_proto(&self, _: &AppContext) -> rpc::proto::File {
unimplemented!()
}

View File

@@ -8,7 +8,7 @@ use language::{
Buffer, OffsetRangeExt, ToOffset,
};
use settings::Settings;
use std::{path::Path, sync::Arc, time::Duration};
use std::{ops::Range, path::Path, sync::Arc, time::Duration};
pub const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
@@ -239,7 +239,7 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
buffer: &Model<Buffer>,
cursor_position: language::Anchor,
cx: &'a AppContext,
) -> Option<&'a str> {
) -> Option<(&'a str, Option<Range<language::Anchor>>)> {
let buffer_id = buffer.entity_id();
let buffer = buffer.read(cx);
let completion = self.active_completion()?;
@@ -269,7 +269,7 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
if completion_text.trim().is_empty() {
None
} else {
Some(completion_text)
Some((completion_text, None))
}
} else {
None

27
crates/dap/Cargo.toml Normal file
View File

@@ -0,0 +1,27 @@
[package]
name = "dap"
version = "0.1.0"
edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lints]
workspace = true
[dependencies]
anyhow.workspace = true
async-std = "1.12.0"
dap-types = { git = "https://github.com/zed-industries/dap-types" }
futures.workspace = true
gpui.workspace = true
log.workspace = true
parking_lot.workspace = true
postage.workspace = true
release_channel.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_json_lenient.workspace = true
smol.workspace = true
task.workspace = true
util.workspace = true

646
crates/dap/src/client.rs Normal file
View File

@@ -0,0 +1,646 @@
use crate::transport::{Events, Payload, Response, Transport};
use anyhow::{anyhow, Context, Result};
use dap_types::{
requests::{
Attach, ConfigurationDone, Continue, Disconnect, Initialize, Launch, Next, Pause, Request,
Restart, RunInTerminal, SetBreakpoints, StartDebugging, StepBack, StepIn, StepOut,
},
AttachRequestArguments, ConfigurationDoneArguments, ContinueArguments, ContinueResponse,
DisconnectArguments, InitializeRequestArgumentsPathFormat, LaunchRequestArguments,
NextArguments, PauseArguments, RestartArguments, RunInTerminalRequestArguments,
RunInTerminalResponse, Scope, SetBreakpointsArguments, SetBreakpointsResponse, Source,
SourceBreakpoint, StackFrame, StartDebuggingRequestArguments, StepBackArguments,
StepInArguments, StepOutArguments, SteppingGranularity, Variable,
};
use futures::{AsyncBufRead, AsyncReadExt, AsyncWrite};
use gpui::{AppContext, AsyncAppContext};
use parking_lot::{Mutex, MutexGuard};
use serde_json::Value;
use smol::{
channel::{bounded, unbounded, Receiver, Sender},
io::BufReader,
net::{TcpListener, TcpStream},
process::{self, Child},
};
use std::{
collections::HashMap,
net::{Ipv4Addr, SocketAddrV4},
path::PathBuf,
process::Stdio,
sync::{
atomic::{AtomicU64, Ordering},
Arc,
},
time::Duration,
};
use task::{DebugAdapterConfig, DebugConnectionType, DebugRequestType, TCPHost};
use util::ResultExt;
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ThreadStatus {
#[default]
Running,
Stopped,
Exited,
Ended,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct DebugAdapterClientId(pub usize);
#[derive(Debug, Default, Clone)]
pub struct ThreadState {
pub status: ThreadStatus,
pub stack_frames: Vec<StackFrame>,
pub scopes: HashMap<u64, Vec<Scope>>, // stack_frame_id -> scopes
pub variables: HashMap<u64, Vec<Variable>>, // scope.variable_reference -> variables
pub current_stack_frame_id: Option<u64>,
}
pub struct DebugAdapterClient {
id: DebugAdapterClientId,
_process: Option<Child>,
server_tx: Sender<Payload>,
sequence_count: AtomicU64,
capabilities: Arc<Mutex<Option<dap_types::Capabilities>>>,
config: DebugAdapterConfig,
thread_states: Arc<Mutex<HashMap<u64, ThreadState>>>, // thread_id -> thread_state
sub_client: Arc<Mutex<Option<Arc<Self>>>>,
}
impl DebugAdapterClient {
/// Creates & returns a new debug adapter client
///
/// # Parameters
/// - `id`: The id that [`Project`](project::Project) uses to keep track of specific clients
/// - `config`: The adapter specific configurations from debugger task that is starting
/// - `command`: The command that starts the debugger
/// - `args`: Arguments of the command that starts the debugger
/// - `project_path`: The absolute path of the project that is being debugged
/// - `cx`: The context that the new client belongs too
pub async fn new<F>(
id: DebugAdapterClientId,
config: DebugAdapterConfig,
command: &str,
args: Vec<&str>,
project_path: PathBuf,
event_handler: F,
cx: &mut AsyncAppContext,
) -> Result<Arc<Self>>
where
F: FnMut(Events, &mut AppContext) + 'static + Send + Sync + Clone,
{
match config.connection.clone() {
DebugConnectionType::TCP(host) => {
Self::create_tcp_client(
id,
config,
host,
command,
args,
project_path,
event_handler,
cx,
)
.await
}
DebugConnectionType::STDIO => {
Self::create_stdio_client(
id,
config,
command,
args,
project_path,
event_handler,
cx,
)
.await
}
}
}
/// Creates a debug client that connects to an adapter through tcp
///
/// TCP clients don't have an error communication stream with an adapter
///
/// # Parameters
/// - `id`: The id that [`Project`](project::Project) uses to keep track of specific clients
/// - `config`: The adapter specific configurations from debugger task that is starting
/// - `command`: The command that starts the debugger
/// - `args`: Arguments of the command that starts the debugger
/// - `project_path`: The absolute path of the project that is being debugged
/// - `cx`: The context that the new client belongs too
#[allow(clippy::too_many_arguments)]
async fn create_tcp_client<F>(
id: DebugAdapterClientId,
config: DebugAdapterConfig,
host: TCPHost,
command: &str,
args: Vec<&str>,
project_path: PathBuf,
event_handler: F,
cx: &mut AsyncAppContext,
) -> Result<Arc<Self>>
where
F: FnMut(Events, &mut AppContext) + 'static + Send + Sync + Clone,
{
let mut port = host.port;
if port.is_none() {
port = Self::get_port().await;
}
let mut command = process::Command::new(command);
command
.current_dir(project_path)
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.kill_on_drop(true);
let process = command
.spawn()
.with_context(|| "failed to start debug adapter.")?;
if let Some(delay) = host.delay {
// some debug adapters need some time to start the TCP server
// so we have to wait few milliseconds before we can connect to it
cx.background_executor()
.timer(Duration::from_millis(delay))
.await;
}
let address = SocketAddrV4::new(
host.host.unwrap_or_else(|| Ipv4Addr::new(127, 0, 0, 1)),
port.unwrap(),
);
let (rx, tx) = TcpStream::connect(address).await?.split();
Self::handle_transport(
id,
config,
Box::new(BufReader::new(rx)),
Box::new(tx),
None,
Some(process),
event_handler,
cx,
)
}
/// Get an open port to use with the tcp client when not supplied by debug config
async fn get_port() -> Option<u16> {
Some(
TcpListener::bind(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 0))
.await
.ok()?
.local_addr()
.ok()?
.port(),
)
}
/// Creates a debug client that connects to an adapter through std input/output
///
/// # Parameters
/// - `id`: The id that [`Project`](project::Project) uses to keep track of specific clients
/// - `config`: The adapter specific configurations from debugger task that is starting
/// - `command`: The command that starts the debugger
/// - `args`: Arguments of the command that starts the debugger
/// - `project_path`: The absolute path of the project that is being debugged
/// - `cx`: The context that the new client belongs too
async fn create_stdio_client<F>(
id: DebugAdapterClientId,
config: DebugAdapterConfig,
command: &str,
args: Vec<&str>,
project_path: PathBuf,
event_handler: F,
cx: &mut AsyncAppContext,
) -> Result<Arc<Self>>
where
F: FnMut(Events, &mut AppContext) + 'static + Send + Sync + Clone,
{
let mut command = process::Command::new(command);
command
.current_dir(project_path)
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.kill_on_drop(true);
let mut process = command
.spawn()
.with_context(|| "failed to spawn command.")?;
let stdin = process
.stdin
.take()
.ok_or_else(|| anyhow!("Failed to open stdin"))?;
let stdout = process
.stdout
.take()
.ok_or_else(|| anyhow!("Failed to open stdout"))?;
let stderr = process
.stderr
.take()
.ok_or_else(|| anyhow!("Failed to open stderr"))?;
let stdin = Box::new(stdin);
let stdout = Box::new(BufReader::new(stdout));
let stderr = Box::new(BufReader::new(stderr));
Self::handle_transport(
id,
config,
stdout,
stdin,
Some(stderr),
Some(process),
event_handler,
cx,
)
}
#[allow(clippy::too_many_arguments)]
pub fn handle_transport<F>(
id: DebugAdapterClientId,
config: DebugAdapterConfig,
rx: Box<dyn AsyncBufRead + Unpin + Send>,
tx: Box<dyn AsyncWrite + Unpin + Send>,
err: Option<Box<dyn AsyncBufRead + Unpin + Send>>,
process: Option<Child>,
event_handler: F,
cx: &mut AsyncAppContext,
) -> Result<Arc<Self>>
where
F: FnMut(Events, &mut AppContext) + 'static + Send + Sync + Clone,
{
let (server_rx, server_tx) = Transport::start(rx, tx, err, cx);
let (client_tx, client_rx) = unbounded::<Payload>();
let client = Arc::new(Self {
id,
config,
_process: process,
sub_client: Default::default(),
server_tx: server_tx.clone(),
capabilities: Default::default(),
thread_states: Default::default(),
sequence_count: AtomicU64::new(1),
});
cx.update(|cx| {
cx.background_executor()
.spawn(Self::handle_recv(server_rx, client_tx))
.detach_and_log_err(cx);
cx.spawn({
let client = client.clone();
|mut cx| async move {
Self::handle_events(client, client_rx, server_tx, event_handler, &mut cx).await
}
})
.detach_and_log_err(cx);
})?;
Ok(client)
}
/// Set's up a client's event handler.
///
/// This function should only be called once or else errors will arise
/// # Parameters
/// `client`: A pointer to the client to pass the event handler too
/// `event_handler`: The function that is called to handle events
/// should be DebugPanel::handle_debug_client_events
/// `cx`: The context that this task will run in
pub async fn handle_events<F>(
this: Arc<Self>,
client_rx: Receiver<Payload>,
server_tx: Sender<Payload>,
mut event_handler: F,
cx: &mut AsyncAppContext,
) -> Result<()>
where
F: FnMut(Events, &mut AppContext) + 'static + Send + Sync + Clone,
{
while let Ok(payload) = client_rx.recv().await {
match payload {
Payload::Event(event) => cx.update(|cx| event_handler(*event, cx))?,
Payload::Request(request) => {
if RunInTerminal::COMMAND == request.command {
Self::handle_run_in_terminal_request(request, &server_tx).await?;
} else if StartDebugging::COMMAND == request.command {
Self::handle_start_debugging_request(&this, request, cx).await?;
} else {
unreachable!("Unknown reverse request {}", request.command);
}
}
_ => unreachable!(),
}
}
anyhow::Ok(())
}
async fn handle_run_in_terminal_request(
request: crate::transport::Request,
server_tx: &Sender<Payload>,
) -> Result<()> {
let arguments: RunInTerminalRequestArguments =
serde_json::from_value(request.arguments.unwrap_or_default())?;
let mut args = arguments.args.clone();
let mut command = process::Command::new(args.remove(0));
let envs = arguments.env.as_ref().and_then(|e| e.as_object()).map(|e| {
e.iter()
.map(|(key, value)| (key.clone(), value.clone().to_string()))
.collect::<HashMap<String, String>>()
});
if let Some(envs) = envs {
command.envs(envs);
}
let process = command
.current_dir(arguments.cwd)
.args(args)
.spawn()
.with_context(|| "failed to spawn run in terminal command.")?;
server_tx
.send(Payload::Response(Response {
request_seq: request.seq,
success: true,
command: RunInTerminal::COMMAND.into(),
message: None,
body: Some(serde_json::to_value(RunInTerminalResponse {
process_id: Some(process.id() as u64),
shell_process_id: None,
})?),
}))
.await?;
anyhow::Ok(())
}
async fn handle_start_debugging_request(
this: &Arc<Self>,
request: crate::transport::Request,
cx: &mut AsyncAppContext,
) -> Result<()> {
dbg!(&request);
let arguments: StartDebuggingRequestArguments =
serde_json::from_value(request.arguments.clone().unwrap_or_default())?;
let sub_client = DebugAdapterClient::new(
DebugAdapterClientId(1),
this.config.clone(),
"node",
vec![
"/Users/remcosmits/Downloads/js-debug/src/dapDebugServer.js",
"8134",
"127.0.0.1",
],
PathBuf::from("/Users/remcosmits/Documents/code/prettier-test"),
|event, _cx| {
dbg!(event);
},
cx,
)
.await?;
dbg!(&arguments);
let res = sub_client.launch(request.arguments).await?;
dbg!(res);
*this.sub_client.lock() = Some(sub_client);
anyhow::Ok(())
}
async fn handle_recv(server_rx: Receiver<Payload>, client_tx: Sender<Payload>) -> Result<()> {
while let Ok(payload) = server_rx.recv().await {
match payload {
Payload::Event(ev) => client_tx.send(Payload::Event(ev)).await?,
Payload::Response(_) => unreachable!(),
Payload::Request(req) => client_tx.send(Payload::Request(req)).await?,
};
}
anyhow::Ok(())
}
/// Send a request to an adapter and get a response back
/// Note: This function will block until a response is sent back from the adapter
pub async fn request<R: Request>(&self, arguments: R::Arguments) -> Result<R::Response> {
let serialized_arguments = serde_json::to_value(arguments)?;
let (callback_tx, callback_rx) = bounded::<Result<Response>>(1);
let request = crate::transport::Request {
back_ch: Some(callback_tx),
seq: self.next_sequence_id(),
command: R::COMMAND.to_string(),
arguments: Some(serialized_arguments),
};
self.server_tx.send(Payload::Request(request)).await?;
let response = callback_rx.recv().await??;
match response.success {
true => Ok(serde_json::from_value(response.body.unwrap_or_default())?),
false => Err(anyhow!("Request failed")),
}
}
pub fn id(&self) -> DebugAdapterClientId {
self.id
}
pub fn config(&self) -> DebugAdapterConfig {
self.config.clone()
}
pub fn request_type(&self) -> DebugRequestType {
self.config.request.clone()
}
pub fn capabilities(&self) -> dap_types::Capabilities {
self.capabilities.lock().clone().unwrap_or_default()
}
/// Get the next sequence id to be used in a request
/// # Side Effect
/// This function also increment's client's sequence count by one
pub fn next_sequence_id(&self) -> u64 {
self.sequence_count.fetch_add(1, Ordering::Relaxed)
}
pub fn update_thread_state_status(&self, thread_id: u64, status: ThreadStatus) {
if let Some(thread_state) = self.thread_states().get_mut(&thread_id) {
thread_state.status = status;
};
}
pub fn thread_states(&self) -> MutexGuard<HashMap<u64, ThreadState>> {
self.thread_states.lock()
}
pub fn thread_state_by_id(&self, thread_id: u64) -> ThreadState {
self.thread_states.lock().get(&thread_id).cloned().unwrap()
}
pub async fn initialize(&self) -> Result<dap_types::Capabilities> {
let args = dap_types::InitializeRequestArguments {
client_id: Some("zed".to_owned()),
client_name: Some("Zed".to_owned()),
adapter_id: self.config.id.clone(),
locale: Some("en-us".to_owned()),
path_format: Some(InitializeRequestArgumentsPathFormat::Path),
supports_variable_type: Some(true),
supports_variable_paging: Some(false),
supports_run_in_terminal_request: Some(false), // TODO: we should support this
supports_memory_references: Some(true),
supports_progress_reporting: Some(true),
supports_invalidated_event: Some(false),
lines_start_at1: Some(true),
columns_start_at1: Some(true),
supports_memory_event: Some(true),
supports_args_can_be_interpreted_by_shell: None,
supports_start_debugging_request: Some(true),
};
let capabilities = self.request::<Initialize>(args).await?;
*self.capabilities.lock() = Some(capabilities.clone());
Ok(capabilities)
}
pub async fn launch(&self, args: Option<Value>) -> Result<()> {
self.request::<Launch>(LaunchRequestArguments {
raw: args.unwrap_or(Value::Null),
})
.await
}
pub async fn attach(&self, args: Option<Value>) -> Result<()> {
self.request::<Attach>(AttachRequestArguments {
raw: args.unwrap_or(Value::Null),
})
.await
}
pub async fn resume(&self, thread_id: u64) -> Result<ContinueResponse> {
self.request::<Continue>(ContinueArguments {
thread_id,
single_thread: Some(true),
})
.await
}
pub async fn step_over(&self, thread_id: u64) -> Result<()> {
self.request::<Next>(NextArguments {
thread_id,
granularity: Some(SteppingGranularity::Statement),
single_thread: Some(true),
})
.await
}
pub async fn step_in(&self, thread_id: u64) -> Result<()> {
self.request::<StepIn>(StepInArguments {
thread_id,
target_id: None,
granularity: Some(SteppingGranularity::Statement),
single_thread: Some(true),
})
.await
}
pub async fn step_out(&self, thread_id: u64) -> Result<()> {
self.request::<StepOut>(StepOutArguments {
thread_id,
granularity: Some(SteppingGranularity::Statement),
single_thread: Some(true),
})
.await
}
pub async fn step_back(&self, thread_id: u64) -> Result<()> {
self.request::<StepBack>(StepBackArguments {
thread_id,
single_thread: Some(true),
granularity: Some(SteppingGranularity::Statement),
})
.await
}
pub async fn restart(&self) {
self.request::<Restart>(RestartArguments {
raw: self
.config
.request_args
.as_ref()
.map(|v| v.args.clone())
.unwrap_or(Value::Null),
})
.await
.log_err();
}
pub async fn pause(&self, thread_id: u64) {
self.request::<Pause>(PauseArguments { thread_id })
.await
.log_err();
}
pub async fn stop(&self) {
self.request::<Disconnect>(DisconnectArguments {
restart: Some(false),
terminate_debuggee: Some(false),
suspend_debuggee: Some(false),
})
.await
.log_err();
}
pub async fn set_breakpoints(
&self,
path: PathBuf,
breakpoints: Option<Vec<SourceBreakpoint>>,
) -> Result<SetBreakpointsResponse> {
let adapter_data = self.config.request_args.clone().map(|c| c.args);
self.request::<SetBreakpoints>(SetBreakpointsArguments {
source: Source {
path: Some(String::from(path.to_string_lossy())),
name: None,
source_reference: None,
presentation_hint: None,
origin: None,
sources: None,
adapter_data,
checksums: None,
},
breakpoints,
source_modified: None,
lines: None,
})
.await
}
pub async fn configuration_done(&self) -> Result<()> {
self.request::<ConfigurationDone>(ConfigurationDoneArguments)
.await
}
}

3
crates/dap/src/lib.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod client;
pub mod transport;
pub use dap_types::*;

283
crates/dap/src/transport.rs Normal file
View File

@@ -0,0 +1,283 @@
use anyhow::{anyhow, Context, Result};
use dap_types::{
BreakpointEvent, Capabilities, CapabilitiesEvent, ContinuedEvent, ExitedEvent,
InvalidatedEvent, LoadedSourceEvent, MemoryEvent, ModuleEvent, OutputEvent, ProcessEvent,
ProgressEndEvent, ProgressStartEvent, ProgressUpdateEvent, StoppedEvent, TerminatedEvent,
ThreadEvent,
};
use futures::{AsyncBufRead, AsyncWrite};
use gpui::AsyncAppContext;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use smol::{
channel::{unbounded, Receiver, Sender},
io::{AsyncBufReadExt as _, AsyncReadExt as _, AsyncWriteExt},
lock::Mutex,
};
use std::{collections::HashMap, sync::Arc};
#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum Payload {
Event(Box<Events>),
Response(Response),
Request(Request),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(tag = "event", content = "body")]
#[serde(rename_all = "camelCase")]
pub enum Events {
Initialized(Option<Capabilities>),
Stopped(StoppedEvent),
Continued(ContinuedEvent),
Exited(ExitedEvent),
Terminated(Option<TerminatedEvent>),
Thread(ThreadEvent),
Output(OutputEvent),
Breakpoint(BreakpointEvent),
Module(ModuleEvent),
LoadedSource(LoadedSourceEvent),
Process(ProcessEvent),
Capabilities(CapabilitiesEvent),
ProgressStart(ProgressStartEvent),
ProgressUpdate(ProgressUpdateEvent),
ProgressEnd(ProgressEndEvent),
Invalidated(InvalidatedEvent),
Memory(MemoryEvent),
#[serde(untagged)]
Other(HashMap<String, Value>),
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Request {
#[serde(skip)]
pub back_ch: Option<Sender<Result<Response>>>,
pub seq: u64,
pub command: String,
pub arguments: Option<Value>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
pub struct Response {
pub request_seq: u64,
pub success: bool,
pub command: String,
pub message: Option<String>,
#[serde(default, deserialize_with = "deserialize_empty_object")]
pub body: Option<Value>,
}
fn deserialize_empty_object<'de, D>(deserializer: D) -> Result<Option<Value>, D::Error>
where
D: Deserializer<'de>,
{
let value = Value::deserialize(deserializer)?;
if value == Value::Object(serde_json::Map::new()) {
Ok(None)
} else {
Ok(Some(value))
}
}
#[derive(Debug)]
pub struct Transport {
pending_requests: Mutex<HashMap<u64, Sender<Result<Response>>>>,
}
impl Transport {
pub fn start(
server_stdout: Box<dyn AsyncBufRead + Unpin + Send>,
server_stdin: Box<dyn AsyncWrite + Unpin + Send>,
server_stderr: Option<Box<dyn AsyncBufRead + Unpin + Send>>,
cx: &mut AsyncAppContext,
) -> (Receiver<Payload>, Sender<Payload>) {
let (client_tx, server_rx) = unbounded::<Payload>();
let (server_tx, client_rx) = unbounded::<Payload>();
let transport = Arc::new(Self {
pending_requests: Mutex::new(HashMap::default()),
});
let _ = cx.update(|cx| {
let transport = transport.clone();
cx.background_executor()
.spawn(Self::receive(transport.clone(), server_stdout, client_tx))
.detach_and_log_err(cx);
cx.background_executor()
.spawn(Self::send(transport.clone(), server_stdin, client_rx))
.detach_and_log_err(cx);
if let Some(stderr) = server_stderr {
cx.background_executor()
.spawn(Self::err(stderr))
.detach_and_log_err(cx);
}
});
(server_rx, server_tx)
}
async fn recv_server_message(
reader: &mut Box<dyn AsyncBufRead + Unpin + Send>,
buffer: &mut String,
) -> Result<Payload> {
let mut content_length = None;
loop {
buffer.truncate(0);
if reader
.read_line(buffer)
.await
.with_context(|| "reading a message from server")?
== 0
{
return Err(anyhow!("reader stream closed"));
};
if buffer == "\r\n" {
break;
}
let parts = buffer.trim().split_once(": ");
match parts {
Some(("Content-Length", value)) => {
content_length = Some(value.parse().context("invalid content length")?);
}
_ => {}
}
}
let content_length = content_length.context("missing content length")?;
let mut content = vec![0; content_length];
reader
.read_exact(&mut content)
.await
.with_context(|| "reading after a loop")?;
let msg = std::str::from_utf8(&content).context("invalid utf8 from server")?;
Ok(serde_json::from_str::<Payload>(msg)?)
}
async fn recv_server_error(
err: &mut (impl AsyncBufRead + Unpin + Send),
buffer: &mut String,
) -> Result<()> {
buffer.truncate(0);
if err.read_line(buffer).await? == 0 {
return Err(anyhow!("error stream closed"));
};
Ok(())
}
async fn send_payload_to_server(
&self,
server_stdin: &mut Box<dyn AsyncWrite + Unpin + Send>,
mut payload: Payload,
) -> Result<()> {
if let Payload::Request(request) = &mut payload {
if let Some(back) = request.back_ch.take() {
self.pending_requests.lock().await.insert(request.seq, back);
}
}
self.send_string_to_server(server_stdin, serde_json::to_string(&payload)?)
.await
}
async fn send_string_to_server(
&self,
server_stdin: &mut Box<dyn AsyncWrite + Unpin + Send>,
request: String,
) -> Result<()> {
server_stdin
.write_all(format!("Content-Length: {}\r\n\r\n{}", request.len(), request).as_bytes())
.await?;
server_stdin.flush().await?;
Ok(())
}
fn process_response(response: Response) -> Result<Response> {
if response.success {
Ok(response)
} else {
Err(anyhow!("Received failed response"))
}
}
async fn process_server_message(
&self,
client_tx: &Sender<Payload>,
payload: Payload,
) -> Result<()> {
match payload {
Payload::Response(res) => {
if let Some(tx) = self.pending_requests.lock().await.remove(&res.request_seq) {
if !tx.is_closed() {
tx.send(Self::process_response(res)).await?;
} else {
log::warn!(
"Response stream associated with request seq: {} is closed",
&res.request_seq
); // TODO: Fix this case so it never happens
}
} else {
client_tx.send(Payload::Response(res)).await?;
};
}
Payload::Request(_) => {
client_tx.send(payload).await?;
}
Payload::Event(_) => {
client_tx.send(payload).await?;
}
}
Ok(())
}
async fn receive(
transport: Arc<Self>,
mut server_stdout: Box<dyn AsyncBufRead + Unpin + Send>,
client_tx: Sender<Payload>,
) -> Result<()> {
let mut recv_buffer = String::new();
loop {
transport
.process_server_message(
&client_tx,
Self::recv_server_message(&mut server_stdout, &mut recv_buffer).await?,
)
.await
.context("Process server message failed in transport::receive")?;
}
}
async fn send(
transport: Arc<Self>,
mut server_stdin: Box<dyn AsyncWrite + Unpin + Send>,
client_rx: Receiver<Payload>,
) -> Result<()> {
while let Ok(payload) = client_rx.recv().await {
transport
.send_payload_to_server(&mut server_stdin, payload)
.await?;
}
Ok(())
}
async fn err(mut server_stderr: Box<dyn AsyncBufRead + Unpin + Send>) -> Result<()> {
let mut recv_buffer = String::new();
loop {
Self::recv_server_error(&mut server_stderr, &mut recv_buffer).await?;
}
}
}

View File

@@ -0,0 +1,32 @@
[package]
name = "debugger_ui"
version = "0.1.0"
edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lints]
workspace = true
[dependencies]
anyhow.workspace = true
dap.workspace = true
db.workspace = true
editor.workspace = true
futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
picker.workspace = true
project.workspace = true
serde.workspace = true
serde_derive.workspace = true
task.workspace = true
tasks_ui.workspace = true
ui.workspace = true
workspace.workspace = true
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
project = { workspace = true, features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }

View File

@@ -0,0 +1,602 @@
use anyhow::Result;
use dap::client::{DebugAdapterClientId, ThreadState, ThreadStatus};
use dap::requests::{Disconnect, Scopes, StackTrace, Variables};
use dap::{client::DebugAdapterClient, transport::Events};
use dap::{
Capabilities, ContinuedEvent, DisconnectArguments, ExitedEvent, Scope, ScopesArguments,
StackFrame, StackTraceArguments, StoppedEvent, TerminatedEvent, ThreadEvent, ThreadEventReason,
Variable, VariablesArguments,
};
use editor::Editor;
use futures::future::try_join_all;
use gpui::{
actions, Action, AppContext, AsyncWindowContext, EventEmitter, FocusHandle, FocusableView,
Subscription, Task, View, ViewContext, WeakView,
};
use std::path::Path;
use std::{collections::HashMap, sync::Arc};
use task::DebugRequestType;
use ui::prelude::*;
use workspace::Pane;
use workspace::{
dock::{DockPosition, Panel, PanelEvent},
Workspace,
};
use crate::debugger_panel_item::DebugPanelItem;
enum DebugCurrentRowHighlight {}
#[derive(Debug)]
pub enum DebugPanelEvent {
Stopped((DebugAdapterClientId, StoppedEvent)),
Thread((DebugAdapterClientId, ThreadEvent)),
}
actions!(debug_panel, [ToggleFocus]);
pub struct DebugPanel {
size: Pixels,
pane: View<Pane>,
focus_handle: FocusHandle,
workspace: WeakView<Workspace>,
_subscriptions: Vec<Subscription>,
}
impl DebugPanel {
pub fn new(workspace: &Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
cx.new_view(|cx| {
let pane = cx.new_view(|cx| {
let mut pane = Pane::new(
workspace.weak_handle(),
workspace.project().clone(),
Default::default(),
None,
None,
cx,
);
pane.set_can_split(false, cx);
pane.set_can_navigate(true, cx);
pane.display_nav_history_buttons(None);
pane.set_should_display_tab_bar(|_| true);
pane
});
let project = workspace.project().clone();
let _subscriptions = vec![cx.subscribe(&project, {
move |this: &mut Self, _, event, cx| match event {
project::Event::DebugClientEvent { event, client_id } => {
Self::handle_debug_client_events(
this,
this.debug_client_by_id(*client_id, cx),
event,
cx,
);
}
project::Event::DebugClientStarted(client_id) => {
let client = this.debug_client_by_id(*client_id, cx);
cx.spawn(|_, _| async move {
client.initialize().await?;
let request_args = client.config().request_args.map(|a| a.args);
// send correct request based on adapter config
match client.config().request {
DebugRequestType::Launch => client.launch(request_args).await,
DebugRequestType::Attach => client.attach(request_args).await,
}
})
.detach_and_log_err(cx);
}
_ => {}
}
})];
Self {
pane,
size: px(300.),
_subscriptions,
focus_handle: cx.focus_handle(),
workspace: workspace.weak_handle(),
}
})
}
pub fn load(
workspace: WeakView<Workspace>,
cx: AsyncWindowContext,
) -> Task<Result<View<Self>>> {
cx.spawn(|mut cx| async move {
workspace.update(&mut cx, |workspace, cx| DebugPanel::new(workspace, cx))
})
}
fn debug_client_by_id(
&self,
client_id: DebugAdapterClientId,
cx: &mut ViewContext<Self>,
) -> Arc<DebugAdapterClient> {
self.workspace
.update(cx, |this, cx| {
this.project()
.read(cx)
.debug_adapter_by_id(client_id)
.unwrap()
})
.unwrap()
}
fn handle_debug_client_events(
this: &mut Self,
client: Arc<DebugAdapterClient>,
event: &Events,
cx: &mut ViewContext<Self>,
) {
match event {
Events::Initialized(event) => Self::handle_initialized_event(client, event, cx),
Events::Stopped(event) => Self::handle_stopped_event(client, event, cx),
Events::Continued(event) => Self::handle_continued_event(client, event, cx),
Events::Exited(event) => Self::handle_exited_event(client, event, cx),
Events::Terminated(event) => Self::handle_terminated_event(this, client, event, cx),
Events::Thread(event) => Self::handle_thread_event(client, event, cx),
Events::Output(_) => {}
Events::Breakpoint(_) => {}
Events::Module(_) => {}
Events::LoadedSource(_) => {}
Events::Capabilities(_) => {}
Events::Memory(_) => {}
Events::Process(_) => {}
Events::ProgressEnd(_) => {}
Events::ProgressStart(_) => {}
Events::ProgressUpdate(_) => {}
Events::Invalidated(_) => {}
Events::Other(_) => {}
}
}
pub async fn go_to_stack_frame(
workspace: WeakView<Workspace>,
stack_frame: StackFrame,
client: Arc<DebugAdapterClient>,
clear_highlights: bool,
mut cx: AsyncWindowContext,
) -> Result<()> {
let path = stack_frame.clone().source.unwrap().path.unwrap().clone();
let row = (stack_frame.line.saturating_sub(1)) as u32;
let column = (stack_frame.column.saturating_sub(1)) as u32;
if clear_highlights {
Self::remove_highlights(workspace.clone(), client, cx.clone()).await?;
}
let task = workspace.update(&mut cx, |workspace, cx| {
let project_path = workspace.project().read_with(cx, |project, cx| {
project.project_path_for_absolute_path(&Path::new(&path), cx)
});
if let Some(project_path) = project_path {
workspace.open_path_preview(project_path, None, false, true, cx)
} else {
Task::ready(Err(anyhow::anyhow!(
"No project path found for path: {}",
path
)))
}
})?;
let editor = task.await?.downcast::<Editor>().unwrap();
workspace.update(&mut cx, |_, cx| {
editor.update(cx, |editor, cx| {
editor.go_to_line::<DebugCurrentRowHighlight>(
row,
column,
Some(cx.theme().colors().editor_debugger_active_line_background),
cx,
);
})
})
}
async fn remove_highlights(
workspace: WeakView<Workspace>,
client: Arc<DebugAdapterClient>,
cx: AsyncWindowContext,
) -> Result<()> {
let mut tasks = Vec::new();
for thread_state in client.thread_states().values() {
for stack_frame in thread_state.stack_frames.clone() {
tasks.push(Self::remove_editor_highlight(
workspace.clone(),
stack_frame,
cx.clone(),
));
}
}
if !tasks.is_empty() {
try_join_all(tasks).await?;
}
anyhow::Ok(())
}
async fn remove_highlights_for_thread(
workspace: WeakView<Workspace>,
client: Arc<DebugAdapterClient>,
thread_id: u64,
cx: AsyncWindowContext,
) -> Result<()> {
let mut tasks = Vec::new();
if let Some(thread_state) = client.thread_states().get(&thread_id) {
for stack_frame in thread_state.stack_frames.clone() {
tasks.push(Self::remove_editor_highlight(
workspace.clone(),
stack_frame.clone(),
cx.clone(),
));
}
}
if !tasks.is_empty() {
try_join_all(tasks).await?;
}
anyhow::Ok(())
}
async fn remove_editor_highlight(
workspace: WeakView<Workspace>,
stack_frame: StackFrame,
mut cx: AsyncWindowContext,
) -> Result<()> {
let path = stack_frame.clone().source.unwrap().path.unwrap().clone();
let task = workspace.update(&mut cx, |workspace, cx| {
let project_path = workspace.project().read_with(cx, |project, cx| {
project.project_path_for_absolute_path(&Path::new(&path), cx)
});
if let Some(project_path) = project_path {
workspace.open_path(project_path, None, false, cx)
} else {
Task::ready(Err(anyhow::anyhow!(
"No project path found for path: {}",
path
)))
}
})?;
let editor = task.await?.downcast::<Editor>().unwrap();
editor.update(&mut cx, |editor, _| {
editor.clear_row_highlights::<DebugCurrentRowHighlight>();
})
}
fn handle_initialized_event(
client: Arc<DebugAdapterClient>,
_: &Option<Capabilities>,
cx: &mut ViewContext<Self>,
) {
cx.spawn(|this, mut cx| async move {
let task = this.update(&mut cx, |this, cx| {
this.workspace.update(cx, |workspace, cx| {
workspace.project().update(cx, |project, cx| {
let client = client.clone();
project.send_breakpoints(client, cx)
})
})
})??;
task.await?;
client.configuration_done().await
})
.detach_and_log_err(cx);
}
fn handle_continued_event(
client: Arc<DebugAdapterClient>,
event: &ContinuedEvent,
cx: &mut ViewContext<Self>,
) {
let all_threads = event.all_threads_continued.unwrap_or(false);
if all_threads {
for thread in client.thread_states().values_mut() {
thread.status = ThreadStatus::Running;
}
} else {
client.update_thread_state_status(event.thread_id, ThreadStatus::Running);
}
cx.notify();
}
fn handle_stopped_event(
client: Arc<DebugAdapterClient>,
event: &StoppedEvent,
cx: &mut ViewContext<Self>,
) {
let Some(thread_id) = event.thread_id else {
return;
};
let client_id = client.id();
cx.spawn({
let event = event.clone();
|this, mut cx| async move {
let stack_trace_response = client
.request::<StackTrace>(StackTraceArguments {
thread_id,
start_frame: None,
levels: None,
format: None,
})
.await?;
let current_stack_frame =
stack_trace_response.stack_frames.first().unwrap().clone();
let mut scope_tasks = Vec::new();
for stack_frame in stack_trace_response.stack_frames.clone().into_iter() {
let frame_id = stack_frame.id;
let client = client.clone();
scope_tasks.push(async move {
anyhow::Ok((
frame_id,
client
.request::<Scopes>(ScopesArguments { frame_id })
.await?,
))
});
}
let mut scopes: HashMap<u64, Vec<Scope>> = HashMap::new();
let mut variables: HashMap<u64, Vec<Variable>> = HashMap::new();
let mut variable_tasks = Vec::new();
for (thread_id, response) in try_join_all(scope_tasks).await? {
scopes.insert(thread_id, response.scopes.clone());
for scope in response.scopes {
let scope_reference = scope.variables_reference;
let client = client.clone();
variable_tasks.push(async move {
anyhow::Ok((
scope_reference,
client
.request::<Variables>(VariablesArguments {
variables_reference: scope_reference,
filter: None,
start: None,
count: None,
format: None,
})
.await?,
))
});
}
}
for (scope_reference, response) in try_join_all(variable_tasks).await? {
variables.insert(scope_reference, response.variables.clone());
}
this.update(&mut cx, |this, cx| {
let mut thread_state = client.thread_states();
let thread_state = thread_state
.entry(thread_id)
.or_insert(ThreadState::default());
thread_state.current_stack_frame_id = Some(current_stack_frame.clone().id);
thread_state.stack_frames = stack_trace_response.stack_frames;
thread_state.scopes = scopes;
thread_state.variables = variables;
thread_state.status = ThreadStatus::Stopped;
let existing_item = this
.pane
.read(cx)
.items()
.filter_map(|item| item.downcast::<DebugPanelItem>())
.any(|item| {
let item = item.read(cx);
item.client().id() == client_id && item.thread_id() == thread_id
});
if !existing_item {
let debug_panel = cx.view().clone();
this.pane.update(cx, |this, cx| {
let tab = cx.new_view(|cx| {
DebugPanelItem::new(debug_panel, client.clone(), thread_id, cx)
});
this.add_item(Box::new(tab.clone()), false, false, None, cx)
});
}
cx.emit(DebugPanelEvent::Stopped((client_id, event)));
if let Some(item) = this.pane.read(cx).active_item() {
if let Some(pane) = item.downcast::<DebugPanelItem>() {
let pane = pane.read(cx);
if pane.thread_id() == thread_id && pane.client().id() == client_id {
let workspace = this.workspace.clone();
let client = client.clone();
return cx.spawn(|_, cx| async move {
Self::go_to_stack_frame(
workspace,
current_stack_frame.clone(),
client,
true,
cx,
)
.await
});
}
}
}
Task::ready(anyhow::Ok(()))
})?
.await
}
})
.detach_and_log_err(cx);
}
fn handle_thread_event(
client: Arc<DebugAdapterClient>,
event: &ThreadEvent,
cx: &mut ViewContext<Self>,
) {
let thread_id = event.thread_id;
if event.reason == ThreadEventReason::Started {
client
.thread_states()
.insert(thread_id, ThreadState::default());
} else {
client.update_thread_state_status(thread_id, ThreadStatus::Ended);
// TODO: we want to figure out for witch clients/threads we should remove the highlights
cx.spawn({
let client = client.clone();
|this, mut cx| async move {
let workspace = this.update(&mut cx, |this, _| this.workspace.clone())?;
Self::remove_highlights_for_thread(workspace, client, thread_id, cx).await?;
anyhow::Ok(())
}
})
.detach_and_log_err(cx);
}
cx.emit(DebugPanelEvent::Thread((client.id(), event.clone())));
}
fn handle_exited_event(
client: Arc<DebugAdapterClient>,
_: &ExitedEvent,
cx: &mut ViewContext<Self>,
) {
cx.spawn(|_, _| async move {
for thread_state in client.thread_states().values_mut() {
thread_state.status = ThreadStatus::Exited;
}
})
.detach();
}
fn handle_terminated_event(
this: &mut Self,
client: Arc<DebugAdapterClient>,
event: &Option<TerminatedEvent>,
cx: &mut ViewContext<Self>,
) {
let restart_args = event.clone().and_then(|e| e.restart);
let workspace = this.workspace.clone();
cx.spawn(|_, cx| async move {
let should_restart = restart_args.is_some();
Self::remove_highlights(workspace, client.clone(), cx).await?;
client
.request::<Disconnect>(DisconnectArguments {
restart: Some(should_restart),
terminate_debuggee: None,
suspend_debuggee: None,
})
.await?;
if should_restart {
match client.request_type() {
DebugRequestType::Launch => client.launch(restart_args).await,
DebugRequestType::Attach => client.attach(restart_args).await,
}
} else {
anyhow::Ok(())
}
})
.detach_and_log_err(cx);
}
}
impl EventEmitter<PanelEvent> for DebugPanel {}
impl EventEmitter<DebugPanelEvent> for DebugPanel {}
impl EventEmitter<project::Event> for DebugPanel {}
impl FocusableView for DebugPanel {
fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
self.focus_handle.clone()
}
}
impl Panel for DebugPanel {
fn persistent_name() -> &'static str {
"DebugPanel"
}
fn position(&self, _cx: &WindowContext) -> DockPosition {
DockPosition::Bottom
}
fn position_is_valid(&self, position: DockPosition) -> bool {
position == DockPosition::Bottom
}
fn set_position(&mut self, _position: DockPosition, _cx: &mut ViewContext<Self>) {}
fn size(&self, _cx: &WindowContext) -> Pixels {
self.size
}
fn set_size(&mut self, size: Option<Pixels>, _cx: &mut ViewContext<Self>) {
self.size = size.unwrap();
}
fn icon(&self, _cx: &WindowContext) -> Option<IconName> {
None
}
fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
None
}
fn toggle_action(&self) -> Box<dyn Action> {
Box::new(ToggleFocus)
}
fn icon_label(&self, _: &WindowContext) -> Option<String> {
None
}
fn is_zoomed(&self, _cx: &WindowContext) -> bool {
false
}
fn starts_open(&self, _cx: &WindowContext) -> bool {
false
}
fn set_zoomed(&mut self, _zoomed: bool, _cx: &mut ViewContext<Self>) {}
fn set_active(&mut self, _active: bool, _cx: &mut ViewContext<Self>) {}
}
impl Render for DebugPanel {
fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
v_flex()
.key_context("DebugPanel")
.track_focus(&self.focus_handle)
.size_full()
.child(self.pane.clone())
.into_any()
}
}

View File

@@ -0,0 +1,514 @@
use crate::debugger_panel::{DebugPanel, DebugPanelEvent};
use anyhow::Result;
use dap::client::{DebugAdapterClient, DebugAdapterClientId, ThreadState, ThreadStatus};
use dap::{Scope, StackFrame, StoppedEvent, ThreadEvent, Variable};
use gpui::{
actions, list, AnyElement, AppContext, AsyncWindowContext, EventEmitter, FocusHandle,
FocusableView, ListState, Subscription, View, WeakView,
};
use std::sync::Arc;
use ui::WindowContext;
use ui::{prelude::*, Tooltip};
use workspace::item::{Item, ItemEvent};
pub struct DebugPanelItem {
thread_id: u64,
focus_handle: FocusHandle,
stack_frame_list: ListState,
client: Arc<DebugAdapterClient>,
_subscriptions: Vec<Subscription>,
current_stack_frame_id: Option<u64>,
}
actions!(
debug_panel_item,
[Continue, StepOver, StepIn, StepOut, Restart, Pause, Stop]
);
impl DebugPanelItem {
pub fn new(
debug_panel: View<DebugPanel>,
client: Arc<DebugAdapterClient>,
thread_id: u64,
cx: &mut ViewContext<Self>,
) -> Self {
let focus_handle = cx.focus_handle();
let weakview = cx.view().downgrade();
let stack_frame_list =
ListState::new(0, gpui::ListAlignment::Top, px(1000.), move |ix, cx| {
if let Some(view) = weakview.upgrade() {
view.update(cx, |view, cx| {
view.render_stack_frame(ix, cx).into_any_element()
})
} else {
div().into_any()
}
});
let _subscriptions = vec![cx.subscribe(&debug_panel, {
move |this: &mut Self, _, event: &DebugPanelEvent, cx| {
match event {
DebugPanelEvent::Stopped((client_id, event)) => {
Self::handle_stopped_event(this, client_id, event, cx)
}
DebugPanelEvent::Thread((client_id, event)) => {
Self::handle_thread_event(this, client_id, event, cx)
}
};
}
})];
Self {
client,
thread_id,
focus_handle,
_subscriptions,
stack_frame_list,
current_stack_frame_id: None,
}
}
fn should_skip_event(
this: &mut Self,
client_id: &DebugAdapterClientId,
thread_id: u64,
) -> bool {
thread_id != this.thread_id || *client_id != this.client.id()
}
fn handle_stopped_event(
this: &mut Self,
client_id: &DebugAdapterClientId,
event: &StoppedEvent,
cx: &mut ViewContext<Self>,
) {
if Self::should_skip_event(this, client_id, event.thread_id.unwrap_or_default()) {
return;
}
this.stack_frame_list
.reset(this.current_thread_state().stack_frames.len());
cx.notify();
}
fn handle_thread_event(
this: &mut Self,
client_id: &DebugAdapterClientId,
event: &ThreadEvent,
_: &mut ViewContext<Self>,
) {
if Self::should_skip_event(this, client_id, event.thread_id) {
return;
}
// TODO: handle thread event
}
}
impl EventEmitter<ItemEvent> for DebugPanelItem {}
impl FocusableView for DebugPanelItem {
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
self.focus_handle.clone()
}
}
impl Item for DebugPanelItem {
type Event = ItemEvent;
fn tab_content(
&self,
params: workspace::item::TabContentParams,
_: &WindowContext,
) -> AnyElement {
Label::new(format!(
"{} - Thread {}",
self.client.config().id,
self.thread_id
))
.color(if params.selected {
Color::Default
} else {
Color::Muted
})
.into_any_element()
}
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
Some(SharedString::from(format!(
"{} Thread {} - {:?}",
self.client.config().id,
self.thread_id,
self.current_thread_state().status
)))
}
}
impl DebugPanelItem {
pub fn client(&self) -> Arc<DebugAdapterClient> {
self.client.clone()
}
pub fn thread_id(&self) -> u64 {
self.thread_id
}
fn stack_frame_for_index(&self, ix: usize) -> StackFrame {
self.client
.thread_state_by_id(self.thread_id)
.stack_frames
.get(ix)
.cloned()
.unwrap()
}
fn current_thread_state(&self) -> ThreadState {
self.client
.thread_states()
.get(&self.thread_id)
.cloned()
.unwrap()
}
fn render_stack_frames(&self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
v_flex()
.w_1_3()
.gap_3()
.h_full()
.child(list(self.stack_frame_list.clone()).size_full())
.into_any()
}
fn render_stack_frame(&self, ix: usize, cx: &mut ViewContext<Self>) -> impl IntoElement {
let stack_frame = self.stack_frame_for_index(ix);
let source = stack_frame.source.clone();
let selected_frame_id = self.current_stack_frame_id;
let is_selected_frame = Some(stack_frame.id) == selected_frame_id;
let formatted_path = format!(
"{}:{}",
source.clone().and_then(|s| s.name).unwrap_or_default(),
stack_frame.line,
);
v_flex()
.rounded_md()
.group("")
.id(("stack-frame", stack_frame.id))
.tooltip({
let formatted_path = formatted_path.clone();
move |cx| Tooltip::text(formatted_path.clone(), cx)
})
.p_1()
.when(is_selected_frame, |this| {
this.bg(cx.theme().colors().element_hover)
})
.on_click(cx.listener({
let stack_frame = stack_frame.clone();
move |this, _, _| {
this.current_stack_frame_id = Some(stack_frame.id);
// let client = this.client();
// DebugPanel::go_to_stack_frame(&stack_frame, client, true, cx)
// .detach_and_log_err(cx);
// TODO:
// this.go_to_stack_frame(&stack_frame, this.client.clone(), false, cx)
// .detach_and_log_err(cx);
// cx.notify();
}
}))
.hover(|s| s.bg(cx.theme().colors().element_hover).cursor_pointer())
.child(
h_flex()
.gap_0p5()
.text_ui_sm(cx)
.child(stack_frame.name.clone())
.child(formatted_path),
)
.child(
h_flex()
.text_ui_xs(cx)
.text_color(cx.theme().colors().text_muted)
.when_some(source.and_then(|s| s.path), |this, path| this.child(path)),
)
.into_any()
}
fn render_scopes(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let thread_state = self.current_thread_state();
let Some(scopes) = thread_state
.current_stack_frame_id
.and_then(|id| thread_state.scopes.get(&id))
else {
return div().child("No scopes for this thread yet").into_any();
};
div()
.w_3_4()
.gap_3()
.text_ui_sm(cx)
.children(
scopes
.iter()
.map(|scope| self.render_scope(&thread_state, scope, cx)),
)
.into_any()
}
fn render_scope(
&self,
thread_state: &ThreadState,
scope: &Scope,
cx: &mut ViewContext<Self>,
) -> impl IntoElement {
div()
.id(("scope", scope.variables_reference))
.p_1()
.text_ui_sm(cx)
.hover(|s| s.bg(cx.theme().colors().element_hover).cursor_pointer())
.child(scope.name.clone())
.child(
div()
.ml_2()
.child(self.render_variables(thread_state, scope, cx)),
)
.into_any()
}
fn render_variables(
&self,
thread_state: &ThreadState,
scope: &Scope,
cx: &mut ViewContext<Self>,
) -> impl IntoElement {
let Some(variables) = thread_state.variables.get(&scope.variables_reference) else {
return div().child("No variables for this thread yet").into_any();
};
div()
.gap_3()
.text_ui_sm(cx)
.children(
variables
.iter()
.map(|variable| self.render_variable(variable, cx)),
)
.into_any()
}
fn render_variable(&self, variable: &Variable, cx: &mut ViewContext<Self>) -> impl IntoElement {
h_flex()
.id(("variable", variable.variables_reference))
.p_1()
.gap_1()
.text_ui_sm(cx)
.hover(|s| s.bg(cx.theme().colors().element_hover).cursor_pointer())
.child(variable.name.clone())
.child(
div()
.text_ui_xs(cx)
.text_color(cx.theme().colors().text_muted)
.child(variable.value.clone()),
)
.into_any()
}
// if the debug adapter does not send the continued event,
// and the status of the thread did not change we have to assume the thread is running
// so we have to update the thread state status to running
fn update_thread_state(
this: WeakView<Self>,
previous_status: ThreadStatus,
all_threads_continued: Option<bool>,
mut cx: AsyncWindowContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
if previous_status == this.current_thread_state().status {
if all_threads_continued.unwrap_or(false) {
for thread in this.client.thread_states().values_mut() {
thread.status = ThreadStatus::Running;
}
} else {
this.client
.update_thread_state_status(this.thread_id, ThreadStatus::Running);
}
cx.notify();
}
})
}
fn handle_continue_action(&mut self, _: &Continue, cx: &mut ViewContext<Self>) {
let client = self.client.clone();
let thread_id = self.thread_id;
let previous_status = self.current_thread_state().status;
cx.spawn(|this, cx| async move {
let response = client.resume(thread_id).await?;
Self::update_thread_state(this, previous_status, response.all_threads_continued, cx)
})
.detach_and_log_err(cx);
}
fn handle_step_over_action(&mut self, _: &StepOver, cx: &mut ViewContext<Self>) {
let client = self.client.clone();
let thread_id = self.thread_id;
let previous_status = self.current_thread_state().status;
cx.spawn(|this, cx| async move {
client.step_over(thread_id).await?;
Self::update_thread_state(this, previous_status, None, cx)
})
.detach_and_log_err(cx);
}
fn handle_step_in_action(&mut self, _: &StepIn, cx: &mut ViewContext<Self>) {
let client = self.client.clone();
let thread_id = self.thread_id;
let previous_status = self.current_thread_state().status;
cx.spawn(|this, cx| async move {
client.step_in(thread_id).await?;
Self::update_thread_state(this, previous_status, None, cx)
})
.detach_and_log_err(cx);
}
fn handle_step_out_action(&mut self, _: &StepOut, cx: &mut ViewContext<Self>) {
let client = self.client.clone();
let thread_id = self.thread_id;
let previous_status = self.current_thread_state().status;
cx.spawn(|this, cx| async move {
client.step_out(thread_id).await?;
Self::update_thread_state(this, previous_status, None, cx)
})
.detach_and_log_err(cx);
}
fn handle_restart_action(&mut self, _: &Restart, cx: &mut ViewContext<Self>) {
let client = self.client.clone();
cx.background_executor()
.spawn(async move { client.restart().await })
.detach();
}
fn handle_pause_action(&mut self, _: &Pause, cx: &mut ViewContext<Self>) {
let client = self.client.clone();
let thread_id = self.thread_id;
cx.background_executor()
.spawn(async move { client.pause(thread_id).await })
.detach();
}
fn handle_stop_action(&mut self, _: &Stop, cx: &mut ViewContext<Self>) {
let client = self.client.clone();
cx.background_executor()
.spawn(async move { client.stop().await })
.detach();
}
}
impl Render for DebugPanelItem {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let thread_status = self.current_thread_state().status;
v_flex()
.key_context("DebugPanelItem")
.track_focus(&self.focus_handle)
.capture_action(cx.listener(Self::handle_continue_action))
.capture_action(cx.listener(Self::handle_step_over_action))
.capture_action(cx.listener(Self::handle_step_in_action))
.capture_action(cx.listener(Self::handle_step_out_action))
.capture_action(cx.listener(Self::handle_restart_action))
.capture_action(cx.listener(Self::handle_pause_action))
.capture_action(cx.listener(Self::handle_stop_action))
.p_2()
.size_full()
.items_start()
.child(
h_flex()
.gap_2()
.map(|this| {
if self.current_thread_state().status == ThreadStatus::Running {
this.child(
IconButton::new("debug-pause", IconName::DebugPause)
.on_click(
cx.listener(|_, _, cx| cx.dispatch_action(Box::new(Pause))),
)
.tooltip(move |cx| Tooltip::text("Pause program", cx)),
)
} else {
this.child(
IconButton::new("debug-continue", IconName::DebugContinue)
.on_click(cx.listener(|_, _, cx| {
cx.dispatch_action(Box::new(Continue))
}))
.disabled(thread_status != ThreadStatus::Stopped)
.tooltip(move |cx| Tooltip::text("Continue program", cx)),
)
}
})
.child(
IconButton::new("debug-step-over", IconName::DebugStepOver)
.on_click(
cx.listener(|_, _, cx| cx.dispatch_action(Box::new(StepOver))),
)
.disabled(thread_status != ThreadStatus::Stopped)
.tooltip(move |cx| Tooltip::text("Step over", cx)),
)
.child(
IconButton::new("debug-step-in", IconName::DebugStepInto)
.on_click(cx.listener(|_, _, cx| cx.dispatch_action(Box::new(StepIn))))
.disabled(thread_status != ThreadStatus::Stopped)
.tooltip(move |cx| Tooltip::text("Step in", cx)),
)
.child(
IconButton::new("debug-step-out", IconName::DebugStepOut)
.on_click(cx.listener(|_, _, cx| cx.dispatch_action(Box::new(StepOut))))
.disabled(thread_status != ThreadStatus::Stopped)
.tooltip(move |cx| Tooltip::text("Step out", cx)),
)
.child(
IconButton::new("debug-restart", IconName::DebugRestart)
.on_click(cx.listener(|_, _, cx| cx.dispatch_action(Box::new(Restart))))
.disabled(
!self
.client
.capabilities()
.supports_restart_request
.unwrap_or_default()
|| thread_status != ThreadStatus::Stopped
&& thread_status != ThreadStatus::Running,
)
.tooltip(move |cx| Tooltip::text("Restart", cx)),
)
.child(
IconButton::new("debug-stop", IconName::DebugStop)
.on_click(cx.listener(|_, _, cx| cx.dispatch_action(Box::new(Stop))))
.disabled(
thread_status != ThreadStatus::Stopped
&& thread_status != ThreadStatus::Running,
)
.tooltip(move |cx| Tooltip::text("Stop", cx)),
),
)
.child(
h_flex()
.size_full()
.items_start()
.p_1()
.gap_4()
.child(self.render_stack_frames(cx))
.child(self.render_scopes(cx)),
)
.into_any()
}
}

View File

@@ -0,0 +1,18 @@
use debugger_panel::{DebugPanel, ToggleFocus};
use gpui::AppContext;
use workspace::{StartDebugger, Workspace};
pub mod debugger_panel;
mod debugger_panel_item;
pub fn init(cx: &mut AppContext) {
cx.observe_new_views(|workspace: &mut Workspace, _| {
workspace.register_action(|workspace, _: &ToggleFocus, cx| {
workspace.toggle_panel_focus::<DebugPanel>(cx);
});
workspace.register_action(|workspace: &mut Workspace, _: &StartDebugger, cx| {
tasks_ui::toggle_modal(workspace, cx, task::TaskType::Debug).detach();
});
})
.detach();
}

View File

@@ -4,13 +4,13 @@ mod toolbar_controls;
#[cfg(test)]
mod diagnostics_tests;
mod grouped_diagnostics;
pub(crate) mod grouped_diagnostics;
use anyhow::Result;
use collections::{BTreeSet, HashSet};
use editor::{
diagnostic_block_renderer,
display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock},
display_map::{BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, RenderBlock},
highlight_diagnostic_message,
scroll::Autoscroll,
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
@@ -85,7 +85,7 @@ struct DiagnosticGroupState {
primary_diagnostic: DiagnosticEntry<language::Anchor>,
primary_excerpt_ix: usize,
excerpts: Vec<ExcerptId>,
blocks: HashSet<BlockId>,
blocks: HashSet<CustomBlockId>,
block_count: usize,
}
@@ -237,13 +237,13 @@ impl ProjectDiagnosticsEditor {
fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
if let Some(existing) = workspace.item_of_type::<ProjectDiagnosticsEditor>(cx) {
workspace.activate_item(&existing, cx);
workspace.activate_item(&existing, true, true, cx);
} else {
let workspace_handle = cx.view().downgrade();
let diagnostics = cx.new_view(|cx| {
ProjectDiagnosticsEditor::new(workspace.project().clone(), workspace_handle, cx)
});
workspace.add_item_to_active_pane(Box::new(diagnostics), None, cx);
workspace.add_item_to_active_pane(Box::new(diagnostics), None, true, cx);
}
}
@@ -649,11 +649,7 @@ impl Item for ProjectDiagnosticsEditor {
fn tab_content(&self, params: TabContentParams, _: &WindowContext) -> AnyElement {
if self.summary.error_count == 0 && self.summary.warning_count == 0 {
Label::new("No problems")
.color(if params.selected {
Color::Default
} else {
Color::Muted
})
.color(params.text_color())
.into_any_element()
} else {
h_flex()
@@ -663,13 +659,10 @@ impl Item for ProjectDiagnosticsEditor {
h_flex()
.gap_1()
.child(Icon::new(IconName::XCircle).color(Color::Error))
.child(Label::new(self.summary.error_count.to_string()).color(
if params.selected {
Color::Default
} else {
Color::Muted
},
)),
.child(
Label::new(self.summary.error_count.to_string())
.color(params.text_color()),
),
)
})
.when(self.summary.warning_count > 0, |then| {
@@ -677,13 +670,10 @@ impl Item for ProjectDiagnosticsEditor {
h_flex()
.gap_1()
.child(Icon::new(IconName::ExclamationTriangle).color(Color::Warning))
.child(Label::new(self.summary.warning_count.to_string()).color(
if params.selected {
Color::Default
} else {
Color::Muted
},
)),
.child(
Label::new(self.summary.warning_count.to_string())
.color(params.text_color()),
),
)
})
.into_any_element()

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