Compare commits

..

359 Commits

Author SHA1 Message Date
Thorsten Ball
b53c649cfb prompts: Extend edit_workflow prompt with Go examples 2024-08-14 14:54:00 +02:00
Piotr Osiewicz
e28681c27e outline: Match on full item path in Outline::find_most_similar (#16206)
Previously, we were only looking at a simple syntax node name at a time
and not the full path to an item. E.g. in a rust-toolchain.toml file:
```rs
[toolchain]
targets = [ "x86_64-apple-darwin", "aarch64-apple-darwin", "x86_64-unknown-linux-gnu", "wasm32-wasi" ]
```
When matching against a query "toolchain targets" from the Assistant,
we'd try to match it against "toolchain" and "targets" and not against
"toolchain targets" - we only look at the name of the innermost node and
not it's full path.
I'd expect it to significantly improve precision of outline item
matching.

Release Notes:

- N/A

Co-authored-by: Bennet Bo <bennet@zed.dev>
2024-08-14 14:03:28 +02:00
Thorsten Ball
b7dcd4e4d6 assistant panel: Fix pending completions not being cleaned up (#16201)
Turns out that you could always cancel a completion, even if it was
already done and completed, because it was never cleaned up.

Release Notes:

- N/A
2024-08-14 12:13:09 +02:00
Thorsten Ball
8b8335f449 assistant panel: Stop animation & show explicit state if canceled (#16200)
This fixes a bug by stopping the animation when a completion is canceled
and it also makes the state more explicit, which I think is very
valuable.



https://github.com/user-attachments/assets/9ede9b25-86ac-4901-8434-7407896bb799


Release Notes:

- N/A
2024-08-14 11:18:40 +02:00
Mikayla Maki
97469cd049 Improve slash commands (#16195)
This PR:

- Makes slash commands easier to compose by adding a concept,
`CompletionIntent`. When using `tab` on a completion in the assistant
panel, that completion item will be expanded but the associated command
will not be run. Using `enter` will still either run the completion item
or continue command composition as before.
- Fixes a bug where running `/diagnostics` on a project with no
diagnostics will delete the entire command, rather than rendering an
empty header.
- Improves the autocomplete rendering for files, showing when
directories are selected and re-arranging the results to have the file
name or trailing directory show first.

<img width="642" alt="Screenshot 2024-08-13 at 8 12 43 PM"
src="https://github.com/user-attachments/assets/97c96cd2-741f-4f15-ad03-7cf78129a71c">


Release Notes:

- N/A
2024-08-13 23:06:07 -07:00
Nathan Sobo
5cb4de4ec6 Fix regression: Restore creation of multiple assist editors on ctrl-enter when selections span across multiple excerpts (#16190)
Release Notes:

- N/A
2024-08-13 17:19:10 -06:00
Marshall Bowers
47628515e1 assistant: Put /docs and /project behind a setting (#16186)
This PR puts the availability of the `/docs` and `/project` slash
commands behind their respective settings.

Release Notes:

- N/A
2024-08-13 17:32:24 -04:00
jvmncs
c3edbd7d9a Simplify inline assist to avoid spurious xml in completions (#16184)
Some prompt changes to highlight:
- Removes `<rewrite_section>` rendering, preferring to just show
`<rewrite_section_with_selections>`
- Concise, terse instructions throughout

I'd like to have experimented with prefilling the assistant response,
but I don't think OpenAI allows for that and wouldn't want to break
compatibility with gpt-4 et al.

Release Notes:

- N/A
2024-08-13 17:05:50 -04:00
Marshall Bowers
aa12ae0e3c Allow extensions to suggest packages for /docs completions (#16185)
This PR gives extensions the ability to suggest packages to show up in
`/docs` completions by default.

Release Notes:

- N/A
2024-08-13 16:50:57 -04:00
Danilo Leal
c6a1d9aa33 assistant: Polish terms of service toast design (#16183)
Pushing in tiny design tweaks and wording change on the button so it's a bit more explicit.

Release Notes:

- N/A
2024-08-13 17:31:46 -03:00
Danilo Leal
07c21bb8ee assistant: Change the quote selection crease icon (#16180)
Felt like this icon was more fitting to the idea of selected text, and looked less like a document.

Release Notes:

- N/A
2024-08-13 17:31:23 -03:00
Danilo Leal
2f5031bd28 Adjust pulsating animation ranges (#16179)
Just a fine-grain refinement to the pulsating animation range.

Release Notes:

- N/A
2024-08-13 17:31:10 -03:00
Danilo Leal
170ad46f5e assistant: Add gap to the context toolbar (#16178)
Not the ideal solution yet, but just a small treatment so these two blocks don't collide.

Release Notes:

- N/A
2024-08-13 17:30:57 -03:00
Marshall Bowers
a81e355dc5 Promote package suggestions to a first-class concept on IndexedDocsProviders (#16177)
This PR promotes package suggestions to a first-class concept on the
`IndexedDocsProvider` trait.

This will allow any implementer of `IndexedDocsProvider` to provide a
list of package names to suggest for use with `/docs`.

For the docs.rs provider we use the 250 most popular Rust crates (as
identified [here](https://lib.rs/std)), and for the rustdoc provider we
use the packages in the Cargo workspace.

Release Notes:

- N/A
2024-08-13 16:01:58 -04:00
Peter Tripp
bd71e9192c Match VSCode behavior for ctrl-a/ctrl-e on MacOS (#15981)
- Make `ctrl-a` and `ctrl-e` ignore soft_wraps on MacOS, matching the behavior of VSCode.
- Unchanged: `home`, `end`, `cmd-left`, `cmd-right` respect softwrap (both in Zed and VSCode).
2024-08-13 15:12:20 -04:00
Piotr Osiewicz
7eeb37262a assistant: Fix nested slash command rendering (#16173)
/rant on
We have this issue where if a prompt starts with a slash command (e.g.
/workflow), the rendering is a bit messed up. The nested slash command
gets picked up as the parent of a command that includes it (/prompt).
This is due to how we parse slash commands; their output is obtained
asynchronously. When we run `/prompt "My prompt"` whose contents are
`/workflow`, we first include the prompt content verbatim and then
reparse the whole buffer, picking up /workflow as a new command (as if
it was typed by an user). The problem with that is that the range of
parent /prompt does not include the expanded range of a /workflow; in
fact, after doing full expansion of "My prompt", we lose track of the
parent-children relationship of these two slash commands and treat them
as if top-level user prompt was `/workflow/prompt "My prompt"` and not
`/prompt "My prompt"` (which, by the way, would not be parsable for us).

The "proper" fix would be to update the parent range whenever we parse a
new children within it. We could do that. But then, the question is;
what do we gain from it? Slash command output is put behind a crease,
which is fundamentally a fold. Given "My prompt", we'd have to put two
fold indicators on a single line even if the ranges were set up
correctly. So that merely moves the target elsewhere into yet another
issue. Even if we did solve two-fold problem somehow (by e.g. sorting
same-line folds by the end point), we would still be stuck with
suboptimal render. What do we gain from all that anyways? Proper
handling of a relatively obscure (although - at the same time - quite
common) edge case which may as well be handled by having /prompt insert
a new line if there's a slight chance that the edge case could occur.
And that hacky, "inproper" solution is what this PR does; in fact, it's
not the first time it was done, as /default also has the same issue
which it solves in precisely the same manner.

/rant off

Release Notes:

- N/A
2024-08-13 20:50:51 +02:00
Athish Pranav D
d4761a3296 Recognize Cuda files as C++ (#16098)
Signed-off-by: Athish Pranav D <athishanna@gmail.com>
2024-08-13 14:44:48 -04:00
Marshall Bowers
ac30ed0754 assistant: Populate /docs rustdoc with workspace crates (#16172)
This PR makes the `/docs` slash command populate the list with all of
the workspace crates when using the local rustdoc provider.

The workspace crates are shown in the search results when a crate is not
already indexed:

<img width="577" alt="Screenshot 2024-08-13 at 2 18 39 PM"
src="https://github.com/user-attachments/assets/39bee576-8e1a-4b21-a9f8-7951ebae4cc3">

These crates are shown with an `(unindexed)` suffix to convey this:

<img width="570" alt="Screenshot 2024-08-13 at 2 18 45 PM"
src="https://github.com/user-attachments/assets/4eeb07f7-378f-44d4-ae11-4ffe45a23964">

Release Notes:

- N/A
2024-08-13 14:31:34 -04:00
Richard Feldman
b1a581e81b Copy/paste images into editors (Mac only) (#15782)
For future reference: WIP branch of copy/pasting a mixture of images and
text: https://github.com/zed-industries/zed/tree/copy-paste-images -
we'll come back to that one after landing this one.

Release Notes:

- You can now paste images into the Assistant Panel to include them as
context. Currently works only on Mac, and with Anthropic models. Future
support is planned for more models, operating systems, and image
clipboard operations.

---------

Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Jason <jason@zed.dev>
Co-authored-by: Kyle <kylek@zed.dev>
2024-08-13 13:18:25 -04:00
Marshall Bowers
e3b0de5dda assistant: Auto-suggest crates for /docs when using the docs.rs provider (#16169)
This PR improves the initial experience of using `/docs docs-rs` with an
empty index.

We now show a brief explainer of what is expected (a crate name) as well
as list some popular Rust crates to try:

<img width="540" alt="Screenshot 2024-08-13 at 12 25 39 PM"
src="https://github.com/user-attachments/assets/942de250-2901-45df-9e3e-52ff3b3fc517">

Release Notes:

- N/A
2024-08-13 12:37:13 -04:00
张小白
03796e79b0 repl: Don't show cmd window on Windows (#16016)
Closes #15955 .

Release Notes:

- Fixed `cmd` window showing when repl executing
commands([#15955](https://github.com/zed-industries/zed/issues/15955) ).
2024-08-13 09:12:42 -07:00
Piotr Osiewicz
fa51651d06 assistant: Fix debug inspector removing workflow step it's applied to (#16166)
Debug inspector broke immediately after merge as #16036 landed in
parallel; one of the changes of that PR is removing any steps whose
content was edited, which is what debug inspector happened to do. The
fix is to make the edit right past the step block.


Release Notes:

- N/A
2024-08-13 18:01:55 +02:00
Kirill Bulatov
7aed240729 Improve /tabs completion workflow (#16168)
Follow-up of https://github.com/zed-industries/zed/pull/16154

Reworks /tabs arguments to allow:
* current tab by default, if no arguments are present
* fuzzy-matching over paths of the related tabs
* `all` case to insert all tabs at once

Release Notes:

- N/A
2024-08-13 18:40:24 +03:00
Marshall Bowers
7b613cb169 Revert "Remove extra empty space for files when file icons are turned off (#16142) (#16167)
This PR reverts #16142, as it isn't what we want from a design
standpoint.

Having the file names misaligned from the folder names is not the
desired behavior:

<img width="243" alt="Screenshot 2024-08-13 at 11 16 53 AM"
src="https://github.com/user-attachments/assets/12914e89-2641-4932-96c2-00e89e56d6d7">

We can revisit when we have design bandwidth.

This reverts commit ee6a40137f.

Release Notes:

- Reverted #16142.
  - @JosephTLyons @notpeter for release notes curation
2024-08-13 11:37:29 -04:00
Marshall Bowers
8a9c58e515 zed_extension_api: Add HttpRequestBuilder (#16165)
This PR adds an `HttpRequestBuilder` to the extension API to allow for a
more ergonomic way for constructing HTTP requests within extensions.

The HTTP client functionality is now also exposed via the
`zed_extension_api::http_client` module instead of top-level.

Release Notes:

- N/A
2024-08-13 11:12:10 -04:00
Piotr Osiewicz
0dbecee03f assistant: Refine workflow step labels (#16161)
https://github.com/user-attachments/assets/f6325507-091a-482e-ac28-dd09877ebaa2


Release Notes:

- N/A

---------

Co-authored-by: Danilo <daniloleal09@gmail.com>
2024-08-13 17:08:55 +02:00
Marshall Bowers
82529499df Fix min and max versions for v0.1.0 of the extension API (#16163)
Missed this in #16158.

Release Notes:

- N/A
2024-08-13 10:40:29 -04:00
Marshall Bowers
4450ebff6b Allow extensions to control the redirect policy for the HTTP client (#16162)
This PR extends the extension API with support for controlling the
redirect policy used by the HTTP client.

Release Notes:

- N/A
2024-08-13 10:40:21 -04:00
Marshall Bowers
98a2ab0686 zed_extension_api: Bump to v0.1.0 (#16158)
This PR changes v0.0.7 of the extension API to v0.1.0.

We had a false-start in releasing v0.0.7, which has since been yanked,
so we need a new version number. We'll publish v0.1.0 to crates.io once
the Preview build is out tomorrow.

We're incrementing the minor version so that we have some leeway in
putting out patch releases of the crate within a given extension API
release.

Release Notes:

- N/A
2024-08-13 10:04:34 -04:00
Rudolf Kastl
47eed12f77 Update zed.desktop.in to include the MimeType for empty files by default (#15623)
Update zed.desktop.in to include the MimeType for empty files.
Seems to be the default for all "text editors" .desktop files.

Release Notes:

- Improved MimeType list in XDG .desktop file
2024-08-13 16:56:44 +03:00
Marshall Bowers
cade9fbd3d assistant: Show a better error when /docs is used without a package name (#16157)
This PR makes it so that running `/docs` without providing a package
name gives a better error message:

<img width="248" alt="Screenshot 2024-08-13 at 9 24 58 AM"
src="https://github.com/user-attachments/assets/c1cc794e-0fa0-490a-871a-a56702b03d42">

<img width="228" alt="Screenshot 2024-08-13 at 9 25 05 AM"
src="https://github.com/user-attachments/assets/45dca2d7-171f-48f0-a03c-254b552cb50d">

Release Notes:

- N/A
2024-08-13 09:39:26 -04:00
CharlesChen0823
fe190359d5 editor: Add revert file action to command palette (#16012)
Release Notes:

- Added an `editor::RevertFile` action
2024-08-13 14:44:41 +03:00
fcolecumberri
ab98f16280 Update key-bindings.md (#15994)
Closes https://github.com/zed-industries/zed/pull/15238

Release Notes:

- N/A
2024-08-13 14:40:27 +03:00
张小白
ac6bff12b9 windows: Remove unused dependencies (#15857)
I have removed some unused dependencies, reducing the total number of
packages during the build from 1141 to 1112. This should slightly
decrease the build time.

![Screenshot 2024-08-06
230726](https://github.com/user-attachments/assets/58a49fd4-4a0a-4026-b6b7-79b95529ec74)


Release Notes:

- N/A
2024-08-13 13:31:02 +03:00
Kirill Bulatov
081cbcebd9 Merge /active command into /tabs one (#16154)
Now, tabs have arguments, `active` (default, applied also for no
arguments case) and `all` to insert the active tab only or all tabs.

Release Notes:

- N/A
2024-08-13 13:15:57 +03:00
Stanislav Alekseev
c2b254a67a Fallback to using tree-sitter when determining ranges for info popovers (#16062)
Closes #15382

Release Notes:

- Added fallback to a smallest tree sitter node when hovering over a
symbol
2024-08-13 12:01:14 +02:00
Thorsten Ball
af36d4934c assistant panel: Animate assistant label if message is pending (#16152)
This adds a pulsating effect to the assistant header in case the message
is pending.

The pulsating effect is capped between 0.2 and 1.0 and I tried (with the
help of Claude) to give it a "breathing" effect, since I found the
normal bounce a bit too much.

Also opted for setting the `alpha` on the `LabelLike` things, vs.
overwriting the color, since I think that's cleaner instead of exposing
the color and mutating that.


https://github.com/user-attachments/assets/4a94a1c5-8dc7-4c40-b30f-d92d112db7b5


Release Notes:

- N/A
2024-08-13 11:41:44 +02:00
Kirill Bulatov
b36d1386a9 Fix editor::Cancel action not inline assistant inputs created for empty selections (#16150)
Release Notes:

- N/A
2024-08-13 12:11:57 +03:00
Uladzislau Kaminski
ee6a40137f Remove extra empty space for files when file icons are turned off (#16142)
Closes #16073

<img width="269" alt="image" src="https://github.com/user-attachments/assets/88b7ff9f-17ec-4764-b37a-c218d7ad14ec">

Release Notes:

- Removed extra empty space for files when file icons are turned off ([#16073](https://github.com/zed-industries/zed/issues/16073))
2024-08-13 12:09:13 +03:00
Nathan Sobo
1c189e82a0 Improve tooltip text (#16147)
![image](https://github.com/user-attachments/assets/90faf9ce-0515-4a99-92ca-c69b17b5149e)

Release Notes:

- Include a count of the context tokens when hovering token counts in
the inline assist.
2024-08-12 23:24:27 -06:00
Nathan Sobo
a515442a36 Rely on model to determine indentation level and always rewrite the full line (#16145)
This PR simplifies our approach to indentation in the inline assistant
in hopes of improving our experience for Python. We tell the model to
generate the correct indentation in the prompt, and always start
generating at the start of the line. This may fall down for less capable
models, but I want to get a solid experience on the best models and then
figure the rest out later.

Also: We now prefer `./assets/prompts` as an overrides directory when
stdout is a PTY, so you can do `cargo run` and then iterate prompts for
the current run inside the current working copy.

cc @trishume @dsp-ant 

Release Notes:

- Zed now allows the model to control indentation when performing inline
transformation. We're hoping this improves the indentation experience in
Python and other indentation-sensitive languages, but it does require
more from the model.

---------

Co-authored-by: Mikayla <mikayla@zed.dev>
2024-08-12 22:41:24 -06:00
Marshall Bowers
e662bfc74f Configure squawk rules (#16144)
This PR configures [`squawk`](https://squawkhq.com/) to match our
database conventions.

Release Notes:

- N/A
2024-08-12 22:11:36 -04:00
Marshall Bowers
b4c22cc861 collab: Add ability to revoke LLM service access tokens (#16143)
This PR adds the ability to revoke access tokens for the LLM service.

There is a new `revoked_access_tokens` table that contains the
identifiers (`jti`) of revoked access tokens.

To revoke an access token, insert a record into this table:

```sql
insert into revoked_access_tokens (jti) values ('1e887b9e-37f5-49e8-8feb-3274e5a86b67');
```

We now attach the `jti` as `authn.jti` to the tracing spans so that we
can associate an access token with a given request to the LLM service.

Release Notes:

- N/A
2024-08-12 21:47:05 -04:00
Piotr Osiewicz
0bc9fc9487 assistant: Slash command tweaks (#16140)
Release Notes:

- N/A
2024-08-13 02:34:03 +02:00
Max Brunsfeld
dbcd06642c Track lifetime spending for each user and model (#16137)
Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
2024-08-12 20:15:26 -04:00
Piotr Osiewicz
68ae347077 chore: Remove a bunch of unused structs (#16139)
Found by beta clippy.

Release Notes:

- N/A
2024-08-13 01:43:19 +02:00
Stanislav Alekseev
f956257638 haskell: Pass environment to hls (#15998)
Fixed environment not being passed to hls, breaking local nix-based
installations

Release Notes:

- N/A
2024-08-12 18:38:23 -04:00
Marshall Bowers
8a148f3a13 Add feature-flagged access to LLM service (#16136)
This PR adds feature-flagged access to the LLM service.

We've repurposed the `language-models` feature flag to be used for
providing access to Claude 3.5 Sonnet through the Zed provider.

The remaining RPC endpoints that were previously behind the
`language-models` feature flag are now behind a staff check.

We also put some Zed Pro related messaging behind a feature flag.

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
2024-08-12 18:13:40 -04:00
Kirill Bulatov
3bebb8b401 Allow to cycle through center/top/bot scroll positions (#16134)
On top of `editor::ScrollCursorCenter`, `editor::ScrollCursorTop`,
`editor::ScrollCursorBottom` actions, adds an
`editor::ScrollCursorCenterTopBottom` one, that allows using a single
keybinding to scroll between positions on the screen.

The implementation matches a corresponding Emacs feature: there's a
timeout (1s) that is kept after every switch, to allow continuously
changing the positions, center (initial) -> top -> bottom
Scrolling behavior is the same as the existing actions (e.g. editor will
ignore scroll to bottom, if there's not enough space above).

After 1s, next position is reset to the initial, center, one.


Release Notes:

- Added an `editor::ScrollCursorCenterTopBottom` action for toggling
scroll position with a single keybinding

---------

Co-authored-by: Alex Kladov <aleksey.kladov@gmail.com>
2024-08-13 00:32:30 +03:00
Marshall Bowers
98516b5527 collab: Restrict usage of the LLM service to accounts older than 30 days (#16133)
This PR restricts usage of the LLM service to accounts older than 30
days.

We now store the GitHub user's `created_at` timestamp to check the
GitHub account age. If this is not set—which it won't be for existing
users—then we use the `created_at` timestamp in the Zed database.

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
2024-08-12 17:27:21 -04:00
Kirill Bulatov
f398ecc3fb Allow inserting text into the editor via the action (#16131)
Improves workflows, based on the keymaps that group actions behind a
certain symbol.
E.g.

```json5
", o k": "zed::OpenKeymap",
", o K": "zed::OpenDefaultKeymap",
// other `,`-based keymaps
```

Now, it's possible to do 
```json
", ,": ["editor::HandleInput", ","]
```

and type `,` without waiting for the timeout due to `,`-based bindings.

Release Notes:

- Add an `editor::HandleInput` action to ease typing symbols that are
part of keymaps. E.g. if `, o k` keybinding is bound, `", ,":
["editor::HandleInput", ","]` would allow to type `,` without timeouts.

---------

Co-authored-by: Alex Kladov <aleksey.kladov@gmail.com>
2024-08-13 00:02:52 +03:00
Max Brunsfeld
a3c79218c4 Report telemetry events for rate limit errors (#16130)
clickhouse telemetry schema:

```
CREATE TABLE default.llm_rate_limit_events
(
    `time` DateTime64(3),
    `user_id` Int32,
    `is_staff` Bool,
    `plan` LowCardinality(String),
    `model` String,
    `provider` LowCardinality(String),
    `usage_measure` LowCardinality(String),
    `requests_this_minute` UInt64,
    `tokens_this_minute` UInt64,
    `tokens_this_day` UInt64,
    `max_requests_per_minute` UInt64,
    `max_tokens_per_minute` UInt64,
    `max_tokens_per_day` UInt64,
    `users_in_recent_minutes` UInt64,
    `users_in_recent_days` UInt64
)
ORDER BY tuple()
```

Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
2024-08-12 16:31:11 -04:00
Max Brunsfeld
1674e12ccb Expose anthropic API errors to the client (#16129)
Now, when an anthropic request is invalid or anthropic's API is down,
we'll expose that to the user instead of just returning a generic 500.

Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
2024-08-12 13:11:48 -07:00
Marshall Bowers
f3ec8d425f collab: Use a separate Anthropic API key for Zed staff (#16128)
This PR makes it so Zed staff can use a separate Anthropic API key for
the LLM service.

We also added an `is_staff` column to the `usages` table so that we can
exclude staff usage from the "active users" metrics that influence the
rate limits.

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
2024-08-12 15:20:34 -04:00
Marshall Bowers
ebdde5994d collab: Don't issue LLM API tokens if the user has not accepted the ToS (#16123)
This PR adds a check to the LLM API token issuance to ensure that we
only issue tokens to users that have accepted the terms of service.

Release Notes:

- N/A
2024-08-12 14:10:08 -04:00
Neil
df70e901af docs: Add "dock" to "terminal" section (#16122)
Documentation-related fix for #16121.

Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>

Release Notes:
* N/A
2024-08-12 20:23:55 +03:00
Michael Angerman
63aef7f798 command_palette: Remove project as a dependency in Cargo.toml (#16082)
The *project crate* is only needed as a dev dependency in the command
palette..
So I am doing some code / dependency cleanup...

Release Notes:

- N/A
2024-08-12 13:01:32 -04:00
renovate[bot]
9ae0d5388e Update Rust crate quote to v1.0.36 (#15945)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [quote](https://togithub.com/dtolnay/quote) | dependencies | patch |
`1.0.35` -> `1.0.36` |

---

### Release Notes

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

### [`v1.0.36`](https://togithub.com/dtolnay/quote/releases/tag/1.0.36)

[Compare
Source](https://togithub.com/dtolnay/quote/compare/1.0.35...1.0.36)

-   Documentation improvements

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC4yMC4xIiwidXBkYXRlZEluVmVyIjoiMzguMjAuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-12 12:51:13 -04:00
Marshall Bowers
ebdb755fef Surface upstream rate limits from Anthropic (#16118)
This PR makes it so hitting upstream rate limits from Anthropic result
in an HTTP 429 response instead of an HTTP 500.

To do this we need to surface structured errors out of the `anthropic`
crate.

Release Notes:

- N/A
2024-08-12 11:59:24 -04:00
Thorsten Ball
fbb533b3e0 assistant: Require user to accept TOS for cloud provider (#16111)
This adds the requirement for users to accept the terms of service the
first time they send a message with the Cloud provider.

Once this is out and in a nightly, we need to add the check to the
server side too, to authenticate access to the models.

Demo:


https://github.com/user-attachments/assets/0edebf74-8120-4fa2-b801-bb76f04e8a17



Release Notes:

- N/A
2024-08-12 17:43:35 +02:00
Piotr Osiewicz
98f314ba21 assistant: Add debug inspector (#16105)
I went with inline approach directly within the panel.
First, enable workflow debugging in the hamburger menu (this works
retroactively as well):


![image](https://github.com/user-attachments/assets/d2ab8edf-bb7b-49a4-8f70-9a6fe94dc7dd)

This enables debug buttons in the header of each step:

![image](https://github.com/user-attachments/assets/3b5d479f-7473-4c41-a2e7-8c10bb71f0ff)
Enabling one pretty-prints the workflow step internals:

![image](https://github.com/user-attachments/assets/e651e826-1270-49ff-8bb6-046c07c006bf)


Release Notes:

- N/A
2024-08-12 17:05:54 +02:00
Piotr Osiewicz
b6b081596a assistant: Show tooltips on workflow step buttons only when cursor is in step (#16108)
Release Notes:

- N/A
2024-08-12 16:53:11 +02:00
Nathan Sobo
fc64843dd5 Enhance HTTP API for extensions (#16067)
# HTTP Client Improvements for Extension API

This PR enhances the HTTP client functionality in the Zed extension API,
providing more control over requests and allowing for streaming
responses.

## Key Changes

1. Extended `HttpRequest` struct:
   - Added `method` field to specify HTTP method
   - Added `headers` field for custom headers
   - Added optional `body` field for request payload

2. Introduced `HttpMethod` enum for supported HTTP methods

3. Updated `HttpResponse` struct:
   - Added `headers` field to access response headers
- Changed `body` type from `String` to `Vec<u8>` for binary data support

4. Added streaming support:
   - New `fetch_stream` function to get a response stream
   - Introduced `HttpResponseStream` resource for chunked reading

5. Updated internal implementations to support these new features

6. Modified the Gleam extension to use the new API structure

## Motivation

These changes provide extension developers with more flexibility and
control over HTTP requests. The streaming support is particularly useful
for handling large responses efficiently or ideally streaming into the
UI.

## Testing

- [x] Updated existing tests
- [ ] Added new tests for streaming functionality

## Next Steps

- Consider adding more comprehensive examples in the documentation
- Evaluate performance impact of streaming for large responses

Please review and let me know if any adjustments are needed.

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-08-12 10:36:49 -04:00
Marshall Bowers
f952126319 collab: Remove LLM completions over RPC (#16114)
This PR removes the LLM completion messages from the RPC protocol, as
these now go through the LLM service as of #16113.

Release Notes:

- N/A
2024-08-12 10:08:56 -04:00
Nate Butler
f992cfdc7f Update provider logos (#16115)
- Updates provider logos for Anthropic, Google and Ollama
- Increases the size of icons in the model selector

Release Notes:

- N/A
2024-08-12 09:55:00 -04:00
Marshall Bowers
6389c613a2 Always stream completions through the LLM service (#16113)
This PR removes the `llm-service` feature flag and makes it so all
completions are done via the LLM service when using the Zed provider.

Release Notes:

- N/A
2024-08-12 09:33:24 -04:00
Son
bab4da78b7 Fix corner radius when doing rounded_full (#15663)
Release Notes:

- Fixed issue when doing `rounded_full`, it should render a pill-shape
rect instead of current eye-shape.

For example with this code
`div().h_4().w_16().bg(rgb(0xffffff)).rounded_full()`

Current:

<img width="144" alt="image"
src="https://github.com/user-attachments/assets/f8b20c7c-d91f-4c20-9f38-d435f59e72b7">

Fixed:

<img width="172" alt="image"
src="https://github.com/user-attachments/assets/ff8bbe26-2b31-4ef1-a2fb-25b458386ffb">
2024-08-12 15:24:32 +02:00
Antonio Scandurra
a15a9565ab Accept finished inline transformations only if the user saves manually (#16112)
Closes https://github.com/zed-industries/zed/issues/16042

This commit modifies the behavior of inline transformations to only
accept finished transformations when the user manually saves the file.
Previously, transformations were automatically accepted on any save
event, including autosaves.

This was achieved by updating the `Pane` and `Workspace` structs to emit
a new `UserSavedItem` event when a manual save occurs, and modifying the
`InlineAssistant` to register and handle this new event (instead of
`editor::Saved`).

Release Notes:

- N/A
2024-08-12 15:16:55 +02:00
Antonio Scandurra
48f6193628 Improve workflow step pruning and symbol similarity matching (#16036)
This PR improves workflow step management and symbol matching. We've
optimized step pruning to remove any step that intersects an edit and
switched to normalized Levenshtein distance for more accurate symbol
matching.

Release Notes:

- N/A
2024-08-12 11:09:07 +02:00
Nathan Sobo
355aebd0e4 Introduce prompt override script and adjust names (#16094)
This PR introduces a new script for iterative development of prompt
overrides in Zed.

Just `script/prompts link` and your running Zed should start using
prompts from `zed/assets/prompts`. Use `script/prompts unlink` to undo.
You can also link with `script/prompts link --worktree` to store the
prompts to a `../zed_prompts` worktree that's a sibling of your repo, in
case you don't want to mess with your working copy. Just don't forget
about it!

Key changes:
- Add new `script/prompts` for managing prompt overrides
- Rename `prompt_templates_dir` to `prompt_overrides_dir` for clarity
- Update paths to use `~/.config/zed/prompt_overrides` instead of
`~/.config/zed/prompts/templates`
- Adjust `PromptBuilder` to use the new `prompt_overrides_dir` function

These changes simplify the process of customizing prompts and provide a
more intuitive naming convention for override-related functionality.

Release Notes:

- N/A
2024-08-11 17:21:17 -06:00
Joseph T Lyons
10937c6e37 Fetch staff members from GitHub org 2024-08-11 15:10:45 -04:00
Joseph T Lyons
4818e0b7eb Don't thank staff members in release notes 2024-08-11 15:02:57 -04:00
Joseph T Lyons
2490b050ee Fix warning message 2024-08-11 14:48:58 -04:00
Joseph T Lyons
e99113e4cb Fix warning message 2024-08-11 14:45:46 -04:00
Marshall Bowers
3140d6ce8c collab: Temporarily bypass LLM rate limiting for staff (#16089)
This PR makes it so staff members will be exempt from rate limiting by
the LLM service.

This is just a temporary measure until we can tweak the rate-limiting
heuristics.

Staff members are still subject to upstream LLM provider rate limits.

Release Notes:

- N/A
2024-08-11 14:41:49 -04:00
Joseph T Lyons
492f6b9cdf Update issue-detection RegEx 2024-08-11 14:40:27 -04:00
Joseph T Lyons
36a560dcd0 Update the dangerfile to check for issue links 2024-08-11 14:18:19 -04:00
Nathan Sobo
6f104fecad Copy extension_api Rust files to OUT_DIR when building extension to work around rust-analyzer limitation (#16064)
Copies rust files from extension_api/wit to the OUT_DIR to allow
including them from within the crate, which is supported by
rust-analyzer. This allows rust-analyzer to deal with the included
files. It doesn't currently support files outside the crate.

Release Notes:

- N/A
2024-08-10 18:19:11 -06:00
Kyle Kelley
550e139b61 repl: Set the default lines ✖️ columns for the REPL to 32x128 (#16061)
![image](https://github.com/user-attachments/assets/dce938ef-bdfd-4412-a074-90c883286263)

Release Notes:

- Improved visuals of repl stdout/stderr by reducing default line count
to 32
2024-08-10 10:28:56 -07:00
Max Brunsfeld
33e120d964 Capture telemetry data on per-user monthly LLM spending (#16050)
Release Notes:

- N/A

---------

Co-authored-by: Marshall <marshall@zed.dev>
2024-08-09 16:38:37 -07:00
Max Brunsfeld
8688b2ad19 Add telemetry for LLM usage (#16049)
Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
2024-08-09 18:15:57 -04:00
Max Brunsfeld
423c7b999a Larger rate limit integers (#16047)
Tokens per day may exceed the range of Postgres's 32-bit `integer` data
type.

Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
2024-08-09 14:07:49 -07:00
Marshall Bowers
0932818829 zed_extension_api: Release v0.0.7 (#16048)
This PR releases v0.0.7 of the Zed extension API.

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

Release Notes:

- N/A
2024-08-09 16:52:16 -04:00
Max Brunsfeld
fbebb73d7b Use LLM service for tool call requests (#16046)
Release Notes:

- N/A

---------

Co-authored-by: Marshall <marshall@zed.dev>
2024-08-09 16:22:58 -04:00
Max Brunsfeld
d96afde5bf Avoid insert ... on conflict on startup (#16045)
These queries advance the id sequence even when there's nothing to
insert

Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
2024-08-09 15:32:11 -04:00
Max Brunsfeld
b1c69c2178 Fix usage recording in llm service (#16044)
Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
2024-08-09 11:48:18 -07:00
Peter Tripp
eb3c4b0e46 Docs Party 2024 (#15876)
Co-authored-by: Raunak Raj <nkray21111983@gmail.com>
Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
Co-authored-by: Bennet <bennet@zed.dev>
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
Co-authored-by: Joseph T Lyons <JosephTLyons@gmail.com>
Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Jason <jason@zed.dev>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Marshall <marshall@zed.dev>
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Jason Mancuso <7891333+jvmncs@users.noreply.github.com>
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2024-08-09 13:37:54 -04:00
Bennet Bo Fenner
c633fa5a10 assistant: Improve quote selection (#16038)
https://github.com/user-attachments/assets/fe283eec-169f-4dfd-bae2-cc72c1c3f223



Release Notes:

- Include more context when using `assistant: Quote selection` to insert
text into the assistant panel

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-08-09 18:29:27 +02:00
renovate[bot]
46a4dd3ab1 Update Rust crate prometheus to v0.13.4 (#15941)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [prometheus](https://togithub.com/tikv/rust-prometheus) | dependencies
| patch | `0.13.3` -> `0.13.4` |

---

### Release Notes

<details>
<summary>tikv/rust-prometheus (prometheus)</summary>

###
[`v0.13.4`](https://togithub.com/tikv/rust-prometheus/blob/HEAD/CHANGELOG.md#0134)

[Compare
Source](https://togithub.com/tikv/rust-prometheus/compare/v0.13.3...v0.13.4)

- Improvement: Add PullingGauge
([#&#8203;405](https://togithub.com/tikv/rust-prometheus/issues/405))

- Improvement: Let cargo know which example requires which features
([#&#8203;511](https://togithub.com/tikv/rust-prometheus/issues/511))

- Bug fix: Prevent `clippy::ignored_unit_patterns` in macro expansions
([#&#8203;497](https://togithub.com/tikv/rust-prometheus/issues/497))

- Internal change: Add CI job for minimum toolchain (MSRV)
([#&#8203;467](https://togithub.com/tikv/rust-prometheus/issues/467))

- Internal change: Update CI to `actions/checkout@v4`
([#&#8203;499](https://togithub.com/tikv/rust-prometheus/issues/499))

-   Internal change: Update dependencies

</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:eyJjcmVhdGVkSW5WZXIiOiIzOC4yMC4xIiwidXBkYXRlZEluVmVyIjoiMzguMjAuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-09 11:54:08 -04:00
Marshall Bowers
49f760eeda collab: Set LLM_DATABASE_MAX_CONNECTIONS (#16035)
This PR updates the collab template to set the
`LLM_DATABASE_MAX_CONNECTIONS` environment variable for the LLM service.

Release Notes:

- N/A
2024-08-09 11:16:02 -04:00
Max Brunsfeld
225726ba4a Remove code paths that skip LLM db in prod (#16008)
Release Notes:

- N/A
2024-08-09 10:41:50 -04:00
Thorsten Ball
c1872e9cb0 vim: Fix ctrl-u/ctrl-d with high vertical_scroll_margin (#16031)
This makes sure that the `vertical_scroll_margin` doesn't leave the
cursor out of screen or somewhere it shouldn't go when it's higher than
the visible lines on screen.

So we cap it to `visible_line_count / 2`, similar to nvim:


5aa1a9532c/src/nvim/window.c (L6560)

Fixes #15101

Release Notes:

- Fixed `ctrl-u`/`ctrl-d` in Vim mode not working correctly when
`vertical_scroll_margin` is set to a really high value.

Co-authored-by: Bennet <bennet@zed.dev>
2024-08-09 16:00:05 +02:00
Thorsten Ball
09c9ed4765 vim: Fix panic due to overflow when scrolling (#16029)
When setting `"vertical_scroll_margin": 99` or other high values this
can lead to a panic that crashes Zed.

Release Notes:

- vim: Fixed a possible panic that could happen when using a very high
value for `vertical_scroll_margin` that exceeded the number of visible
lines on the screen.

Co-authored-by: Bennet <bennet@zed.dev>
2024-08-09 15:15:27 +02:00
Thorsten Ball
19d8422933 stories: Get OverflowScrollStory to scroll again (#15982)
This makes it at least scroll again, but it doesn't scroll down to the
last element yet. We haven't figured out why yet.


Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
Co-authored-by: Antonio <antonio@zed.dev>
2024-08-09 12:32:26 +02:00
Thorsten Ball
173f6e7c8f assistant panel: Make configuration view scrollable (#16022)
We had to resort to this "hack" to get it to work, since nothing else we
tried (changing the `Pane`, changing the `ConfigurationView`, changing
the `AssistantPanel`, ...) worked.

Release Notes:

- N/A

Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Bennet <bennet@zed.dev>
2024-08-09 12:21:22 +02:00
Massimo Mund
e783142528 Fix vertical_scroll_margin not being capped for large vertical_scroll_margin values (#15473)
When switching to function definitions in a new buffer the
`AutoscrollStrategy::Focused` being emitted to scroll to the found
location.
If a large value `vertical_scroll_margin` (e.g.: 99) is set, this leads
to an incorrectly calculated position and the new buffer opens in the
wrong place.

In `autoscroll_vertically()` there is a margin calculated to cap the
value of `vertical_scroll_margin` for certain AutoscrollStrategies. This
margin is being used in `AutoscrollStrategy::Fit` and
`AutoscrollStrategy::Newest` but was probably forgotten for
`AutoscrollStrategy::Focused`.

- Might fix [#15101](https://github.com/zed-industries/zed/issues/15101)

Release Notes:

- N/A
2024-08-09 09:35:57 +02:00
Santeri Salmijärvi
06833d7360 Support MSbuild row-column format in PathWithPosition (#15589)
This implements #15412. Row-column parsing is changed into a regex to
support more complex patterns like the MSBuild diagnostics. Terminal
`word_regex` is also relaxed to match those suffixes.

Release Notes:

- N/A
2024-08-09 09:21:56 +02:00
Robin Malfait
7a600411cf Add injections for tagged template literals (#15984)
This PR adds syntax highlighting support for `css`, `html`, `js`,
`json`, `sql`, `ts`, `yaml` and `yml` in `javascript`, `typescript` and
`tsx` languages where the contents of tagged template literals are now
highlighted.

This does not actually enable language features (like LSP support), it
only does syntax highlighting.

Before this change:
<img width="561" alt="image"
src="https://github.com/user-attachments/assets/74bace1b-5ce1-4b17-8a97-035bba152d9c">


After this change:
<img width="607" alt="image"
src="https://github.com/user-attachments/assets/f227145b-3f4a-4c27-b14f-7143bb19b4cf">

/cc @mrnugget 

Release Notes:

- Added syntax highlighting for tagged template literals in
`javascript`, `typescript` and `tsx` languages for `css`, `html`, `js`,
`json`, `sql`, `ts`, `yaml` and `yml`.
([#15984](https://github.com/zed-industries/zed/issues/15984))
2024-08-09 09:16:48 +02:00
Max Brunsfeld
240b7c641c Fix llm queries (#16006)
Release Notes:

- N/A

---------

Co-authored-by: Marshall <marshall@zed.dev>
2024-08-08 17:21:38 -07:00
Max Brunsfeld
06625bfe94 Apply rate limits in LLM service (#15997)
Release Notes:

- N/A

---------

Co-authored-by: Marshall <marshall@zed.dev>
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-08-08 15:46:33 -07:00
Marshall Bowers
2bc503771b assistant: Show an error when /fetch returns no content (#16001)
This PR makes it so the `/fetch` slash command will display an error
when the URL does not have any textual content:

<img width="308" alt="Screenshot 2024-08-08 at 4 51 55 PM"
src="https://github.com/user-attachments/assets/bf4cbc18-c65b-48a2-aedd-26b50f47f018">

Release Notes:

- N/A
2024-08-08 17:07:42 -04:00
Joseph T Lyons
5b8ce91048 Fix isStaff boolean logic 2024-08-08 17:00:10 -04:00
Marshall Bowers
abb6d40fbf Put /docs behind a feature flag (#16000)
This PR puts the `/docs` slash command behind a feature flag.

Release Notes:

- N/A
2024-08-08 16:51:35 -04:00
Conrad Irwin
bd59af1df5 vim: Support ranges in command (#15985)
The most requested feature here is "search and replace in visual mode",
but as a happy side effect we can now support things like :2,12j to join
those lines, and much much more.



Release Notes:

- vim: Added support for range syntax in command
([#9428](https://github.com/zed-industries/zed/issues/9428)).
- vim: Prefill command with `:'<,'>` from visual mode
([#13535](https://github.com/zed-industries/zed/issues/13535)).
2024-08-08 21:47:27 +01:00
Joseph T Lyons
b7d6b0a096 Filter out staff members from thanks line 2024-08-08 16:26:45 -04:00
Joseph T. Lyons
b28507d2e0 Link to pull requests in changelog notes (#15996)
This PR changes how we ask users to draft up PRs and how release note
generation happens.

We no longer force the user to create the markdown URL link, but we do
ask them to use the `closes` [GitHub magic
word](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)
to link the PR to an issue, so that the issue is closed automatically
when closing the PR.

As for the changelog release notes, we are no longer linking to the
issues, but the PR itself, which should contain the issue if a reader
wants to dive further back. This makes our output more consistent, as
every line will have a link, even if there is no issue associated, and
it removes the need for us to try to parse the issue url in the body to
try to correct mistakes in how they were forming Markdown urls - the PR
url is always returned in the request, which makes it easy. **Lastly,
it's just a lot less annoying to make the release notes.**

The new PR format will be:

```
Closes #ISSUE

Release Notes:

- Added/Fixed/Improved ...
```

The new script output format will be:

```
PR Title: theme: Use a non-transparent color for the fallback `title_bar.inactive_background`
Credit: ([#15709](https://github.com/zed-industries/zed/pull/15709); thanks [maxdeviant](https://github.com/maxdeviant))
Release Notes:

- linux: Changed the fallback color of `title_bar.inactive_background` to a non-transparent value.
--------------------------------------------------------------------------------
PR Title: Skip over folded regions when iterating over multibuffer chunks
Credit: ([#15646](https://github.com/zed-industries/zed/pull/15646); thanks [osiewicz](https://github.com/osiewicz))
Release Notes:

- Fixed poor performance when editing in the assistant panel after inserting large files using slash commands
--------------------------------------------------------------------------------
```

This still requires us to manually apply the credit line, but the line
is already fully formed, so this should still be faster than having to
manually create that line / fix any line where someone messed it up
(which was all the time). I would just automatically apply it to the
release notes, but sometimes we have multiple bullet points in a single
PR and no real structure is enforced, so I foresee doing anything
automatic breaking and needing manual adjustment.

Release Notes:

- N/A
2024-08-08 15:26:17 -04:00
Marshall Bowers
a39f1f5133 Fix Windows build in CI (#15990)
This PR fixes the Windows build in CI, which was failing due to Clippy
warnings.

Release Notes:

- N/A
2024-08-08 12:55:26 -04:00
Marshall Bowers
fbc629df7d assistant: Put /search behind a feature flag (#15987)
This PR puts the `/search` slash command behind a feature flag.

Release Notes:

- N/A
2024-08-08 12:33:59 -04:00
renovate[bot]
8ba5207c6c Update Rust crate proc-macro2 to v1.0.86 (#15940)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [proc-macro2](https://togithub.com/dtolnay/proc-macro2) | dependencies
| patch | `1.0.81` -> `1.0.86` |

---

### Release Notes

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

###
[`v1.0.86`](https://togithub.com/dtolnay/proc-macro2/releases/tag/1.0.86)

[Compare
Source](https://togithub.com/dtolnay/proc-macro2/compare/1.0.85...1.0.86)

-   Documentation improvements

###
[`v1.0.85`](https://togithub.com/dtolnay/proc-macro2/releases/tag/1.0.85)

[Compare
Source](https://togithub.com/dtolnay/proc-macro2/compare/1.0.84...1.0.85)

- Mark some tests as only for 64-bit targets
([#&#8203;463](https://togithub.com/dtolnay/proc-macro2/issues/463))

###
[`v1.0.84`](https://togithub.com/dtolnay/proc-macro2/releases/tag/1.0.84)

[Compare
Source](https://togithub.com/dtolnay/proc-macro2/compare/1.0.83...1.0.84)

- Documentation improvements
([#&#8203;455](https://togithub.com/dtolnay/proc-macro2/issues/455),
thanks
[@&#8203;CensoredUsername](https://togithub.com/CensoredUsername))

###
[`v1.0.83`](https://togithub.com/dtolnay/proc-macro2/releases/tag/1.0.83)

[Compare
Source](https://togithub.com/dtolnay/proc-macro2/compare/1.0.82...1.0.83)

- Optimize the representation of `Ident`
([#&#8203;462](https://togithub.com/dtolnay/proc-macro2/issues/462))

###
[`v1.0.82`](https://togithub.com/dtolnay/proc-macro2/releases/tag/1.0.82)

[Compare
Source](https://togithub.com/dtolnay/proc-macro2/compare/1.0.81...1.0.82)

- Resolve unexpected_cfgs warning
([#&#8203;456](https://togithub.com/dtolnay/proc-macro2/issues/456))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOC4yMC4xIiwidXBkYXRlZEluVmVyIjoiMzguMjAuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-08 12:04:26 -04:00
renovate[bot]
c3cfaade7d Update Python to v3.12.5 (#15944)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [python](https://togithub.com/containerbase/python-prebuild) |
dependencies | patch | `3.12.4` -> `3.12.5` |

---

### Release Notes

<details>
<summary>containerbase/python-prebuild (python)</summary>

###
[`v3.12.5`](https://togithub.com/containerbase/python-prebuild/releases/tag/3.12.5)

[Compare
Source](https://togithub.com/containerbase/python-prebuild/compare/3.12.4...3.12.5)

##### Bug Fixes

-   **deps:** update dependency python to v3.12.5

</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:eyJjcmVhdGVkSW5WZXIiOiIzOC4yMC4xIiwidXBkYXRlZEluVmVyIjoiMzguMjAuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-08 11:23:23 -04:00
renovate[bot]
8681eeb0e2 Update Rust crate parking_lot to v0.12.3 (#15934)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [parking_lot](https://togithub.com/Amanieu/parking_lot) |
workspace.dependencies | patch | `0.12.1` -> `0.12.3` |

---

### Release Notes

<details>
<summary>Amanieu/parking_lot (parking_lot)</summary>

###
[`v0.12.3`](https://togithub.com/Amanieu/parking_lot/blob/HEAD/CHANGELOG.md#parkinglot-0123-2024-05-24)

[Compare
Source](https://togithub.com/Amanieu/parking_lot/compare/0.12.2...0.12.3)

- Export types provided by arc_lock feature
([#&#8203;442](https://togithub.com/Amanieu/parking_lot/issues/442))

###
[`v0.12.2`](https://togithub.com/Amanieu/parking_lot/blob/HEAD/CHANGELOG.md#parkinglot-0122-parkinglotcore-0910-lockapi-0412-2024-04-15)

[Compare
Source](https://togithub.com/Amanieu/parking_lot/compare/0.12.1...0.12.2)

- Fixed panic when calling `with_upgraded` twice on a
`ArcRwLockUpgradableReadGuard`
([#&#8203;431](https://togithub.com/Amanieu/parking_lot/issues/431))
-   Fixed `RwLockUpgradeableReadGuard::with_upgraded`
- Added lock_api::{Mutex, ReentrantMutex, RwLock}::from_raw methods
([#&#8203;429](https://togithub.com/Amanieu/parking_lot/issues/429))
- Added Apple visionOS support
([#&#8203;433](https://togithub.com/Amanieu/parking_lot/issues/433))

</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:eyJjcmVhdGVkSW5WZXIiOiIzOC4yMC4xIiwidXBkYXRlZEluVmVyIjoiMzguMjAuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-08 11:22:26 -04:00
flundar
8f9bcbe739 Update windows.md (#15790)
Release Notes:

- N/A

---------

Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
2024-08-08 16:50:10 +02:00
Thorsten Ball
9a211b239c script/bootstrap: Fix sqlx command by using newer version (#15980)
Version 0.5.7 doesn't have the `--database-url` command line flag, so
`script/bootstrap` didn't work.

Since we use `0.7` in collab (see
[here](73fb8277fc/crates/collab/Cargo.toml (L60)))
and sqlx 0.7.2 has the `--database-url` flag, we use that instead.


Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-08-08 16:10:18 +02:00
CharlesChen0823
a71bfd41cc recent_project: Fix overflow sub (#15965)
close: #15783 

Release Notes:

- Fixed a potential panic that can occur when deleting entries from the
recent-projects menu
([#15783](https://github.com/zed-industries/zed/issues/15783))
2024-08-08 15:58:59 +02:00
Piotr Osiewicz
73fb8277fc assistant: Polish /workflow and steps UI (#15936)
Fixes #15923
Release Notes:

- Assistant workflow steps can now be applied and reverted directly from
within the assistant panel.

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Antonio <antonio@zed.dev>
2024-08-08 15:46:33 +02:00
Bennet Bo Fenner
514b79e461 collab: Always use newest anthropic model version (#15978)
When Anthropic releases a new version of their models, Zed AI users
should always get access to the new version even when using an old
version of zed.

Co-Authored-By: Thorsten <thorsten@zed.dev>

Release Notes:

- N/A

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-08-08 15:24:08 +02:00
Bennet Bo Fenner
793cd88792 keymap: Show error notification when keymap is invalid (#15977)
This adds an error notification that pops up when the user has an
invalid keymap, similar to what we added for settings in #15905.

Release Notes:

- Added a popup that is displayed when the keymap is invalid
2024-08-08 14:11:46 +02:00
renovate[bot]
92496f33e7 Update Rust crate ordered-float to v2.10.1 (#15933)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [ordered-float](https://togithub.com/reem/rust-ordered-float) |
workspace.dependencies | patch | `2.10.0` -> `2.10.1` |

---

### Release Notes

<details>
<summary>reem/rust-ordered-float (ordered-float)</summary>

###
[`v2.10.1`](https://togithub.com/reem/rust-ordered-float/releases/tag/v2.10.1)

[Compare
Source](https://togithub.com/reem/rust-ordered-float/compare/v2.10.0...v2.10.1)

#### What's Changed

- Refactor Hash implementation by
[@&#8203;jogru0](https://togithub.com/jogru0) in
[https://github.com/reem/rust-ordered-float/pull/129](https://togithub.com/reem/rust-ordered-float/pull/129)
- Optimize Ord implementation by
[@&#8203;orlp](https://togithub.com/orlp) in
[https://github.com/reem/rust-ordered-float/pull/144](https://togithub.com/reem/rust-ordered-float/pull/144)

**Full Changelog**:
https://github.com/reem/rust-ordered-float/compare/v2.10.0...v2.10.1

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC4yMC4xIiwidXBkYXRlZEluVmVyIjoiMzguMjAuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-08 07:48:43 -04:00
renovate[bot]
b9159d98ea Update Rust crate hyper to v0.14.30 (#15930)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [hyper](https://hyper.rs)
([source](https://togithub.com/hyperium/hyper)) | workspace.dependencies
| patch | `0.14.27` -> `0.14.30` |

---

### Release Notes

<details>
<summary>hyperium/hyper (hyper)</summary>

###
[`v0.14.30`](https://togithub.com/hyperium/hyper/releases/tag/v0.14.30)

[Compare
Source](https://togithub.com/hyperium/hyper/compare/v0.14.29...v0.14.30)

#### Bug Fixes

- **http1:** reject final chunked if missing 0
([4a51b2af](4a51b2afef))

###
[`v0.14.29`](https://togithub.com/hyperium/hyper/releases/tag/v0.14.29)

[Compare
Source](https://togithub.com/hyperium/hyper/compare/v0.14.28...v0.14.29)

#### Bug Fixes

- **http1:** start header read timeout immediately
([#&#8203;3305](https://togithub.com/hyperium/hyper/issues/3305))
([b5c2592f](b5c2592fde))

#### Features

- **http2:** add config for `max_local_error_reset_streams` in server
([#&#8203;3528](https://togithub.com/hyperium/hyper/issues/3528))
([dedcb674](dedcb674f3))

#### New Contributors

- [@&#8203;jeromegn](https://togithub.com/jeromegn) made their first
contribution in
[https://github.com/hyperium/hyper/pull/3305](https://togithub.com/hyperium/hyper/pull/3305)

**Full Changelog**:
https://github.com/hyperium/hyper/compare/v0.14.28...v0.14.29

###
[`v0.14.28`](https://togithub.com/hyperium/hyper/releases/tag/v0.14.28)

[Compare
Source](https://togithub.com/hyperium/hyper/compare/v0.14.27...v0.14.28)

#### Features

- **body:** deprecate to_bytes() and aggregate()
([#&#8203;3466](https://togithub.com/hyperium/hyper/issues/3466))
([7f382ad6](7f382ad643))
- **client:** add `conn::http1::Connection::without_shutdown()` method
([#&#8203;3431](https://togithub.com/hyperium/hyper/issues/3431))
([ad504977](ad504977b5))
- **server:** add `Builder::local_addr()`
([#&#8203;3278](https://togithub.com/hyperium/hyper/issues/3278))
([d342c2c7](d342c2c714))

#### Bug Fixes

-   **client:**
- panic when pool idle timeout set to zero
([#&#8203;3365](https://togithub.com/hyperium/hyper/issues/3365))
([34d38008](34d3800849))
- divide by zero error when DNS returns no addrs
([#&#8203;3355](https://togithub.com/hyperium/hyper/issues/3355))
([41eaf204](41eaf2042b))
- Do not strip `path` and `scheme` components from URIs for HTTP/2
Extended CONNEC
([45aa6249](45aa624941))
- early respond from server shouldn't propagate reset error
([#&#8203;3274](https://togithub.com/hyperium/hyper/issues/3274))
([aac6760e](aac6760e03),
closes [#&#8203;2872](https://togithub.com/hyperium/hyper/issues/2872))
-   **http1:**
- add internal limit for chunked extensions
([#&#8203;3495](https://togithub.com/hyperium/hyper/issues/3495))
([344a8782](344a878229))
- reject chunked headers missing a digit
([#&#8203;3494](https://togithub.com/hyperium/hyper/issues/3494))
([5eca028f](5eca028f41))

#### New Contributors

- [@&#8203;bdbai](https://togithub.com/bdbai) made their first
contribution in
[https://github.com/hyperium/hyper/pull/3242](https://togithub.com/hyperium/hyper/pull/3242)
- [@&#8203;gngpp](https://togithub.com/gngpp) made their first
contribution in
[https://github.com/hyperium/hyper/pull/3355](https://togithub.com/hyperium/hyper/pull/3355)

</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:eyJjcmVhdGVkSW5WZXIiOiIzOC4yMC4xIiwidXBkYXRlZEluVmVyIjoiMzguMjAuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-08 07:47:49 -04:00
Piotr Osiewicz
f3abb7e724 assistant: Grab focus when clicking on history icon 2024-08-08 13:18:31 +02:00
Bennet Bo Fenner
389cb86e43 assistant: Dismiss model selector after changing model (#15974)
Co-Authored-By: Thorsten <thorsten@zed.dev>

Release Notes:

- N/A

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-08-08 13:09:03 +02:00
Bennet Bo Fenner
fea8f16df0 assistant: Show regenerate button only on hover (#15972)
https://github.com/user-attachments/assets/92006a45-5b4e-4ec4-a056-9ef7dd76394d



Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-08-08 13:08:57 +02:00
Danilo Leal
76d58ac295 Add design tweaks to the AI configuration panel (#15894)
This PR polishes elements around setting up LLM providers on the
Assistant panel, including:

- [x] Adding banners for promoting Zed AI and to deal with the "No
provider set up" scenario
- [x] Tweaking the error popover whenever there's no API key added
- [ ] Making configuration panel scrollable

--- 

Release Notes:

- N/A

---------

Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
Co-authored-by: Bennet Bo Fenner <53836821+bennetbo@users.noreply.github.com>
Co-authored-by: Marshall Bowers <1486634+maxdeviant@users.noreply.github.com>
2024-08-08 12:12:36 +02:00
Bennet Bo Fenner
e69b0833aa markdown preview: Detect language of buffer correctly (#15961)
Fixes #15958

Release Notes:

- Fixed an issue where the markdown preview button would not show up for
some markdown files
([#15958](https://github.com/zed-industries/zed/issues/15958)).
2024-08-08 11:53:37 +02:00
Nathan Sobo
da8d1306af Open workflow step editors as preview tabs (#15928)
This PR opens workflow step editors as preview tabs and closes them upon
exiting the step if they are still in preview mode and they weren't
already open before entering the step.

Making this work was tricky, because we often edit the buffer as part of
displaying the workflow step suggestions to create empty lines where we
can generate. We undo these edits if the transformation is not applied,
but they were causing the preview to be dismissed.

After trying a few approaches, I decided to give workspace `Item`s a
`preserve_preview` method that defaults to false. When the workspace
sees an edit event for the item, it checks if the item wants to preserve
its preview. For buffers, after editing, you can call `refresh_preview`,
which sets a preview version to the current version of the buffer. Any
edits after this version will cause preview to not be preserved.

One final issue is with async auto-indent. To ensure these async edits
don't dismiss the preview, I automatically refresh the preview version
if preview was preserved prior to performing the auto-indent. The
assumption is that these are edits created by other edits, and if we
didn't want to dismiss the preview with the originating edits, then the
auto-indent edits shouldn't dismiss it either.

Release Notes:

- N/A

---------

Co-authored-by: Jason <jason@zed.dev>
2024-08-07 19:33:58 -06:00
jvmncs
a5961c8d45 Point PROTOC to nixpkgs.protobuf pkg in shell.nix (#15931)
This fixes an issue on NixOS where Zed's proto crate fails to build.
Cargo expects to find protoc in the Cargo registry, but due to the
distro's non-standard filesystem this expectation is invalid.

Release Notes:

- N/A
2024-08-07 16:35:49 -04:00
renovate[bot]
e9ddca1075 Update actions/upload-artifact digest to 834a144 (#15929)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/upload-artifact](https://togithub.com/actions/upload-artifact)
| action | digest | `0b2256b` -> `834a144` |

---

### 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:eyJjcmVhdGVkSW5WZXIiOiIzOC4yMC4xIiwidXBkYXRlZEluVmVyIjoiMzguMjAuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-07 16:25:17 -04:00
Marshall Bowers
6f6eeb6595 collab: Update how mode is displayed in root endpoint (#15911)
This PR adjusts how we display the "mode" collab is running in on the
root endpoint.

It's minor, but it does make things a bit cleaner.

Release Notes:

- N/A
2024-08-07 12:09:43 -04:00
Joseph T Lyons
22162e884b v0.149.x dev 2024-08-07 10:59:51 -04:00
Jonathan Dickinson
864c443f4b csharp: Use lsp settings to locate binary if present (#15885)
C# extension now honors the "lsp" config section

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-08-07 10:58:13 -04:00
Marshall Bowers
5ce6c374b0 gleam: Revert version bump (#15908)
This PR reverts a version bump to the Gleam extension that was included
in #15866, as we're not ready to publish a new version.

Release Notes:

- N/A
2024-08-07 10:27:34 -04:00
Bennet Bo Fenner
3a52d6cc52 assistant: Limit model access for Zed AI users to Claude-3.5-sonnet (#15904)
This prevents users from accessing other models, such as OpenAI's GPT-4
or Google's Gemini-Pro.
Staff members can still access all models.

Co-authored-by: Thorsten <thorsten@zed.dev>

Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-08-07 16:26:56 +02:00
Bennet Bo Fenner
efbf7ada28 settings: Show error notification when settings are invalid (#15905)
https://github.com/user-attachments/assets/07627142-e730-4446-a50b-7ef46f8e661c

We want to improve the design in the future, but it fixes a long
standing paper cut for now.

Release Notes:

- Added a popup that is displayed when the settings are invalid

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-08-07 15:54:44 +02:00
Thorsten Ball
94028290cc assistant panel: Use feature flag for Zed Pro mentions (#15903)
This removes Zed Pro mentions.

Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-08-07 14:44:00 +02:00
Son
f24f601e05 Adjust erf estimation function (#15423)
Release Notes:

- Fixed a (potential) small error in erf estimation. Technically, the
error is negligible.

I am not sure where the current calculation for erf come from and if it
is intended or a simple mistake. However it looks slightly different
from the official calculation, notably
[this](https://en.wikipedia.org/wiki/Error_function#Approximation_with_elementary_functions)
from Wikipedia.

I will add a comment if it is intended.
2024-08-07 14:12:26 +02:00
Piotr Osiewicz
2db2b636f2 assistant: Add annotations to more languages (#15866)
Release Notes:

- N/A
2024-08-07 13:47:21 +02:00
Piotr Osiewicz
8aa1ba8cbc assistant: Tweak tab bar layout (#15901)
- Add "New Context" button next to the hamburger
- Add "History"
- Allow Pane tab rendering callback to return items for both left and
right side of the tab bar.


![image](https://github.com/user-attachments/assets/d5aa599d-c9e4-4f26-ad66-ffc290c53c29)


Release Notes:

- N/A
2024-08-07 13:46:59 +02:00
Piotr Osiewicz
bf403ca788 chore: Remove assistant labels for users without feature flag enabled (#15899)
Release Notes:

- N/A
2024-08-07 12:46:49 +02:00
Nathan Sobo
990774247e Allow /workflow and step resolution prompts to be overridden (#15892)
This will help us as we hit issues with the /workflow and step
resolution. We can override the baked-in prompts and make tweaks, then
import our refinements back into the source tree when we're ready.

Release Notes:

- N/A
2024-08-06 21:47:42 -06:00
Nathan Sobo
c8f1358629 Allow prompt templates to be overridden in the zed configuration directory (#15887)
I need this to refine our prompts on the fly as I work.

Release Notes:

- Templates for prompts driving inline transformation in editors and the
terminal can now be overridden in the `~/.config/zed/prompts/templates`
directory. This is an advanced feature, and prevents you from getting
upstream changes. It's intended for use by Zed developers.
2024-08-06 19:30:48 -06:00
Kyle Kelley
6065db174a repl: Improve kernelspec discoverability (#15886)
<img width="862" alt="image"
src="https://github.com/user-attachments/assets/ae8c479d-d9f9-4c46-bb1a-be411ab07876">

Release Notes:

- Added additional context about available to kernel sessions
- Fixed bug in kernelspec launch choosing first available kernel
matching the language rather than selected name

---------

Co-authored-by: Jason <jason@zed.dev>
2024-08-06 16:58:56 -07:00
Marshall Bowers
a54e16b7ea collab: Add usages table to LLM database (#15884)
This PR adds a `usages` table to the LLM database.

We'll use this to track usage for rate-limiting purposes.

Release Notes:

- N/A
2024-08-06 18:40:10 -04:00
Xander
4f69336024 php: Add bracket highlighting (#15881)
Improved PHP extension by adding bracket highlighting 

Release Notes:

- N/A

Screenshot: 
<img width="402" alt="image"
src="https://github.com/user-attachments/assets/4dd02b41-8637-49b8-b3ce-41791227f945">

<img width="537" alt="image"
src="https://github.com/user-attachments/assets/b6dc7091-4588-460b-9051-4675eb0df538">
2024-08-06 17:36:40 -04:00
Marshall Bowers
b19f85f9b5 collab: Remove unused parameter to run_database_migrations (#15883)
This PR removes the unused `ignore_checksum_mismatch` parameter to
`run_database_migrations`.

We were always passing `false`, which meant the behavior didn't need to
be parameterized.

Release Notes:

- N/A
2024-08-06 17:31:52 -04:00
Marshall Bowers
7f6d0919c9 collab: Setup database for LLM service (#15882)
This PR puts the initial infrastructure for the LLM service's database
in place.

The LLM service will be using a separate Postgres database, with its own
set of migrations.

Currently we only connect to the database in development, as we don't
yet have the database setup for the staging/production environments.

Release Notes:

- N/A
2024-08-06 17:18:08 -04:00
Joseph T. Lyons
a64906779b Use vanity Discord link (#15880)
Release Notes:

- N/A
2024-08-06 16:55:51 -04:00
Joseph T. Lyons
11c6ada08f Add tooltip for toggling filters (#15879)
Release Notes:

- N/A
2024-08-06 15:51:53 -04:00
apricotbucket28
7366c85dd3 linux: Fix arrow keys in command palette (#15871)
Fixes https://github.com/zed-industries/zed/issues/15870 (a recent
regression from
1f97741067)

Release Notes:

- N/A
2024-08-06 11:50:37 -07:00
Marshall Bowers
591b579ccf elixir: Bump to v0.0.7 (#15867)
This PR bumps the Elixir extension to v0.0.7.

Changes:

- #14831

Release Notes:

- N/A
2024-08-06 13:07:47 -04:00
Marshall Bowers
a82f318d72 collab: Add missing ZED_LLM_LOAD_BALANCER_SIZE_UNIT variable (#15868)
We missed this in #15863.

Release Notes:

- N/A
2024-08-06 13:02:00 -04:00
Antonio Scandurra
104d7adc49 Don't insert unnecessary space below the end of an inline transformation (#15865)
We achieved this by allowing block decorations to have a height of `0`
and superimposing the border on top of the line, as opposed to carving
out space below it.

Release Notes:

- N/A

---------

Co-authored-by: Jason <jason@zed.dev>
2024-08-06 19:00:23 +02:00
Bennet Bo Fenner
2e27448d5f assistant: Fix inline assistant not working for non-terminal panel views (#15864)
Fixes #15729

Release Notes:

- Fixed an issue where the terminal inline assistant would not appear
when opening a terminal in the center pane
([#15729](https://github.com/zed-industries/zed/issues/15729)).
2024-08-06 18:41:54 +02:00
Max Brunsfeld
33afbe9a94 Add LLM service to kubernetes deployment action (#15863)
Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
2024-08-06 12:35:00 -04:00
Marshall Bowers
cf5f4dddf5 Authorize access to language model providers based on country (#15859)
This PR updates the LLM service to authorize access to language model
providers based on the requester's country.

We detect the country using Cloudflare's
[`CF-IPCountry`](https://developers.cloudflare.com/fundamentals/reference/http-request-headers/#cf-ipcountry)
header.

The country code is then checked against the list of supported countries
for the given LLM provider. Countries that are not supported will
receive an `HTTP 451: Unavailable For Legal Reasons` response.

Release Notes:

- N/A
2024-08-06 11:49:04 -04:00
Antonio Scandurra
9c6ccaffe3 Allow user to restart transformation after stopping without a diff (#15858)
Release Notes:

- N/A

Co-authored-by: Jason <jason@zed.dev>
2024-08-06 17:25:13 +02:00
Antonio Scandurra
411934bb61 Introduce a new /workflow command (#15854)
This subsumes the previous built-in prompt.

Release Notes:

- N/A
2024-08-06 16:18:07 +02:00
Vitaly Slobodin
889a14a2c2 ruby: Bump to v0.1.0 (#15855)
ruby: Bump extension version to v0.1.0 Why not v0.0.9? I think the Ruby
extension is mature, and it's time to release the first minor version.
But I am totally OK with changing it to 0.0.9.

Included changes:

- https://github.com/zed-industries/zed/pull/15778
- https://github.com/zed-industries/zed/pull/15762
- https://github.com/zed-industries/zed/pull/15110
- https://github.com/zed-industries/zed/pull/15297

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-08-06 09:53:02 -04:00
Piotr Osiewicz
874f0c0712 assistant: Use tools in other providers (#15803)
- [x] OpenAI
- [ ] ~Google~ Moved into a separate branch at:
https://github.com/zed-industries/zed/tree/tool-calls-in-google-ai I've
ran into issues with having the API digest our schema without tripping
over itself - the function call parameters are malformed and whatnot. We
can resume from that branch if needed.
- [x] Ollama
- [x] Cloud
- [ ] ~Copilot Chat (?)~

Release Notes:

- Added tool calling capabilities to OpenAI and Ollama models.
2024-08-06 15:45:47 +02:00
Kirill Bulatov
be514f23e1 Improve streaming git diffs to be less jumpy during inline assistant generation (#15853)
Release Notes:

- Improved streaming git diffs to be less jumpy during inline assistant
generation

---------

Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2024-08-06 16:13:29 +03:00
Marshall Bowers
a054a2a9a3 ruff: Refactor language server loading (#15852)
This PR refactors the language server loading in the Ruff extension to
mirror our other extensions.

Also fixed an issue where the cached binary path was not being
respected.

Release Notes:

- N/A
2024-08-06 09:10:18 -04:00
Marshall Bowers
82310092a2 inline_completion_button: Do some cleanup (#15849)
This PR does some cleanup in the `inline_completion_button` after
#15443.

Release Notes:

- N/A
2024-08-06 08:55:27 -04:00
Vitaly Slobodin
1c3f303594 ruby: Support "binary" settings for Rubocop and Solargraph (#15110)
Hello, this pull request adds support for specifying and using the
"binary" settings for Rubocop and Solargraph LSPs. AFAIK, Ruby LSP does
not require the bundler context but that could be added later easily.

In Ruby world, like in Node.js world, almost all
projects rely on project specific packages (gems) and their versions.
Solargraph and Rubocop gems are usually installed as project
dependencies. Attempting to use global installation of them fail in most
cases due to incompatible or missing dependencies (gems).

To avoid that, Ruby engineers have the `bundler`
gem that provides the `exec` command. This command executes the given
command in the context of the bundle.

This pull request adds support for pulling the `binary` settings to use
them in starting both LSPs. For instance, to start the Solargraph gem in
the context of the bundler, the end user must configure the binary
settings in the folder-specific settings file like so:

```json
{
  "lsp": {
    "solargraph": {
      "binary": {
        "path": "/Users/vslobodin/Development/festivatica/bin/rubocop"
      }
    }
  }
}
```

The `path` key must be an absolute path to the `binstub` of the
`solargraph` gem. The same applies to the "rubocop" gem. Side note but
it would be awesome to use Zed specific environment variables to make
this a bit easier. For instance, we could use the `ZED_WORKTREE_ROOT`
environment variable:

```json
{
  "lsp": {
    "solargraph": {
      "binary": {
        "path": "${ZED_WORKTREE_ROOT}/bin/rubocop"
      }
    }
  }
}
```

But this is out of the scope of this pull request. The code is a bit
messy and repeatable in some places, I am happy to improve it here or
later.

References:
- https://bundler.io/v2.4/man/bundle-exec.1.html
- https://solargraph.org/guides/troubleshooting
- https://bundler.io/v2.5/man/bundle-binstubs.1.html

This pull request is based on these two pull requests:

- https://github.com/zed-industries/zed/pull/14655
- https://github.com/zed-industries/zed/issues/15001

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

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-08-06 08:36:14 -04:00
Kevin Wang
7cef5b2956 Add menu item to switch Supermaven and Copilot (#15443)
Adds a "Use Supermaven" and "Use Copilot" menu item to the signed out
menus for each of the autocomplete providers to make it easier to switch
between them without having to update your local settings json.

<img width="222" alt="image"
src="https://github.com/user-attachments/assets/6f760f4e-5527-4971-bdaf-383bc99649bd">

Release Notes:

- Added menu items to quickly switch between Supermaven and Copilot
inline completions when the provider is not configured
2024-08-06 13:54:30 +02:00
Konstantin Matsiushonak
f4a58e5411 typescript: Make it possible to use local vtsls (#15775)
Related things:
https://github.com/zed-industries/zed/issues/7902
https://github.com/zed-industries/zed/issues/4978

Release Notes:
- Added: positibily to use locally installed vtsls
2024-08-06 12:37:13 +02:00
Bennet Bo Fenner
d6e5265e84 assistant: Limit model access (#15820)
Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-08-06 12:19:19 +02:00
Bennet Bo Fenner
56abd68d0e assistant: Cleanup model selector (#15843)
We changed the following for the model selector:
- Fixed displaying checkmarks for selected models when using models with
the same name from different providers
- We now show the icon for the active model instead of displaying the
provider name in the trigger of the model selector
- Only display the footer when the language models feature flag is zed,
so that we don't release the hint for Zed Pro to preview tomorrow

<img width="253" alt="image"
src="https://github.com/user-attachments/assets/f95ccfb6-c0cf-43d4-9637-e2823100a427">


Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-08-06 11:59:54 +02:00
Jaakko Sirén
fb1cd7cae2 blade: Update to pick up Intel memory coherency fix & fix calling blade params (#15829)
Builds on @kvark's PR https://github.com/zed-industries/zed/pull/15781
by fixing call to Blades `create_texture_view` the arguments of which
had changed.


Picks up https://github.com/kvark/blade/pull/153

Release Notes:

- Fixed Zed flickering on Linux when using Intel graphics.
([#14101](https://github.com/zed-industries/zed/issues/14101)).

---------

Co-authored-by: Dzmitry Malyshau <kvark@fastmail.com>
Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
2024-08-06 11:33:18 +02:00
Antonio Scandurra
44ae9efb27 Cancel workflow step automatically when moving outside <step> tag (#15842)
Also, open edit suggestions automatically as soon as the edit step is
resolved.

Release Notes:

- N/A
2024-08-06 11:30:09 +02:00
Luis Cossío
7b5fdcee7f lsp: Support Goto Declaration (#15785)
Adds support for [Goto
Declaration](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_declaration)
LSP command.

I am particularly interested in [this for Rust
projects](https://rust-analyzer.github.io/manual.html#go-to-declaration),
to be able to navigate to the place where a trait method is declared,
coming from a trait method implementation.

I noticed this was something I could do in VSCode before, but was
somehow missing is Zed. Thanks to the already existing infrastructure
for Goto Definition, I just followed and copy-paste-adapted it for Goto
Declaration.

As a bonus, I added `ctrl-F12` and `alt-ctrl-F12` as default macOS
keybindings for `GoToDeclaration` and `GoToDeclarationSplit`,
respectively. They are not keybindings from another editor, but I
figured they made sense to be grouped along with the other *F12
commands.

### Release Notes:

- Added "Go to declaration" editor action.
- vim: Breaking change to keybindings after introduction of the `Go to
declaration` editor action. The new keybindings are the following (and
can be found [here](https://zed.dev/docs/vim), alongside the other key
bindings):
  - `g d` - Go to definition
  - `g D` - Go to declaration
  - `g y` - Go to type definition
  - `g I` - Go to implementation




https://github.com/user-attachments/assets/ee5c10a8-94f0-4e50-afbb-6f71db540c1b

---------

Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
2024-08-06 11:20:51 +02:00
apricotbucket28
82db5dedfb terminal: Fix context menu keybindings flicker (#15771)
Fixes https://github.com/zed-industries/zed/issues/15156


[Screencast_20240804_112659.webm](https://github.com/user-attachments/assets/d6d15e65-ece2-4342-b2ca-55e3737a4736)

Release Notes:

- N/A
2024-08-06 10:13:26 +02:00
apricotbucket28
1f97741067 linux: Add shortcuts for left/right keys in prompts (#15779)
Fixes https://github.com/zed-industries/zed/issues/15151

Release Notes:

- Fixed prompts on Linux not being navigable by arrow keys
([#15151](https://github.com/zed-industries/zed/issues/15151)).
2024-08-06 10:09:59 +02:00
Vitaly Slobodin
45afe9dffa ruby: Add support for *.rbs files (#15778)
Hi, this pull request adds support for RBS files for the Ruby language.
[RBS](https://github.com/ruby/rbs) is a language to describe the
structure of Ruby programs.

This pull request:

- adds a new grammar to the Ruby extension for RBS
https://github.com/joker1007/tree-sitter-rbs
- configures the Ruby extension to use the added grammar.

Release Notes:

- N/A
2024-08-06 09:51:44 +02:00
Marshall Bowers
ca9511393b collab: Add support for more providers to the LLM service (#15832)
This PR adds support for additional providers to the LLM service:

- OpenAI
- Google
- Custom Zed models (through Hugging Face)

Release Notes:

- N/A
2024-08-05 21:16:18 -04:00
Max Brunsfeld
8e9c2b1125 Introduce a separate backend service for LLM calls (#15831)
This PR introduces a separate backend service for making LLM calls.

It exposes an HTTP interface that can be called by Zed clients. To call
these endpoints, the client must provide a `Bearer` token. These tokens
are issued/refreshed by the collab service over RPC.

We're adding this in a backwards-compatible way. Right now the access
tokens can only be minted for Zed staff, and calling this separate LLM
service is behind the `llm-service` feature flag (which is not
automatically enabled for Zed staff).

Release Notes:

- N/A

---------

Co-authored-by: Marshall <marshall@zed.dev>
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-08-05 20:26:21 -04:00
Marshall Bowers
4ed43e6e6f collab: Ignore Stripe events that are older than an hour (#15830)
This PR makes it so any Stripe events we receive that occurred over an
hour ago are marked as processed.

We don't want to process an old event long after it occurred and
potentially overwrite more recent updates.

This also makes running collab locally a bit nicer, as we won't be
getting errors for a bunch of older events that will never get processed
successfully.

The period after time after which we consider an event "stale" can be
modified, as needed.

Release Notes:

- N/A
2024-08-05 17:15:38 -04:00
Kyle Kelley
f6a0fef5cf repl: Address simple fixes (#15828)
Release Notes:

- Added logging in debug mode of raw kernel output from REPL.
- Fixed REPL kernels not being shut down completely on close of Editor
2024-08-05 13:48:15 -07:00
apricotbucket28
ab1fabef68 gpui: Align SVG sprites to whole pixels (#15822)
Related to https://github.com/zed-industries/zed/issues/12352 (and old
issue https://github.com/zed-industries/zed/issues/643)

This fixes visual glitches that could happen when rendering icons on
Linux and Windows (and in the Blade backend)


![image](https://github.com/user-attachments/assets/02646d1d-d35b-48be-89c9-189416510cf2)

![image](https://github.com/user-attachments/assets/ccf99867-25d2-40fb-8735-c540f8cf793a)

![image](https://github.com/user-attachments/assets/8d1124a3-669e-4be5-8b46-5dc2df14a28a)


Release Notes:

- Linux: Fixed visual glitches when rendering icons.
2024-08-05 12:43:23 -07:00
Max Brunsfeld
27779e33fb Refactor: Restructure collab main function to prepare for new subcommand: serve llm (#15824)
This is just a refactor that we're landing ahead of any functional
changes to make sure we haven't broken anything.

Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
Co-authored-by: Jason <jason@zed.dev>
2024-08-05 12:07:38 -07:00
Marshall Bowers
705f7e7a03 collab: Include geoip_country_code in initial span (#15819)
This PR changes how we report the `geoip_country_code` in the tracing
spans.

I wasn't seeing it come through in the logs, and I think it was because
we didn't declare the field on the initial span.

Release Notes:

- N/A
2024-08-05 12:24:47 -04:00
Marshall Bowers
04ad088eb8 assistant: Remove old model selector code (#15817)
This PR removes the old model selector code, as it was no longer used
after #15693.

Release Notes:

- N/A
2024-08-05 12:19:16 -04:00
Nate Butler
03cc18dd20 assistant-panel: Update model selector to a combo-box (#15693)
This updates the model selector to be a combobox (filterable list)

This PR causes the following regression: There is no longer a message in
the inline assistant explaining context is included from the assistant
panel. Will follow up with some sort of solution soon.

Before:
![CleanShot 2024-08-02 at 13 11
12@2x](https://github.com/user-attachments/assets/648ec4e3-48bc-4720-aaad-7659d848a4fa)

After:
![CleanShot 2024-08-02 at 13 10
37@2x](https://github.com/user-attachments/assets/09de098b-1a4a-44be-a6ae-6879f233d9a4)
![CleanShot 2024-08-02 at 13 10
48@2x](https://github.com/user-attachments/assets/701ce01c-3d6c-4c63-a6fc-53deff5d56c7)

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <1486634+maxdeviant@users.noreply.github.com>
2024-08-05 11:52:07 -04:00
Marshall Bowers
f11f3f2599 collab: Attach GeoIP country code to RPC sessions (#15814)
This PR updates collab to attach the user's GeoIP country code to their
RPC session.

We source the country code from the
[`CF-IPCountry`](https://developers.cloudflare.com/fundamentals/reference/http-request-headers/#cf-ipcountry)
header.

Release Notes:

- N/A
2024-08-05 11:11:49 -04:00
Bennet Bo Fenner
be0ccf47ee assistant: Show error messages in popover notification (#15808)
<img width="644" alt="image"
src="https://github.com/user-attachments/assets/ee6f2e60-e50a-481e-98b4-6c4b72a9b882">


Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-08-05 17:11:42 +02:00
Thorsten Ball
294892c470 assist panel: Update Zed AI notice if provider changes (#15805)
Release Notes:

- N/A

---------

Co-authored-by: Bennet <bennet@zed.dev>
2024-08-05 15:36:08 +02:00
Thorsten Ball
49d0672cdd assistant panel: Fix wrong state for Zed.dev provider being shown (#15800)
Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-08-05 15:35:58 +02:00
Thorsten Ball
0d97b236e7 assistant panel: Make "Configure" button in menu open Configuration view (#15799)
Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-08-05 13:31:03 +02:00
Thorsten Ball
0fba36469b assistant panel: Show Zed AI notice & configuration hint (#15798)
This adds two possible notices to the assistant panel:

- Shows notice if currently selected provider is not configured
- Shows notice if user is signed-out and (does not have provider OR
provider is zed.dev) and tells user to sign in

Design needs to be tweaked. cc @iamnbutler 

![screenshot-2024-08-05-13 11
21@2x](https://github.com/user-attachments/assets/ada2d881-2f81-49ed-bebf-2efbf06e7d82)


Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-08-05 13:21:16 +02:00
Bennet Bo Fenner
d9977718d2 assistant: Fix prompts not being editable (#15796)
Fixes a bug introduced in #15615, where prompts could not be edited in
the prompt library.
This PR fixes the behavior by only preventing users to edit built-in
prompts.

Release Notes:

- N/A
2024-08-05 12:52:58 +02:00
apricotbucket28
559ce87b4e linux: Save opened workspace when closing last window (#15754)
Fixes https://github.com/zed-industries/zed/issues/15642
(also fixes a bug where replacing a workspace in the same window would
reopen the old project after closing with `CTRL + Q`)

Release Notes:

- Linux: Fixed last workspace not being restored on startup
([#15642](https://github.com/zed-industries/zed/issues/15642)).
- Fixed a bug where a closed workspace could be reopened on startup.
2024-08-05 09:54:02 +02:00
Antonio Scandurra
0ec29d6866 Restructure workflow step resolution and fix inserting newlines (#15720)
Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-08-05 09:18:06 +02:00
Reese is on Codeberg
49e736d8ef ruby: Recognize Steepfiles as Ruby (#15762)
Release Notes:

- N/A
2024-08-04 11:57:34 -04:00
Kyle Kelley
f8234aec6a repl: Add ctrl-alt-enter binding to run in place (#15743)
Release Notes:

- Added `ctrl-alt-enter` keybinding for `repl::RunInPlace`
(`ctrl-option-enter` on MacOS). Keeps your screen position and cursor in
place when running any block.
2024-08-03 10:27:05 -07:00
Kyle Kelley
b7eae7fbd9 repl: Render markdown output from kernels (#15742)
<img width="1268" alt="image"
src="https://github.com/user-attachments/assets/73e03a28-f5e3-4395-a58c-cabd07f57889">

Release Notes:

- Added markdown rendering for Jupyter/REPL outputs. Push Markdown from
Deno/Typescript with `Deno.jupyter.md` and in IPython use
`IPython.display.Markdown`.
2024-08-03 09:41:12 -07:00
Kyle Kelley
36b61a8b87 repl: Process display IDs for updatable displays (#15738)
Release Notes:

- Added `update_display_data` support for REPL.


https://github.com/user-attachments/assets/d618e457-e314-482e-954a-6384f185629a
2024-08-03 08:15:36 -07:00
Adam Johnson
68446d2ed1 Use lowercase command names in initial settings files (#15717)
Release Notes:

- N/A

Making these two comments match what I see in the command palette:

<img width="629" alt="Xnapper-2024-08-03-08 09 26"
src="https://github.com/user-attachments/assets/f3eab05b-1560-4b0a-8df2-0eaf5746e422">
2024-08-03 09:35:09 -04:00
Kyle Kelley
4528e9d582 repl: Create better terminal output for REPL stdio (#15715)
Rely on our implementation of a GPUI powered alacritty terminal to
render stdout & stderr from the repl.

Release Notes:

- Fixed ANSI escape code and carriage return handling in repl outputs
(https://github.com/zed-industries/zed/issues/15640,
https://github.com/zed-industries/zed/issues/14855)


https://github.com/user-attachments/assets/bd3f1584-863a-4afa-b60b-9d222a830ff8

---------

Co-authored-by: Mikayla <mikayla@zed.dev>
2024-08-03 05:48:16 -07:00
Kyle Kelley
b6a3556a32 repl: Rely on block decorations to size according to content (#15713)
We no longer have to use line height calculations now that
https://github.com/zed-industries/zed/pull/15536 is in. Blocks resize
according to their content. This fixes up some of the rendering issues
on plaintext outputs as well.

Release Notes:

- Fix repl plain text output wrapping around and covering editor text
(https://github.com/zed-industries/zed/issues/15491,
https://github.com/zed-industries/zed/issues/14855)
2024-08-02 19:15:15 -07:00
Marshall Bowers
5c54596027 theme: Use a non-transparent color for the fallback title_bar.inactive_background (#15709)
This PR changes the fallback color for the
`title_bar.inactive_background` theme property so that the title bar
doesn't just disappear when a theme doesn't have a value set.

You're welcome, @naomieow.

Release Notes:

- linux: Changed the fallback color of `title_bar.inactive_background`
to a non-transparent value.
2024-08-02 18:44:22 -04:00
Marshall Bowers
91bbf0efc4 assistant: Normalize line endings for prompts loaded from the prompt library (#15708)
This PR makes it so we normalize the line endings for prompts to LFs
(`\n`) when we load a prompt from the library.

In some cases, prompts could end up with CRLF (`\r\n`) line endings.
When these prompts were used with the `/prompt` slash command and
summarily run, the prompt text would be converted into a rope, causing
the line endings to be normalized to LFs.

However, this would happen _after_ the ranges for the
`SlashCommandOutputSection`s were computed based on the text that still
contained the CRLFs. This would then cause these ranges to be invalid
for the text with the normalized endings, resulting in a panic when
converting them to anchors.

Fixes https://github.com/zed-industries/zed/issues/15652.

Release Notes:

- N/A

Co-authored-by: Max <max@zed.dev>
2024-08-02 18:13:17 -04:00
Marshall Bowers
3755f0d901 docs: Fix links to "Backend dependencies" setup (#15706)
This PR fixes the links to the "Backend dependencies" section from the
local collaboration docs.

We now provide links to the section in the platform-specific development
docs.

Release Notes:

- N/A
2024-08-02 16:47:45 -04:00
Marshall Bowers
c6580da889 assistant: Don't unwrap built-in step resolution prompt (#15704)
This PR removes some `unwrap`s while loading the built-in step
resolution prompt.

While these `unwrap`s are safe today, they are relying on some implicit
contracts that might change in the future. We're using this in a context
where it's easy to propagate an error upwards, so we may as well avoid
the `unwrap`s entirely and use a `Result`.

Release Notes:

- N/A
2024-08-02 16:07:43 -04:00
Danilo Leal
24afe7c3f9 Change the terminal assistant settings icon (#15702)
Change the terminal assistant settings icon so its consistent with the one we're using on the inline assistant.

--- 

Release Notes:

- N/A
2024-08-02 16:52:00 -03:00
Marshall Bowers
d6e558e3c9 copilot: Colocate copilot_chat_config_path with the rest of the Copilot code (#15703)
This PR moves the `copilot_chat_config_path` out of `paths` and into
`copilot` with the rest of the Copilot code.

Since this doesn't actually return a Zed path, it doesn't belong in
`paths`.

Release Notes:

- N/A
2024-08-02 15:46:06 -04:00
Piotr Osiewicz
e2eb68abca chore: Remove leftover code from #15646 (#15697)
Release Notes:

- N/A
2024-08-02 20:08:57 +02:00
Piotr Osiewicz
ad11d83724 Skip over folded regions when iterating over multibuffer chunks (#15646)
This commit weaves through new APIs for language::BufferChunks, multi_buffer::MultiBufferChunks and inlay_map::InlayChunks that allow seeking with an upper-bound. This allows us to omit doing syntax highligting and looking up diagnostics for folded ranges. This in turn directly improves performance of assistant panel with large contexts.

Release Notes:

- Fixed poor performance when editing in the assistant panel after
inserting large files using slash commands

---------

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2024-08-02 19:51:26 +02:00
Thorsten Ball
390815dd76 assistant panel: Tab-less configuration view (#15682)
TODOs for follow-up:
- [ ] When opening panel: nudge user to sign in if they're not signed-in
and have no provider configured (or if they're not signed-in and have
Zed AI configured)
- [ ] Configuration page is not scrollable
- [ ] Design tweaks

Current status:



https://github.com/user-attachments/assets/d26d65ea-43e8-481b-81a3-b3cba01704a8


Release Notes:

- N/A
2024-08-02 17:16:18 +02:00
Joseph T Lyons
35a3b00255 Add link to docs for configuring ruff 2024-08-02 11:06:01 -04:00
Egor Kotov
69cefb3a0c docs: Add brief R/Ark instructions (#15357)
Added brief instructions for [`Ark`](https://github.com/posit-dev/ark)
kernel for `R`.

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-08-02 10:22:45 -04:00
Vladimir Kukushkin
55fbafee35 docs: Fix Ruff-related links (#15511)
Release Notes:

- N/A
2024-08-02 10:16:13 -04:00
Mayank Jikadara
61365e034f Relax "test" runnable check (#15647)
This commit updates the Tree-sitter query to match Rust attributes
containing the word "test". The previous query only matched attributes
with the exact string "test", which was too restrictive for custom test
macros like https://docs.rs/test-log/0.2.10/test_log/#

Discussion - https://github.com/zed-industries/zed/discussions/15580
2024-08-02 15:22:01 +02:00
Antonio Scandurra
b88b9dcdd1 Extend symbol ranges by their annotation range when suggesting edits (#15677)
Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-08-02 11:40:29 +02:00
Thorsten Ball
e4608e7f12 assistant panel: Intermediate fix for focus problems (#15674)
This is an intermediate fix for the focus problems in the assistant
panel. Intermediate because I'm going to shred the whole
ConfigurationView now and replace the tabs inside with a list.


Release Notes:

- N/A
2024-08-02 11:05:30 +02:00
Nate Butler
b4dcd6d394 Update model selector (#15665)
Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-08-01 21:57:51 -04:00
Marshall Bowers
5e011ab029 language_model: Denote the availability of language models (#15660)
This PR updates the `LanguageModel` trait with a new method for denoting
the availability of a model.

Right now we have two variants:

- `Public` for models that have no additional restrictions (other than
their respective setup/authentication requirements)
- `RequiresPlan` for models that require a specific Zed plan

Release Notes:

- N/A
2024-08-01 18:26:27 -04:00
Marshall Bowers
906d9736d5 gleam: Update Tree-sitter grammar for label shorthand syntax (#15659)
This PR updates the Gleam Tree-sitter grammar to support label shorthand
syntax.

Release Notes:

- N/A
2024-08-01 17:46:23 -04:00
Marshall Bowers
771424e4f9 extension: Make worktree argument to run_slash_command optional (#15658)
This PR updates the extension API to make the `worktree` argument to
`run_slash_command` optional.

We may not always have a worktree, and not all slash commands need them,
so by making it optional we can allow individual slash commands to
decide what to do when there is no worktree.

Release Notes:

- N/A
2024-08-01 17:34:44 -04:00
Marshall Bowers
88f29c8355 assistant: Don't require a worktree to run slash commands (#15655)
This PR fixes an issue where slash commands were not able to run when
Zed did not have any worktrees opened.

This requirement was only necessary for slash commands originating from
extensions, and we can enforce the presence of a worktree just for
those:

<img width="378" alt="Screenshot 2024-08-01 at 5 01 58 PM"
src="https://github.com/user-attachments/assets/38bea947-e33b-4c64-853c-c1f36c63d779">

Release Notes:

- N/A
2024-08-01 17:14:13 -04:00
Bilal Elmoussaoui
55fc1f0afb docs: Remove wrongly documented secrets portals (#15303)
- The secrets portal is only used if the application is sandboxed
without access to the org.freedesktop.secrets on the host but as zed is
not really working correctly if sandboxed as is, it is better to
completely omit that part
- The org.freedestkop.secrets is not a portal but a spec file that is
implemented by both gnome in gnome-keyring, kde in kwallet but also by
other password managers like keepassxc and it is a requirement for a
"correctly" set up linux desktop so just remove that wrongly documented
bit

Release Notes:

- N/A
2024-08-01 17:07:26 -04:00
Antonio Scandurra
1fa4cc3c7a Use the right max_token_count for Qwen
Co-Authored-By: Nathan <nathan@zed.dev>
2024-08-01 22:42:10 +02:00
Nathan Sobo
1b85793ebc Fix inline assist deletion blocks (#15651)
After the changes in #15536, block decorations need to be given an
explicit height if their content doesn't consume height on its own. We
missed that inline transformation deletion decorations didn't do this,
creating weird results. This fixes the issue and prevents block
decorations from ever having a zero height. That helps avoid major
weirdness, but this still a bit of a gotcha.

We need to back port this to Preview Channel (0.147.x)

Release Notes:

- N/A

---------

Co-authored-by: Antonio <antonio@zed.dev>
2024-08-01 22:41:25 +02:00
Marshall Bowers
0799dfb2b8 assistant: Only insert the host's default prompt when creating a new context (#15650)
This PR fixes an issue where creating a new context as a guest would
insert multiple default prompts: one for the host and one for the guest.

When creating a new context as a guest while collaborating, we should
only use the host's default prompt.

Release Notes:

- N/A
2024-08-01 16:28:45 -04:00
Antonio Scandurra
21816d1ff5 Add Qwen2-7B to the list of zed.dev models (#15649)
Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-08-01 22:26:07 +02:00
Marshall Bowers
60127f2a8d Fix .env.toml paths (#15645)
This PR fixes the `.env.toml` paths, since we inadvertently broke them
in https://github.com/zed-industries/zed/pull/15557.

There's likely a better way we can do this, but I wanted to restore the
previous behavior, for now.

Release Notes:

- N/A
2024-08-01 15:43:29 -04:00
Marshall Bowers
4bfb8fda8d Rename zed.dev/settings to zed.dev/account (#15636)
This PR renames the links to the `zed.dev/settings` page to the
`zed.dev/account`.

Some of these spots will likely link out to a marketing page later.

Release Notes:

- N/A
2024-08-01 13:59:21 -04:00
Marshall Bowers
7652045903 Remove primary/secondary distinction for CachedLspAdapter (#15634)
This PR removes the primary/secondary distinction for
`CachedLspAdapter`s.

After #15624 we weren't relying on the `is_primary` field anywhere, so
we can remove it.

Release Notes:

- N/A
2024-08-01 13:51:34 -04:00
Thorsten Ball
80594cc7f8 docs: Fix ordering of language servers in Ruby docs (#15633)
Follow-up to https://github.com/zed-industries/zed/pull/15624.

Since #15624 introduced a fix for the language server ordering, this
fixes the ordering in the Ruby docs.

Release Notes:

- N/A
2024-08-01 19:33:36 +02:00
Thorsten Ball
5afa799f37 assistant panel: Add button to open new context to configuration page (#15628)
This adds a button to the `Configuration` page for providers so it's
easy to start a new context _with the given provider_ selected.

![screenshot-2024-08-01-17 53
07@2x](https://github.com/user-attachments/assets/f25ecbe0-0b96-4a32-ac98-a5113b08ec2a)

Obviously not the most beautiful form this button can have, but works!

cc @iamnbutler 

Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-08-01 19:33:00 +02:00
Nate Butler
70b2da78f8 Update assistant config UI (#15630)
![CleanShot 2024-08-01 at 12 55
01@2x](https://github.com/user-attachments/assets/f9ed44ba-6bff-4805-ad71-2e3538315e57)

- Remove assisstant_description for now.
- Updates assistant config UI
- Updates Ollama and zed.dev provider UIs
- Updates download icon

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <1486634+maxdeviant@users.noreply.github.com>
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-08-01 13:30:35 -04:00
Marshall Bowers
3bd9a3f478 Respect the language_servers setting's order when determining the primary language server (#15624)
This PR updates how we determine the "primary" language server for a
buffer to make it respect the order specified by the `language_servers`
setting.

Previously we were relying on the language servers to be registered in
the right order in order to select the primary one effectively.

However, in my testing I observed some cases where a native language
server (e.g., `tailwindcss-language-server`) could end up first in the
list of language servers despite not being first in the
`language_servers` setting.

While this wasn't a problem for the Tailwind or ESLint language servers
on account of them being defined natively with the designation of
"secondary" language servers, this could cause problems with
extension-based language servers.

To remedy this, every time we start up language servers we reorder the
list of language servers for a given language to reflect the order in
the `language_servers` setting. This ordering then allows us to treat
the first language server in the list as the "primary" one.

Related issues:

- https://github.com/zed-industries/zed/issues/15023
- https://github.com/zed-industries/zed/issues/15279

Release Notes:

- The ordering of language servers will now respect the order in the
`language_servers` setting.
- The first language server in this list will be used as the primary
language server.
2024-08-01 11:58:23 -04:00
Antonio Scandurra
0b175ac66e Give edit steps multibuffer a title (#15625)
Release Notes:

- N/A

Co-authored-by: Nathan <nathan@zed.dev>
2024-08-01 17:48:56 +02:00
Thorsten Ball
ed7952f5ef assistant panel: Update active tab in config panel when provider changes (#15621)
Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-08-01 17:24:55 +02:00
Thorsten Ball
6c83c7906a assistant panel: Ensure always active tab is shown (#15618)
Small bug that snuck in with #15490. When closing the configuration tab
explicitly and then activating the assistant tab again, it wouldn't show
the configuration page.

Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-08-01 16:14:31 +02:00
Piotr Osiewicz
ac1a15f5d7 assistant: Report all worktree entries in /file completions (#15617)
We were reporting file count as worktree entry count, which led to us
missing some of the entries in /file command completion.

/cc @bennetbo

The other components that used `PathMatchCandidateSet` are
`/diagnostics` and file finder. File finder is unaffected, as it used
`Candidates::Files` - thus previously reported count was correct for it;
`/diagnostics` were using `::Entries` as well, so it could miss entries
just like `/files`.

Release Notes:

- Fixed /file and /diagnostics slash commands omitting entries in it's
completions menu.
2024-08-01 16:09:53 +02:00
Nathan Sobo
a9c6e435f7 Bundle editing workflow prompt as a read-only built-in prompt (#15615)
Built-in prompts can still be removed from the default prompt, but they
can't be edited and are automatically updated with new Zed releases.

Release Notes:

- N/A

---------

Co-authored-by: Antonio <antonio@zed.dev>
2024-08-01 15:56:17 +02:00
Bennet Bo Fenner
be3a8584ff assistant: Add a Configuration page (#15490)
- [x] bug: setting a key doesn't update anything
- [x] show high-level text on configuration page to explain what it is
- [x] show "everything okay!" status when credentials are set
- [x] maybe: add "verify" button to check credentials
- [x] open configuration page when opening panel for first time and
nothing is configured
- [x] BUG: need to fix empty assistant panel if provider is `zed.dev`
but not logged in


Co-Authored-By: Thorsten <thorsten@zed.dev>

Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
Co-authored-by: Nate Butler <iamnbutler@gmail.com>
Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
2024-08-01 15:54:47 +02:00
Piotr Osiewicz
79213637e2 assistant: Display hamburger menu even when the panel is not focused (#15612)
This commit gives tab button renderers control over the condition in
which they should be displayed. Previously we displayed tab buttons only
when the pane was focused. Now tab renderers can return an Option of
AnyElement, which in turn makes it possible for them to control when and
how they're rendered. Pane and Terminal handlers still check for self
focus condition and Assistant Panel does not.



Release Notes:

- N/A
2024-08-01 15:08:48 +02:00
Antonio Scandurra
dc0f7436e0 Add support for backwards compatibility in PromptStore (#15602)
Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-08-01 15:07:14 +02:00
Ramkumar
04065d9a6a Fix GitHub Copilot chat authentication on Windows (#15587)
This PR fixes Copilot chat authentication on Windows. GitHub Copilot
chat in assistant panel uses auth token file generated by [copilot
language server](https://github.com/zed-industries/copilot). The path to
authentication token file is platform dependent. On Windows, it happens
to be `~\AppData\Local\github-copilot\hosts.json`.

Release Notes:

- Fixed Copilot chat sign in issue on Windows ([#4673](https://github.com/zed-industries/zed/issues/4673))
2024-08-01 14:06:13 +03:00
Antonio Scandurra
1246010e44 Fix padding for end_hover_slot on ListItems (#15598)
Release Notes:

- N/A

Co-authored-by: Nathan <nathan@zed.dev>
2024-08-01 12:10:45 +02:00
Kirill Bulatov
c98918aed8 Properly calculate y offsets for multi buffer context menus (#15594)
Follow-up of https://github.com/zed-industries/zed/pull/14727

Release Notes:

- Fixed multi buffer context menus not showing properly

Co-authored-by: Max Brunsfeld <max@zed.dev>
2024-08-01 12:35:46 +03:00
renovate[bot]
9b24e7d6de Update Rust crate image to v0.25.2 (#15578)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [image](https://togithub.com/image-rs/image) | dependencies | patch |
`0.25.1` -> `0.25.2` |
| [image](https://togithub.com/image-rs/image) | workspace.dependencies
| patch | `0.25.1` -> `0.25.2` |

---

### Release Notes

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

###
[`v0.25.2`](https://togithub.com/image-rs/image/blob/HEAD/CHANGES.md#Version-0252)

[Compare
Source](https://togithub.com/image-rs/image/compare/v0.25.1...v0.25.2)

Features:

- Added the HDR encoder to supported formats in generic write methods
with the
`hdr` feature enabled. Supports 32-bit float RGB color only, for now.
- When cloning `ImageBuffer`, `DynamicImage` and `Frame` the existing
buffer
    will now be reused if possible.
-   Added `image::ImageReader` as an alias.
-   Implement `ImageEncoder` for `HdrEncoder`.

Structural changes

- Switch from `byteorder` to `byteorder-lite`, consolidating some
casting
    unsafety to `bytemuck`.
- Many methods on `DynamicImage` and buffers gained `#[must_use]`
indications.

Bug fixes:

-   Removed test data included in the crate archive.
- The WebP animation decoder stops when reaching the indicate frame
count.
-   Fixed bugs in the `bmp` decoder.
-   Format support gated on the `exr` feature now compiles in isolation.

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy40NDAuNyIsInVwZGF0ZWRJblZlciI6IjM3LjQ0MC43IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 11:19:51 +03:00
renovate[bot]
9307cffd46 Update Rust crate ipc-channel to v0.18.2 (#15579)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [ipc-channel](https://togithub.com/servo/ipc-channel) | dependencies |
patch | `0.18.0` -> `0.18.2` |

---

### Release Notes

<details>
<summary>servo/ipc-channel (ipc-channel)</summary>

###
[`v0.18.2`](https://togithub.com/servo/ipc-channel/releases/tag/v0.18.2)

[Compare
Source](https://togithub.com/servo/ipc-channel/compare/v0.18.1...v0.18.2)

Changes:

-   Upgraded to version `1.0` of `mio`

###
[`v0.18.1`](https://togithub.com/servo/ipc-channel/releases/tag/v0.18.1)

[Compare
Source](https://togithub.com/servo/ipc-channel/compare/v0.18.0...v0.18.1)

Includes
[https://github.com/servo/ipc-channel/pull/335](https://togithub.com/servo/ipc-channel/pull/335)

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 11:19:09 +03:00
renovate[bot]
f26d746c71 Update Rust crate linkme to v0.3.27 (#15581)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [linkme](https://togithub.com/dtolnay/linkme) | dependencies | patch |
`0.3.17` -> `0.3.27` |

---

### Release Notes

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

### [`v0.3.27`](https://togithub.com/dtolnay/linkme/releases/tag/0.3.27)

[Compare
Source](https://togithub.com/dtolnay/linkme/compare/0.3.26...0.3.27)

- Add support for OpenBSD
([#&#8203;90](https://togithub.com/dtolnay/linkme/issues/90), thanks
[@&#8203;lcheylus](https://togithub.com/lcheylus))

### [`v0.3.26`](https://togithub.com/dtolnay/linkme/releases/tag/0.3.26)

[Compare
Source](https://togithub.com/dtolnay/linkme/compare/0.3.25...0.3.26)

- Resolve unexpected_cfgs warning
([#&#8203;87](https://togithub.com/dtolnay/linkme/issues/87))

### [`v0.3.25`](https://togithub.com/dtolnay/linkme/releases/tag/0.3.25)

[Compare
Source](https://togithub.com/dtolnay/linkme/compare/0.3.24...0.3.25)

- Improve error messages precipitated by slice typechecking
([#&#8203;84](https://togithub.com/dtolnay/linkme/issues/84), thanks
[@&#8203;CAD97](https://togithub.com/CAD97))

### [`v0.3.24`](https://togithub.com/dtolnay/linkme/releases/tag/0.3.24)

[Compare
Source](https://togithub.com/dtolnay/linkme/compare/0.3.23...0.3.24)

- Fix soundness bug in which coercion can bypass distributed slice
typecheck ([#&#8203;83](https://togithub.com/dtolnay/linkme/issues/83),
thanks [@&#8203;CAD97](https://togithub.com/CAD97))

### [`v0.3.23`](https://togithub.com/dtolnay/linkme/releases/tag/0.3.23)

[Compare
Source](https://togithub.com/dtolnay/linkme/compare/0.3.22...0.3.23)

-   Fix unused_imports warnings when compiled by rustc 1.78

### [`v0.3.22`](https://togithub.com/dtolnay/linkme/releases/tag/0.3.22)

[Compare
Source](https://togithub.com/dtolnay/linkme/compare/0.3.21...0.3.22)

- Add support for mipsel-sony-psp target
([#&#8203;80](https://togithub.com/dtolnay/linkme/issues/80), thanks
[@&#8203;SK83RJOSH](https://togithub.com/SK83RJOSH))

### [`v0.3.21`](https://togithub.com/dtolnay/linkme/releases/tag/0.3.21)

[Compare
Source](https://togithub.com/dtolnay/linkme/compare/0.3.20...0.3.21)

- Update proc-macro2 to fix caching issue when using a rustc-wrapper
such as sccache

### [`v0.3.20`](https://togithub.com/dtolnay/linkme/releases/tag/0.3.20)

[Compare
Source](https://togithub.com/dtolnay/linkme/compare/0.3.19...0.3.20)

-   Support `deny(unsafe_op_in_unsafe_fn)` and 2024 edition

### [`v0.3.19`](https://togithub.com/dtolnay/linkme/releases/tag/0.3.19)

[Compare
Source](https://togithub.com/dtolnay/linkme/compare/0.3.18...0.3.19)

- Avoid a new pedantic clippy lint from being triggered inside code
generated by `distributed_slice`

### [`v0.3.18`](https://togithub.com/dtolnay/linkme/releases/tag/0.3.18)

[Compare
Source](https://togithub.com/dtolnay/linkme/compare/0.3.17...0.3.18)

- Documentation improvements
([#&#8203;76](https://togithub.com/dtolnay/linkme/issues/76),
[#&#8203;77](https://togithub.com/dtolnay/linkme/issues/77), thanks
[@&#8203;stepancheg](https://togithub.com/stepancheg))

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 11:17:07 +03:00
renovate[bot]
903195d391 Update Rust crate mimalloc to v0.1.43 (#15582)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

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

---

### Release Notes

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

###
[`v0.1.43`](https://togithub.com/purpleprotocol/mimalloc_rust/releases/tag/v0.1.43):
Version 0.1.43

[Compare
Source](https://togithub.com/purpleprotocol/mimalloc_rust/compare/v0.1.42...v0.1.43)

##### Changes

-   Mimalloc `v2.1.7`
- Fix static builds (credits
[@&#8203;BlackDex](https://togithub.com/BlackDex))

###
[`v0.1.42`](https://togithub.com/purpleprotocol/mimalloc_rust/releases/tag/v0.1.42):
Version 0.1.42

[Compare
Source](https://togithub.com/purpleprotocol/mimalloc_rust/compare/v0.1.41...v0.1.42)

##### Changes

-   MiMalloc `v2.1.6`
- Expose `usable_size` and `version`. Credits
[@&#8203;nathaniel-daniel](https://togithub.com/nathaniel-daniel).
- Link with libatomic on armv6-linux. Credits
[@&#8203;notorca](https://togithub.com/notorca).
- Add no_thp option for Linux/Android. Credits
[@&#8203;devnexen](https://togithub.com/devnexen).

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 11:16:52 +03:00
renovate[bot]
33d06c4d96 Update Rust crate futures to v0.3.30 (#15575)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [futures](https://rust-lang.github.io/futures-rs)
([source](https://togithub.com/rust-lang/futures-rs)) |
workspace.dependencies | patch | `0.3.28` -> `0.3.30` |

---

### Release Notes

<details>
<summary>rust-lang/futures-rs (futures)</summary>

###
[`v0.3.30`](https://togithub.com/rust-lang/futures-rs/blob/HEAD/CHANGELOG.md#0330---2023-12-24)

[Compare
Source](https://togithub.com/rust-lang/futures-rs/compare/0.3.29...0.3.30)

- Add `{BiLock,SplitStream,SplitSink,ReadHalf,WriteHalf}::is_pair_of`
([#&#8203;2797](https://togithub.com/rust-lang/futures-rs/issues/2797))
- Fix panic in `FuturesUnordered::clear`
([#&#8203;2809](https://togithub.com/rust-lang/futures-rs/issues/2809))
- Fix panic in `AsyncBufReadExt::fill_buf`
([#&#8203;2801](https://togithub.com/rust-lang/futures-rs/issues/2801),
[#&#8203;2812](https://togithub.com/rust-lang/futures-rs/issues/2812))
- Improve support for targets without atomic CAS
([#&#8203;2811](https://togithub.com/rust-lang/futures-rs/issues/2811))
- Remove build scripts
([#&#8203;2811](https://togithub.com/rust-lang/futures-rs/issues/2811))

###
[`v0.3.29`](https://togithub.com/rust-lang/futures-rs/blob/HEAD/CHANGELOG.md#0329---2023-10-26)

[Compare
Source](https://togithub.com/rust-lang/futures-rs/compare/0.3.28...0.3.29)

- Add `TryStreamExt::try_ready_chunks`
([#&#8203;2757](https://togithub.com/rust-lang/futures-rs/issues/2757))
- Add `TryStreamExt::{try_all,try_any}`
([#&#8203;2783](https://togithub.com/rust-lang/futures-rs/issues/2783))
- Add `UnboundedSender::{len,is_empty}`
([#&#8203;2750](https://togithub.com/rust-lang/futures-rs/issues/2750))
- Fix `Sync` impl of `FuturesUnordered`
([#&#8203;2788](https://togithub.com/rust-lang/futures-rs/issues/2788))
- Fix infinite loop caused by invalid UTF-8 bytes
([#&#8203;2785](https://togithub.com/rust-lang/futures-rs/issues/2785))
- Fix build error with -Z minimal-versions
([#&#8203;2761](https://togithub.com/rust-lang/futures-rs/issues/2761))

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-31 19:35:55 -04:00
renovate[bot]
9ca21c1454 Update Rust crate etagere to v0.2.13 (#15574)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [etagere](https://togithub.com/nical/etagere) | dependencies | patch |
`0.2.8` -> `0.2.13` |

---

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-31 19:34:28 -04:00
Marshall Bowers
218776a119 collab: Increase the frequency at which we poll for Stripe events (#15577)
This PR increases the frequency at which we poll for Stripe events.

This decreases the amount of time we have to wait in order for changes
in Stripe to be reflected in our system.

We now poll for events every 5 seconds, down from every 5 minutes.

In order to avoid needlessly over-fetching data from Stripe, we put a
cap on the number of pages consisting entirely of already-processed
events that we can see before stopping. This is set to 4, so once we've
seen 4 pages of processed events (400 events in total), we'll stop
fetching subsequent pages.

Release Notes:

- N/A
2024-07-31 19:33:41 -04:00
Danilo Leal
a6f73c10d1 Fix symlink icon in the project panel (#15537)
Closes https://github.com/zed-industries/zed/issues/15481

---

Release Notes:

- N/A
2024-07-31 18:58:07 -03:00
Danilo Leal
236e72df38 Fix editor & assistant panel toolbar height mismatch (#15552)
This PR ensures that the toolbar within the editor and the assistant
panel have the same height. I also pushed in some other tiny design
(spacing, really) tweaks. There's still a mismatch between the editor
toolbar and the assistant panel's history tab, but I'll try to tackle
this separately.

---

Release Notes:

- N/A
2024-07-31 18:57:55 -03:00
renovate[bot]
851aca9514 Update Rust crate emojis to v0.6.3 (#15568)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [emojis](https://togithub.com/rossmacarthur/emojis) |
workspace.dependencies | patch | `0.6.1` -> `0.6.3` |

---

### Release Notes

<details>
<summary>rossmacarthur/emojis (emojis)</summary>

###
[`v0.6.3`](https://togithub.com/rossmacarthur/emojis/compare/0.6.2...0.6.3)

[Compare
Source](https://togithub.com/rossmacarthur/emojis/compare/0.6.2...0.6.3)

###
[`v0.6.2`](https://togithub.com/rossmacarthur/emojis/compare/0.6.1...0.6.2)

[Compare
Source](https://togithub.com/rossmacarthur/emojis/compare/0.6.1...0.6.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:eyJjcmVhdGVkSW5WZXIiOiIzNy40NDAuNyIsInVwZGF0ZWRJblZlciI6IjM3LjQ0MC43IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-31 17:37:45 -04:00
Marshall Bowers
0d9a6d246a collab: Fix spelling of "cancellation" (#15569)
English is hard.

In US English the forms of "cancel" are as follows:

- `cancel`
- `cancels`
- `canceling`
- `canceled`
- `cancellation`

Note that `cancellation` _does_ use the double `l`, which all the rest
of the forms do not.

Release Notes:

- N/A
2024-07-31 17:36:26 -04:00
renovate[bot]
54f774bfeb Update Rust crate embed-resource to v2.4.3 (#15567)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[embed-resource](https://togithub.com/nabijaczleweli/rust-embed-resource)
| build-dependencies | patch | `2.4.2` -> `2.4.3` |

---

### Release Notes

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

###
[`v2.4.3`](https://togithub.com/nabijaczleweli/rust-embed-resource/compare/v2.4.2...v2.4.3)

[Compare
Source](https://togithub.com/nabijaczleweli/rust-embed-resource/compare/v2.4.2...v2.4.3)

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-31 17:36:16 -04:00
renovate[bot]
b27d0c816f Update Rust crate core-graphics to v0.23.2 (#15565)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [core-graphics](https://togithub.com/servo/core-foundation-rs) |
dependencies | patch | `0.23.1` -> `0.23.2` |

---

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-31 17:12:26 -04:00
renovate[bot]
48d7929ce8 Update Rust crate async-trait to v0.1.81 (#15564)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [async-trait](https://togithub.com/dtolnay/async-trait) |
workspace.dependencies | patch | `0.1.77` -> `0.1.81` |

---

### Release Notes

<details>
<summary>dtolnay/async-trait (async-trait)</summary>

###
[`v0.1.81`](https://togithub.com/dtolnay/async-trait/releases/tag/0.1.81)

[Compare
Source](https://togithub.com/dtolnay/async-trait/compare/0.1.80...0.1.81)

- Turn off unneeded features of `syn` dependency
([#&#8203;272](https://togithub.com/dtolnay/async-trait/issues/272),
thanks [@&#8203;klensy](https://togithub.com/klensy))

###
[`v0.1.80`](https://togithub.com/dtolnay/async-trait/releases/tag/0.1.80)

[Compare
Source](https://togithub.com/dtolnay/async-trait/compare/0.1.79...0.1.80)

- Fix unreachable code warning for async functions that return `!`
([#&#8203;265](https://togithub.com/dtolnay/async-trait/issues/265),
thanks [@&#8203;de-vri-es](https://togithub.com/de-vri-es))

###
[`v0.1.79`](https://togithub.com/dtolnay/async-trait/releases/tag/0.1.79)

[Compare
Source](https://togithub.com/dtolnay/async-trait/compare/0.1.78...0.1.79)

-   Clean up some dead code

###
[`v0.1.78`](https://togithub.com/dtolnay/async-trait/releases/tag/0.1.78)

[Compare
Source](https://togithub.com/dtolnay/async-trait/compare/0.1.77...0.1.78)

- Prevent unused_qualifications lint being triggered in generated code
in nightly-2024-03-05 and up
([#&#8203;260](https://togithub.com/dtolnay/async-trait/issues/260))

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-31 17:11:44 -04:00
Marshall Bowers
2bc0a58f32 collab: Use the right zed.dev URL for the environment (#15566)
This PR updates collab to pick the `zed.dev` URL based on the current
environment rather than always using `https://zed.dev`.

This means that when developing locally we'll use
`http://localhost:3000` to be taken to the locally-running version of
`zed.dev`.

Release Notes:

- N/A
2024-07-31 17:08:29 -04:00
renovate[bot]
7b03b7e753 Update Rust crate async-task to v4.7.1 (#15559)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [async-task](https://togithub.com/smol-rs/async-task) | dependencies |
patch | `4.7.0` -> `4.7.1` |

---

### Release Notes

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

###
[`v4.7.1`](https://togithub.com/smol-rs/async-task/blob/HEAD/CHANGELOG.md#Version-471)

[Compare
Source](https://togithub.com/smol-rs/async-task/compare/v4.7.0...v4.7.1)

- Improve the panic message for when a task is polled after completion.
([#&#8203;73](https://togithub.com/smol-rs/async-task/issues/73))

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-31 16:39:52 -04:00
renovate[bot]
0ea1d6ee8f Update Rust crate async-compression to v0.4.12 (#15558)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [async-compression](https://togithub.com/Nullus157/async-compression)
| workspace.dependencies | patch | `0.4.6` -> `0.4.12` |

---

### Release Notes

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

###
[`v0.4.12`](https://togithub.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#0412---2024-07-21)

[Compare
Source](https://togithub.com/Nullus157/async-compression/compare/v0.4.11...v0.4.12)

##### Feature

-   Enable customizing Zstd decoding parameters.

###
[`v0.4.11`](https://togithub.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#0411---2024-05-30)

[Compare
Source](https://togithub.com/Nullus157/async-compression/compare/v0.4.10...v0.4.11)

##### Other

-   Expose total_in/total_out from underlying flate2 encoder types.

###
[`v0.4.10`](https://togithub.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#0410---2024-05-09)

##### Other

- *(deps)* update brotli requirement from 5.0 to 6.0
([#&#8203;274](https://togithub.com/Nullus157/async-compression/pull/274))
- Fix pipeline doc: Warn on unexpected cfgs instead of error
([#&#8203;276](https://togithub.com/Nullus157/async-compression/pull/276))
-   Update name of release-pr.yml
-   Create release.yml
-   Create release-pr.yml

###
[`v0.4.9`](https://togithub.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#049)

-   bump dep brotli from 4.0 to 5.0

###
[`v0.4.8`](https://togithub.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#048)

[Compare
Source](https://togithub.com/Nullus157/async-compression/compare/async-compression-v0.4.7...async-compression-v0.4.8)

-   bump dep brotli from 3.3 to 4.0

###
[`v0.4.7`](https://togithub.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#047)

[Compare
Source](https://togithub.com/Nullus157/async-compression/compare/async-compression-v0.4.6...async-compression-v0.4.7)

-   Flush available data in decoder even when there's no incoming input.

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-31 16:39:31 -04:00
Max Brunsfeld
1b2d4ee132 Allow users to stop a previously scheduled cancelation of their Zed Pro plan (#15562)
Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
2024-07-31 16:36:46 -04:00
Max Brunsfeld
3f1c091b87 Add the ability to store secret dev-only env vars in .env.secret.toml (#15557)
Release Notes:

- N/A

---------

Co-authored-by: Marshall <marshall@zed.dev>
2024-07-31 12:41:09 -07:00
Kirill Bulatov
86f266d37f Mention in the docs that we can show and revert inline diffs (#15556)
Release Notes:

- N/A
2024-07-31 22:23:24 +03:00
Max Brunsfeld
9751e61101 Show rate limit notices (#15515)
This UI change is behind a `ZedPro` feature flag so that it won't be
visible until we're ready to launch that service.

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
Co-authored-by: Marshall <marshall@zed.dev>
2024-07-31 12:05:19 -07:00
Marshall Bowers
8c54a46202 collab: Adapt rate limits based on plan (#15548)
This PR updates the rate limits to adapt based on the user's current
plan.

For the free plan rate limits I just took one-tenth of the existing rate
limits (which are now the Pro limits). We can adjust, as needed.

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
2024-07-31 14:27:28 -04:00
Piotr Osiewicz
7a0149f17c multi_buffer: Reuse buffer chunks iterator when seeking across folds within an excerpt (#15532)
Release Notes:

- N/A

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2024-07-31 10:29:15 -07:00
Joseph T Lyons
7259d092a7 v0.148.x dev 2024-07-31 12:17:58 -04:00
Antonio Scandurra
fef95110bb If paths from edit steps omit worktree root, search worktrees for relative path (#15543)
Release Notes:

- N/A

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-31 17:55:11 +02:00
Remco Smits
3c404dec92 Send pane removeItem event before removing the item (#15541)
This PR renames and added a new pane event to indicate the difference
between `removing` and `removed` event. This change is needed for the
debugger implementation, if you close a pane we have to send a
`terminateThread` request to the adapter because it's not supported to
reopen a pane. So when the pane is removing we have to know what thread
it is what is stored on the panel itself, so we have to be able to get
this information before the pane is actually removed.

So my idea how to fix this was by adding a new event called
`RemovedItem` which is a rename of `RemoveItem` which also makes a bit
more sense because the item is removed at that point. And seeing the
name `RemoveItem` does not really say that it's removed, more like we
are removing the item.

/cc @mikayla-maki

Release Notes:

- N/A
2024-07-31 08:48:19 -07:00
Antonio Scandurra
5b1ea7eda0 Clean up inline assist editor rendering (#15536)
Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Max <max@zed.dev>
2024-07-31 17:43:08 +02:00
Kirill Bulatov
73d8370177 Use upstream tree-sitter-go-mod grammar (#15539)
Release Notes:

- N/A
2024-07-31 18:23:12 +03:00
Thorsten Ball
874fedd717 assistant panel: Fix panic when opening panel with zed.dev provider (#15538)
There was/is some race condition that gets triggered only with the
zed.dev provider when opening the provider that would cause a
double-borrow on workspace.

This PR fixes the issue by cloning the workspace weakly. Turns out we
can go very far with just the weak reference.

We're still a bit unsure why exactly the race condition happened, since
it's hard to reproduce, but we're working on configuration
view/management in #15490 anyway.

Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-07-31 16:57:24 +02:00
Peter Tripp
64bfec533e Revert terminal: Add Alt key bindings for terminal (#15535)
- reverts commit 66ada3e44c.
- change default `option_as_meta` to true
2024-07-31 10:54:58 -04:00
Kirill Bulatov
9384f665bb Properly extract errors from the Anthropic API (#15534)
Before, we missed "successful" responses with the API errors, now they
are properly shown in the assistant panel.


![image](https://github.com/user-attachments/assets/0c0936af-86c2-4def-9a58-25d5e0912b97)


Release Notes:

- N/A
2024-07-31 16:31:11 +03:00
Bennet Bo Fenner
821ce2fc7c assistant panel: Fix panel not reloading after entering credentials (#15531)
This is the revised version of #15527.

We also added new events to notify subscribers when new providers are
added or removed.

Co-Authored-by: Thorsten <thorsten@zed.dev>

Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
2024-07-31 14:12:17 +02:00
Kirill Bulatov
a31dba9fc1 Reflect token limit errors and warnings in the assistant panel Send button style (#15529)
When used token count is below 80%, the Send button is displayed as
usual: no tooltip, regular style:


![image](https://github.com/user-attachments/assets/06198869-c49e-4231-b0cb-53b8f678c547)

When it exceeds 80% but below the `max_tokens` threshold, the button
starts to show as a warning, with the corresponding tooltip:

![warning](https://github.com/user-attachments/assets/83b02059-9610-4af7-97c6-9bca14364511)

When it is over the `max_token` threshold, the button starts to show as
an error, with another tooltip:

![error](https://github.com/user-attachments/assets/380d426a-cda9-4479-83e0-e018771291b6)

The buttons are not blocked in any case, in case the token calculation
is wrong, to allow using the assistant panel nonetheless.


Release Notes:

- N/A
2024-07-31 14:37:39 +03:00
Bennet Bo Fenner
380a19fcf2 Revert "assistant panel: Fix entering credentials not updating view" (#15528)
Reverts zed-industries/zed#15527

We broke the assistant panel in the process...

Release Notes:

- N/A
2024-07-31 13:26:27 +02:00
Thorsten Ball
b571bc800d assistant panel: Fix entering credentials not updating view (#15527)
Co-authored-by: Bennet <bennet@zed.dev>

Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-07-31 12:51:41 +02:00
Thorsten Ball
c78ea0df7e assistant panel: Ensure we prompt for creds when panel is activated (#15524)
Something seems to have broke on `main` in last few days.

Bug was reproducible like this:

- Open assistant panel
- Choose provider that wasn't authenticated
- See auth prompt
- Close the tab (!) in the assistant panel
- Reactivate the assistant panel
- Bug: panel is blank!

This fixes the bug by consolidating the behavior of checking credentials
and showing auth prompt.

Release Notes:

- N/A
2024-07-31 11:48:31 +02:00
Thorsten Ball
d8f7322725 assistant panel: Show provider name in model selector (#15523)
With zed.dev provider becoming more popular, it helps alleviate
confusion when showing not only the model name but also the provider
name.

Release Notes:

- N/A
2024-07-31 11:05:16 +02:00
apricotbucket28
8e6af73784 x11: Do not check hover state on properties change (#15518)
This fixes an issue where the window's hovered state would be incorrect,
causing the cursor not to update because it would think the window
wasn't hovered ([relevant
check](a03beeeb5b/crates/gpui/src/window.rs (L3016-L3017))).

The code here doesn't really seem to make sense, since there's already
the `XinputEnter` and `XinputLeave` events that indicate mouse focus
state on the window. The properties change event wouldn't necessarily
indicate when mouse focus changes.

Thanks @Emc2356 for reporting this on the Discord and helping figure out
the issue!
 
Release Notes:

- Linux: Fixed the cursor sometimes not changing on X11
2024-07-30 19:02:48 -07:00
Marshall Bowers
2b019ff9e2 collab: Add GET /billing/subscriptions endpoint (#15516)
This PR adds a new `GET /billing/subscriptions` endpoint to collab for
retrieving the subscriptions to display on the account settings page.

Release Notes:

- N/A
2024-07-30 21:17:35 -04:00
Marshall Bowers
a08e020e32 extension: Always use a real HTTP client in the ExtensionBuilder (#15514)
This PR makes it so we always construct a real HTTP client in the
`ExtensionBuilder`.

This used to be the case, but was inadvertently changed in
https://github.com/zed-industries/zed/pull/15470.

Release Notes:

- N/A
2024-07-30 18:04:53 -04:00
Marshall Bowers
a7ffc2b6f3 Show current plan in user menu (#15513)
This PR updates the user menu to show the user's current plan.

Also adds a new RPC message to send this information down to the client
when Zed starts.

This is behind a feature flag.

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
2024-07-30 17:38:16 -04:00
Peter Tripp
161c6ca6a4 terminal: Fix ctrl-a keybind on Linux (#15512)
- Disable Terminal ctrl-a (conflicts with readline).
- Standardize modifier order to use ctrl-shift instead of shift-ctrl to match existing keys.
- Move ctrl-shift-c (collab) to \!Terminal context (fix flickering in terminal right click menu).
- Consolidate two Terminal blocks in linux keybind
2024-07-30 17:18:06 -04:00
Marshall Bowers
7c5f4b72fb collab: Rework Stripe event processing (#15510)
This PR reworks how we process Stripe events for reconciliation
purposes.

The previous approach in #15480 turns out to not be workable, on account
of the Stripe event IDs not being strictly in order. This meant that we
couldn't reliably compare two arbitrary event IDs and determine which
one was more recent.

This new approach leans on the guidance that Stripe provides for
webhooks events:

> Webhook endpoints might occasionally receive the same event more than
once. You can guard against duplicated event receipts by logging the
[event IDs](https://docs.stripe.com/api/events/object#event_object-id)
you’ve processed, and then not processing already-logged events.
>
> https://docs.stripe.com/webhooks#handle-duplicate-events

We now record processed Stripe events in the `processed_stripe_events`
table and use this to filter out events that have already been
processed, so we do not process them again.

When retrieving events from the Stripe events API we now buffer the
unprocessed events so that we can sort them by their `created` timestamp
and process them in (roughly) the order they occurred.

Release Notes:

- N/A
2024-07-30 16:35:11 -04:00
Bennet Bo Fenner
dca9400edf assistant: Ensure that zed provider is listed as first option (#15496)
Release Notes:

- N/A
2024-07-30 19:38:52 +02:00
Kirill Bulatov
eedef487ac Add more context to the terminal assistant (#15492)
Release Notes:

- N/A
2024-07-30 20:21:45 +03:00
Peter Tripp
73e3dfc0c3 Switch editor::AcceptPartialInlineCompletion keybind to match VSCode (#15495)
Release Notes:

- Fixed editor::AcceptPartialInlineCompletion keybind to match VSCode
([#15487](https://github.com/zed-industries/zed/issues/15487)).
2024-07-30 13:12:07 -04:00
Piotr Osiewicz
4bf2826655 Assistant Panel: add keybinds for history and prompt library (#15486)
Release Notes:

- N/A
2024-07-30 17:13:10 +02:00
Jason Lee
1982a5aed1 gpui: Improve img element to support load from Assets (#15482)
Release Notes:

- N/A


Currently, the `img` element provided by GPUI only supports FilePath or
URL, but in actual applications we need to let `img` load an image
embedded in Assets.

The `svg` element can currently support this, but `img` cannot.

For example:

We have such an Assets directory:

```
assets
|- icons
|- images
|--- foo.png
```

```rs
// If give a path, considered an Asset
img("images/foo.png");
// If give a URI, considered a Remote image
img("https://foo.bar/images/foo.png");
// If give a PathBuf, considered a Local file
img(PathBuf::from("path/to/foo.png"));
```


## Example test

```
cargo run -p gpui --example image 
```

<img width="827" alt="image"
src="https://github.com/user-attachments/assets/e45dcf7f-4626-4fb0-aca9-9b6e1045a952">

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-30 10:55:48 -04:00
Antonio Scandurra
99bc90a372 Allow customization of the model used for tool calling (#15479)
We also eliminate the `completion` crate and moved its logic into
`LanguageModelRegistry`.

Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-30 16:18:53 +02:00
Kirill Bulatov
1bfea9d443 Show "full" paths in the /file command output (#15483)
Same as /tabs command outputs them

Before:
![Screenshot 2024-07-30 at 16 52
11](https://github.com/user-attachments/assets/acf1c79e-4607-41fc-bd5e-b20dc668eff4)

![Screenshot 2024-07-30 at 16 52
03](https://github.com/user-attachments/assets/dd7fa42f-3ba8-4f1c-9420-2a0246df64b7)

After:
![Screenshot 2024-07-30 at 16 53
16](https://github.com/user-attachments/assets/ee866ecb-2142-4303-9385-c6b856310d43)

![Screenshot 2024-07-30 at 16 53
21](https://github.com/user-attachments/assets/d42b9c77-d5e9-4aaf-8de9-3b160078e622)


Release Notes:

- N/A
2024-07-30 17:03:16 +03:00
Marshall Bowers
b160e13f20 collab: Keep track of last seen Stripe event for each record (#15480)
This PR improves our Stripe event handling by keeping track of the last
event we've seen for each record.

The `billing_customers` and `billing_subscriptions` tables both have a
new `last_stripe_event_id` column. When we apply an event to one of
these records, we store the event ID that was applied.

Then, when we are going through events we can ignore any event that has
an ID that came before the `last_stripe_event_id` (based on the
lexicographical ordering of the IDs).

Release Notes:

- N/A
2024-07-30 10:00:16 -04:00
Bennet Bo Fenner
2ada2964c5 assistant: Make it easier to define custom models (#15442)
This PR makes it easier to specify custom models for the Google, OpenAI,
and Anthropic provider:

Before (google):

```json
{
  "language_models": {
    "google": {
      "available_models": [
        {
          "custom": {
            "name": "my-custom-google-model",
            "max_tokens": 12345
          }
        }
      ]
    }
  }
}
```

After (google):

```json
{
  "language_models": {
    "google": {
      "available_models": [
        {
          "name": "my-custom-google-model",
          "max_tokens": 12345
        }
      ]
    }
  }
}
```

Before (anthropic):

```json
{
  "language_models": {
    "anthropic": {
      "available_models": [
        {
          "custom": {
            "name": "my-custom-anthropic-model",
            "max_tokens": 12345
          }
        }
      ]
    }
  }
}
```

After (anthropic):

```json
{
  "language_models": {
    "anthropic": {
      "version": "1",
      "available_models": [
        {
          "name": "my-custom-anthropic-model",
          "max_tokens": 12345
        }
      ]
    }
  }
}

```

The settings will be auto-upgraded so the old versions will continue to
work (except for Google since that one has not been released).

/cc @as-cii 

Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-07-30 15:46:39 +02:00
Santeri Salmijärvi
13dcb42c1c Change PathLikeWithPosition<P> into a non-generic type and replace ad-hoc Windows path parsing (#15373)
This simplifies `PathWithPosition` by making the common use case
concrete and removing the manual, incomplete Windows path parsing.
Windows paths also don't get '/'s replaced by '\\'s anymore to limit the
responsibility of the code to just parsing out the suffix and creating
`PathBuf` from the rest. `Path::file_name()` is now used to extract the
filename and potential suffix instead of manual parsing from the full
input. This way e.g. Windows paths that begin with a drive letter are
handled correctly without platform-specific hacks.

Release Notes:

- N/A
2024-07-30 16:39:33 +03:00
Thorsten Ball
41c550cbe1 assistant panel: Avoid auth prompt on provider switch (#15478)
Previously, the following lead to a bug:

1. Set OpenAI key
2. Switch to Anthropic
3. Restart Zed
4. Switch provider to OpenAI -> get prompted for authentication prompt

With this change, you won't get prompted for the OpenAI key again.

Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-07-30 14:25:02 +02:00
Piotr Osiewicz
21ebbc387a Inline Assist: replace "accept" action on generation error with "restart" (#15476)
Release Notes:

- N/A
2024-07-30 13:40:22 +02:00
Piotr Osiewicz
530feecdaa tab_switcher: Add support for tab switcher in assistant panel (#15475)
Additionally, I've generalized the implementation of tab switcher so
that - instead of explicitly listing panels it supports (at the time of
writing it was just the terminal panel and nothing else), it now relies
on Panel::pane trait method. As long as that's implemented, you get a
tab switcher support for free.

Release Notes:

- Added support for tab switcher in Assistant panel.
2024-07-30 13:32:13 +02:00
Bennet Bo Fenner
0540291204 settings: Introduce PRESERVED_KEYS to write default values (#15474)
This adds the optional `PRESERVED_KEYS` constant to the `Settings`
trait,
which allows users of the trait to specify which keys should be written
to
the settings file, even if their current value matches the default
value.

That's useful for tagged settings that have, for example, a `"version"`
field
that should always be present in the user settings file, so we can then
reparse
the user settings based on the version.

Co-Authored-By: Thorsten <thorsten@zed.dev>

Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-07-30 13:09:50 +02:00
Antonio Scandurra
fa19bc98ac Provide user agent when performing HTTP requests (#15470)
Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-30 11:12:37 +02:00
Bennet Bo Fenner
2a649fa824 language model: Fix missing use_tool method in CopilotChatLanguageModel (#15466)
Broke CI after merging #14842 

Release Notes:

- N/A

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-07-30 09:54:42 +02:00
Jason Lee
0c8b17d252 Hide blinking cursor when window is deactivated (#15408)
## Before 

https://github.com/user-attachments/assets/affb8407-a20e-4258-a8f7-b271da5d7b77

## After

https://github.com/user-attachments/assets/654fe7b9-330a-40c8-8885-72e69fa85b0a

Release Notes:

- Hide blinking cursor when window is deactivated ([4710](https://github.com/zed-industries/zed/issues/4710))
2024-07-30 10:49:15 +03:00
Ryan Hawkins
6f0655810e Add GitHub Copilot Chat Support (#14842)
# Summary

This commit implements Github Copilot Chat support within the existing
Assistant panel/framework. It required a little bit of trickery and
internal API modification, as Copilot doesn't use the same
authentication-style as all of the existing providers, opting to use
OAuth and a short lived API key instead of a straight API key. All
existing Assistant features should work.

Release Notes:
- Added Github Copilot Chat support
([#4673](https://github.com/zed-industries/zed/issues/4673)).

## Screenshots
<img width="1552" alt="A screenshot showing a conversation between a
user and Github Copilot Chat within the Zed editor."
src="https://github.com/user-attachments/assets/73eaf6a2-792b-4c40-a7fe-f763bd6417d7">

---------

Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
2024-07-30 09:32:58 +02:00
Marshall Bowers
d93891ba63 collab: Lay groundwork for reconciling with Stripe using the events API (#15459)
This PR lays the initial groundwork for using the Stripe events API to
reconcile the data in our system with what's in Stripe.

We're using the events API over webhooks so that we don't need to stand
up the associated infrastructure needed to handle webhooks effectively
(namely an asynchronous job queue).

Since we haven't configured the Stripe API keys yet, we won't actually
spawn the reconciliation background task yet, so this is currently a
no-op.

Release Notes:

- N/A
2024-07-29 23:50:07 -04:00
Marshall Bowers
28c14cdee4 collab: Add separate billing_customers table (#15457)
This PR adds a new `billing_customers` table to hold the billing
customers.

Previously we were storing both the `stripe_customer_id` and
`stripe_subscription_id` in the `billable_subscriptions` table. However,
this creates problems when we need to correlate subscription events back
to the subscription record, as we don't know the user that the Stripe
event corresponds to.

By moving the `stripe_customer_id` to a separate table we can create the
Stripe customer earlier in the flow—before we create the Stripe Checkout
session—and associate that customer with a user. This way when we
receive events down the line we can use the Stripe customer ID to
correlate it back to the user.

We're doing some destructive actions to the `billing_subscriptions`
table, but this is fine, as we haven't started using them yet.

Release Notes:

- N/A
2024-07-29 22:48:21 -04:00
Marshall Bowers
66121fa0e8 collab: Add endpoint for managing a billing subscription (#15455)
This PR adds a new `POST /billing/subscriptions/manage` endpoint that
can be used to manage a billing subscription.

The endpoint accepts a `github_user_id` to identify the user, as well as
an optional `subscription_id` for managing a specific subscription. If
`subscription_id` is not provided, it try and use the active
subscription, if there is only one.

Right now the endpoint only supports cancelling an active subscription.
This is done by passing `"intent": "cancel"` in the request body.

The endpoint will return the URL to a Stripe customer portal session,
which the caller can redirect the user to.

Here's an example of how to call it:

```sh
curl -X POST "http://localhost:8080/billing/subscriptions/manage" \
     -H "Authorization: <ADMIN_TOKEN>" \
     -H "Content-Type: application/json" \
     -d '{"github_user_id": 12345, "intent": "cancel"}'
```

Release Notes:

- N/A
2024-07-29 20:05:17 -04:00
Peter Tripp
4d8ad7ae42 docs: Update youtube link to new @zeddotdev channel name (#15453) 2024-07-29 17:55:35 -04:00
Marshall Bowers
e15d59c445 collab: Add endpoint for initiating a billing subscription (#15452)
This PR adds a new `POST /billing/subscriptions` endpoint that can be
used to initiate a billing subscription.

The endpoint will use the provided `github_user_id` to look up a user,
generate a Stripe Checkout session, and then return the URL.

The caller would then redirect the user to the URL to initiate the
checkout flow.

Here's an example of how to call it:

```sh
curl -X POST "http://localhost:8080/billing/subscriptions" \
     -H "Authorization: <ADMIN_TOKEN>" \
     -H "Content-Type: application/json" \
     -d '{"github_user_id": 12345}'
```

Release Notes:

- N/A
2024-07-29 17:31:36 -04:00
Kyle Kelley
8bb34fd93e repl: Log Jupyter kernel process stderr and stdout (#15391)
Super simple piping of logs from the Jupyter kernels to the Zed logs.

Release Notes:

- Added logging of stderr from Jupyter kernels to the Zed logs
2024-07-29 14:23:35 -07:00
Antonio Scandurra
02c51c9b56 Fix rendering of focused offscreen block when scrolled to bottom (#15449)
This change fixes a bug when a block is focused but offscreen.
Previously, we used the last row, but this caused a spurious block to be
rendered when scrolled to the end of the file. With this change we
always render off-screen blocks below the editor's clipping box.

Release Notes:

- Fixed a bug that caused the inline assistant to be displayed twice in
certain circumstances.

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-29 20:39:21 +02:00
Marshall Bowers
085d41b121 collab: Add billing_subscriptions table (#15448)
This PR adds a new `billing_subscriptions` table to the database, as
well as some accompanying models/queries.

In this table we store a minimal amount of data from Stripe:

- The Stripe customer ID
- The Stripe subscription ID
- The status of the Stripe subscription

This should be enough for interactions with the Stripe API (e.g., to
[create a customer portal
session](https://docs.stripe.com/api/customer_portal/sessions/create)),
as well as determine whether a subscription is active (based on the
`status`).

Release Notes:

- N/A
2024-07-29 14:32:13 -04:00
Joseph T. Lyons
0702ed5cd6 Add is_staff and destination to download events (#15445)
Release Notes:

- N/A
2024-07-29 14:21:29 -04:00
Antonio Scandurra
2b871a631a Use fuzzy-matching to locate symbols when resolving edit steps (#15447)
Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-29 20:21:19 +02:00
Michael Angerman
5e1aa898d4 workspace: Remove unused code (#15440)
Release Notes:

- N/A
2024-07-29 12:12:10 -04:00
Marshall Bowers
aa1633ba40 assistant: Allow guests to create new contexts on the host (#15439)
This PR extends collaboration in the Assistant to allow guests to create
new contexts on the host when collaborating.

Release Notes:

- N/A
2024-07-29 12:01:09 -04:00
Thorsten Ball
2b0c60043d file finder: Fix ./ breaking new-path prompt (#15438)
Fixes #15426.

The `./` was implicitly assumed to be there by the prompt, so we'd end
up with `././foobar` when typing in an explicit `./`.

This fixes the issue by stripping `./` from the query, like we also
strip `/`.

Release Notes:

- Fixed paths starting with `./` breaking the new-path file picker when
the system prompts are disabled.
([#15426](https://github.com/zed-industries/zed/issues/15426)).
2024-07-29 17:46:52 +02:00
Bennet Bo Fenner
0b4afe518b assistant: Remove feature flag for terminal inline assistant (#15432)
This PR removes the feature flag for the terminal inline assistant,
making it available to everyone.

Release Notes:

- Use Ctrl+Enter to summon the inline assistant in the terminal, which
let's you generate terminal commands based on your description
([demo](https://twitter.com/thorstenball/status/1814241447383605329))
2024-07-29 17:37:11 +02:00
Suhun Han
f124ca6474 project_panel: Double-click on blank space in project panel to create a new file (#15353)
Similar feature implemented in VSCode.


https://github.com/user-attachments/assets/ae250b5f-283c-4211-8947-d5d5703eb2d0


Release Notes:

- Added double-click to create a new file when clicking on blank space
in the project panel.
2024-07-29 18:33:13 +03:00
Peter Tripp
b8f20053d8 docs: Remove availability via homebrew on Linux (linuxbrew) (#15437)
- See: https://github.com/zed-industries/zed/issues/14108
2024-07-29 11:31:35 -04:00
Conrad Irwin
6db33b83d8 vim: Fix >... (#15404)
Co-Authored-By: @Alextopher

Release Notes:

- vim: Fixed a hang when repeating an aborted operation
([#15399](https://github.com/zed-industries/zed/issues/15399)).
2024-07-29 09:22:25 -06:00
Thorsten Ball
f58ef9b82b zed: Persist window stack order across restarts (#15419)
This changes the workspace/session serialization to also persist the
order of windows across restarts.

Release Notes:

- Improved restoring of windows across restarts: the order of the
windows is now also restored. That means windows that were in the
foreground when Zed was quit will be in the foreground after restart.
(Right now only supported on Linux/X11, not on Linux/Wayland.)

Demo:



https://github.com/user-attachments/assets/0b8162f8-f06d-43df-88d3-c45d8460fb68
2024-07-29 17:05:56 +02:00
Antonio Scandurra
6e1f7c6e1d Use tool calling instead of XML parsing to generate edit operations (#15385)
Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-29 16:42:08 +02:00
Bennet Bo Fenner
f6012cd86e assistant: Improve discoverability of terminal inline assist (#15431)
This adds a button to the terminal tab bar:

<img width="444" alt="image"
src="https://github.com/user-attachments/assets/71baadae-0ecf-493f-853c-34d4d6a48310">


Release Notes:

- N/A
2024-07-29 15:38:25 +02:00
renovate[bot]
de8f0ce861 Update Rust crate base64 to v0.22.1 (#15429)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [base64](https://togithub.com/marshallpierce/rust-base64) |
workspace.dependencies | patch | `0.22.0` -> `0.22.1` |

---

### Release Notes

<details>
<summary>marshallpierce/rust-base64 (base64)</summary>

###
[`v0.22.1`](https://togithub.com/marshallpierce/rust-base64/blob/HEAD/RELEASE-NOTES.md#0221)

[Compare
Source](https://togithub.com/marshallpierce/rust-base64/compare/v0.22.0...v0.22.1)

-   Correct the symbols used for the predefined `alphabet::BIN_HEX`.

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-29 09:25:40 -04:00
renovate[bot]
8cf93ba947 Update Rust crate serde_json to v1.0.121 (#15425)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [serde_json](https://togithub.com/serde-rs/json) | dependencies |
patch | `1.0.120` -> `1.0.121` |
| [serde_json](https://togithub.com/serde-rs/json) |
workspace.dependencies | patch | `1.0.120` -> `1.0.121` |

---

### Release Notes

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

###
[`v1.0.121`](https://togithub.com/serde-rs/json/releases/tag/v1.0.121)

[Compare
Source](https://togithub.com/serde-rs/json/compare/v1.0.120...v1.0.121)

- Optimize position search in error path
([#&#8203;1160](https://togithub.com/serde-rs/json/issues/1160), thanks
[@&#8203;purplesyringa](https://togithub.com/purplesyringa))

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy40NDAuNyIsInVwZGF0ZWRJblZlciI6IjM3LjQ0MC43IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-29 09:07:55 -04:00
Marshall Bowers
e20db039d6 Upgrade pnpm/action-setup to v4.0.0 (#15427)
This PR upgrades the `pnpm/action-setup` GitHub Action to v4.0.0.

Trying to see if I can fix the Renovate warning stemming from
https://github.com/pnpm/action-setup/issues/95.

Release Notes:

- N/A
2024-07-29 09:06:19 -04:00
earomc
1a9af10ca2 rust: Add highlight selector for doc comments (#15384)
## Release Notes:

- Added ability to style doc comments in Rust separately with
`comment.doc`
([#15322](https://github.com/zed-industries/zed/issues/15322)).


Just required adding another query to the highlights.scm file. Took
inspiration from the tree-sitter-rust repository
[here](https://github.com/tree-sitter/tree-sitter-rust/blob/master/queries/highlights.scm)

#### Doc comments customized in the theme are working now:

![grafik](https://github.com/user-attachments/assets/549f20d6-534c-4c3b-a317-3be6bc44352e)

#### Snippet from the .json theme file

![grafik](https://github.com/user-attachments/assets/da1d7e3d-b6a5-4ba5-9fef-047d69f9ea03)
2024-07-29 08:53:17 -04:00
Piotr Osiewicz
c97d035eea chore: Clean up allocs around project panel (#15422)
A drive-by I did when looking at something else.

Release Notes:

- N/A
2024-07-29 14:21:41 +02:00
Zack
6af72ab53a Add Assistant and Outline Panels to Docs (#15349)
Adds assistant and outline panel's descriptions to docs

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-29 10:38:34 +03:00
Ephram
78a2539d59 Selectable diagnostic popover text (#14518)
Release Notes:

- Added the ability to select and copy text from diagnostic popovers

- Fixed #12695




https://github.com/user-attachments/assets/b896bacf-ecbc-48f5-8228-a3821f0b1a4e

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-07-28 23:13:13 -06:00
Conrad Irwin
583b6235fb SSH remoting: terminal & tasks (#15321)
This also rolls back the `TerminalWorkDir` abstraction I added for the
original remoting, and tidies up the terminal creation code to be clear
about whether we're creating a task *or* a terminal. The previous logic
was a little muddy because it assumed we could be doing both at the same
time (which was not true).

Release Notes:

- remoting alpha: Removed the ability to specify `gh cs ssh` or `gcloud
compute ssh` etc. See https://zed.dev/docs/remote-development for
alternatives.
- remoting alpha: Added support for terminal and tasks to new
experimental ssh remoting
2024-07-28 22:45:00 -06:00
Benjamin Davies
26d0a33e79 Add Vim digraphs (#14887)
Vim digraphs are a way to insert special characters using sequences of
two ASCII characters. I've implemented the feature using a new `Digraph`
operator, following the example of `AddSurrounds`. There are still a few
issues that I'm not sure what the best way to resolve them is.

- To insert `ş`, the user must pause between pressing `ctrl-k` and `s
,`, otherwise it triggers the binding for `ctrl-k s`. Is there a way to
disable `ctrl-k *` bindings while in insert, replace or waiting mode?
- Is there a better way to insert a string at all of the cursors? At the
moment I'm constructing the edits manually.
- The table of default digraphs is a 1.4k line rust expression. Is this
okay as long as it's in its own module?
- I'd like a second opinion on how best to structure the settings.json
entry.
- I have omitted the "meta character" feature as I don't think it makes
sense when editing UTF-8 text.

Release Notes:

- Added support for Vim digraphs.

Resolves #11871
2024-07-28 22:44:32 -06:00
Michael Angerman
b87028956f Remove the file session.rs in crate/zed which is no longer used (#15394)
There was an empty file *session.rs* in the crate zed which was empty so
I am going ahead and removing it...

Release Notes:

- N/A
2024-07-28 21:32:03 -04:00
Piotr Osiewicz
a875dd153d lsp: Add server-side tracing support (#15230)
This PR adds another row to the LSP log view: Server traces

![image](https://github.com/user-attachments/assets/e3f77944-45e0-4d04-92fd-aea212859e86)


[Traces](https://docs.rs/lsp-types/latest/lsp_types/notification/enum.LogTrace.html)
are intended for logging execution diagnostics, which is different from
`LogMessage` that we currently support.
When `Server trace` is selected, user can select the level of tracing
(`off`/`messages`/`verbose`) to their liking.

Release Notes:

- Added support for language server tracing to the LSP log view.
2024-07-29 01:53:30 +02:00
Kyle Kelley
bb188f673e repl: Pass session id to kernel connections (#15389)
Updated runtimelib to 0.14 and passed a kernel session ID through to
identify our client.

Release Notes:

- N/A
2024-07-28 14:37:25 -07:00
Mikayla Maki
771a7bb4b6 Update linux.md 2024-07-28 12:08:09 -07:00
Peter Tripp
49f87165ea Show -v verbose flag in go test task label (#15375) 2024-07-28 11:48:40 -04:00
CharlesChen0823
66ada3e44c terminal: Add Alt key bindings for terminal (#14556)
- Makes using emacs-style alt/option/meta key shortcuts work in terminal (e.g. `M-%`)
- Fixes: #14543 

Co-authored-by: Peter Tripp <peter@zed.dev>
2024-07-28 09:42:17 -04:00
Antonio Scandurra
d6bdaa8a91 Simplify LLM protocol (#15366)
In this pull request, we change the zed.dev protocol so that we pass the
raw JSON for the specified provider directly to our server. This avoids
the need to define a protobuf message that's a superset of all these
formats.

@bennetbo: We also changed the settings for available_models under
zed.dev to be a flat format, because the nesting seemed too confusing.
Can you help us upgrade the local provider configuration to be
consistent with this? We do whatever we need to do when parsing the
settings to make this simple for users, even if it's a bit more complex
on our end. We want to use versioning to avoid breaking existing users,
but need to keep making progress.

```json
"zed.dev": {
  "available_models": [
    {
      "provider": "anthropic",
        "name": "some-newly-released-model-we-havent-added",
        "max_tokens": 200000
      }
  ]
}
```

Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-28 11:07:10 +02:00
Antonio Scandurra
e0fe7f632c Restore "Avoid buffering line content to compute indent guides" (#15284)
Fixes https://github.com/zed-industries/zed/issues/15218
Reverts zed-industries/zed#15282

Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-28 10:52:39 +02:00
Marshall Bowers
fee49fcf65 docs: Fix project_panel setting defaults (#15358)
This PR fixes the defaults for the `project_panel` setting shown in the
docs.

Release Notes:

- N/A
2024-07-27 16:39:32 -04:00
Marshall Bowers
58755a6c88 docs: Check formatting in CI (#15355)
This PR adds a CI step to ensure the docs are properly formatted.

Release Notes:

- N/A
2024-07-27 15:47:48 -04:00
apricotbucket28
04e25525bf x11: Fix window menu not showing (#15328)
Fixes
https://github.com/zed-industries/zed/issues/15245#issuecomment-2252790889

Release Notes:

- Linux: Fixed window menu not showing on X11
2024-07-27 12:29:48 -07:00
Marshall Bowers
3a44a59f8e docs: Format docs (#15352)
This PR formats the rest of the docs' source files.

I'll follow up with a CI check to ensure they stay formatted.

Release Notes:

- N/A
2024-07-27 14:52:21 -04:00
Marshall Bowers
53a3d6424f docs: Format "Configuring Zed" docs (#15350)
This PR formats the "Configuring Zed" docs.

Release Notes:

- N/A
2024-07-27 14:35:39 -04:00
renovate[bot]
0171b7d53c Update Rust crate uuid to v1.10.0 (#15348)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [uuid](https://togithub.com/uuid-rs/uuid) | workspace.dependencies |
minor | `1.8.0` -> `1.10.0` |

---

### Release Notes

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

### [`v1.10.0`](https://togithub.com/uuid-rs/uuid/releases/tag/1.10.0)

[Compare
Source](https://togithub.com/uuid-rs/uuid/compare/1.9.1...1.10.0)

#### Deprecations

This release deprecates and renames the following functions:

- `Builder::from_rfc4122_timestamp` ->
`Builder::from_gregorian_timestamp`
- `Builder::from_sorted_rfc4122_timestamp` ->
`Builder::from_sorted_gregorian_timestamp`
-   `Timestamp::from_rfc4122` -> `Timestamp::from_gregorian`
-   `Timestamp::to_rfc4122` -> `Timestamp::to_gregorian`

#### What's Changed

- Use const identifier in uuid macro by
[@&#8203;Vrajs16](https://togithub.com/Vrajs16) in
[https://github.com/uuid-rs/uuid/pull/764](https://togithub.com/uuid-rs/uuid/pull/764)
- Rename most methods referring to RFC4122 by
[@&#8203;Mikopet](https://togithub.com/Mikopet) /
[@&#8203;KodrAus](https://togithub.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/765](https://togithub.com/uuid-rs/uuid/pull/765)
- prepare for 1.10.0 release by
[@&#8203;KodrAus](https://togithub.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/766](https://togithub.com/uuid-rs/uuid/pull/766)

#### New Contributors

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

**Full Changelog**:
https://github.com/uuid-rs/uuid/compare/1.9.1...1.10.0

### [`v1.9.1`](https://togithub.com/uuid-rs/uuid/releases/tag/1.9.1)

[Compare
Source](https://togithub.com/uuid-rs/uuid/compare/1.9.0...1.9.1)

#### What's Changed

- Add an example of generating bulk v7 UUIDs by
[@&#8203;KodrAus](https://togithub.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/761](https://togithub.com/uuid-rs/uuid/pull/761)
- Avoid taking the shared lock when getting usable bits in Uuid::now_v7
by [@&#8203;KodrAus](https://togithub.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/762](https://togithub.com/uuid-rs/uuid/pull/762)
- Prepare for 1.9.1 release by
[@&#8203;KodrAus](https://togithub.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/763](https://togithub.com/uuid-rs/uuid/pull/763)

**Full Changelog**:
https://github.com/uuid-rs/uuid/compare/1.9.0...1.9.1

### [`v1.9.0`](https://togithub.com/uuid-rs/uuid/releases/tag/1.9.0)

[Compare
Source](https://togithub.com/uuid-rs/uuid/compare/1.8.0...1.9.0)

#### `Uuid::now_v7()` is guaranteed to be monotonic

Before this release, `Uuid::now_v7()` would only use the
millisecond-precision timestamp for ordering. It now also uses a global
42-bit counter that's re-initialized each millisecond so that the
following will always pass:

```rust
let a = Uuid::now_v7();
let b = Uuid::now_v7();

assert!(a < b);
```

#### What's Changed

- Add a get_node_id method for v1 and v6 UUIDs by
[@&#8203;KodrAus](https://togithub.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/748](https://togithub.com/uuid-rs/uuid/pull/748)
- Update atomic and zerocopy to latest by
[@&#8203;KodrAus](https://togithub.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/750](https://togithub.com/uuid-rs/uuid/pull/750)
- Add repository field to uuid-macro-internal crate by
[@&#8203;paolobarbolini](https://togithub.com/paolobarbolini) in
[https://github.com/uuid-rs/uuid/pull/752](https://togithub.com/uuid-rs/uuid/pull/752)
- update docs to updated RFC (from 4122 to 9562) by
[@&#8203;Mikopet](https://togithub.com/Mikopet) in
[https://github.com/uuid-rs/uuid/pull/753](https://togithub.com/uuid-rs/uuid/pull/753)
- Support counters in v7 UUIDs by
[@&#8203;KodrAus](https://togithub.com/KodrAus) in
[https://github.com/uuid-rs/uuid/pull/755](https://togithub.com/uuid-rs/uuid/pull/755)

#### New Contributors

- [@&#8203;paolobarbolini](https://togithub.com/paolobarbolini) made
their first contribution in
[https://github.com/uuid-rs/uuid/pull/752](https://togithub.com/uuid-rs/uuid/pull/752)
- [@&#8203;Mikopet](https://togithub.com/Mikopet) made their first
contribution in
[https://github.com/uuid-rs/uuid/pull/753](https://togithub.com/uuid-rs/uuid/pull/753)

**Full Changelog**:
https://github.com/uuid-rs/uuid/compare/1.8.0...1.9.0

</details>

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy40MzguMCIsInVwZGF0ZWRJblZlciI6IjM3LjQzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-27 14:19:34 -04:00
Marshall Bowers
26aec4ba99 ui: Remove old settings components (#15347)
This PR removes the old settings components, as they've been adapted
into other components for the settings UI.

Release Notes:

- N/A
2024-07-27 14:12:32 -04:00
Marshall Bowers
cb07e02ce9 ui: Apply elevation outside SettingsContainer (#15346)
This PR changes the `SettingsContainer` component such that the
elevation styles are applied by the parent instead of
`SettingsContainer` itself.

This means that components using `SettingsContainer` can be embedded in
different contexts, like the settings UI or a popover containing the
settings.

Release Notes:

- N/A
2024-07-27 14:00:03 -04:00
Harsh Narayan Jha
b05d532991 Add "Copy Path" and "Copy Relative Path" items into tab context menu (#15260)
Release Notes:

- Added "Copy Path" and "Copy Relative Path" items into tab context menu ([#13970](https://github.com/zed-industries/zed/issues/13970))

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2024-07-27 20:29:53 +03:00
张小白
f88278111e Remove TODO in JsonLspAdapter (#15338)
As the post-install issue is fixed via #15331 , we can remove this
`TODO` now. I have tested on win11, it works fine.

Release Notes:

- N/A
2024-07-27 13:05:48 -04:00
Marshall Bowers
f1d777434b ui: Give NumericSteppers an ID (#15344)
This PR gives the `NumericStepper` component an ID.

This prevents the UI and buffer font size settings controls from having
their increment/decrement buttons visually change when the other one is
pressed.

Release Notes:

- N/A
2024-07-27 13:02:27 -04:00
renovate[bot]
c7a78fafac Update Rust crate sea-orm to v0.12.15 (#15343)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

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

---

### Release Notes

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

###
[`v0.12.15`](https://togithub.com/SeaQL/sea-orm/releases/tag/0.12.15)

[Compare
Source](https://togithub.com/SeaQL/sea-orm/compare/0.12.14...0.12.15)

##### Enhancements

- `DerivePartialModel` macro attribute `entity` now supports `syn::Type`
[https://github.com/SeaQL/sea-orm/pull/2137](https://togithub.com/SeaQL/sea-orm/pull/2137)

```rust

#[derive(DerivePartialModel)]
#[sea_orm(entity = "<entity::Model as ModelTrait>::Entity")]
struct EntityNameNotAIdent {
    #[sea_orm(from_col = "foo2")]
    _foo: i32,
    #[sea_orm(from_col = "bar2")]
    _bar: String,
}
```

- Added `RelationDef::from_alias()`
[https://github.com/SeaQL/sea-orm/pull/2146](https://togithub.com/SeaQL/sea-orm/pull/2146)

```rust
assert_eq!(
    cake::Entity::find()
        .join_as(
            JoinType::LeftJoin,
            cake_filling::Relation::Cake.def().rev(),
            cf.clone()
        )
        .join(
            JoinType::LeftJoin,
            cake_filling::Relation::Filling.def().from_alias(cf)
        )
        .build(DbBackend::MySql)
        .to_string(),
    [
        "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
        "LEFT JOIN `cake_filling` AS `cf` ON `cake`.`id` = `cf`.`cake_id`",
        "LEFT JOIN `filling` ON `cf`.`filling_id` = `filling`.`id`",
    ]
    .join(" ")
);
```

###
[`v0.12.14`](https://togithub.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#01214---2024-02-05)

[Compare
Source](https://togithub.com/SeaQL/sea-orm/compare/0.12.12...0.12.14)

- Added feature flag `sqlite-use-returning-for-3_35` to use SQLite's
returning
[https://github.com/SeaQL/sea-orm/pull/2070](https://togithub.com/SeaQL/sea-orm/pull/2070)
- Added Loco example
[https://github.com/SeaQL/sea-orm/pull/2092](https://togithub.com/SeaQL/sea-orm/pull/2092)

###
[`v0.12.12`](https://togithub.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#01212---2024-01-22)

[Compare
Source](https://togithub.com/SeaQL/sea-orm/compare/0.12.11...0.12.12)

##### Bug Fixes

- \[sea-orm-cli] Fix entity generation for non-alphanumeric enum
variants[https://github.com/SeaQL/sea-orm/pull/1821](https://togithub.com/SeaQL/sea-orm/pull/1821)1
- \[sea-orm-cli] Fix entity generation for relations with composite
keys[https://github.com/SeaQL/sea-orm/pull/2071](https://togithub.com/SeaQL/sea-orm/pull/2071)1

##### Enhancements

-   Added `ConnectOptions::test_before_acquire`

###
[`v0.12.11`](https://togithub.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#01211---2024-01-14)

[Compare
Source](https://togithub.com/SeaQL/sea-orm/compare/0.12.10...0.12.11)

##### New Features

- Added `desc` to `Cursor` paginator
[https://github.com/SeaQL/sea-orm/pull/2037](https://togithub.com/SeaQL/sea-orm/pull/2037)

##### Enhancements

- Improve query performance of `Paginator`'s `COUNT` query
[https://github.com/SeaQL/sea-orm/pull/2030](https://togithub.com/SeaQL/sea-orm/pull/2030)
- Added SQLx slow statements logging to `ConnectOptions`
[https://github.com/SeaQL/sea-orm/pull/2055](https://togithub.com/SeaQL/sea-orm/pull/2055)
- Added `QuerySelect::lock_with_behavior`
[https://github.com/SeaQL/sea-orm/pull/1867](https://togithub.com/SeaQL/sea-orm/pull/1867)

##### Bug Fixes

- \[sea-orm-macro] Qualify types in `DeriveValueType` macro
[https://github.com/SeaQL/sea-orm/pull/2054](https://togithub.com/SeaQL/sea-orm/pull/2054)

##### House keeping

- Fix clippy warnings on 1.75
[https://github.com/SeaQL/sea-orm/pull/2057](https://togithub.com/SeaQL/sea-orm/pull/2057)

###
[`v0.12.10`](https://togithub.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#01210---2023-12-14)

[Compare
Source](https://togithub.com/SeaQL/sea-orm/compare/0.12.9...0.12.10)

##### New Features

- \[sea-orm-macro] Comment attribute for Entity (`#[sea_orm(comment =
"action")]`); `create_table_from_entity` supports comment
[https://github.com/SeaQL/sea-orm/pull/2009](https://togithub.com/SeaQL/sea-orm/pull/2009)
- Added "proxy" (feature flag `proxy`) to database backend
[https://github.com/SeaQL/sea-orm/pull/1881](https://togithub.com/SeaQL/sea-orm/pull/1881),
[https://github.com/SeaQL/sea-orm/pull/2000](https://togithub.com/SeaQL/sea-orm/pull/2000)

##### Enhancements

- Cast enums in `is_in` and `is_not_in`
[https://github.com/SeaQL/sea-orm/pull/2002](https://togithub.com/SeaQL/sea-orm/pull/2002)

##### Upgrades

- Updated `sea-query` to `0.30.5`
https://github.com/SeaQL/sea-query/releases/tag/0.30.5

###
[`v0.12.9`](https://togithub.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#0129---2023-12-08)

[Compare
Source](https://togithub.com/SeaQL/sea-orm/compare/0.12.8...0.12.9)

##### Enhancements

- Add source annotations to errors
[https://github.com/SeaQL/sea-orm/pull/1999](https://togithub.com/SeaQL/sea-orm/pull/1999)

##### Upgrades

- Updated `sea-query` to `0.30.4`
https://github.com/SeaQL/sea-query/releases/tag/0.30.4

###
[`v0.12.8`](https://togithub.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#0128---2023-12-04)

[Compare
Source](https://togithub.com/SeaQL/sea-orm/compare/0.12.7...0.12.8)

##### Enhancements

- Implement `StatementBuilder` for `sea_query::WithQuery`
[https://github.com/SeaQL/sea-orm/issues/1960](https://togithub.com/SeaQL/sea-orm/issues/1960)

##### Upgrades

- Upgrade `axum` example to `0.7`
[https://github.com/SeaQL/sea-orm/pull/1984](https://togithub.com/SeaQL/sea-orm/pull/1984)

###
[`v0.12.7`](https://togithub.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#0127---2023-11-22)

[Compare
Source](https://togithub.com/SeaQL/sea-orm/compare/0.12.6...0.12.7)

##### Enhancements

- Added method `expr_as_` that accepts `self`
[https://github.com/SeaQL/sea-orm/pull/1979](https://togithub.com/SeaQL/sea-orm/pull/1979)

##### Upgrades

- Updated `sea-query` to `0.30.3`
https://github.com/SeaQL/sea-query/releases/tag/0.30.3

###
[`v0.12.6`](https://togithub.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#0126---2023-11-13)

[Compare
Source](https://togithub.com/SeaQL/sea-orm/compare/0.12.5...0.12.6)

##### New Features

- Added `#[sea_orm(skip)]` for `FromQueryResult` derive macro
[https://github.com/SeaQL/sea-orm/pull/1954](https://togithub.com/SeaQL/sea-orm/pull/1954)

###
[`v0.12.5`](https://togithub.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#0125---2023-11-12)

[Compare
Source](https://togithub.com/SeaQL/sea-orm/compare/0.12.4...0.12.5)

##### Bug Fixes

- \[sea-orm-cli] Fix duplicated active enum use statements on generated
entities[https://github.com/SeaQL/sea-orm/pull/1953](https://togithub.com/SeaQL/sea-orm/pull/1953)3
- \[sea-orm-cli] Added `--enum-extra-derives`
[https://github.com/SeaQL/sea-orm/pull/1934](https://togithub.com/SeaQL/sea-orm/pull/1934)
- \[sea-orm-cli] Added `--enum-extra-attributes`
[https://github.com/SeaQL/sea-orm/pull/1952](https://togithub.com/SeaQL/sea-orm/pull/1952)

###
[`v0.12.4`](https://togithub.com/SeaQL/sea-orm/blob/HEAD/CHANGELOG.md#0124---2023-10-19)

[Compare
Source](https://togithub.com/SeaQL/sea-orm/compare/0.12.3...0.12.4)

##### New Features

- Add support for root JSON arrays
[https://github.com/SeaQL/sea-orm/pull/1898](https://togithub.com/SeaQL/sea-orm/pull/1898)
Now the following works (requires the `json-array` / `postgres-array`
feature)!

```rust

#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "json_struct_vec")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    #[sea_orm(column_type = "Json")]
    pub struct_vec: Vec<JsonColumn>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)]
pub struct JsonColumn {
    pub value: String,
}
```

##### Enhancements

- Loader: use `ValueTuple` as hash key
[https://github.com/SeaQL/sea-orm/pull/1868](https://togithub.com/SeaQL/sea-orm/pull/1868)

##### Upgrades

- Updated `sea-query` to `0.30.2`
https://github.com/SeaQL/sea-query/releases/tag/0.30.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 these
updates 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-27 12:52:30 -04:00
Marshall Bowers
b8982ad385 Factor out construction of font-related JSON schemas (#15341)
This PR factors out the construction of the font-related JSON schemas,
as they were used in multiple places.

Release Notes:

- N/A
2024-07-27 12:28:50 -04:00
Marshall Bowers
1ffb34c5fc Fix more instances of JSON schema getting clobbered when attaching references (#15339)
This PR extends the fix from #15336 to more places that had the same
issue.

An `add_references_to_properties` helper function has been added to
handle these cases uniformly.

Release Notes:

- N/A
2024-07-27 11:44:40 -04:00
Marshall Bowers
8b22f09b6f Don't clobber other schema fields when attaching references (#15336)
This PR fixes an issue where we would clobber the other JSON Schema
fields for any field that we attached a reference to.

This resulted in these fields (e.g., `buffer_font_family`,
`ui_font_family`) losing things like their descriptions.

The approach has been adjusted that references are now added in an
additive fashion, rather than overriding the entire schema object.

Release Notes:

- Fixed an issue where font-related settings in `settings.json` were
missing their descriptions.
2024-07-27 11:08:13 -04:00
Nick Cernis
e72f33d79e Fix typo in remote projects sign-in prompt (#15325)
Visible at File → Open Recent → Open Remote Folder…

| Before | After |
| - | - |
| ![Screenshot showing
"featuers"](https://github.com/user-attachments/assets/6e1822c7-74da-4166-a097-ab2315fddd1a)
| ![Corrected
"features"](https://github.com/user-attachments/assets/038380a6-15c1-4c71-ad47-ddd477094d85)
|

Release Notes:

- Fixed typo in remote projects sign-in prompt.
2024-07-27 10:34:48 -04:00
Danilo Leal
af24967195 docs: Add table of contents navigation (#15212)
To ease navigating on pages that are long and having a birds-eye view of
all the available content.

This is done client-side and done via the files initially generated by
the `mdbook-pagetoc` plugin ([crates.io link
here](https://crates.io/crates/mdbook-pagetoc)).

<img width="600" alt="Screenshot 2024-07-25 at 13 34 08"
src="https://github.com/user-attachments/assets/a78c69e5-8cc4-4414-9d9c-27a4ceb27620">

---

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-27 10:34:14 -04:00
Marshall Bowers
acea6f9c0f docs: Format CSS with Prettier (#15333)
This PR formats the CSS in the docs with Prettier.

The indentation of these CSS files kept changing based on who last
touched them, so I added settings to the Zed repo to try and keep the
formatting intact until we can enforce it in CI.

Release Notes:

- N/A
2024-07-27 10:25:51 -04:00
张小白
4976a9e9d8 windows: Fix eslint installation (#15331)
Close #13786. To make `eslint` running on Windows, I made the following
changes:

1. Ensure that `zed` downloads the `.zip` file.
2. Handle the `$shared` symbolic link by copying files to the link
location.
3. In #13891, I mentioned that the `npm` `post-install` script was
always failing. After debugging, I found it was due to missing
environment variables. This has been fixed, and I will submit a new PR
to address the changes in #13891.

With this PR, `eslint` can now successfully run on Windows. Video:



https://github.com/user-attachments/assets/e85451b8-0388-490a-8a75-01c12d744f7c



Release Notes:

- Fixed `eslint` not running on Windows
([#13786](https://github.com/zed-industries/zed/issues/13786)).

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-07-27 10:24:05 -04:00
Marshall Bowers
138c3fcfdd http_client: Replace build_tarball_url with a more extensible function (#15332)
This PR replaces the `build_tarball_url` with `build_asset_url` that
accepts an `AssetKind` enum to support downloading different kinds of
assets from GitHub.

Right now the only asset kind we support is still `.tar.gz`, but the new
structure is more amenable to adding more asset kinds.

Release Notes:

- N/A
2024-07-27 10:00:45 -04:00
Antonio Scandurra
64add2f222 Add debugging info when a symbol is not found (#15330)
Release Notes:

- N/A

Co-authored-by: Nathan <nathan@zed.dev>
2024-07-27 15:17:36 +02:00
598 changed files with 33876 additions and 13619 deletions

View File

@@ -1,13 +1,15 @@
Closes #ISSUE
Release Notes:
- Added/Fixed/Improved ... ([#NNNNN](https://github.com/zed-industries/zed/issues/NNNNN)).
- Added/Fixed/Improved ...
Optionally, include screenshots / media showcasing your addition that can be included in the release notes.
### Or...
Closes #ISSUE
Release Notes:
- N/A

View File

@@ -231,20 +231,20 @@ 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@0b2256b8c012f0828dc542b3febcab082c67f72b # v4
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # 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@0b2256b8c012f0828dc542b3febcab082c67f72b # v4
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # 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@0b2256b8c012f0828dc542b3febcab082c67f72b # v4
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # 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
@@ -319,7 +319,7 @@ jobs:
run: script/bundle-linux
- name: Upload Linux bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # 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
@@ -403,7 +403,7 @@ jobs:
run: script/bundle-linux
- name: Upload Linux bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # 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

View File

@@ -16,7 +16,7 @@ jobs:
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- uses: pnpm/action-setup@v3
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9

View File

@@ -106,10 +106,12 @@ jobs:
export ZED_KUBE_NAMESPACE=production
export ZED_COLLAB_LOAD_BALANCER_SIZE_UNIT=10
export ZED_API_LOAD_BALANCER_SIZE_UNIT=2
export ZED_LLM_LOAD_BALANCER_SIZE_UNIT=2
elif [[ $GITHUB_REF_NAME = "collab-staging" ]]; then
export ZED_KUBE_NAMESPACE=staging
export ZED_COLLAB_LOAD_BALANCER_SIZE_UNIT=1
export ZED_API_LOAD_BALANCER_SIZE_UNIT=1
export ZED_LLM_LOAD_BALANCER_SIZE_UNIT=1
else
echo "cowardly refusing to deploy from an unknown branch"
exit 1
@@ -134,3 +136,9 @@ jobs:
envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f -
kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch
echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}"
export ZED_SERVICE_NAME=llm
export ZED_LOAD_BALANCER_SIZE_UNIT=$ZED_LLM_LOAD_BALANCER_SIZE_UNIT
envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f -
kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch
echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}"

24
.github/workflows/docs.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Docs
on:
pull_request:
paths:
- "docs/**"
push:
branches:
- main
jobs:
check_formatting:
name: "Check formatting"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9
- run: pnpm dlx prettier . --check
working-directory: ./docs

3
.gitignore vendored
View File

@@ -29,3 +29,6 @@ DerivedData/
.vscode
.wrangler
.flatpak-builder
# Don't commit any secrets to the repo.
.env.secret.toml

View File

@@ -26,6 +26,10 @@
"tab_size": 2,
"formatter": "prettier"
},
"CSS": {
"tab_size": 2,
"formatter": "prettier"
},
"Rust": {
"tasks": {
"variables": {

2812
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,6 @@ members = [
"crates/collections",
"crates/command_palette",
"crates/command_palette_hooks",
"crates/completion",
"crates/copilot",
"crates/db",
"crates/dev_server_projects",
@@ -190,7 +189,6 @@ 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" }
db = { path = "crates/db" }
dev_server_projects = { path = "crates/dev_server_projects" }
@@ -211,6 +209,7 @@ go_to_line = { path = "crates/go_to_line" }
google_ai = { path = "crates/google_ai" }
gpui = { path = "crates/gpui" }
gpui_macros = { path = "crates/gpui_macros" }
handlebars = "4.3"
headless = { path = "crates/headless" }
html_to_markdown = { path = "crates/html_to_markdown" }
http_client = { path = "crates/http_client" }
@@ -300,7 +299,7 @@ zed_actions = { path = "crates/zed_actions" }
#
aho-corasick = "1.1"
alacritty_terminal = "0.23"
alacritty_terminal = { git = "https://github.com/alacritty/alacritty", rev = "cacdb5bb3b72bad2c729227537979d95af75978f" }
any_vec = "0.14"
anyhow = "1.0.86"
ashpd = "0.9.1"
@@ -316,9 +315,9 @@ async-watch = "0.3.1"
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
base64 = "0.22"
bitflags = "2.6.0"
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" }
blade-graphics = { git = "https://github.com/kvark/blade", rev = "ac25c77ed8d86c386a541c935ffe0a0f6024e701" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "ac25c77ed8d86c386a541c935ffe0a0f6024e701" }
blade-util = { git = "https://github.com/kvark/blade", rev = "ac25c77ed8d86c386a541c935ffe0a0f6024e701" }
cargo_metadata = "0.18"
cargo_toml = "0.20"
chrono = { version = "0.4", features = ["serde"] }
@@ -342,6 +341,7 @@ git2 = { version = "0.19", default-features = false }
globset = "0.4"
heed = { version = "0.20.1", features = ["read-txn-no-tls"] }
hex = "0.4.3"
hyper = "0.14"
html5ever = "0.27.0"
ignore = "0.4.22"
image = "0.25.1"
@@ -377,7 +377,7 @@ rand = "0.8.5"
regex = "1.5"
repair_json = "0.1.0"
rsa = "0.9.6"
runtimelib = { version = "0.12", default-features = false, features = [
runtimelib = { version = "0.14", default-features = false, features = [
"async-dispatcher-runtime",
] }
rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
@@ -401,6 +401,7 @@ similar = "1.3"
simplelog = "0.12.2"
smallvec = { version = "1.6", features = ["union"] }
smol = "1.2"
strsim = "0.11"
strum = { version = "0.25.0", features = ["derive"] }
subtle = "2.5.0"
sys-locale = "0.3.1"
@@ -427,7 +428,7 @@ tree-sitter-css = "0.21"
tree-sitter-elixir = "0.2"
tree-sitter-embedded-template = "0.20.0"
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-go-mod = { git = "https://github.com/camdencheek/tree-sitter-go-mod", rev = "1f55029bacd0a6a11f6eb894c4312d429dcf735c", 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"
@@ -459,6 +460,19 @@ wasmtime-wasi = "21.0.1"
which = "6.0.0"
wit-component = "0.201"
[workspace.dependencies.async-stripe]
version = "0.37"
default-features = false
features = [
"runtime-tokio-hyper-rustls",
"billing",
"checkout",
"events",
# The features below are only enabled to get the `events` feature to build.
"chrono",
"connect",
]
[workspace.dependencies.windows]
version = "0.58"
features = [

View File

@@ -27,5 +27,7 @@ RUN apt-get update; \
WORKDIR app
COPY --from=builder /app/collab /app/collab
COPY --from=builder /app/crates/collab/migrations /app/migrations
COPY --from=builder /app/crates/collab/migrations_llm /app/migrations_llm
ENV MIGRATIONS_PATH=/app/migrations
ENV LLM_DATABASE_MIGRATIONS_PATH=/app/migrations_llm
ENTRYPOINT ["/app/collab"]

View File

@@ -1,3 +1,3 @@
collab: RUST_LOG=${RUST_LOG:-info} cargo run --package=collab serve
collab: RUST_LOG=${RUST_LOG:-info} cargo run --package=collab serve all
livekit: livekit-server --dev
blob_store: ./script/run-local-minio

2
Procfile.postgrest Normal file
View File

@@ -0,0 +1,2 @@
app: postgrest crates/collab/postgrest_app.conf
llm: postgrest crates/collab/postgrest_llm.conf

View File

@@ -0,0 +1,11 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1896_18)">
<path d="M11.094 3.09999H8.952L12.858 12.9H15L11.094 3.09999Z" fill="#1F1F1E"/>
<path d="M4.906 3.09999L1 12.9H3.184L3.98284 10.842H8.06915L8.868 12.9H11.052L7.146 3.09999H4.906ZM4.68928 9.02199L6.026 5.57799L7.3627 9.02199H4.68928Z" fill="#1F1F1E"/>
</g>
<defs>
<clipPath id="clip0_1896_18">
<rect width="14" height="9.8" fill="white" transform="translate(1 3.09999)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 530 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.8695 8.14262C13.8695 11.6221 11.4867 14.0984 7.96785 14.0984C4.59408 14.0984 1.86949 11.3738 1.86949 7.99999C1.86949 4.62622 4.59408 1.90163 7.96785 1.90163C9.61048 1.90163 10.9924 2.50409 12.0572 3.49754L10.3974 5.09344C8.22605 2.99836 4.18834 4.57213 4.18834 7.99999C4.18834 10.127 5.88752 11.8508 7.96785 11.8508C10.3826 11.8508 11.2875 10.1197 11.4301 9.22213H7.96785V7.12458H13.7736C13.8301 7.43688 13.8695 7.73688 13.8695 8.14262Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 569 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.0768 6.72994C14.3987 5.77663 14.2879 4.73232 13.7731 3.86519C12.9989 2.53519 11.4427 1.85094 9.92272 2.17294C9.24656 1.42132 8.2751 0.993879 7.25664 1C5.70301 0.996504 4.32452 1.9835 3.84655 3.44213C2.84849 3.64382 1.98699 4.26025 1.48286 5.13394C0.70294 6.46044 0.880738 8.13257 1.9227 9.27007C1.6008 10.2234 1.71164 11.2677 2.22642 12.1348C3.00057 13.4648 4.55686 14.1491 6.07679 13.8271C6.75251 14.5787 7.72441 15.0061 8.74287 14.9996C10.2974 15.0035 11.6763 14.0156 12.1543 12.5557C13.1524 12.354 14.0139 11.7376 14.518 10.8639C15.297 9.53738 15.1188 7.86657 14.0773 6.72907L14.0768 6.72994ZM8.74376 14.0848C8.12169 14.0856 7.51912 13.8708 7.0416 13.4775C7.06332 13.4661 7.10101 13.4456 7.1254 13.4307L9.95066 11.8207C10.0952 11.7398 10.1839 11.5879 10.183 11.4239V7.49382L11.377 8.17413C11.3899 8.18025 11.3983 8.1925 11.4001 8.2065V11.4611C11.3983 12.9083 10.2105 14.0817 8.74376 14.0848ZM3.03116 11.6772C2.71946 11.1461 2.60729 10.5235 2.71414 9.91932C2.73498 9.93157 2.77178 9.95388 2.79794 9.96875L5.6232 11.5788C5.76642 11.6614 5.94377 11.6614 6.08743 11.5788L9.53654 9.6135V10.9741C9.53742 10.9881 9.53077 11.0017 9.51969 11.0104L6.66383 12.6375C5.39175 13.3603 3.76719 12.9306 3.03161 11.6772H3.03116ZM2.2876 5.592C2.59797 5.06 3.08792 4.65313 3.67141 4.44182C3.67141 4.46588 3.67008 4.50832 3.67008 4.53807V7.7585C3.6692 7.92213 3.75787 8.07394 3.90198 8.15488L7.35108 10.1197L6.15704 10.8C6.14507 10.8079 6.12999 10.8092 6.11669 10.8035L3.26039 9.17513C1.99098 8.44975 1.55557 6.84719 2.28716 5.59244L2.2876 5.592ZM12.098 7.84469L8.64887 5.87944L9.84292 5.19957C9.85489 5.19169 9.86996 5.19038 9.88326 5.19607L12.7396 6.82313C14.0112 7.54807 14.447 9.15325 13.7124 10.408C13.4015 10.9391 12.912 11.346 12.329 11.5578V8.24107C12.3303 8.07744 12.2421 7.92607 12.0984 7.84469H12.098ZM13.2863 6.07982C13.2654 6.06713 13.2286 6.04525 13.2025 6.03038L10.3772 4.42038C10.234 4.33769 10.0566 4.33769 9.91297 4.42038L6.46386 6.38563V5.025C6.46298 5.011 6.46963 4.99744 6.48071 4.98869L9.33657 3.36294C10.6086 2.63888 12.235 3.06982 12.9683 4.32544C13.2783 4.85569 13.3905 5.4765 13.2854 6.07982H13.2863ZM5.81475 8.50488L4.62026 7.82457C4.6074 7.81844 4.59898 7.80619 4.59721 7.79219V4.53763C4.59809 3.08863 5.78947 1.91438 7.25797 1.91525C7.87916 1.91525 8.48039 2.1305 8.95792 2.5225C8.93619 2.53388 8.89894 2.55444 8.87412 2.56932L6.04885 4.17932C5.90431 4.26025 5.81563 4.41163 5.81652 4.57569L5.81475 8.504V8.50488ZM6.46342 7.125L7.99976 6.24957L9.53609 7.12457V8.875L7.99976 9.75L6.46342 8.875V7.125Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

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

@@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1882_101)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.3125 1.875C2.07088 1.875 1.875 2.07088 1.875 2.3125V11.9375H1V2.3125C1 1.58763 1.58763 1 2.3125 1H14.0344C14.6191 1 14.9118 1.70688 14.4984 2.12029L7.27887 9.33984H9.3125V8.4375H10.1875V9.55859C10.1875 9.92103 9.89369 10.2148 9.53125 10.2148H6.40387L4.89996 11.7187H11.7187V6.25H12.5937V11.7187C12.5937 12.202 12.202 12.5937 11.7187 12.5937H4.02496L2.49371 14.125H13.6875C13.9291 14.125 14.125 13.9291 14.125 13.6875V4.0625H15V13.6875C15 14.4124 14.4124 15 13.6875 15H1.96561C1.38095 15 1.08816 14.2931 1.50157 13.8797L8.69379 6.6875H6.6875V7.5625H5.8125V6.46875C5.8125 6.10631 6.10631 5.8125 6.46875 5.8125H9.56879L11.1 4.28125H4.28125V9.75H3.40625V4.28125C3.40625 3.798 3.798 3.40625 4.28125 3.40625H11.975L13.5063 1.875H2.3125Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_1882_101">
<rect width="14" height="14" fill="white" transform="translate(1 1)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,10 +1 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_430_1270)">
<path d="M4.30957 0.857736V4.28922L2.35703 4.28788C2.10067 4.28788 1.86816 4.44057 1.76636 4.67656C1.66511 4.91229 1.71332 5.18633 1.88959 5.37304L5.53269 9.23312C5.77565 9.49028 6.22488 9.49028 6.46784 9.23312L10.1123 5.37277C10.2875 5.18794 10.3354 4.9147 10.2342 4.6763C10.1337 4.44057 9.90066 4.28788 9.66761 4.28788H7.73891L7.73891 0.857736C7.73891 0.383865 7.35504 2.35669e-07 6.88171 2.14979e-07L5.16731 1.4004e-07C4.66906 -0.000267757 4.30957 0.383865 4.30957 0.857736ZM11.1433 11.1187C11.1434 10.6687 10.7595 10.2856 10.2861 10.2856H1.71413C1.23972 10.2856 0.856659 10.6687 0.856659 11.1187C0.856659 11.6169 1.23972 12 1.71386 12H10.2861C10.7595 12 11.1433 11.6169 11.1433 11.1187Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_430_1270">
<rect width="12" height="12" fill="white"/>
</clipPath>
</defs>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>

Before

Width:  |  Height:  |  Size: 954 B

After

Width:  |  Height:  |  Size: 347 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-microscope"><path d="M6 18h8"/><path d="M3 22h18"/><path d="M14 22a7 7 0 1 0 0-14h-1"/><path d="M9 14h2"/><path d="M9 12a2 2 0 0 1-2-2V6h6v4a2 2 0 0 1-2 2Z"/><path d="M12 6V3a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v3"/></svg>

After

Width:  |  Height:  |  Size: 418 B

1
assets/icons/route.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-route"><circle cx="6" cy="19" r="3"/><path d="M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15"/><circle cx="18" cy="5" r="3"/></svg>

After

Width:  |  Height:  |  Size: 340 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-text-select"><path d="M5 3a2 2 0 0 0-2 2"/><path d="M19 3a2 2 0 0 1 2 2"/><path d="M21 19a2 2 0 0 1-2 2"/><path d="M5 21a2 2 0 0 1-2-2"/><path d="M9 3h1"/><path d="M9 21h1"/><path d="M14 3h1"/><path d="M14 21h1"/><path d="M3 9v1"/><path d="M21 9v1"/><path d="M3 14v1"/><path d="M21 14v1"/><line x1="7" x2="15" y1="8" y2="8"/><line x1="7" x2="17" y1="12" y2="12"/><line x1="7" x2="13" y1="16" y2="16"/></svg>

After

Width:  |  Height:  |  Size: 610 B

1
assets/icons/undo.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-undo"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/></svg>

After

Width:  |  Height:  |  Size: 288 B

View File

@@ -2,13 +2,11 @@
// Standard Linux bindings
{
"bindings": {
"up": "menu::SelectPrev",
"shift-tab": "menu::SelectPrev",
"home": "menu::SelectFirst",
"pageup": "menu::SelectFirst",
"shift-pageup": "menu::SelectFirst",
"ctrl-p": "menu::SelectPrev",
"down": "menu::SelectNext",
"tab": "menu::SelectNext",
"end": "menu::SelectLast",
"pagedown": "menu::SelectLast",
@@ -33,6 +31,20 @@
"f11": "zed::ToggleFullScreen"
}
},
{
"context": "Picker || menu",
"bindings": {
"up": "menu::SelectPrev",
"down": "menu::SelectNext"
}
},
{
"context": "Prompt",
"bindings": {
"left": "menu::SelectPrev",
"right": "menu::SelectNext"
}
},
{
"context": "Editor",
"bindings": {
@@ -121,7 +133,7 @@
"bindings": {
"alt-]": "editor::NextInlineCompletion",
"alt-[": "editor::PreviousInlineCompletion",
"alt-right": "editor::AcceptPartialInlineCompletion"
"ctrl-right": "editor::AcceptPartialInlineCompletion"
}
},
{
@@ -149,7 +161,9 @@
"bindings": {
"ctrl-g": "search::SelectNextMatch",
"ctrl-shift-g": "search::SelectPrevMatch",
"alt-m": "assistant::ToggleModelSelector"
"alt-m": "assistant::ToggleModelSelector",
"ctrl-k h": "assistant::DeployHistory",
"ctrl-k l": "assistant::DeployPromptLibrary"
}
},
{
@@ -244,13 +258,6 @@
"ctrl-alt-shift-x": "search::ToggleRegex"
}
},
{
"context": "Terminal",
"bindings": {
"ctrl-w": ["terminal::SendKeystroke", "ctrl-w"],
"ctrl-e": ["terminal::SendKeystroke", "ctrl-e"]
}
},
// Bindings from VS Code
{
"context": "Editor",
@@ -430,7 +437,7 @@
"context": "Editor && showing_completions",
"bindings": {
"enter": "editor::ConfirmCompletion",
"tab": "editor::ConfirmCompletion"
"tab": "editor::ComposeCompletion"
}
},
{
@@ -460,12 +467,16 @@
{
"bindings": {
"ctrl-alt-shift-f": "workspace::FollowNextCollaborator",
// TODO: Move this to a dock open action
"ctrl-shift-c": "collab_panel::ToggleFocus",
"ctrl-alt-i": "zed::DebugElements",
"ctrl-:": "editor::ToggleInlayHints"
}
},
{
"context": "!Terminal",
"bindings": {
"ctrl-shift-c": "collab_panel::ToggleFocus"
}
},
{
"context": "Editor && mode == full",
"bindings": {
@@ -480,7 +491,8 @@
{
"context": "Editor && jupyter && !ContextEditor",
"bindings": {
"ctrl-shift-enter": "repl::Run"
"ctrl-shift-enter": "repl::Run",
"ctrl-alt-enter": "repl::RunInPlace"
}
},
{
@@ -599,12 +611,14 @@
"context": "Terminal",
"bindings": {
"ctrl-alt-space": "terminal::ShowCharacterPalette",
"shift-ctrl-c": "terminal::Copy",
"ctrl-shift-c": "terminal::Copy",
"ctrl-insert": "terminal::Copy",
"ctrl-a": "editor::SelectAll",
"shift-ctrl-v": "terminal::Paste",
// "ctrl-a": "editor::SelectAll", // conflicts with readline
"ctrl-shift-v": "terminal::Paste",
"shift-insert": "terminal::Paste",
"ctrl-enter": "assistant::InlineAssist",
"ctrl-w": ["terminal::SendKeystroke", "ctrl-w"],
"ctrl-e": ["terminal::SendKeystroke", "ctrl-e"],
"up": ["terminal::SendKeystroke", "up"],
"pageup": ["terminal::SendKeystroke", "pageup"],
"down": ["terminal::SendKeystroke", "down"],

View File

@@ -89,9 +89,9 @@
"alt-right": "editor::MoveToNextWordEnd",
"alt-f": "editor::MoveToNextWordEnd",
"cmd-left": "editor::MoveToBeginningOfLine",
"ctrl-a": "editor::MoveToBeginningOfLine",
"ctrl-a": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": false }],
"cmd-right": "editor::MoveToEndOfLine",
"ctrl-e": "editor::MoveToEndOfLine",
"ctrl-e": ["editor::MoveToEndOfLine", { "stop_at_soft_wraps": false }],
"cmd-up": "editor::MoveToBeginning",
"cmd-down": "editor::MoveToEnd",
"shift-up": "editor::SelectUp",
@@ -127,7 +127,9 @@
"cmd-'": "editor::ToggleHunkDiff",
"cmd-\"": "editor::ExpandAllHunkDiffs",
"cmd-alt-g b": "editor::ToggleGitBlame",
"cmd-i": "editor::ShowSignatureHelp"
"cmd-i": "editor::ShowSignatureHelp",
"ctrl-f12": "editor::GoToDeclaration",
"alt-ctrl-f12": "editor::GoToDeclarationSplit"
}
},
{
@@ -152,7 +154,7 @@
"bindings": {
"alt-]": "editor::NextInlineCompletion",
"alt-[": "editor::PreviousInlineCompletion",
"alt-right": "editor::AcceptPartialInlineCompletion"
"cmd-right": "editor::AcceptPartialInlineCompletion"
}
},
{
@@ -178,7 +180,8 @@
{
"context": "Editor && jupyter && !ContextEditor",
"bindings": {
"ctrl-shift-enter": "repl::Run"
"ctrl-shift-enter": "repl::Run",
"ctrl-alt-enter": "repl::RunInPlace"
}
},
{
@@ -186,7 +189,9 @@
"bindings": {
"cmd-g": "search::SelectNextMatch",
"cmd-shift-g": "search::SelectPrevMatch",
"alt-m": "assistant::ToggleModelSelector"
"alt-m": "assistant::ToggleModelSelector",
"cmd-k h": "assistant::DeployHistory",
"cmd-k l": "assistant::DeployPromptLibrary"
}
},
{
@@ -469,7 +474,7 @@
"context": "Editor && showing_completions",
"bindings": {
"enter": "editor::ConfirmCompletion",
"tab": "editor::ConfirmCompletion"
"tab": "editor::ComposeCompletion"
}
},
{

View File

@@ -3,7 +3,7 @@
// For information on binding keys, see the Zed
// documentation: https://zed.dev/docs/key-bindings
//
// To see the default key bindings run `zed: Open Default Keymap`
// To see the default key bindings run `zed: open default keymap`
// from the command palette.
[
{

View File

@@ -4,7 +4,6 @@
"bindings": {
"i": ["vim::PushOperator", { "Object": { "around": false } }],
"a": ["vim::PushOperator", { "Object": { "around": true } }],
":": "command_palette::Toggle",
"h": "vim::Left",
"left": "vim::Left",
"backspace": "vim::Backspace",
@@ -89,8 +88,9 @@
"g t": "pane::ActivateNextItem",
"g shift-t": "pane::ActivatePrevItem",
"g d": "editor::GoToDefinition",
"g shift-d": "editor::GoToTypeDefinition",
"g cmd-d": "editor::GoToImplementation",
"g shift-d": "editor::GoToDeclaration",
"g y": "editor::GoToTypeDefinition",
"g shift-i": "editor::GoToImplementation",
"g x": "editor::OpenUrl",
"g n": "vim::SelectNextMatch",
"g shift-n": "vim::SelectPreviousMatch",
@@ -198,17 +198,12 @@
"ctrl-6": "pane::AlternateFile"
}
},
{
"context": "VimControl && VimCount",
"bindings": {
"0": ["vim::Number", 0]
}
},
{
"context": "vim_mode == normal",
"bindings": {
"escape": "editor::Cancel",
"ctrl-[": "editor::Cancel",
":": "command_palette::Toggle",
".": "vim::Repeat",
"c": ["vim::PushOperator", "Change"],
"shift-c": "vim::ChangeToEndOfLine",
@@ -256,9 +251,17 @@
"g c": ["vim::PushOperator", "ToggleComments"]
}
},
{
"context": "VimControl && VimCount",
"bindings": {
"0": ["vim::Number", 0],
":": "vim::CountCommand"
}
},
{
"context": "vim_mode == visual",
"bindings": {
":": "vim::VisualCommand",
"u": "vim::ConvertToLowerCase",
"U": "vim::ConvertToUpperCase",
"o": "vim::OtherEnd",
@@ -316,6 +319,7 @@
"ctrl-u": "editor::DeleteToBeginningOfLine",
"ctrl-t": "vim::Indent",
"ctrl-d": "vim::Outdent",
"ctrl-k": ["vim::PushOperator", { "Digraph": {} }],
"ctrl-r": ["vim::PushOperator", "Register"]
}
},
@@ -325,6 +329,7 @@
"escape": "vim::NormalBefore",
"ctrl-c": "vim::NormalBefore",
"ctrl-[": "vim::NormalBefore",
"ctrl-k": ["vim::PushOperator", { "Digraph": {} }],
"backspace": "vim::UndoReplace",
"tab": "vim::Tab",
"enter": "vim::Enter"
@@ -337,7 +342,8 @@
"enter": "vim::Enter",
"escape": "vim::ClearOperators",
"ctrl-c": "vim::ClearOperators",
"ctrl-[": "vim::ClearOperators"
"ctrl-[": "vim::ClearOperators",
"ctrl-k": ["vim::PushOperator", { "Digraph": {} }]
}
},
{

View File

@@ -0,0 +1,41 @@
{{#if language_name}}
File language: {{language_name}}
{{/if}}
<document>
{{{document_content}}}
</document>
{{#if is_truncated}}
Note: Context around the relevant section has been truncated for brevity.
{{/if}}
Editing instructions:
1. Rewrite the section marked with <rewrite_this></rewrite_this> tags based on this prompt:
<prompt>
{{{user_prompt}}}
</prompt>
2. Within <rewrite_this></rewrite_this>, make changes only in these subsections:
{{#if has_insertion}}
- Insert text where marked with <insert_here></insert_here> tags
{{/if}}
{{#if has_replacement}}
- Edit text surrounded by <edit_here></edit_here> tags
{{/if}}
3. Section to edit:
<rewrite_this>
{{{rewrite_section_with_selections}}}
</rewrite_this>
4. Guidelines:
- Only make changes necessary to fulfill the prompt
- Preserve all surrounding {{content_type}}
- Maintain the original indentation level
- Rewrite the entire section, even if no changes are needed
- Do not include <rewrite_this>, <insert_here>, or <edit_here> tags in your output
Output format:
Immediately start with the following, ensuring no leading whitespace:
```{{REWRITTEN_CODE}}```

View File

@@ -0,0 +1,181 @@
Guide the user through code changes in numbered steps that focus on individual functions, type definitions, etc.
Surround each distinct step in a <step></step> XML tag.
<instructions>
- Always include code or a file action in each step. Do not close the `<step></step>` tags with code following after them.
</instructions>
Here's an example of a user-assistant dialog with step numbers and a follow-up question in Rust:
<example language="Rust">
<turn number="1">
<user>I want to create a User struct with name and age fields, and a method to print the user's information. Can you help me with that?</user>
<assistant>Certainly! Let's create a User struct with the specified fields and a method to print the information:
<step>Define the User struct
```rust
struct User {
name: String,
age: u32,
}
```
</step>
<step>Implement methods for the User struct
```rust
impl User {
fn new(name: String, age: u32) -> Self {
User { name, age }
}
fn print_info(&self) {
println!("Name: {}, Age: {}", self.name, self.age);
}
}
```
</step>
This code defines a User struct and implements two methods:
- `new`: a constructor to create a new User
- `print_info`: a method to print the user's information</assistant>
</turn>
<turn number="2">
<user>Great! Now, how can I create a User instance and call the print_info method?</user>
<assistant>You can create a User instance and call the print_info method like this:
<step>Create a User instance and call print_info
```rust
fn main() {
let user = User::new(String::from("Alice"), 30);
user.print_info();
}
```
</step>
This will output: `Name: Alice, Age: 30`</assistant>
</turn>
<turn number="3">
<user>Can you add an email field to the User struct and update the print_info method to include it?</user>
<assistant>Certainly! Let's modify the User struct and update the print_info method:
<step>Add email field to User struct
```rust
struct User {
name: String,
age: u32,
email: String,
}
```
</step>
<step>Update the new method and print_info method
```rust
impl User {
fn new(name: String, age: u32, email: String) -> Self {
User { name, age, email }
}
fn print_info(&self) {
println!("Name: {}, Age: {}, Email: {}", self.name, self.age, self.email);
}
}
```
</step>
<step>Update the main function
```rust
fn main() {
let user = User::new(String::from("Alice"), 30, String::from("alice@example.com"));
user.print_info();
}
```
</step>
This will now output: `Name: Alice, Age: 30, Email: alice@example.com`
The changes include:
1. Adding the `email` field to the User struct
2. Updating the `new` method to accept an email parameter
3. Modifying the `print_info` method to include the email
4. Updating the main function to provide an email when creating a User instance
</assistant>
</turn>
</example>
<example language="go">
<turn number="1">
<user>I want to create a Person struct with name and age fields, and a method to greet. Can you help me with that in Go?</user>
<assistant>Certainly! Let's create a Person struct with the specified fields and a method to greet in Go:
<step>Define the Person struct
```go
type Person struct {
Name string
Age int
}
```
</step>
<step>Implement the Greet method for the Person struct
```go
func (p Person) Greet() string {
return fmt.Sprintf("Hello, my name is %s and I'm %d years old.", p.Name, p.Age)
}
```
</step>
This code defines a Person struct and implements a Greet method that returns a greeting string.</assistant>
</turn>
<turn number="2">
<user>Great! How can I create a Person instance and use the Greet method?</user>
<assistant>You can create a Person instance and use the Greet method like this:
<step>Create a Person instance and call the Greet method
```go
func main() {
person := Person{Name: "Alice", Age: 30}
fmt.Println(person.Greet())
}
```
</step>
This will output: `Hello, my name is Alice and I'm 30 years old.`</assistant>
</turn>
<turn number="3">
<user>Can you add an Email field to the Person struct and update the Greet method to include it?</user>
<assistant>Certainly! Let's modify the Person struct and update the Greet method:
<step>Add Email field to Person struct
```go
type Person struct {
Name string
Age int
Email string
}
```
</step>
<step>Update the Greet method
```go
func (p Person) Greet() string {
return fmt.Sprintf("Hello, my name is %s, I'm %d years old, and my email is %s.", p.Name, p.Age, p.Email)
}
```
</step>
<step>Update the main function
```go
func main() {
person := Person{Name: "Alice", Age: 30, Email: "alice@example.com"}
fmt.Println(person.Greet())
}
```
</step>
This will now output: `Hello, my name is Alice, I'm 30 years old, and my email is alice@example.com.`
The changes include:
1. Adding the `Email` field to the Person struct
2. Modifying the `Greet` method to include the email
3. Updating the main function to provide an email when creating a Person instance
</assistant>
</turn>
</example>

View File

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

@@ -0,0 +1,413 @@
Your task is to map a step from the conversation above to suggestions 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 suggestions on a parent symbol and one of its children in the same suggestions block.
- Never nest an operation with another operation or include CDATA or other content. All suggestions 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 suggestions except delete.
- When generating multiple suggestions, 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 suggestions at the same location. Instead, combine them together in a single operation with a succinct combined description.
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 }
}
}
```
<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 suggestions for the step: <step>Add a new method 'calculate_area' to the Rectangle struct</step>
A (wrong):
{
"title": "Add Rectangle methods",
"suggestions": [
{
"kind": "AppendChild",
"path": "src/shapes.rs",
"symbol": "impl Rectangle",
"description": "Add calculate_area method"
},
{
"kind": "AppendChild",
"path": "src/shapes.rs",
"symbol": "impl Rectangle",
"description": "Add calculate_perimeter method"
}
]
}
This demonstrates what NOT to do. NEVER append multiple children at the same location.
A (corrected):
{
"title": "Add Rectangle methods",
"suggestions": [
{
"kind": "AppendChild",
"path": "src/shapes.rs",
"symbol": "impl Rectangle",
"description": "Add calculate area and perimeter methods"
}
]
}
User:
What are the suggestions for the step: <step>Implement the 'Display' trait for the Rectangle struct</step>
A:
{
"title": "Implement Display for Rectangle",
"suggestions": [
{
"kind": "InsertSiblingAfter",
"path": "src/shapes.rs",
"symbol": "impl Rectangle",
"description": "Implement Display trait for Rectangle"
}
]
}
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);
}
}
```
<step>Update the 'print_info' method to use formatted output</step>
<step>Remove the 'email' field from the User struct</step>
What are the suggestions for the step: <step>Update the 'print_info' method to use formatted output</step>
A:
{
"title": "Use formatted output",
"suggestions": [
{
"kind": "Update",
"path": "src/user.rs",
"symbol": "impl User pub fn print_info",
"description": "Use formatted output"
}
]
}
User:
What are the suggestions for the step: <step>Remove the 'email' field from the User struct</step>
A:
{
"title": "Remove email field",
"suggestions": [
{
"kind": "Delete",
"path": "src/user.rs",
"symbol": "struct User email"
}
]
}
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);
}
}
```
<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 suggestions for the step: <step>Add a 'use std::fmt;' statement at the beginning of the file</step>
A:
{
"title": "Add use std::fmt statement",
"suggestions": [
{
"kind": "PrependChild",
"path": "src/vehicle.rs",
"description": "Add 'use std::fmt' statement"
}
]
}
User:
What are the suggestions for the step: <step>Add a new method 'start_engine' in the Vehicle impl block</step>
A:
{
"title": "Add start_engine method",
"suggestions": [
{
"kind": "InsertSiblingAfter",
"path": "src/vehicle.rs",
"symbol": "impl Vehicle fn new",
"description": "Add start_engine method"
}
]
}
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;
}
}
```
<step>Make salary an f32</step>
What are the suggestions for the step: <step>Make salary an f32</step>
A (wrong):
{
"title": "Change salary to f32",
"suggestions": [
{
"kind": "Update",
"path": "src/employee.rs",
"symbol": "struct Employee",
"description": "Change the type of salary to an f32"
},
{
"kind": "Update",
"path": "src/employee.rs",
"symbol": "struct Employee salary",
"description": "Change the type to an f32"
}
]
}
This example demonstrates what not to do. `struct Employee salary` is a child of `struct Employee`.
A (corrected):
{
"title": "Change salary to f32",
"suggestions": [
{
"kind": "Update",
"path": "src/employee.rs",
"symbol": "struct Employee salary",
"description": "Change the type to an f32"
}
]
}
User:
What are the correct suggestions for the step: <step>Remove the 'department' field and update the 'print_details' method</step>
A:
{
"title": "Remove department",
"suggestions": [
{
"kind": "Delete",
"path": "src/employee.rs",
"symbol": "struct Employee department"
},
{
"kind": "Update",
"path": "src/employee.rs",
"symbol": "impl Employee fn print_details",
"description": "Don't print the 'department' field"
}
]
}
Example 5:
User:
```rs src/game.rs
struct Player {
name: String,
health: i32,
pub score: u32,
}
impl Player {
pub fn new(name: String) -> Self {
Player { name, health: 100, score: 0 }
}
}
struct Game {
players: Vec<Player>,
}
impl Game {
fn new() -> Self {
Game { players: Vec::new() }
}
}
```
<step>Add a 'level' field to Player and update the 'new' method</step>
A:
{
"title": "Add level field to Player",
"suggestions": [
{
"kind": "InsertSiblingAfter",
"path": "src/game.rs",
"symbol": "struct Player pub score",
"description": "Add level field to Player"
},
{
"kind": "Update",
"path": "src/game.rs",
"symbol": "impl Player pub fn new",
"description": "Initialize level in new method"
}
]
}
Example 6:
User:
```rs src/config.rs
use std::collections::HashMap;
struct Config {
settings: HashMap<String, String>,
}
impl Config {
fn new() -> Self {
Config { settings: HashMap::new() }
}
}
```
<step>Add a 'load_from_file' method to Config and import necessary modules</step>
A:
{
"title": "Add load_from_file method",
"suggestions": [
{
"kind": "PrependChild",
"path": "src/config.rs",
"description": "Import std::fs and std::io modules"
},
{
"kind": "AppendChild",
"path": "src/config.rs",
"symbol": "impl Config",
"description": "Add load_from_file method"
}
]
}
Example 7:
User:
```rs src/database.rs
pub(crate) struct Database {
connection: Connection,
}
impl Database {
fn new(url: &str) -> Result<Self, Error> {
let connection = Connection::connect(url)?;
Ok(Database { connection })
}
async fn query(&self, sql: &str) -> Result<Vec<Row>, Error> {
self.connection.query(sql, &[])
}
}
```
<step>Add error handling to the 'query' method and create a custom error type</step>
A:
{
"title": "Add error handling to query",
"suggestions": [
{
"kind": "PrependChild",
"path": "src/database.rs",
"description": "Import necessary error handling modules"
},
{
"kind": "InsertSiblingBefore",
"path": "src/database.rs",
"symbol": "pub(crate) struct Database",
"description": "Define custom DatabaseError enum"
},
{
"kind": "Update",
"path": "src/database.rs",
"symbol": "impl Database async fn query",
"description": "Implement error handling in query method"
}
]
}
Now generate the suggestions for the following step:

View File

@@ -0,0 +1,18 @@
You are an expert terminal user.
You will be given a description of a command and you need to respond with a command that matches the description.
Do not include markdown blocks or any other text formatting in your response, always respond with a single command that can be executed in the given shell.
Current OS name is '{{os}}', architecture is '{{arch}}'.
{{#if shell}}
Current shell is '{{shell}}'.
{{/if}}
{{#if working_directory}}
Current working directory is '{{working_directory}}'.
{{/if}}
{{#if latest_output}}
Latest non-empty terminal output:
{{#each latest_output as |line|}}
{{line}}
{{/each}}
{{/if}}
Here is the description of the command:
{{{user_prompt}}}

View File

@@ -400,6 +400,19 @@
"model": "gpt-4o"
}
},
// The settings for slash commands.
"slash_commands": {
// Settings for the `/docs` slash command.
"docs": {
// Whether `/docs` is enabled.
"enabled": false
},
// Settings for the `/project` slash command.
"project": {
// Whether `/project` is enabled.
"enabled": false
}
},
// Whether the screen sharing icon is shown in the os status bar.
"show_call_status_icon": true,
// Whether to use language servers to provide code intelligence.
@@ -635,7 +648,7 @@
// "option_to_meta": false,
// 2. Make the option keys behave as a 'meta' key, e.g. for emacs
// "option_to_meta": true,
"option_as_meta": false,
"option_as_meta": true,
// Whether or not selecting text in the terminal will automatically
// copy to the system clipboard.
"copy_on_select": false,
@@ -865,13 +878,18 @@
// Different settings for specific language models.
"language_models": {
"anthropic": {
"version": "1",
"api_url": "https://api.anthropic.com"
},
"openai": {
"api_url": "https://api.openai.com/v1"
"google": {
"api_url": "https://generativelanguage.googleapis.com"
},
"ollama": {
"api_url": "http://localhost:11434"
},
"openai": {
"version": "1",
"api_url": "https://api.openai.com/v1"
}
},
// Zed's Prettier integration settings.
@@ -919,7 +937,8 @@
"vim": {
"use_system_clipboard": "always",
"use_multiline_find": false,
"use_smartcase_find": false
"use_smartcase_find": false,
"custom_digraphs": {}
},
// The server to connect to. If the environment variable
// ZED_SERVER_URL is set, it will override this setting.

View File

@@ -4,8 +4,8 @@
// documentation: https://zed.dev/docs/configuring-zed
//
// To see all of Zed's default settings without changing your
// custom settings, run the `zed: Open Default Settings` command
// from the command palette
// custom settings, run `zed: open default settings` from the
// command palette
{
"ui_font_size": 16,
"buffer_font_size": 16,

View File

@@ -24,6 +24,7 @@ schemars = { workspace = true, optional = true }
serde.workspace = true
serde_json.workspace = true
strum.workspace = true
thiserror.workspace = true
[dev-dependencies]
tokio.workspace = true

View File

@@ -1,10 +1,16 @@
use anyhow::{anyhow, Result};
use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, StreamExt};
mod supported_countries;
use anyhow::{anyhow, Context, Result};
use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
use isahc::config::Configurable;
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, time::Duration};
use strum::EnumIter;
use std::str::FromStr;
use std::time::Duration;
use strum::{EnumIter, EnumString};
use thiserror::Error;
pub use supported_countries::*;
pub const ANTHROPIC_API_URL: &'static str = "https://api.anthropic.com";
@@ -12,16 +18,21 @@ pub const ANTHROPIC_API_URL: &'static str = "https://api.anthropic.com";
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, EnumIter)]
pub enum Model {
#[default]
#[serde(alias = "claude-3-5-sonnet", rename = "claude-3-5-sonnet-20240620")]
#[serde(rename = "claude-3-5-sonnet", alias = "claude-3-5-sonnet-20240620")]
Claude3_5Sonnet,
#[serde(alias = "claude-3-opus", rename = "claude-3-opus-20240229")]
#[serde(rename = "claude-3-opus", alias = "claude-3-opus-20240229")]
Claude3Opus,
#[serde(alias = "claude-3-sonnet", rename = "claude-3-sonnet-20240229")]
#[serde(rename = "claude-3-sonnet", alias = "claude-3-sonnet-20240229")]
Claude3Sonnet,
#[serde(alias = "claude-3-haiku", rename = "claude-3-haiku-20240307")]
#[serde(rename = "claude-3-haiku", alias = "claude-3-haiku-20240307")]
Claude3Haiku,
#[serde(rename = "custom")]
Custom { name: String, max_tokens: usize },
Custom {
name: String,
max_tokens: usize,
/// Override this model with a different Anthropic model for tool calls.
tool_override: Option<String>,
},
}
impl Model {
@@ -44,7 +55,7 @@ impl Model {
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",
Model::Claude3Haiku => "claude-3-haiku-20240307",
Self::Custom { name, .. } => name,
}
}
@@ -68,114 +79,83 @@ impl Model {
Self::Custom { max_tokens, .. } => *max_tokens,
}
}
}
#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Role {
User,
Assistant,
}
impl TryFrom<String> for Role {
type Error = anyhow::Error;
fn try_from(value: String) -> Result<Self> {
match value.as_str() {
"user" => Ok(Self::User),
"assistant" => Ok(Self::Assistant),
_ => Err(anyhow!("invalid role '{value}'")),
pub fn tool_model_id(&self) -> &str {
if let Self::Custom {
tool_override: Some(tool_override),
..
} = self
{
tool_override
} else {
self.id()
}
}
}
impl From<Role> for String {
fn from(val: Role) -> Self {
match val {
Role::User => "user".to_owned(),
Role::Assistant => "assistant".to_owned(),
}
pub async fn complete(
client: &dyn HttpClient,
api_url: &str,
api_key: &str,
request: Request,
) -> Result<Response, AnthropicError> {
let uri = format!("{api_url}/v1/messages");
let request_builder = HttpRequest::builder()
.method(Method::POST)
.uri(uri)
.header("Anthropic-Version", "2023-06-01")
.header("Anthropic-Beta", "tools-2024-04-04")
.header("X-Api-Key", api_key)
.header("Content-Type", "application/json");
let serialized_request =
serde_json::to_string(&request).context("failed to serialize request")?;
let request = request_builder
.body(AsyncBody::from(serialized_request))
.context("failed to construct request body")?;
let mut response = client
.send(request)
.await
.context("failed to send request to Anthropic")?;
if response.status().is_success() {
let mut body = Vec::new();
response
.body_mut()
.read_to_end(&mut body)
.await
.context("failed to read response body")?;
let response_message: Response =
serde_json::from_slice(&body).context("failed to deserialize response body")?;
Ok(response_message)
} else {
let mut body = Vec::new();
response
.body_mut()
.read_to_end(&mut body)
.await
.context("failed to read response body")?;
let body_str =
std::str::from_utf8(&body).context("failed to parse response body as UTF-8")?;
Err(AnthropicError::Other(anyhow!(
"Failed to connect to API: {} {}",
response.status(),
body_str
)))
}
}
#[derive(Debug, Serialize)]
pub struct Request {
pub model: String,
pub messages: Vec<RequestMessage>,
pub stream: bool,
pub system: String,
pub max_tokens: u32,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct RequestMessage {
pub role: Role,
pub content: String,
}
#[derive(Deserialize, Debug)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ResponseEvent {
MessageStart {
message: ResponseMessage,
},
ContentBlockStart {
index: u32,
content_block: ContentBlock,
},
Ping {},
ContentBlockDelta {
index: u32,
delta: TextDelta,
},
ContentBlockStop {
index: u32,
},
MessageDelta {
delta: ResponseMessage,
usage: Usage,
},
MessageStop {},
}
#[derive(Deserialize, Debug)]
pub struct ResponseMessage {
#[serde(rename = "type")]
pub message_type: Option<String>,
pub id: Option<String>,
pub role: Option<String>,
pub content: Option<Vec<String>>,
pub model: Option<String>,
pub stop_reason: Option<String>,
pub stop_sequence: Option<String>,
pub usage: Option<Usage>,
}
#[derive(Deserialize, Debug)]
pub struct Usage {
pub input_tokens: Option<u32>,
pub output_tokens: Option<u32>,
}
#[derive(Deserialize, Debug)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentBlock {
Text { text: String },
}
#[derive(Deserialize, Debug)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum TextDelta {
TextDelta { text: String },
}
pub async fn stream_completion(
client: &dyn HttpClient,
api_url: &str,
api_key: &str,
request: Request,
low_speed_timeout: Option<Duration>,
) -> Result<BoxStream<'static, Result<ResponseEvent>>> {
) -> Result<BoxStream<'static, Result<Event, AnthropicError>>, AnthropicError> {
let request = StreamingRequest {
base: request,
stream: true,
};
let uri = format!("{api_url}/v1/messages");
let mut request_builder = HttpRequest::builder()
.method(Method::POST)
@@ -187,8 +167,16 @@ pub async fn stream_completion(
if let Some(low_speed_timeout) = low_speed_timeout {
request_builder = request_builder.low_speed_timeout(100, low_speed_timeout);
}
let request = request_builder.body(AsyncBody::from(serde_json::to_string(&request)?))?;
let mut response = client.send(request).await?;
let serialized_request =
serde_json::to_string(&request).context("failed to serialize request")?;
let request = request_builder
.body(AsyncBody::from(serialized_request))
.context("failed to construct request body")?;
let mut response = client
.send(request)
.await
.context("failed to send request to Anthropic")?;
if response.status().is_success() {
let reader = BufReader::new(response.into_body());
Ok(reader
@@ -199,69 +187,260 @@ pub async fn stream_completion(
let line = line.strip_prefix("data: ")?;
match serde_json::from_str(line) {
Ok(response) => Some(Ok(response)),
Err(error) => Some(Err(anyhow!(error))),
Err(error) => Some(Err(AnthropicError::Other(anyhow!(error)))),
}
}
Err(error) => Some(Err(anyhow!(error))),
Err(error) => Some(Err(AnthropicError::Other(anyhow!(error)))),
}
})
.boxed())
} else {
let mut body = Vec::new();
response.body_mut().read_to_end(&mut body).await?;
response
.body_mut()
.read_to_end(&mut body)
.await
.context("failed to read response body")?;
let body_str = std::str::from_utf8(&body)?;
let body_str =
std::str::from_utf8(&body).context("failed to parse response body as UTF-8")?;
match serde_json::from_str::<ResponseEvent>(body_str) {
Ok(_) => Err(anyhow!(
"Unexpected success response while expecting an error: {}",
body_str,
)),
Err(_) => Err(anyhow!(
match serde_json::from_str::<Event>(body_str) {
Ok(Event::Error { error }) => Err(AnthropicError::ApiError(error)),
Ok(_) => Err(AnthropicError::Other(anyhow!(
"Unexpected success response while expecting an error: '{body_str}'",
))),
Err(_) => Err(AnthropicError::Other(anyhow!(
"Failed to connect to API: {} {}",
response.status(),
body_str,
)),
))),
}
}
}
// #[cfg(test)]
// mod tests {
// use super::*;
// use http::IsahcHttpClient;
pub fn extract_text_from_events(
response: impl Stream<Item = Result<Event, AnthropicError>>,
) -> impl Stream<Item = Result<String, AnthropicError>> {
response.filter_map(|response| async move {
match response {
Ok(response) => match response {
Event::ContentBlockStart { content_block, .. } => match content_block {
Content::Text { text } => Some(Ok(text)),
_ => None,
},
Event::ContentBlockDelta { delta, .. } => match delta {
ContentDelta::TextDelta { text } => Some(Ok(text)),
_ => None,
},
Event::Error { error } => Some(Err(AnthropicError::ApiError(error))),
_ => None,
},
Err(error) => Some(Err(error)),
}
})
}
// #[tokio::test]
// async fn stream_completion_success() {
// let http_client = IsahcHttpClient::new().unwrap();
#[derive(Debug, Serialize, Deserialize)]
pub struct Message {
pub role: Role,
pub content: Vec<Content>,
}
// let request = Request {
// model: Model::Claude3Opus,
// messages: vec![RequestMessage {
// role: Role::User,
// content: "Ping".to_string(),
// }],
// stream: true,
// system: "Respond to ping with pong".to_string(),
// max_tokens: 4096,
// };
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Role {
User,
Assistant,
}
// let stream = stream_completion(
// &http_client,
// "https://api.anthropic.com",
// &std::env::var("ANTHROPIC_API_KEY").expect("ANTHROPIC_API_KEY not set"),
// request,
// )
// .await
// .unwrap();
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Content {
#[serde(rename = "text")]
Text { text: String },
#[serde(rename = "image")]
Image { source: ImageSource },
#[serde(rename = "tool_use")]
ToolUse {
id: String,
name: String,
input: serde_json::Value,
},
#[serde(rename = "tool_result")]
ToolResult {
tool_use_id: String,
content: String,
},
}
// stream
// .for_each(|event| async {
// match event {
// Ok(event) => println!("{:?}", event),
// Err(e) => eprintln!("Error: {:?}", e),
// }
// })
// .await;
// }
// }
#[derive(Debug, Serialize, Deserialize)]
pub struct ImageSource {
#[serde(rename = "type")]
pub source_type: String,
pub media_type: String,
pub data: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Tool {
pub name: String,
pub description: String,
pub input_schema: serde_json::Value,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum ToolChoice {
Auto,
Any,
Tool { name: String },
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Request {
pub model: String,
pub max_tokens: u32,
pub messages: Vec<Message>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tools: Vec<Tool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_choice: Option<ToolChoice>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub system: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub stop_sequences: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub top_k: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub top_p: Option<f32>,
}
#[derive(Debug, Serialize, Deserialize)]
struct StreamingRequest {
#[serde(flatten)]
pub base: Request,
pub stream: bool,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Metadata {
pub user_id: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Usage {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input_tokens: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output_tokens: Option<u32>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Response {
pub id: String,
#[serde(rename = "type")]
pub response_type: String,
pub role: Role,
pub content: Vec<Content>,
pub model: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stop_reason: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stop_sequence: Option<String>,
pub usage: Usage,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Event {
#[serde(rename = "message_start")]
MessageStart { message: Response },
#[serde(rename = "content_block_start")]
ContentBlockStart {
index: usize,
content_block: Content,
},
#[serde(rename = "content_block_delta")]
ContentBlockDelta { index: usize, delta: ContentDelta },
#[serde(rename = "content_block_stop")]
ContentBlockStop { index: usize },
#[serde(rename = "message_delta")]
MessageDelta { delta: MessageDelta, usage: Usage },
#[serde(rename = "message_stop")]
MessageStop,
#[serde(rename = "ping")]
Ping,
#[serde(rename = "error")]
Error { error: ApiError },
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum ContentDelta {
#[serde(rename = "text_delta")]
TextDelta { text: String },
#[serde(rename = "input_json_delta")]
InputJsonDelta { partial_json: String },
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MessageDelta {
pub stop_reason: Option<String>,
pub stop_sequence: Option<String>,
}
#[derive(Error, Debug)]
pub enum AnthropicError {
#[error("an error occurred while interacting with the Anthropic API: {error_type}: {message}", error_type = .0.error_type, message = .0.message)]
ApiError(ApiError),
#[error("{0}")]
Other(#[from] anyhow::Error),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ApiError {
#[serde(rename = "type")]
pub error_type: String,
pub message: String,
}
/// An Anthropic API error code.
/// https://docs.anthropic.com/en/api/errors#http-errors
#[derive(Debug, PartialEq, Eq, Clone, Copy, EnumString)]
#[strum(serialize_all = "snake_case")]
pub enum ApiErrorCode {
/// 400 - `invalid_request_error`: There was an issue with the format or content of your request.
InvalidRequestError,
/// 401 - `authentication_error`: There's an issue with your API key.
AuthenticationError,
/// 403 - `permission_error`: Your API key does not have permission to use the specified resource.
PermissionError,
/// 404 - `not_found_error`: The requested resource was not found.
NotFoundError,
/// 413 - `request_too_large`: Request exceeds the maximum allowed number of bytes.
RequestTooLarge,
/// 429 - `rate_limit_error`: Your account has hit a rate limit.
RateLimitError,
/// 500 - `api_error`: An unexpected error has occurred internal to Anthropic's systems.
ApiError,
/// 529 - `overloaded_error`: Anthropic's API is temporarily overloaded.
OverloadedError,
}
impl ApiError {
pub fn code(&self) -> Option<ApiErrorCode> {
ApiErrorCode::from_str(&self.error_type).ok()
}
pub fn is_rate_limit_error(&self) -> bool {
match self.error_type.as_str() {
"rate_limit_error" => true,
_ => false,
}
}
}

View File

@@ -0,0 +1,194 @@
use std::collections::HashSet;
use std::sync::LazyLock;
/// Returns whether the given country code is supported by Anthropic.
///
/// https://www.anthropic.com/supported-countries
pub fn is_supported_country(country_code: &str) -> bool {
SUPPORTED_COUNTRIES.contains(&country_code)
}
/// The list of country codes supported by Anthropic.
///
/// https://www.anthropic.com/supported-countries
static SUPPORTED_COUNTRIES: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
vec![
"AL", // Albania
"DZ", // Algeria
"AD", // Andorra
"AO", // Angola
"AG", // Antigua and Barbuda
"AR", // Argentina
"AM", // Armenia
"AU", // Australia
"AT", // Austria
"AZ", // Azerbaijan
"BS", // Bahamas
"BH", // Bahrain
"BD", // Bangladesh
"BB", // Barbados
"BE", // Belgium
"BZ", // Belize
"BJ", // Benin
"BT", // Bhutan
"BO", // Bolivia
"BA", // Bosnia and Herzegovina
"BW", // Botswana
"BR", // Brazil
"BN", // Brunei
"BG", // Bulgaria
"BF", // Burkina Faso
"BI", // Burundi
"CV", // Cabo Verde
"KH", // Cambodia
"CM", // Cameroon
"CA", // Canada
"TD", // Chad
"CL", // Chile
"CO", // Colombia
"KM", // Comoros
"CG", // Congo (Brazzaville)
"CR", // Costa Rica
"CI", // Côte d'Ivoire
"HR", // Croatia
"CY", // Cyprus
"CZ", // Czechia (Czech Republic)
"DK", // Denmark
"DJ", // Djibouti
"DM", // Dominica
"DO", // Dominican Republic
"EC", // Ecuador
"EG", // Egypt
"SV", // El Salvador
"GQ", // Equatorial Guinea
"EE", // Estonia
"SZ", // Eswatini
"FJ", // Fiji
"FI", // Finland
"FR", // France
"GA", // Gabon
"GM", // Gambia
"GE", // Georgia
"DE", // Germany
"GH", // Ghana
"GR", // Greece
"GD", // Grenada
"GT", // Guatemala
"GN", // Guinea
"GW", // Guinea-Bissau
"GY", // Guyana
"HT", // Haiti
"HN", // Honduras
"HU", // Hungary
"IS", // Iceland
"IN", // India
"ID", // Indonesia
"IQ", // Iraq
"IE", // Ireland
"IL", // Israel
"IT", // Italy
"JM", // Jamaica
"JP", // Japan
"JO", // Jordan
"KZ", // Kazakhstan
"KE", // Kenya
"KI", // Kiribati
"KW", // Kuwait
"KG", // Kyrgyzstan
"LA", // Laos
"LV", // Latvia
"LB", // Lebanon
"LS", // Lesotho
"LR", // Liberia
"LI", // Liechtenstein
"LT", // Lithuania
"LU", // Luxembourg
"MG", // Madagascar
"MW", // Malawi
"MY", // Malaysia
"MV", // Maldives
"MT", // Malta
"MH", // Marshall Islands
"MR", // Mauritania
"MU", // Mauritius
"MX", // Mexico
"FM", // Micronesia
"MD", // Moldova
"MC", // Monaco
"MN", // Mongolia
"ME", // Montenegro
"MA", // Morocco
"MZ", // Mozambique
"NA", // Namibia
"NR", // Nauru
"NP", // Nepal
"NL", // Netherlands
"NZ", // New Zealand
"NE", // Niger
"NG", // Nigeria
"MK", // North Macedonia
"NO", // Norway
"OM", // Oman
"PK", // Pakistan
"PW", // Palau
"PS", // Palestine
"PA", // Panama
"PG", // Papua New Guinea
"PY", // Paraguay
"PE", // Peru
"PH", // Philippines
"PL", // Poland
"PT", // Portugal
"QA", // Qatar
"RO", // Romania
"RW", // Rwanda
"KN", // Saint Kitts and Nevis
"LC", // Saint Lucia
"VC", // Saint Vincent and the Grenadines
"WS", // Samoa
"SM", // San Marino
"ST", // São Tomé and Príncipe
"SA", // Saudi Arabia
"SN", // Senegal
"RS", // Serbia
"SC", // Seychelles
"SL", // Sierra Leone
"SG", // Singapore
"SK", // Slovakia
"SI", // Slovenia
"SB", // Solomon Islands
"ZA", // South Africa
"KR", // South Korea
"ES", // Spain
"LK", // Sri Lanka
"SR", // Suriname
"SE", // Sweden
"CH", // Switzerland
"TW", // Taiwan
"TJ", // Tajikistan
"TZ", // Tanzania
"TH", // Thailand
"TL", // Timor-Leste
"TG", // Togo
"TO", // Tonga
"TT", // Trinidad and Tobago
"TN", // Tunisia
"TR", // Türkiye (Turkey)
"TM", // Turkmenistan
"TV", // Tuvalu
"UG", // Uganda
"UA", // Ukraine (except Crimea, Donetsk, and Luhansk regions)
"AE", // United Arab Emirates
"GB", // United Kingdom
"US", // United States of America
"UY", // Uruguay
"UZ", // Uzbekistan
"VU", // Vanuatu
"VA", // Vatican City
"VN", // Vietnam
"ZM", // Zambia
"ZW", // Zimbabwe
]
.into_iter()
.collect()
});

View File

@@ -11,6 +11,7 @@ use rust_embed::RustEmbed;
#[include = "themes/**/*"]
#[exclude = "themes/src/*"]
#[include = "sounds/**/*"]
#[include = "prompts/**/*"]
#[include = "*.md"]
#[exclude = "*.DS_Store"]
pub struct Assets;

View File

@@ -32,13 +32,14 @@ client.workspace = true
clock.workspace = true
collections.workspace = true
command_palette_hooks.workspace = true
completion.workspace = true
db.workspace = true
editor.workspace = true
feature_flags.workspace = true
fs.workspace = true
futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
handlebars.workspace = true
heed.workspace = true
html_to_markdown.workspace = true
http_client.workspace = true
@@ -47,6 +48,7 @@ indoc.workspace = true
language.workspace = true
language_model.workspace = true
log.workspace = true
markdown.workspace = true
menu.workspace = true
multi_buffer.workspace = true
ollama = { workspace = true, features = ["schemars"] }
@@ -55,6 +57,7 @@ ordered-float.workspace = true
parking_lot.workspace = true
paths.workspace = true
project.workspace = true
proto.workspace = true
regex.workspace = true
rope.workspace = true
schemars.workspace = true
@@ -63,11 +66,12 @@ semantic_index.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
similar.workspace = true
smallvec.workspace = true
smol.workspace = true
telemetry_events.workspace = true
terminal.workspace = true
terminal_view.workspace = true
text.workspace = true
theme.workspace = true
toml.workspace = true
ui.workspace = true
@@ -75,16 +79,17 @@ util.workspace = true
uuid.workspace = true
workspace.workspace = true
picker.workspace = true
roxmltree = "0.20.0"
zed_actions.workspace = true
[dev-dependencies]
completion = { workspace = true, features = ["test-support"] }
ctor.workspace = true
editor = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
language = { workspace = true, features = ["test-support"] }
language_model = { workspace = true, features = ["test-support"] }
log.workspace = true
project = { workspace = true, features = ["test-support"] }
rand.workspace = true
serde_json_lenient.workspace = true
text = { workspace = true, features = ["test-support"] }
unindent.workspace = true

View File

@@ -1,12 +1,16 @@
#![cfg_attr(target_os = "windows", allow(unused, dead_code))]
pub mod assistant_panel;
pub mod assistant_settings;
mod context;
pub(crate) mod context_inspector;
pub mod context_store;
mod inline_assistant;
mod model_selector;
mod prompt_library;
mod prompts;
mod slash_command;
pub mod slash_command_settings;
mod streaming_diff;
mod terminal_inline_assistant;
@@ -15,9 +19,9 @@ use assistant_settings::AssistantSettings;
use assistant_slash_command::SlashCommandRegistry;
use client::{proto, Client};
use command_palette_hooks::CommandPaletteFilter;
use completion::LanguageModelCompletionProvider;
pub use context::*;
pub use context_store::*;
use feature_flags::FeatureFlagAppExt;
use fs::Fs;
use gpui::{actions, impl_actions, AppContext, Global, SharedString, UpdateGlobal};
use indexed_docs::IndexedDocsRegistry;
@@ -26,16 +30,21 @@ use language_model::{
LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
};
pub(crate) use model_selector::*;
pub use prompts::PromptBuilder;
use prompts::PromptOverrideContext;
use semantic_index::{CloudEmbeddingProvider, SemanticIndex};
use serde::{Deserialize, Serialize};
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,
default_command, diagnostics_command, docs_command, fetch_command, file_command, now_command,
project_command, prompt_command, search_command, symbols_command, tabs_command,
terminal_command, workflow_command,
};
use std::sync::Arc;
pub(crate) use streaming_diff::*;
use util::ResultExt;
use crate::slash_command_settings::SlashCommandSettings;
actions!(
assistant,
@@ -46,16 +55,18 @@ actions!(
QuoteSelection,
InsertIntoEditor,
ToggleFocus,
ResetKey,
InsertActivePrompt,
ShowConfiguration,
DeployHistory,
DeployPromptLibrary,
ConfirmCommand,
ToggleModelSelector,
DebugEditSteps
DebugWorkflowSteps
]
);
const DEFAULT_CONTEXT_LINES: usize = 20;
#[derive(Clone, Default, Deserialize, PartialEq)]
pub struct InlineAssist {
prompt: Option<String>,
@@ -91,6 +102,7 @@ pub enum MessageStatus {
Pending,
Done,
Error(SharedString),
Canceled,
}
impl MessageStatus {
@@ -101,6 +113,7 @@ impl MessageStatus {
Some(proto::context_message_status::Variant::Error(error)) => {
MessageStatus::Error(error.message.into())
}
Some(proto::context_message_status::Variant::Canceled(_)) => MessageStatus::Canceled,
None => MessageStatus::Pending,
}
}
@@ -124,6 +137,11 @@ impl MessageStatus {
},
)),
},
MessageStatus::Canceled => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Canceled(
proto::context_message_status::Canceled {},
)),
},
}
}
}
@@ -161,9 +179,15 @@ impl Assistant {
}
}
pub fn init(fs: Arc<dyn Fs>, client: Arc<Client>, cx: &mut AppContext) {
pub fn init(
fs: Arc<dyn Fs>,
client: Arc<Client>,
dev_mode: bool,
cx: &mut AppContext,
) -> Arc<PromptBuilder> {
cx.set_global(Assistant::default());
AssistantSettings::register(cx);
SlashCommandSettings::register(cx);
// TODO: remove this when 0.148.0 is released.
if AssistantSettings::get_global(cx).using_outdated_settings_version {
@@ -192,12 +216,31 @@ pub fn init(fs: Arc<dyn Fs>, client: Arc<Client>, cx: &mut AppContext) {
context_store::init(&client);
prompt_library::init(cx);
init_completion_provider(cx);
init_language_model_settings(cx);
assistant_slash_command::init(cx);
register_slash_commands(cx);
assistant_panel::init(cx);
inline_assistant::init(fs.clone(), client.telemetry().clone(), cx);
terminal_inline_assistant::init(fs.clone(), client.telemetry().clone(), cx);
let prompt_builder = prompts::PromptBuilder::new(Some(PromptOverrideContext {
dev_mode,
fs: fs.clone(),
cx,
}))
.log_err()
.map(Arc::new)
.unwrap_or_else(|| Arc::new(prompts::PromptBuilder::new(None).unwrap()));
register_slash_commands(Some(prompt_builder.clone()), cx);
inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
client.telemetry().clone(),
cx,
);
terminal_inline_assistant::init(
fs.clone(),
prompt_builder.clone(),
client.telemetry().clone(),
cx,
);
IndexedDocsRegistry::init_global(cx);
CommandPaletteFilter::update_global(cx, |filter, _cx| {
@@ -215,17 +258,26 @@ pub fn init(fs: Arc<dyn Fs>, client: Arc<Client>, cx: &mut AppContext) {
});
})
.detach();
prompt_builder
}
fn init_completion_provider(cx: &mut AppContext) {
completion::init(cx);
fn init_language_model_settings(cx: &mut AppContext) {
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)
})
cx.subscribe(
&LanguageModelRegistry::global(cx),
|_, event: &language_model::Event, cx| match event {
language_model::Event::ProviderStateChanged
| language_model::Event::AddedProvider(_)
| language_model::Event::RemovedProvider(_) => {
update_active_language_model_from_settings(cx);
}
_ => {}
},
)
.detach();
}
@@ -233,37 +285,61 @@ 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);
});
}
LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
registry.select_active_model(&provider_name, &model_id, cx);
});
}
fn register_slash_commands(cx: &mut AppContext) {
fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut AppContext) {
let slash_command_registry = SlashCommandRegistry::global(cx);
slash_command_registry.register_command(file_command::FileSlashCommand, true);
slash_command_registry.register_command(active_command::ActiveSlashCommand, true);
slash_command_registry.register_command(symbols_command::OutlineSlashCommand, true);
slash_command_registry.register_command(tabs_command::TabsSlashCommand, true);
slash_command_registry.register_command(project_command::ProjectSlashCommand, true);
slash_command_registry.register_command(search_command::SearchSlashCommand, true);
slash_command_registry.register_command(prompt_command::PromptSlashCommand, true);
slash_command_registry.register_command(default_command::DefaultSlashCommand, true);
slash_command_registry.register_command(term_command::TermSlashCommand, true);
slash_command_registry.register_command(now_command::NowSlashCommand, true);
slash_command_registry.register_command(default_command::DefaultSlashCommand, false);
slash_command_registry.register_command(terminal_command::TerminalSlashCommand, true);
slash_command_registry.register_command(now_command::NowSlashCommand, false);
slash_command_registry.register_command(diagnostics_command::DiagnosticsSlashCommand, true);
slash_command_registry.register_command(docs_command::DocsSlashCommand, true);
if let Some(prompt_builder) = prompt_builder {
slash_command_registry.register_command(
workflow_command::WorkflowSlashCommand::new(prompt_builder),
true,
);
}
slash_command_registry.register_command(fetch_command::FetchSlashCommand, false);
update_slash_commands_from_settings(cx);
cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
.detach();
cx.observe_flag::<search_command::SearchSlashCommandFeatureFlag, _>({
let slash_command_registry = slash_command_registry.clone();
move |is_enabled, _cx| {
if is_enabled {
slash_command_registry.register_command(search_command::SearchSlashCommand, true);
}
}
})
.detach();
}
fn update_slash_commands_from_settings(cx: &mut AppContext) {
let slash_command_registry = SlashCommandRegistry::global(cx);
let settings = SlashCommandSettings::get_global(cx);
if settings.docs.enabled {
slash_command_registry.register_command(docs_command::DocsSlashCommand, true);
} else {
slash_command_registry.unregister_command(docs_command::DocsSlashCommand);
}
if settings.project.enabled {
slash_command_registry.register_command(project_command::ProjectSlashCommand, true);
} else {
slash_command_registry.unregister_command(project_command::ProjectSlashCommand);
}
}
pub fn humanize_token_count(count: usize) -> String {

File diff suppressed because it is too large Load Diff

View File

@@ -52,7 +52,7 @@ pub struct AssistantSettings {
pub dock: AssistantDockPosition,
pub default_width: Pixels,
pub default_height: Pixels,
pub default_model: AssistantDefaultModel,
pub default_model: LanguageModelSelection,
pub using_outdated_settings_version: bool,
}
@@ -110,11 +110,15 @@ impl AssistantSettingsContent {
move |content, _| {
if content.anthropic.is_none() {
content.anthropic =
Some(language_model::settings::AnthropicSettingsContent {
api_url,
low_speed_timeout_in_seconds,
..Default::default()
});
Some(language_model::settings::AnthropicSettingsContent::Versioned(
language_model::settings::VersionedAnthropicSettingsContent::V1(
language_model::settings::AnthropicSettingsContentV1 {
api_url,
low_speed_timeout_in_seconds,
available_models: None
}
)
));
}
},
),
@@ -145,12 +149,27 @@ impl AssistantSettingsContent {
cx,
move |content, _| {
if content.openai.is_none() {
let available_models = available_models.map(|models| {
models
.into_iter()
.filter_map(|model| match model {
open_ai::Model::Custom { name, max_tokens } => {
Some(language_model::provider::open_ai::AvailableModel { name, max_tokens })
}
_ => None,
})
.collect::<Vec<_>>()
});
content.openai =
Some(language_model::settings::OpenAiSettingsContent {
api_url,
low_speed_timeout_in_seconds,
available_models,
});
Some(language_model::settings::OpenAiSettingsContent::Versioned(
language_model::settings::VersionedOpenAiSettingsContent::V1(
language_model::settings::OpenAiSettingsContentV1 {
api_url,
low_speed_timeout_in_seconds,
available_models
}
)
));
}
},
),
@@ -179,25 +198,25 @@ impl AssistantSettingsContent {
.clone()
.and_then(|provider| match provider {
AssistantProviderContentV1::ZedDotDev { default_model } => {
default_model.map(|model| AssistantDefaultModel {
default_model.map(|model| LanguageModelSelection {
provider: "zed.dev".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::OpenAi { default_model, .. } => {
default_model.map(|model| AssistantDefaultModel {
default_model.map(|model| LanguageModelSelection {
provider: "openai".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::Anthropic { default_model, .. } => {
default_model.map(|model| AssistantDefaultModel {
default_model.map(|model| LanguageModelSelection {
provider: "anthropic".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::Ollama { default_model, .. } => {
default_model.map(|model| AssistantDefaultModel {
default_model.map(|model| LanguageModelSelection {
provider: "ollama".to_string(),
model: model.id().to_string(),
})
@@ -212,7 +231,7 @@ impl AssistantSettingsContent {
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_height,
default_model: Some(AssistantDefaultModel {
default_model: Some(LanguageModelSelection {
provider: "openai".to_string(),
model: settings
.default_open_ai_model
@@ -249,9 +268,7 @@ impl AssistantSettingsContent {
AssistantSettingsContent::Versioned(settings) => match settings {
VersionedAssistantSettingsContent::V1(settings) => match provider.as_ref() {
"zed.dev" => {
settings.provider = Some(AssistantProviderContentV1::ZedDotDev {
default_model: CloudModel::from_id(&model).ok(),
});
log::warn!("attempted to set zed.dev model on outdated settings");
}
"anthropic" => {
let (api_url, low_speed_timeout_in_seconds) = match &settings.provider {
@@ -308,7 +325,7 @@ impl AssistantSettingsContent {
_ => {}
},
VersionedAssistantSettingsContent::V2(settings) => {
settings.default_model = Some(AssistantDefaultModel { provider, model });
settings.default_model = Some(LanguageModelSelection { provider, model });
}
},
AssistantSettingsContent::Legacy(settings) => {
@@ -365,11 +382,11 @@ pub struct AssistantSettingsContentV2 {
/// Default: 320
default_height: Option<f32>,
/// The default model to use when creating new contexts.
default_model: Option<AssistantDefaultModel>,
default_model: Option<LanguageModelSelection>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
pub struct AssistantDefaultModel {
pub struct LanguageModelSelection {
#[schemars(schema_with = "providers_schema")]
pub provider: String,
pub model: String,
@@ -379,16 +396,18 @@ fn providers_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema:
schemars::schema::SchemaObject {
enum_values: Some(vec![
"anthropic".into(),
"google".into(),
"ollama".into(),
"openai".into(),
"zed.dev".into(),
"copilot_chat".into(),
]),
..Default::default()
}
.into()
}
impl Default for AssistantDefaultModel {
impl Default for LanguageModelSelection {
fn default() -> Self {
Self {
provider: "openai".to_string(),
@@ -421,7 +440,7 @@ pub struct AssistantSettingsContentV1 {
default_height: Option<f32>,
/// The provider of the assistant service.
///
/// This can either be the internal `zed.dev` service or an external `openai` service,
/// This can be "openai", "anthropic", "ollama", "zed.dev"
/// each with their respective default models and configurations.
provider: Option<AssistantProviderContentV1>,
}
@@ -457,6 +476,8 @@ pub struct LegacyAssistantSettingsContent {
impl Settings for AssistantSettings {
const KEY: Option<&'static str> = Some("assistant");
const PRESERVED_KEYS: Option<&'static [&'static str]> = Some(&["version"]);
type FileContent = AssistantSettingsContent;
fn load(
@@ -498,103 +519,70 @@ 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::{ReadGlobal, TestAppContext};
// 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]
async fn test_deserialize_assistant_settings_with_version(cx: &mut TestAppContext) {
let fs = fs::FakeFs::new(cx.executor().clone());
fs.create_dir(paths::settings_file().parent().unwrap())
.await
.unwrap();
// // 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(),
// }
// );
cx.update(|cx| {
let test_settings = settings::SettingsStore::test(cx);
cx.set_global(test_settings);
AssistantSettings::register(cx);
});
// // 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(),
// }
// );
cx.update(|cx| {
assert!(!AssistantSettings::get_global(cx).using_outdated_settings_version);
assert_eq!(
AssistantSettings::get_global(cx).default_model,
LanguageModelSelection {
provider: "openai".into(),
model: "gpt-4o".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
// }
// }
// );
// }
// }
cx.update(|cx| {
settings::SettingsStore::global(cx).update_settings_file::<AssistantSettings>(
fs.clone(),
|settings, _| {
*settings = AssistantSettingsContent::Versioned(
VersionedAssistantSettingsContent::V2(AssistantSettingsContentV2 {
default_model: Some(LanguageModelSelection {
provider: "test-provider".into(),
model: "gpt-99".into(),
}),
enabled: None,
button: None,
dock: None,
default_width: None,
default_height: None,
}),
)
},
);
});
cx.run_until_parked();
let raw_settings_value = fs.load(paths::settings_file()).await.unwrap();
assert!(raw_settings_value.contains(r#""version": "2""#));
#[derive(Debug, Deserialize)]
struct AssistantSettingsTest {
assistant: AssistantSettingsContent,
}
let assistant_settings: AssistantSettingsTest =
serde_json_lenient::from_str(&raw_settings_value).unwrap();
assert!(!assistant_settings.assistant.is_version_outdated());
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,219 @@
use std::{ops::Range, sync::Arc};
use collections::{HashMap, HashSet};
use editor::{
display_map::{BlockDisposition, BlockProperties, BlockStyle, CustomBlockId},
Editor,
};
use gpui::{AppContext, Model, View};
use text::{Bias, ToOffset, ToPoint};
use ui::{
div, h_flex, px, Color, Element as _, ParentElement as _, Styled, ViewContext, WindowContext,
};
use crate::{Context, ResolvedWorkflowStep, WorkflowSuggestion};
type StepRange = Range<language::Anchor>;
struct DebugInfo {
range: Range<editor::Anchor>,
block_id: CustomBlockId,
}
pub(crate) struct ContextInspector {
active_debug_views: HashMap<Range<language::Anchor>, DebugInfo>,
context: Model<Context>,
editor: View<Editor>,
}
impl ContextInspector {
pub(crate) fn new(editor: View<Editor>, context: Model<Context>) -> Self {
Self {
editor,
context,
active_debug_views: Default::default(),
}
}
pub(crate) fn is_active(&self, range: &StepRange) -> bool {
self.active_debug_views.contains_key(range)
}
pub(crate) fn refresh(&mut self, range: &StepRange, cx: &mut WindowContext<'_>) {
if self.deactivate_for(range, cx) {
self.activate_for_step(range.clone(), cx);
}
}
fn crease_content(
context: &Model<Context>,
range: StepRange,
cx: &mut AppContext,
) -> Option<Arc<str>> {
use std::fmt::Write;
let step = context.read(cx).workflow_step_for_range(range)?;
let mut output = String::from("\n\n");
match &step.status {
crate::WorkflowStepStatus::Resolved(ResolvedWorkflowStep { title, suggestions }) => {
writeln!(output, "Resolution:").ok()?;
writeln!(output, " {title:?}").ok()?;
for (buffer, suggestion_groups) in suggestions {
let buffer = buffer.read(cx);
let buffer_path = buffer
.file()
.and_then(|file| file.path().to_str())
.unwrap_or("untitled");
let snapshot = buffer.text_snapshot();
writeln!(output, " {buffer_path}:").ok()?;
for group in suggestion_groups {
for suggestion in &group.suggestions {
pretty_print_workflow_suggestion(&mut output, suggestion, &snapshot);
}
}
}
}
crate::WorkflowStepStatus::Pending(_) => {
writeln!(output, "Resolution: Pending").ok()?;
}
crate::WorkflowStepStatus::Error(error) => {
writeln!(output, "Resolution: Error").ok()?;
writeln!(output, "{error:?}").ok()?;
}
}
Some(output.into())
}
pub(crate) fn activate_for_step(&mut self, range: StepRange, cx: &mut WindowContext<'_>) {
let text = Self::crease_content(&self.context, range.clone(), cx)
.unwrap_or_else(|| Arc::from("Error fetching debug info"));
self.editor.update(cx, |editor, cx| {
let buffer = editor.buffer().read(cx).as_singleton()?;
let snapshot = buffer.read(cx).text_snapshot();
let start_offset = range.end.to_offset(&snapshot) + 1;
let start_offset = snapshot.clip_offset(start_offset, Bias::Right);
let text_len = text.len();
buffer.update(cx, |this, cx| {
this.edit([(start_offset..start_offset, text)], None, cx);
});
let end_offset = start_offset + text_len;
let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
let anchor_before = multibuffer_snapshot.anchor_after(start_offset);
let anchor_after = multibuffer_snapshot.anchor_before(end_offset);
let block_id = editor
.insert_blocks(
[BlockProperties {
position: anchor_after,
height: 0,
style: BlockStyle::Sticky,
render: Box::new(move |cx| {
div()
.w_full()
.px(cx.gutter_dimensions.full_width())
.child(h_flex().h(px(1.)).bg(Color::Warning.color(cx)))
.into_any()
}),
disposition: BlockDisposition::Below,
priority: 0,
}],
None,
cx,
)
.into_iter()
.next()?;
let info = DebugInfo {
range: anchor_before..anchor_after,
block_id,
};
self.active_debug_views.insert(range, info);
Some(())
});
}
fn deactivate_impl(editor: &mut Editor, debug_data: DebugInfo, cx: &mut ViewContext<Editor>) {
editor.remove_blocks(HashSet::from_iter([debug_data.block_id]), None, cx);
editor.edit([(debug_data.range, Arc::<str>::default())], cx)
}
pub(crate) fn deactivate_for(&mut self, range: &StepRange, cx: &mut WindowContext<'_>) -> bool {
if let Some(debug_data) = self.active_debug_views.remove(range) {
self.editor.update(cx, |this, cx| {
Self::deactivate_impl(this, debug_data, cx);
});
true
} else {
false
}
}
pub(crate) fn deactivate(&mut self, cx: &mut WindowContext<'_>) {
let steps_to_disable = std::mem::take(&mut self.active_debug_views);
self.editor.update(cx, move |editor, cx| {
for (_, debug_data) in steps_to_disable {
Self::deactivate_impl(editor, debug_data, cx);
}
});
}
}
fn pretty_print_anchor(
out: &mut String,
anchor: &language::Anchor,
snapshot: &text::BufferSnapshot,
) {
use std::fmt::Write;
let point = anchor.to_point(snapshot);
write!(out, "{}:{}", point.row, point.column).ok();
}
fn pretty_print_range(
out: &mut String,
range: &Range<language::Anchor>,
snapshot: &text::BufferSnapshot,
) {
use std::fmt::Write;
write!(out, " Range: ").ok();
pretty_print_anchor(out, &range.start, snapshot);
write!(out, "..").ok();
pretty_print_anchor(out, &range.end, snapshot);
}
fn pretty_print_workflow_suggestion(
out: &mut String,
suggestion: &WorkflowSuggestion,
snapshot: &text::BufferSnapshot,
) {
use std::fmt::Write;
let (range, description, position) = match suggestion {
WorkflowSuggestion::Update { range, description } => (Some(range), Some(description), None),
WorkflowSuggestion::CreateFile { description } => (None, Some(description), None),
WorkflowSuggestion::AppendChild {
position,
description,
}
| WorkflowSuggestion::InsertSiblingBefore {
position,
description,
}
| WorkflowSuggestion::InsertSiblingAfter {
position,
description,
}
| WorkflowSuggestion::PrependChild {
position,
description,
} => (None, Some(description), Some(position)),
WorkflowSuggestion::Delete { range } => (Some(range), None, None),
};
if let Some(description) = description {
writeln!(out, " Description: {description}").ok();
}
if let Some(range) = range {
pretty_print_range(out, range, snapshot);
}
if let Some(position) = position {
write!(out, " Position: ").ok();
pretty_print_anchor(out, position, snapshot);
write!(out, "\n").ok();
}
write!(out, "\n").ok();
}

View File

@@ -1,6 +1,6 @@
use crate::{
Context, ContextEvent, ContextId, ContextOperation, ContextVersion, SavedContext,
SavedContextMetadata,
prompts::PromptBuilder, Context, ContextEvent, ContextId, ContextOperation, ContextVersion,
SavedContext, SavedContextMetadata,
};
use anyhow::{anyhow, Context as _, Result};
use client::{proto, telemetry::Telemetry, Client, TypedEnvelope};
@@ -8,7 +8,9 @@ use clock::ReplicaId;
use fs::Fs;
use futures::StreamExt;
use fuzzy::StringMatchCandidate;
use gpui::{AppContext, AsyncAppContext, Context as _, Model, ModelContext, Task, WeakModel};
use gpui::{
AppContext, AsyncAppContext, Context as _, EventEmitter, Model, ModelContext, Task, WeakModel,
};
use language::LanguageRegistry;
use paths::contexts_dir;
use project::Project;
@@ -26,6 +28,7 @@ use util::{ResultExt, TryFutureExt};
pub fn init(client: &Arc<Client>) {
client.add_model_message_handler(ContextStore::handle_advertise_contexts);
client.add_model_request_handler(ContextStore::handle_open_context);
client.add_model_request_handler(ContextStore::handle_create_context);
client.add_model_message_handler(ContextStore::handle_update_context);
client.add_model_request_handler(ContextStore::handle_synchronize_contexts);
}
@@ -49,8 +52,15 @@ pub struct ContextStore {
project_is_shared: bool,
client_subscription: Option<client::Subscription>,
_project_subscriptions: Vec<gpui::Subscription>,
prompt_builder: Arc<PromptBuilder>,
}
pub enum ContextStoreEvent {
ContextCreated(ContextId),
}
impl EventEmitter<ContextStoreEvent> for ContextStore {}
enum ContextHandle {
Weak(WeakModel<Context>),
Strong(Model<Context>),
@@ -73,7 +83,11 @@ impl ContextHandle {
}
impl ContextStore {
pub fn new(project: Model<Project>, cx: &mut AppContext) -> Task<Result<Model<Self>>> {
pub fn new(
project: Model<Project>,
prompt_builder: Arc<PromptBuilder>,
cx: &mut AppContext,
) -> Task<Result<Model<Self>>> {
let fs = project.read(cx).fs().clone();
let languages = project.read(cx).languages().clone();
let telemetry = project.read(cx).client().telemetry().clone();
@@ -108,6 +122,7 @@ impl ContextStore {
project_is_shared: false,
client: project.read(cx).client(),
project: project.clone(),
prompt_builder,
};
this.handle_project_changed(project, cx);
this.synchronize_contexts(cx);
@@ -169,6 +184,34 @@ impl ContextStore {
})
}
async fn handle_create_context(
this: Model<Self>,
_: TypedEnvelope<proto::CreateContext>,
mut cx: AsyncAppContext,
) -> Result<proto::CreateContextResponse> {
let (context_id, operations) = this.update(&mut cx, |this, cx| {
if this.project.read(cx).is_remote() {
return Err(anyhow!("can only create contexts as the host"));
}
let context = this.create(cx);
let context_id = context.read(cx).id().clone();
cx.emit(ContextStoreEvent::ContextCreated(context_id.clone()));
anyhow::Ok((
context_id,
context
.read(cx)
.serialize_ops(&ContextVersion::default(), cx),
))
})??;
let operations = operations.await;
Ok(proto::CreateContextResponse {
context_id: context_id.to_proto(),
context: Some(proto::Context { operations }),
})
}
async fn handle_update_context(
this: Model<Self>,
envelope: TypedEnvelope<proto::UpdateContext>,
@@ -293,12 +336,76 @@ impl ContextStore {
pub fn create(&mut self, cx: &mut ModelContext<Self>) -> Model<Context> {
let context = cx.new_model(|cx| {
Context::local(self.languages.clone(), Some(self.telemetry.clone()), cx)
Context::local(
self.languages.clone(),
Some(self.project.clone()),
Some(self.telemetry.clone()),
self.prompt_builder.clone(),
cx,
)
});
self.register_context(&context, cx);
context
}
pub fn create_remote_context(
&mut self,
cx: &mut ModelContext<Self>,
) -> Task<Result<Model<Context>>> {
let project = self.project.read(cx);
let Some(project_id) = project.remote_id() else {
return Task::ready(Err(anyhow!("project was not remote")));
};
if project.is_local() {
return Task::ready(Err(anyhow!("cannot create remote contexts as the host")));
}
let replica_id = project.replica_id();
let capability = project.capability();
let language_registry = self.languages.clone();
let project = self.project.clone();
let telemetry = self.telemetry.clone();
let prompt_builder = self.prompt_builder.clone();
let request = self.client.request(proto::CreateContext { project_id });
cx.spawn(|this, mut cx| async move {
let response = request.await?;
let context_id = ContextId::from_proto(response.context_id);
let context_proto = response.context.context("invalid context")?;
let context = cx.new_model(|cx| {
Context::new(
context_id.clone(),
replica_id,
capability,
language_registry,
prompt_builder,
Some(project),
Some(telemetry),
cx,
)
})?;
let operations = cx
.background_executor()
.spawn(async move {
context_proto
.operations
.into_iter()
.map(|op| ContextOperation::from_proto(op))
.collect::<Result<Vec<_>>>()
})
.await?;
context.update(&mut cx, |context, cx| context.apply_ops(operations, cx))??;
this.update(&mut cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_id(&context_id, cx) {
existing_context
} else {
this.register_context(&context, cx);
this.synchronize_contexts(cx);
context
}
})
})
}
pub fn open_local_context(
&mut self,
path: PathBuf,
@@ -310,6 +417,7 @@ impl ContextStore {
let fs = self.fs.clone();
let languages = self.languages.clone();
let project = self.project.clone();
let telemetry = self.telemetry.clone();
let load = cx.background_executor().spawn({
let path = path.clone();
@@ -318,11 +426,20 @@ impl ContextStore {
SavedContext::from_json(&saved_context)
}
});
let prompt_builder = self.prompt_builder.clone();
cx.spawn(|this, mut cx| async move {
let saved_context = load.await?;
let context = cx.new_model(|cx| {
Context::deserialize(saved_context, path.clone(), languages, Some(telemetry), cx)
Context::deserialize(
saved_context,
path.clone(),
languages,
prompt_builder,
Some(project),
Some(telemetry),
cx,
)
})?;
this.update(&mut cx, |this, cx| {
if let Some(existing_context) = this.loaded_context_for_path(&path, cx) {
@@ -346,7 +463,11 @@ impl ContextStore {
})
}
fn loaded_context_for_id(&self, id: &ContextId, cx: &AppContext) -> Option<Model<Context>> {
pub(super) fn loaded_context_for_id(
&self,
id: &ContextId,
cx: &AppContext,
) -> Option<Model<Context>> {
self.contexts.iter().find_map(|context| {
let context = context.upgrade()?;
if context.read(cx).id() == id {
@@ -377,11 +498,13 @@ impl ContextStore {
let replica_id = project.replica_id();
let capability = project.capability();
let language_registry = self.languages.clone();
let project = self.project.clone();
let telemetry = self.telemetry.clone();
let request = self.client.request(proto::OpenContext {
project_id,
context_id: context_id.to_proto(),
});
let prompt_builder = self.prompt_builder.clone();
cx.spawn(|this, mut cx| async move {
let response = request.await?;
let context_proto = response.context.context("invalid context")?;
@@ -391,6 +514,8 @@ impl ContextStore {
replica_id,
capability,
language_registry,
prompt_builder,
Some(project),
Some(telemetry),
cx,
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,46 @@
use std::sync::Arc;
use feature_flags::ZedPro;
use gpui::DismissEvent;
use language_model::{LanguageModel, LanguageModelAvailability, LanguageModelRegistry};
use proto::Plan;
use crate::{assistant_settings::AssistantSettings, LanguageModelCompletionProvider};
use std::sync::Arc;
use ui::ListItemSpacing;
use crate::assistant_settings::AssistantSettings;
use crate::ShowConfiguration;
use fs::Fs;
use gpui::Action;
use gpui::SharedString;
use language_model::LanguageModelRegistry;
use gpui::Task;
use picker::{Picker, PickerDelegate};
use settings::update_settings_file;
use ui::{prelude::*, ContextMenu, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
use ui::{prelude::*, ListItem, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
const TRY_ZED_PRO_URL: &str = "https://zed.dev/pro";
#[derive(IntoElement)]
pub struct ModelSelector<T: PopoverTrigger> {
handle: Option<PopoverMenuHandle<ContextMenu>>,
handle: Option<PopoverMenuHandle<Picker<ModelPickerDelegate>>>,
fs: Arc<dyn Fs>,
trigger: T,
info_text: Option<SharedString>,
}
pub struct ModelPickerDelegate {
fs: Arc<dyn Fs>,
all_models: Vec<ModelInfo>,
filtered_models: Vec<ModelInfo>,
selected_index: usize,
}
#[derive(Clone)]
struct ModelInfo {
model: Arc<dyn LanguageModel>,
provider_icon: IconName,
availability: LanguageModelAvailability,
is_selected: bool,
}
impl<T: PopoverTrigger> ModelSelector<T> {
pub fn new(fs: Arc<dyn Fs>, trigger: T) -> Self {
ModelSelector {
@@ -25,7 +51,7 @@ impl<T: PopoverTrigger> ModelSelector<T> {
}
}
pub fn with_handle(mut self, handle: PopoverMenuHandle<ContextMenu>) -> Self {
pub fn with_handle(mut self, handle: PopoverMenuHandle<Picker<ModelPickerDelegate>>) -> Self {
self.handle = Some(handle);
self
}
@@ -36,113 +62,238 @@ impl<T: PopoverTrigger> ModelSelector<T> {
}
}
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);
}
impl PickerDelegate for ModelPickerDelegate {
type ListItem = ListItem;
let info_text = self.info_text.clone();
fn match_count(&self) -> usize {
self.filtered_models.len()
}
menu.menu(move |cx| {
ContextMenu::build(cx, |mut menu, cx| {
if let Some(info_text) = info_text.clone() {
menu = menu
.custom_row(move |_cx| {
Label::new(info_text.clone())
.color(Color::Muted)
.into_any_element()
})
.separator();
}
fn selected_index(&self) -> usize {
self.selected_index
}
for (index, provider) in LanguageModelRegistry::global(cx)
.read(cx)
.providers()
.enumerate()
{
if index > 0 {
menu = menu.separator();
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
self.selected_index = ix.min(self.filtered_models.len().saturating_sub(1));
cx.notify();
}
fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
"Select a model...".into()
}
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
let all_models = self.all_models.clone();
cx.spawn(|this, mut cx| async move {
let filtered_models = cx
.background_executor()
.spawn(async move {
if query.is_empty() {
all_models
} else {
all_models
.into_iter()
.filter(|model_info| {
model_info
.model
.name()
.0
.to_lowercase()
.contains(&query.to_lowercase())
})
.collect()
}
menu = menu.header(provider.name().0);
})
.await;
let available_models = provider.provided_models(cx);
if available_models.is_empty() {
menu = menu.custom_entry(
{
move |_| {
h_flex()
.w_full()
.gap_1()
.child(Icon::new(IconName::Settings))
.child(Label::new("Configure"))
.into_any()
}
},
{
let provider = provider.id();
move |cx| {
LanguageModelCompletionProvider::global(cx).update(
cx,
|completion_provider, cx| {
completion_provider
.set_active_provider(provider.clone(), cx)
},
);
}
},
);
}
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)),
)
.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
this.update(&mut cx, |this, cx| {
this.delegate.filtered_models = filtered_models;
this.delegate.set_selected_index(0, cx);
cx.notify();
})
.into()
.ok();
})
.trigger(self.trigger)
.attach(gpui::AnchorCorner::BottomLeft)
}
fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
if let Some(model_info) = self.filtered_models.get(self.selected_index) {
let model = model_info.model.clone();
update_settings_file::<AssistantSettings>(self.fs.clone(), cx, move |settings, _| {
settings.set_model(model.clone())
});
// Update the selection status
let selected_model_id = model_info.model.id();
let selected_provider_id = model_info.model.provider_id();
for model in &mut self.all_models {
model.is_selected = model.model.id() == selected_model_id
&& model.model.provider_id() == selected_provider_id;
}
for model in &mut self.filtered_models {
model.is_selected = model.model.id() == selected_model_id
&& model.model.provider_id() == selected_provider_id;
}
cx.emit(DismissEvent);
}
}
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
fn render_match(
&self,
ix: usize,
selected: bool,
cx: &mut ViewContext<Picker<Self>>,
) -> Option<Self::ListItem> {
use feature_flags::FeatureFlagAppExt;
let model_info = self.filtered_models.get(ix)?;
let show_badges = cx.has_flag::<ZedPro>();
Some(
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.selected(selected)
.start_slot(
div().pr_1().child(
Icon::new(model_info.provider_icon)
.color(Color::Muted)
.size(IconSize::Medium),
),
)
.child(
h_flex()
.w_full()
.justify_between()
.font_buffer(cx)
.min_w(px(200.))
.child(
h_flex()
.gap_2()
.child(Label::new(model_info.model.name().0.clone()))
.children(match model_info.availability {
LanguageModelAvailability::Public => None,
LanguageModelAvailability::RequiresPlan(Plan::Free) => None,
LanguageModelAvailability::RequiresPlan(Plan::ZedPro) => {
show_badges.then(|| {
Label::new("Pro")
.size(LabelSize::XSmall)
.color(Color::Muted)
})
}
}),
)
.child(div().when(model_info.is_selected, |this| {
this.child(
Icon::new(IconName::Check)
.color(Color::Accent)
.size(IconSize::Small),
)
})),
),
)
}
fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<gpui::AnyElement> {
use feature_flags::FeatureFlagAppExt;
let plan = proto::Plan::ZedPro;
let is_trial = false;
Some(
h_flex()
.w_full()
.border_t_1()
.border_color(cx.theme().colors().border)
.p_1()
.gap_4()
.justify_between()
.when(cx.has_flag::<ZedPro>(), |this| {
this.child(match plan {
// Already a zed pro subscriber
Plan::ZedPro => Button::new("zed-pro", "Zed Pro")
.icon(IconName::ZedAssistant)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.icon_position(IconPosition::Start)
.on_click(|_, cx| {
cx.dispatch_action(Box::new(zed_actions::OpenAccountSettings))
}),
// Free user
Plan::Free => Button::new(
"try-pro",
if is_trial {
"Upgrade to Pro"
} else {
"Try Pro"
},
)
.on_click(|_, cx| cx.open_url(TRY_ZED_PRO_URL)),
})
})
.child(
Button::new("configure", "Configure")
.icon(IconName::Settings)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.icon_position(IconPosition::Start)
.on_click(|_, cx| {
cx.dispatch_action(ShowConfiguration.boxed_clone());
}),
)
.into_any(),
)
}
}
impl<T: PopoverTrigger> RenderOnce for ModelSelector<T> {
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let selected_provider = LanguageModelRegistry::read_global(cx)
.active_provider()
.map(|m| m.id());
let selected_model = LanguageModelRegistry::read_global(cx)
.active_model()
.map(|m| m.id());
let all_models = LanguageModelRegistry::global(cx)
.read(cx)
.providers()
.iter()
.flat_map(|provider| {
let provider_id = provider.id();
let provider_icon = provider.icon();
let selected_model = selected_model.clone();
let selected_provider = selected_provider.clone();
provider.provided_models(cx).into_iter().map(move |model| {
let model = model.clone();
ModelInfo {
model: model.clone(),
provider_icon,
availability: model.availability(),
is_selected: selected_model.as_ref() == Some(&model.id())
&& selected_provider.as_ref() == Some(&provider_id),
}
})
})
.collect::<Vec<_>>();
let delegate = ModelPickerDelegate {
fs: self.fs.clone(),
all_models: all_models.clone(),
filtered_models: all_models,
selected_index: 0,
};
let picker_view = cx.new_view(|cx| {
let picker = Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into()));
picker
});
PopoverMenu::new("model-switcher")
.menu(move |_cx| Some(picker_view.clone()))
.trigger(self.trigger)
.attach(gpui::AnchorCorner::BottomLeft)
}
}

View File

@@ -1,9 +1,7 @@
use crate::{
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};
@@ -13,13 +11,18 @@ use futures::{
};
use fuzzy::StringMatchCandidate;
use gpui::{
actions, point, size, transparent_black, AppContext, AssetSource, BackgroundExecutor, Bounds,
EventEmitter, Global, HighlightStyle, PromptLevel, ReadGlobal, Subscription, Task, TextStyle,
actions, point, size, transparent_black, AppContext, BackgroundExecutor, Bounds, EventEmitter,
Global, HighlightStyle, PromptLevel, ReadGlobal, Subscription, Task, TextStyle,
TitlebarOptions, UpdateGlobal, View, WindowBounds, WindowHandle, WindowOptions,
};
use heed::{types::SerdeBincode, Database, RoTxn};
use heed::{
types::{SerdeBincode, SerdeJson, Str},
Database, RoTxn,
};
use language::{language_settings::SoftWrap, Buffer, LanguageRegistry};
use language_model::{LanguageModelRequest, LanguageModelRequestMessage, Role};
use language_model::{
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
};
use parking_lot::RwLock;
use picker::{Picker, PickerDelegate};
use rope::Rope;
@@ -32,6 +35,7 @@ use std::{
sync::{atomic::AtomicBool, Arc},
time::Duration,
};
use text::LineEnding;
use theme::ThemeSettings;
use ui::{
div, prelude::*, IconButtonShape, ListItem, ListItemSpacing, ParentElement, Render,
@@ -62,6 +66,11 @@ pub fn init(cx: &mut AppContext) {
cx.set_global(GlobalPromptStore(prompt_store_future))
}
const BUILT_IN_TOOLTIP_TEXT: &'static str = concat!(
"This prompt supports special functionality.\n",
"It's read-only, but you can remove it from your default prompt."
);
/// This function opens a new prompt library window if one doesn't exist already.
/// If one exists, it brings it to the foreground.
///
@@ -229,15 +238,29 @@ impl PickerDelegate for PromptPickerDelegate {
.end_hover_slot(
h_flex()
.gap_2()
.child(
.child(if prompt_id.is_built_in() {
div()
.id("built-in-prompt")
.child(Icon::new(IconName::FileLock).color(Color::Muted))
.tooltip(move |cx| {
Tooltip::with_meta(
"Built-in prompt",
None,
BUILT_IN_TOOLTIP_TEXT,
cx,
)
})
.into_any()
} else {
IconButton::new("delete-prompt", IconName::Trash)
.icon_color(Color::Muted)
.shape(IconButtonShape::Square)
.tooltip(move |cx| Tooltip::text("Delete Prompt", cx))
.on_click(cx.listener(move |_, _, cx| {
cx.emit(PromptPickerEvent::Deleted { prompt_id })
})),
)
}))
.into_any_element()
})
.child(
IconButton::new("toggle-default-prompt", IconName::Sparkle)
.selected(default)
@@ -350,6 +373,10 @@ impl PromptLibrary {
pub fn save_prompt(&mut self, prompt_id: PromptId, cx: &mut ViewContext<Self>) {
const SAVE_THROTTLE: Duration = Duration::from_millis(500);
if prompt_id.is_built_in() {
return;
}
let prompt_metadata = self.store.metadata(prompt_id).unwrap();
let prompt_editor = self.prompt_editors.get_mut(&prompt_id).unwrap();
let title = prompt_editor.title_editor.read(cx).text(cx);
@@ -459,6 +486,7 @@ impl PromptLibrary {
let mut editor = Editor::auto_width(cx);
editor.set_placeholder_text("Untitled", cx);
editor.set_text(prompt_metadata.title.unwrap_or_default(), cx);
editor.set_read_only(prompt_id.is_built_in());
editor
});
let body_editor = cx.new_view(|cx| {
@@ -470,6 +498,7 @@ impl PromptLibrary {
});
let mut editor = Editor::for_buffer(buffer, None, cx);
editor.set_read_only(prompt_id.is_built_in());
editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
editor.set_show_gutter(false, cx);
editor.set_show_wrap_guides(false, cx);
@@ -636,7 +665,10 @@ impl PromptLibrary {
};
let prompt_editor = &self.prompt_editors[&active_prompt_id].body_editor;
let provider = LanguageModelCompletionProvider::read_global(cx);
let Some(provider) = LanguageModelRegistry::read_global(cx).active_provider() else {
return;
};
let initial_prompt = action.prompt.clone();
if provider.is_authenticated(cx) {
InlineAssistant::update_global(cx, |assistant, cx| {
@@ -725,6 +757,9 @@ impl PromptLibrary {
}
fn count_tokens(&mut self, prompt_id: PromptId, cx: &mut ViewContext<Self>) {
let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
return;
};
if let Some(prompt) = self.prompt_editors.get_mut(&prompt_id) {
let editor = &prompt.body_editor.read(cx);
let buffer = &editor.buffer().read(cx).as_singleton().unwrap().read(cx);
@@ -734,29 +769,27 @@ impl PromptLibrary {
const DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
cx.background_executor().timer(DEBOUNCE_TIMEOUT).await;
if let Some(token_count) = cx.update(|cx| {
LanguageModelCompletionProvider::read_global(cx).count_tokens(
LanguageModelRequest {
messages: vec![LanguageModelRequestMessage {
role: Role::System,
content: body.to_string(),
}],
stop: Vec::new(),
temperature: 1.,
},
cx,
)
})? {
let token_count = token_count.await?;
let token_count = cx
.update(|cx| {
model.count_tokens(
LanguageModelRequest {
messages: vec![LanguageModelRequestMessage {
role: Role::System,
content: vec![body.to_string().into()],
}],
stop: Vec::new(),
temperature: 1.,
},
cx,
)
})?
.await?;
this.update(&mut cx, |this, cx| {
let prompt_editor = this.prompt_editors.get_mut(&prompt_id).unwrap();
prompt_editor.token_count = Some(token_count);
cx.notify();
})
} else {
Ok(())
}
this.update(&mut cx, |this, cx| {
let prompt_editor = this.prompt_editors.get_mut(&prompt_id).unwrap();
prompt_editor.token_count = Some(token_count);
cx.notify();
})
}
.log_err()
});
@@ -806,7 +839,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 = LanguageModelCompletionProvider::read_global(cx).active_model();
let model = LanguageModelRegistry::read_global(cx).active_model();
let settings = ThemeSettings::get_global(cx);
Some(
@@ -916,7 +949,7 @@ impl PromptLibrary {
None,
format!(
"Model: {}",
current_model
model
.as_ref()
.map(|model| model
.name()
@@ -935,7 +968,23 @@ impl PromptLibrary {
)
},
))
.child(
.child(if prompt_id.is_built_in() {
div()
.id("built-in-prompt")
.child(
Icon::new(IconName::FileLock)
.color(Color::Muted),
)
.tooltip(move |cx| {
Tooltip::with_meta(
"Built-in prompt",
None,
BUILT_IN_TOOLTIP_TEXT,
cx,
)
})
.into_any()
} else {
IconButton::new(
"delete-prompt",
IconName::Trash,
@@ -953,8 +1002,9 @@ impl PromptLibrary {
})
.on_click(|_, cx| {
cx.dispatch_action(Box::new(DeletePrompt));
}),
)
})
.into_any_element()
})
.child(
IconButton::new(
"duplicate-prompt",
@@ -1057,20 +1107,30 @@ pub struct PromptMetadata {
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PromptId(Uuid);
#[serde(tag = "kind")]
pub enum PromptId {
User { uuid: Uuid },
EditWorkflow,
}
impl PromptId {
pub fn new() -> PromptId {
PromptId(Uuid::new_v4())
PromptId::User {
uuid: Uuid::new_v4(),
}
}
pub fn is_built_in(&self) -> bool {
!matches!(self, PromptId::User { .. })
}
}
pub struct PromptStore {
executor: BackgroundExecutor,
env: heed::Env,
bodies: Database<SerdeBincode<PromptId>, SerdeBincode<String>>,
metadata: Database<SerdeBincode<PromptId>, SerdeBincode<PromptMetadata>>,
metadata_cache: RwLock<MetadataCache>,
metadata: Database<SerdeJson<PromptId>, SerdeJson<PromptMetadata>>,
bodies: Database<SerdeJson<PromptId>, Str>,
}
#[derive(Default)]
@@ -1081,7 +1141,7 @@ struct MetadataCache {
impl MetadataCache {
fn from_db(
db: Database<SerdeBincode<PromptId>, SerdeBincode<PromptMetadata>>,
db: Database<SerdeJson<PromptId>, SerdeJson<PromptMetadata>>,
txn: &RoTxn,
) -> Result<Self> {
let mut cache = MetadataCache::default();
@@ -1133,35 +1193,123 @@ impl PromptStore {
let db_env = unsafe {
heed::EnvOpenOptions::new()
.map_size(1024 * 1024 * 1024) // 1GB
.max_dbs(2) // bodies and metadata
.max_dbs(4) // Metadata and bodies (possibly v1 of both as well)
.open(db_path)?
};
let mut txn = db_env.write_txn()?;
let bodies = db_env.create_database(&mut txn, Some("bodies"))?;
let metadata = db_env.create_database(&mut txn, Some("metadata"))?;
let metadata = db_env.create_database(&mut txn, Some("metadata.v2"))?;
let bodies = db_env.create_database(&mut txn, Some("bodies.v2"))?;
// Remove edit workflow prompt, as we decided to opt into it using
// a slash command instead.
metadata.delete(&mut txn, &PromptId::EditWorkflow).ok();
bodies.delete(&mut txn, &PromptId::EditWorkflow).ok();
txn.commit()?;
Self::upgrade_dbs(&db_env, metadata, bodies).log_err();
let txn = db_env.read_txn()?;
let metadata_cache = MetadataCache::from_db(metadata, &txn)?;
txn.commit()?;
Ok(PromptStore {
executor,
env: db_env,
bodies,
metadata,
metadata_cache: RwLock::new(metadata_cache),
metadata,
bodies,
})
}
})
}
fn upgrade_dbs(
env: &heed::Env,
metadata_db: heed::Database<SerdeJson<PromptId>, SerdeJson<PromptMetadata>>,
bodies_db: heed::Database<SerdeJson<PromptId>, Str>,
) -> Result<()> {
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
pub struct PromptIdV1(Uuid);
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PromptMetadataV1 {
pub id: PromptIdV1,
pub title: Option<SharedString>,
pub default: bool,
pub saved_at: DateTime<Utc>,
}
let mut txn = env.write_txn()?;
let Some(bodies_v1_db) = env
.open_database::<SerdeBincode<PromptIdV1>, SerdeBincode<String>>(
&txn,
Some("bodies"),
)?
else {
return Ok(());
};
let mut bodies_v1 = bodies_v1_db
.iter(&txn)?
.collect::<heed::Result<HashMap<_, _>>>()?;
let Some(metadata_v1_db) = env
.open_database::<SerdeBincode<PromptIdV1>, SerdeBincode<PromptMetadataV1>>(
&txn,
Some("metadata"),
)?
else {
return Ok(());
};
let metadata_v1 = metadata_v1_db
.iter(&txn)?
.collect::<heed::Result<HashMap<_, _>>>()?;
for (prompt_id_v1, metadata_v1) in metadata_v1 {
let prompt_id_v2 = PromptId::User {
uuid: prompt_id_v1.0,
};
let Some(body_v1) = bodies_v1.remove(&prompt_id_v1) else {
continue;
};
if metadata_db
.get(&txn, &prompt_id_v2)?
.map_or(true, |metadata_v2| {
metadata_v1.saved_at > metadata_v2.saved_at
})
{
metadata_db.put(
&mut txn,
&prompt_id_v2,
&PromptMetadata {
id: prompt_id_v2,
title: metadata_v1.title.clone(),
default: metadata_v1.default,
saved_at: metadata_v1.saved_at,
},
)?;
bodies_db.put(&mut txn, &prompt_id_v2, &body_v1)?;
}
}
txn.commit()?;
Ok(())
}
pub fn load(&self, id: PromptId) -> Task<Result<String>> {
let env = self.env.clone();
let bodies = self.bodies;
self.executor.spawn(async move {
let txn = env.read_txn()?;
bodies
let mut prompt = bodies
.get(&txn, &id)?
.ok_or_else(|| anyhow!("prompt not found"))
.ok_or_else(|| anyhow!("prompt not found"))?
.into();
LineEnding::normalize(&mut prompt);
Ok(prompt)
})
}
@@ -1250,6 +1398,10 @@ impl PromptStore {
default: bool,
body: Rope,
) -> Task<Result<()>> {
if id.is_built_in() {
return Task::ready(Err(anyhow!("built-in prompts cannot be saved")));
}
let prompt_metadata = PromptMetadata {
id,
title,
@@ -1277,16 +1429,26 @@ impl PromptStore {
fn save_metadata(
&self,
id: PromptId,
title: Option<SharedString>,
mut title: Option<SharedString>,
default: bool,
) -> Task<Result<()>> {
let mut cache = self.metadata_cache.write();
if id.is_built_in() {
title = cache
.metadata_by_id
.get(&id)
.and_then(|metadata| metadata.title.clone());
}
let prompt_metadata = PromptMetadata {
id,
title,
default,
saved_at: Utc::now(),
};
self.metadata_cache.write().insert(prompt_metadata.clone());
cache.insert(prompt_metadata.clone());
let db_connection = self.env.clone();
let metadata = self.metadata;
@@ -1303,17 +1465,6 @@ 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,135 +1,284 @@
use assets::Assets;
use fs::Fs;
use futures::StreamExt;
use handlebars::{Handlebars, RenderError, TemplateError};
use language::BufferSnapshot;
use std::{fmt::Write, ops::Range};
use parking_lot::Mutex;
use serde::Serialize;
use std::{ops::Range, sync::Arc, time::Duration};
use util::ResultExt;
pub fn generate_content_prompt(
user_prompt: String,
language_name: Option<&str>,
buffer: BufferSnapshot,
range: Range<usize>,
_project_name: Option<String>,
) -> anyhow::Result<String> {
let mut prompt = String::new();
#[derive(Serialize)]
pub struct ContentPromptContext {
pub content_type: String,
pub language_name: Option<String>,
pub is_truncated: bool,
pub document_content: String,
pub user_prompt: String,
pub rewrite_section: String,
pub rewrite_section_with_selections: String,
pub has_insertion: bool,
pub has_replacement: bool,
}
let content_type = match language_name {
None | Some("Markdown" | "Plain Text") => {
writeln!(
prompt,
"Here's a file of text that I'm going to ask you to make an edit to."
)?;
"text"
#[derive(Serialize)]
pub struct TerminalAssistantPromptContext {
pub os: String,
pub arch: String,
pub shell: Option<String>,
pub working_directory: Option<String>,
pub latest_output: Vec<String>,
pub user_prompt: String,
}
pub struct PromptBuilder {
handlebars: Arc<Mutex<Handlebars<'static>>>,
}
pub struct PromptOverrideContext<'a> {
pub dev_mode: bool,
pub fs: Arc<dyn Fs>,
pub cx: &'a mut gpui::AppContext,
}
impl PromptBuilder {
pub fn new(override_cx: Option<PromptOverrideContext>) -> Result<Self, Box<TemplateError>> {
let mut handlebars = Handlebars::new();
Self::register_templates(&mut handlebars)?;
let handlebars = Arc::new(Mutex::new(handlebars));
if let Some(override_cx) = override_cx {
Self::watch_fs_for_template_overrides(override_cx, handlebars.clone());
}
Some(language_name) => {
writeln!(
prompt,
"Here's a file of {language_name} that I'm going to ask you to make an edit to."
)?;
"code"
Ok(Self { handlebars })
}
fn watch_fs_for_template_overrides(
PromptOverrideContext { dev_mode, fs, cx }: PromptOverrideContext,
handlebars: Arc<Mutex<Handlebars<'static>>>,
) {
cx.background_executor()
.spawn(async move {
let templates_dir = if dev_mode {
std::env::current_dir()
.ok()
.and_then(|pwd| {
let pwd_assets_prompts = pwd.join("assets").join("prompts");
pwd_assets_prompts.exists().then_some(pwd_assets_prompts)
})
.unwrap_or_else(|| paths::prompt_overrides_dir().clone())
} else {
paths::prompt_overrides_dir().clone()
};
// Create the prompt templates directory if it doesn't exist
if !fs.is_dir(&templates_dir).await {
if let Err(e) = fs.create_dir(&templates_dir).await {
log::error!("Failed to create prompt templates directory: {}", e);
return;
}
}
// Initial scan of the prompts directory
if let Ok(mut entries) = fs.read_dir(&templates_dir).await {
while let Some(Ok(file_path)) = entries.next().await {
if file_path.to_string_lossy().ends_with(".hbs") {
if let Ok(content) = fs.load(&file_path).await {
let file_name = file_path.file_stem().unwrap().to_string_lossy();
match handlebars.lock().register_template_string(&file_name, content) {
Ok(_) => {
log::info!(
"Successfully registered template override: {} ({})",
file_name,
file_path.display()
);
},
Err(e) => {
log::error!(
"Failed to register template during initial scan: {} ({})",
e,
file_path.display()
);
},
}
}
}
}
}
// Watch for changes
let (mut changes, watcher) = fs.watch(&templates_dir, Duration::from_secs(1)).await;
while let Some(changed_paths) = changes.next().await {
for changed_path in changed_paths {
if changed_path.extension().map_or(false, |ext| ext == "hbs") {
log::info!("Reloading template: {}", changed_path.display());
if let Some(content) = fs.load(&changed_path).await.log_err() {
let file_name = changed_path.file_stem().unwrap().to_string_lossy();
let file_path = changed_path.to_string_lossy();
match handlebars.lock().register_template_string(&file_name, content) {
Ok(_) => log::info!(
"Successfully reloaded template: {} ({})",
file_name,
file_path
),
Err(e) => log::error!(
"Failed to register template: {} ({})",
e,
file_path
),
}
}
}
}
}
drop(watcher);
})
.detach();
}
fn register_templates(handlebars: &mut Handlebars) -> Result<(), Box<TemplateError>> {
let mut register_template = |id: &str| {
let prompt = Assets::get(&format!("prompts/{}.hbs", id))
.unwrap_or_else(|| panic!("{} prompt template not found", id))
.data;
handlebars
.register_template_string(id, String::from_utf8_lossy(&prompt))
.map_err(Box::new)
};
register_template("content_prompt")?;
register_template("terminal_assistant_prompt")?;
register_template("edit_workflow")?;
register_template("step_resolution")?;
Ok(())
}
pub fn generate_content_prompt(
&self,
user_prompt: String,
language_name: Option<&str>,
buffer: BufferSnapshot,
transform_range: Range<usize>,
selected_ranges: Vec<Range<usize>>,
) -> Result<String, RenderError> {
let content_type = match language_name {
None | Some("Markdown" | "Plain Text") => "text",
Some(_) => "code",
};
const MAX_CTX: usize = 50000;
let mut is_truncated = false;
let before_range = 0..transform_range.start;
let truncated_before = if before_range.len() > MAX_CTX {
is_truncated = true;
transform_range.start - MAX_CTX..transform_range.start
} else {
before_range
};
let after_range = transform_range.end..buffer.len();
let truncated_after = if after_range.len() > MAX_CTX {
is_truncated = true;
transform_range.end..transform_range.end + MAX_CTX
} else {
after_range
};
let mut document_content = String::new();
for chunk in buffer.text_for_range(truncated_before) {
document_content.push_str(chunk);
}
};
const MAX_CTX: usize = 50000;
let mut is_truncated = false;
if range.is_empty() {
prompt.push_str("The point you'll need to insert at is marked with <insert_here></insert_here>.\n\n<document>");
} else {
prompt.push_str("The section you'll need to rewrite is marked with <rewrite_this></rewrite_this> tags.\n\n<document>");
}
// Include file content.
let before_range = 0..range.start;
let truncated_before = if before_range.len() > MAX_CTX {
is_truncated = true;
range.start - MAX_CTX..range.start
} else {
before_range
};
let mut non_rewrite_len = truncated_before.len();
for chunk in buffer.text_for_range(truncated_before) {
prompt.push_str(chunk);
}
if !range.is_empty() {
prompt.push_str("<rewrite_this>\n");
for chunk in buffer.text_for_range(range.clone()) {
prompt.push_str(chunk);
document_content.push_str("<rewrite_this>\n");
for chunk in buffer.text_for_range(transform_range.clone()) {
document_content.push_str(chunk);
}
prompt.push_str("\n<rewrite_this>");
} else {
prompt.push_str("<insert_here></insert_here>");
}
let after_range = range.end..buffer.len();
let truncated_after = if after_range.len() > MAX_CTX {
is_truncated = true;
range.end..range.end + MAX_CTX
} else {
after_range
};
non_rewrite_len += truncated_after.len();
for chunk in buffer.text_for_range(truncated_after) {
prompt.push_str(chunk);
}
document_content.push_str("\n</rewrite_this>");
write!(prompt, "</document>\n\n").unwrap();
for chunk in buffer.text_for_range(truncated_after) {
document_content.push_str(chunk);
}
if is_truncated {
writeln!(prompt, "The context around the relevant section has been truncated (possibly in the middle of a line) for brevity.\n")?;
}
let mut rewrite_section = String::new();
for chunk in buffer.text_for_range(transform_range.clone()) {
rewrite_section.push_str(chunk);
}
if range.is_empty() {
writeln!(
prompt,
"You can't replace {content_type}, your answer will be inserted in place of the `<insert_here></insert_here>` tags. Don't include the insert_here tags in your output.",
)
.unwrap();
writeln!(
prompt,
"Generate {content_type} based on the following prompt:\n\n<prompt>\n{user_prompt}\n</prompt>",
)
.unwrap();
writeln!(prompt, "Match the indentation in the original file in the inserted {content_type}, don't include any indentation on blank lines.\n").unwrap();
prompt.push_str("Immediately start with the following format with no remarks:\n\n```\n{{INSERTED_CODE}}\n```");
} else {
writeln!(prompt, "Edit the section of {content_type} in <rewrite_this></rewrite_this> tags based on the following prompt:'").unwrap();
writeln!(prompt, "\n<prompt>\n{user_prompt}\n</prompt>\n").unwrap();
let rewrite_len = range.end - range.start;
if rewrite_len < 20000 && rewrite_len * 2 < non_rewrite_len {
writeln!(prompt, "And here's the section to rewrite based on that prompt again for reference:\n\n<rewrite_this>\n").unwrap();
for chunk in buffer.text_for_range(range.clone()) {
prompt.push_str(chunk);
let rewrite_section_with_selections = {
let mut section_with_selections = String::new();
let mut last_end = 0;
for selected_range in &selected_ranges {
if selected_range.start > last_end {
section_with_selections.push_str(
&rewrite_section[last_end..selected_range.start - transform_range.start],
);
}
if selected_range.start == selected_range.end {
section_with_selections.push_str("<insert_here></insert_here>");
} else {
section_with_selections.push_str("<edit_here>");
section_with_selections.push_str(
&rewrite_section[selected_range.start - transform_range.start
..selected_range.end - transform_range.start],
);
section_with_selections.push_str("</edit_here>");
}
last_end = selected_range.end - transform_range.start;
}
writeln!(prompt, "\n</rewrite_this>\n").unwrap();
}
writeln!(prompt, "Only make changes that are necessary to fulfill the prompt, leave everything else as-is. All surrounding {content_type} will be preserved.\n").unwrap();
write!(
prompt,
"Start at the indentation level in the original file in the rewritten {content_type}. "
)
.unwrap();
prompt.push_str("Don't stop until you've rewritten the entire section, even if you have no more changes to make, always write out the whole section with no unnecessary elisions.");
prompt.push_str("\n\nImmediately start with the following format with no remarks:\n\n```\n{{REWRITTEN_CODE}}\n```");
if last_end < rewrite_section.len() {
section_with_selections.push_str(&rewrite_section[last_end..]);
}
section_with_selections
};
let has_insertion = selected_ranges.iter().any(|range| range.start == range.end);
let has_replacement = selected_ranges.iter().any(|range| range.start != range.end);
let context = ContentPromptContext {
content_type: content_type.to_string(),
language_name: language_name.map(|s| s.to_string()),
is_truncated,
document_content,
user_prompt,
rewrite_section,
rewrite_section_with_selections,
has_insertion,
has_replacement,
};
self.handlebars.lock().render("content_prompt", &context)
}
Ok(prompt)
}
pub fn generate_terminal_assistant_prompt(
&self,
user_prompt: &str,
shell: Option<&str>,
working_directory: Option<&str>,
latest_output: &[String],
) -> Result<String, RenderError> {
let context = TerminalAssistantPromptContext {
os: std::env::consts::OS.to_string(),
arch: std::env::consts::ARCH.to_string(),
shell: shell.map(|s| s.to_string()),
working_directory: working_directory.map(|s| s.to_string()),
latest_output: latest_output.to_vec(),
user_prompt: user_prompt.to_string(),
};
pub fn generate_terminal_assistant_prompt(
user_prompt: &str,
shell: Option<&str>,
working_directory: Option<&str>,
) -> String {
let mut prompt = String::new();
writeln!(&mut prompt, "You are an expert terminal user.").unwrap();
writeln!(&mut prompt, "You will be given a description of a command and you need to respond with a command that matches the description.").unwrap();
writeln!(&mut prompt, "Do not include markdown blocks or any other text formatting in your response, always respond with a single command that can be executed in the given shell.").unwrap();
if let Some(shell) = shell {
writeln!(&mut prompt, "Current shell is '{shell}'.").unwrap();
self.handlebars
.lock()
.render("terminal_assistant_prompt", &context)
}
if let Some(working_directory) = working_directory {
writeln!(
&mut prompt,
"Current working directory is '{working_directory}'."
)
.unwrap();
pub fn generate_workflow_prompt(&self) -> Result<String, RenderError> {
self.handlebars.lock().render("edit_workflow", &())
}
pub fn generate_step_resolution_prompt(&self) -> Result<String, RenderError> {
self.handlebars.lock().render("step_resolution", &())
}
writeln!(&mut prompt, "Here is the description of the command:").unwrap();
prompt.push_str(user_prompt);
prompt
}

View File

@@ -6,6 +6,7 @@ use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{AppContext, Model, Task, ViewContext, WeakView, WindowContext};
use language::{Anchor, Buffer, CodeLabel, Documentation, HighlightId, LanguageServerId, ToPoint};
use parking_lot::{Mutex, RwLock};
use project::CompletionIntent;
use rope::Point;
use std::{
ops::Range,
@@ -17,7 +18,6 @@ use std::{
use ui::ActiveTheme;
use workspace::Workspace;
pub mod active_command;
pub mod default_command;
pub mod diagnostics_command;
pub mod docs_command;
@@ -29,7 +29,8 @@ pub mod prompt_command;
pub mod search_command;
pub mod symbols_command;
pub mod tabs_command;
pub mod term_command;
pub mod terminal_command;
pub mod workflow_command;
pub(crate) struct SlashCommandCompletionProvider {
cancel_flag: Mutex<Arc<AtomicBool>>,
@@ -106,20 +107,24 @@ impl SlashCommandCompletionProvider {
let command_range = command_range.clone();
let editor = editor.clone();
let workspace = workspace.clone();
Arc::new(move |cx: &mut WindowContext| {
editor
.update(cx, |editor, cx| {
editor.run_command(
command_range.clone(),
&command_name,
None,
true,
workspace.clone(),
cx,
);
})
.ok();
}) as Arc<_>
Arc::new(
move |intent: CompletionIntent, cx: &mut WindowContext| {
if intent.is_complete() {
editor
.update(cx, |editor, cx| {
editor.run_command(
command_range.clone(),
&command_name,
None,
true,
workspace.clone(),
cx,
);
})
.ok();
}
},
) as Arc<_>
})
},
);
@@ -151,7 +156,6 @@ impl SlashCommandCompletionProvider {
let mut flag = self.cancel_flag.lock();
flag.store(true, SeqCst);
*flag = new_cancel_flag.clone();
let commands = SlashCommandRegistry::global(cx);
if let Some(command) = commands.command(command_name) {
let completions = command.complete_argument(
@@ -177,19 +181,21 @@ impl SlashCommandCompletionProvider {
let command_range = command_range.clone();
let command_name = command_name.clone();
let command_argument = command_argument.new_text.clone();
move |cx: &mut WindowContext| {
editor
.update(cx, |editor, cx| {
editor.run_command(
command_range.clone(),
&command_name,
Some(&command_argument),
true,
workspace.clone(),
cx,
);
})
.ok();
move |intent: CompletionIntent, cx: &mut WindowContext| {
if intent.is_complete() {
editor
.update(cx, |editor, cx| {
editor.run_command(
command_range.clone(),
&command_name,
Some(&command_argument),
true,
workspace.clone(),
cx,
);
})
.ok();
}
}
}) as Arc<_>
})
@@ -204,7 +210,7 @@ impl SlashCommandCompletionProvider {
project::Completion {
old_range: argument_range.clone(),
label: CodeLabel::plain(command_argument.label, None),
label: command_argument.label,
new_text,
documentation: None,
server_id: LanguageServerId(0),

View File

@@ -1,102 +0,0 @@
use super::{
diagnostics_command::write_single_file_diagnostics,
file_command::{build_entry_output_section, codeblock_fence_for_path},
SlashCommand, SlashCommandOutput,
};
use anyhow::{anyhow, Result};
use assistant_slash_command::ArgumentCompletion;
use editor::Editor;
use gpui::{AppContext, Task, WeakView};
use language::LspAdapterDelegate;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use ui::WindowContext;
use workspace::Workspace;
pub(crate) struct ActiveSlashCommand;
impl SlashCommand for ActiveSlashCommand {
fn name(&self) -> String {
"active".into()
}
fn description(&self) -> String {
"insert active tab".into()
}
fn menu_text(&self) -> String {
"Insert Active Tab".into()
}
fn complete_argument(
self: Arc<Self>,
_query: String,
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Err(anyhow!("this command does not require argument")))
}
fn requires_argument(&self) -> bool {
false
}
fn run(
self: Arc<Self>,
_argument: Option<&str>,
workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let output = workspace.update(cx, |workspace, cx| {
let Some(active_item) = workspace.active_item(cx) else {
return Task::ready(Err(anyhow!("no active tab")));
};
let Some(buffer) = active_item
.downcast::<Editor>()
.and_then(|editor| editor.read(cx).buffer().read(cx).as_singleton())
else {
return Task::ready(Err(anyhow!("active tab is not an editor")));
};
let snapshot = buffer.read(cx).snapshot();
let path = snapshot.resolve_file_path(cx, true);
let task = cx.background_executor().spawn({
let path = path.clone();
async move {
let mut output = String::new();
output.push_str(&codeblock_fence_for_path(path.as_deref(), None));
for chunk in snapshot.as_rope().chunks() {
output.push_str(chunk);
}
if !output.ends_with('\n') {
output.push('\n');
}
output.push_str("```\n");
let has_diagnostics =
write_single_file_diagnostics(&mut output, path.as_deref(), &snapshot);
if output.ends_with('\n') {
output.pop();
}
(output, has_diagnostics)
}
});
cx.foreground_executor().spawn(async move {
let (text, has_diagnostics) = task.await;
let range = 0..text.len();
Ok(SlashCommandOutput {
text,
sections: vec![build_entry_output_section(
range,
path.as_deref(),
false,
None,
)],
run_commands_in_text: has_diagnostics,
})
})
});
output.unwrap_or_else(|error| Task::ready(Err(error)))
}
}

View File

@@ -2,7 +2,7 @@ use super::{SlashCommand, SlashCommandOutput};
use crate::prompt_library::PromptStore;
use anyhow::{anyhow, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use gpui::{AppContext, Task, WeakView};
use gpui::{Task, WeakView};
use language::LspAdapterDelegate;
use std::{
fmt::Write,
@@ -35,7 +35,7 @@ impl SlashCommand for DefaultSlashCommand {
_query: String,
_cancellation_flag: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
_cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Err(anyhow!("this command does not require argument")))
}
@@ -44,7 +44,7 @@ impl SlashCommand for DefaultSlashCommand {
self: Arc<Self>,
_argument: Option<&str>,
_workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let store = PromptStore::global(cx);

View File

@@ -43,6 +43,7 @@ impl DiagnosticsSlashCommand {
worktree_id: entry.worktree_id.to_usize(),
path: entry.path.clone(),
path_prefix: path_prefix.clone(),
is_dir: false, // Diagnostics can't be produced for directories
distance_to_relative_ancestor: 0,
})
.collect(),
@@ -107,7 +108,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
query: String,
cancellation_flag: Arc<AtomicBool>,
workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
let Some(workspace) = workspace.and_then(|workspace| workspace.upgrade()) else {
return Task::ready(Err(anyhow!("workspace was dropped")));
@@ -146,7 +147,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
Ok(matches
.into_iter()
.map(|completion| ArgumentCompletion {
label: completion.clone(),
label: completion.clone().into(),
new_text: completion,
run_command: true,
})
@@ -158,7 +159,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
self: Arc<Self>,
argument: Option<&str>,
workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let Some(workspace) = workspace.upgrade() else {
@@ -168,58 +169,66 @@ impl SlashCommand for DiagnosticsSlashCommand {
let options = Options::parse(argument);
let task = collect_diagnostics(workspace.read(cx).project().clone(), options, cx);
cx.spawn(move |_| async move {
let Some((text, sections)) = task.await? else {
return Ok(SlashCommandOutput::default());
return Ok(SlashCommandOutput {
sections: vec![SlashCommandOutputSection {
range: 0..1,
icon: IconName::Library,
label: "No Diagnostics".into(),
}],
text: "\n".to_string(),
run_commands_in_text: true,
});
};
let sections = sections
.into_iter()
.map(|(range, placeholder_type)| SlashCommandOutputSection {
range,
icon: match placeholder_type {
PlaceholderType::Root(_, _) => IconName::ExclamationTriangle,
PlaceholderType::File(_) => IconName::File,
PlaceholderType::Diagnostic(DiagnosticType::Error, _) => IconName::XCircle,
PlaceholderType::Diagnostic(DiagnosticType::Warning, _) => {
IconName::ExclamationTriangle
}
},
label: match placeholder_type {
PlaceholderType::Root(summary, source) => {
let mut label = String::new();
label.push_str("Diagnostics");
if let Some(source) = source {
write!(label, " ({})", source).unwrap();
}
if summary.error_count > 0 || summary.warning_count > 0 {
label.push(':');
if summary.error_count > 0 {
write!(label, " {} errors", summary.error_count).unwrap();
if summary.warning_count > 0 {
label.push_str(",");
}
}
if summary.warning_count > 0 {
write!(label, " {} warnings", summary.warning_count).unwrap();
}
}
label.into()
}
PlaceholderType::File(file_path) => file_path.into(),
PlaceholderType::Diagnostic(_, message) => message.into(),
},
})
.collect();
Ok(SlashCommandOutput {
text,
sections: sections
.into_iter()
.map(|(range, placeholder_type)| SlashCommandOutputSection {
range,
icon: match placeholder_type {
PlaceholderType::Root(_, _) => IconName::ExclamationTriangle,
PlaceholderType::File(_) => IconName::File,
PlaceholderType::Diagnostic(DiagnosticType::Error, _) => {
IconName::XCircle
}
PlaceholderType::Diagnostic(DiagnosticType::Warning, _) => {
IconName::ExclamationTriangle
}
},
label: match placeholder_type {
PlaceholderType::Root(summary, source) => {
let mut label = String::new();
label.push_str("Diagnostics");
if let Some(source) = source {
write!(label, " ({})", source).unwrap();
}
if summary.error_count > 0 || summary.warning_count > 0 {
label.push(':');
if summary.error_count > 0 {
write!(label, " {} errors", summary.error_count).unwrap();
if summary.warning_count > 0 {
label.push_str(",");
}
}
if summary.warning_count > 0 {
write!(label, " {} warnings", summary.warning_count)
.unwrap();
}
}
label.into()
}
PlaceholderType::File(file_path) => file_path.into(),
PlaceholderType::Diagnostic(_, message) => message.into(),
},
})
.collect(),
sections,
run_commands_in_text: false,
})
})

View File

@@ -164,7 +164,7 @@ impl SlashCommand for DocsSlashCommand {
query: String,
_cancel: Arc<AtomicBool>,
workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
self.ensure_rust_doc_providers_are_registered(workspace, cx);
@@ -182,7 +182,7 @@ impl SlashCommand for DocsSlashCommand {
items
.into_iter()
.map(|item| ArgumentCompletion {
label: item.clone(),
label: item.clone().into(),
new_text: format!("{provider} {item}"),
run_command: true,
})
@@ -194,7 +194,7 @@ impl SlashCommand for DocsSlashCommand {
let providers = indexed_docs_registry.list_providers();
if providers.is_empty() {
return Ok(vec![ArgumentCompletion {
label: "No available docs providers.".to_string(),
label: "No available docs providers.".into(),
new_text: String::new(),
run_command: false,
}]);
@@ -203,7 +203,7 @@ impl SlashCommand for DocsSlashCommand {
Ok(providers
.into_iter()
.map(|provider| ArgumentCompletion {
label: provider.to_string(),
label: provider.to_string().into(),
new_text: provider.to_string(),
run_command: false,
})
@@ -222,8 +222,38 @@ impl SlashCommand for DocsSlashCommand {
drop(store.clone().index(package.as_str().into()));
}
let items = store.search(package).await;
Ok(build_completions(provider, items))
let suggested_packages = store.clone().suggest_packages().await?;
let search_results = store.search(package).await;
let mut items = build_completions(provider.clone(), search_results);
let workspace_crate_completions = suggested_packages
.into_iter()
.filter(|package_name| {
!items
.iter()
.any(|item| item.label.text() == package_name.as_ref())
})
.map(|package_name| ArgumentCompletion {
label: format!("{package_name} (unindexed)").into(),
new_text: format!("{provider} {package_name}"),
run_command: true,
})
.collect::<Vec<_>>();
items.extend(workspace_crate_completions);
if items.is_empty() {
return Ok(vec![ArgumentCompletion {
label: format!(
"Enter a {package_term} name.",
package_term = package_term(&provider)
)
.into(),
new_text: provider.to_string(),
run_command: false,
}]);
}
Ok(items)
}
DocsSlashCommandArgs::SearchItemDocs {
provider,
@@ -242,7 +272,7 @@ impl SlashCommand for DocsSlashCommand {
self: Arc<Self>,
argument: Option<&str>,
_workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let Some(argument) = argument else {
@@ -269,6 +299,13 @@ impl SlashCommand for DocsSlashCommand {
} => (provider, item_path),
};
if key.trim().is_empty() {
bail!(
"no {package_term} name provided",
package_term = package_term(&provider)
);
}
let store = store?;
if let Some(package) = args.package() {
@@ -391,6 +428,15 @@ impl DocsSlashCommandArgs {
}
}
/// Returns the term used to refer to a package.
fn package_term(provider: &ProviderId) -> &'static str {
if provider == &DocsDotRsProvider::id() || provider == &LocalRustdocProvider::id() {
return "crate";
}
"package"
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -8,7 +8,7 @@ use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
};
use futures::AsyncReadExt;
use gpui::{AppContext, Task, WeakView};
use gpui::{Task, WeakView};
use html_to_markdown::{convert_html_to_markdown, markdown, TagHandler};
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
use language::LspAdapterDelegate;
@@ -120,7 +120,7 @@ impl SlashCommand for FetchSlashCommand {
_query: String,
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
_cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Ok(Vec::new()))
}
@@ -129,7 +129,7 @@ impl SlashCommand for FetchSlashCommand {
self: Arc<Self>,
argument: Option<&str>,
workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let Some(argument) = argument else {
@@ -150,6 +150,10 @@ impl SlashCommand for FetchSlashCommand {
let url = SharedString::from(url);
cx.foreground_executor().spawn(async move {
let text = text.await?;
if text.trim().is_empty() {
bail!("no textual content found");
}
let range = 0..text.len();
Ok(SlashCommandOutput {
text,

View File

@@ -3,7 +3,7 @@ use anyhow::{anyhow, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use fuzzy::PathMatch;
use gpui::{AppContext, Model, Task, View, WeakView};
use language::{BufferSnapshot, LineEnding, LspAdapterDelegate};
use language::{BufferSnapshot, CodeLabel, HighlightId, LineEnding, LspAdapterDelegate};
use project::{PathMatchCandidateSet, Project};
use std::{
fmt::Write,
@@ -29,11 +29,30 @@ impl FileSlashCommand {
let workspace = workspace.read(cx);
let project = workspace.project().read(cx);
let entries = workspace.recent_navigation_history(Some(10), cx);
let entries = entries
.into_iter()
.map(|entries| (entries.0, false))
.chain(project.worktrees(cx).flat_map(|worktree| {
let worktree = worktree.read(cx);
let id = worktree.id();
worktree.child_entries(Path::new("")).map(move |entry| {
(
project::ProjectPath {
worktree_id: id,
path: entry.path.clone(),
},
entry.kind.is_dir(),
)
})
}))
.collect::<Vec<_>>();
let path_prefix: Arc<str> = Arc::default();
Task::ready(
entries
.into_iter()
.filter_map(|(entry, _)| {
.filter_map(|(entry, is_dir)| {
let worktree = project.worktree_for_id(entry.worktree_id, cx)?;
let mut full_path = PathBuf::from(worktree.read(cx).root_name());
full_path.push(&entry.path);
@@ -44,6 +63,7 @@ impl FileSlashCommand {
path: full_path.into(),
path_prefix: path_prefix.clone(),
distance_to_relative_ancestor: 0,
is_dir,
})
})
.collect(),
@@ -54,6 +74,7 @@ impl FileSlashCommand {
.into_iter()
.map(|worktree| {
let worktree = worktree.read(cx);
PathMatchCandidateSet {
snapshot: worktree.snapshot(),
include_ignored: worktree
@@ -104,29 +125,42 @@ impl SlashCommand for FileSlashCommand {
query: String,
cancellation_flag: Arc<AtomicBool>,
workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
let Some(workspace) = workspace.and_then(|workspace| workspace.upgrade()) else {
return Task::ready(Err(anyhow!("workspace was dropped")));
};
let paths = self.search_paths(query, cancellation_flag, &workspace, cx);
let comment_id = cx.theme().syntax().highlight_id("comment").map(HighlightId);
cx.background_executor().spawn(async move {
Ok(paths
.await
.into_iter()
.map(|path_match| {
.filter_map(|path_match| {
let text = format!(
"{}{}",
path_match.path_prefix,
path_match.path.to_string_lossy()
);
ArgumentCompletion {
label: text.clone(),
let mut label = CodeLabel::default();
let file_name = path_match.path.file_name()?.to_string_lossy();
let label_text = if path_match.is_dir {
format!("{}/ ", file_name)
} else {
format!("{} ", file_name)
};
label.push_str(label_text.as_str(), None);
label.push_str(&text, comment_id);
label.filter_range = 0..file_name.len();
Some(ArgumentCompletion {
label,
new_text: text,
run_command: true,
}
})
})
.collect())
})
@@ -136,7 +170,7 @@ impl SlashCommand for FileSlashCommand {
self: Arc<Self>,
argument: Option<&str>,
workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let Some(workspace) = workspace.upgrade() else {
@@ -273,20 +307,25 @@ fn collect_files(
continue;
};
if let Some(buffer) = open_buffer_task.await.log_err() {
let snapshot = cx.read_model(&buffer, |buffer, _| buffer.snapshot())?;
let buffer_snapshot =
cx.read_model(&buffer, |buffer, _| buffer.snapshot())?;
let prev_len = text.len();
collect_file_content(&mut text, &snapshot, filename.clone());
collect_file_content(
&mut text,
&buffer_snapshot,
path_including_worktree_name.to_string_lossy().to_string(),
);
text.push('\n');
if !write_single_file_diagnostics(
&mut text,
Some(&path_including_worktree_name),
&snapshot,
&buffer_snapshot,
) {
text.pop();
}
ranges.push((
prev_len..text.len(),
PathBuf::from(filename),
path_including_worktree_name,
EntryType::File,
));
text.push('\n');

View File

@@ -6,7 +6,7 @@ use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
};
use chrono::Local;
use gpui::{AppContext, Task, WeakView};
use gpui::{Task, WeakView};
use language::LspAdapterDelegate;
use ui::prelude::*;
use workspace::Workspace;
@@ -35,7 +35,7 @@ impl SlashCommand for NowSlashCommand {
_query: String,
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
_cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Ok(Vec::new()))
}
@@ -44,7 +44,7 @@ impl SlashCommand for NowSlashCommand {
self: Arc<Self>,
_argument: Option<&str>,
_workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
_cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let now = Local::now();

View File

@@ -106,7 +106,7 @@ impl SlashCommand for ProjectSlashCommand {
_query: String,
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
_cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Err(anyhow!("this command does not require argument")))
}
@@ -119,7 +119,7 @@ impl SlashCommand for ProjectSlashCommand {
self: Arc<Self>,
_argument: Option<&str>,
workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let output = workspace.update(cx, |workspace, cx| {

View File

@@ -2,7 +2,7 @@ use super::{SlashCommand, SlashCommandOutput};
use crate::prompt_library::PromptStore;
use anyhow::{anyhow, Context, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use gpui::{AppContext, Task, WeakView};
use gpui::{Task, WeakView};
use language::LspAdapterDelegate;
use std::sync::{atomic::AtomicBool, Arc};
use ui::prelude::*;
@@ -32,7 +32,7 @@ impl SlashCommand for PromptSlashCommand {
query: String,
_cancellation_flag: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
let store = PromptStore::global(cx);
cx.background_executor().spawn(async move {
@@ -42,7 +42,7 @@ impl SlashCommand for PromptSlashCommand {
.filter_map(|prompt| {
let prompt_title = prompt.title?.to_string();
Some(ArgumentCompletion {
label: prompt_title.clone(),
label: prompt_title.clone().into(),
new_text: prompt_title,
run_command: true,
})
@@ -55,7 +55,7 @@ impl SlashCommand for PromptSlashCommand {
self: Arc<Self>,
title: Option<&str>,
_workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let Some(title) = title else {
@@ -77,6 +77,11 @@ impl SlashCommand for PromptSlashCommand {
});
cx.foreground_executor().spawn(async move {
let mut prompt = prompt.await?;
if prompt.starts_with('/') {
// Prevent an edge case where the inserted prompt starts with a slash command (that leads to funky rendering).
prompt.insert(0, '\n');
}
if prompt.is_empty() {
prompt.push('\n');
}

View File

@@ -5,6 +5,7 @@ use super::{
};
use anyhow::Result;
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use feature_flags::FeatureFlag;
use gpui::{AppContext, Task, WeakView};
use language::{CodeLabel, LineEnding, LspAdapterDelegate};
use semantic_index::SemanticIndex;
@@ -17,6 +18,12 @@ use ui::{prelude::*, IconName};
use util::ResultExt;
use workspace::Workspace;
pub(crate) struct SearchSlashCommandFeatureFlag;
impl FeatureFlag for SearchSlashCommandFeatureFlag {
const NAME: &'static str = "search-slash-command";
}
pub(crate) struct SearchSlashCommand;
impl SlashCommand for SearchSlashCommand {
@@ -45,7 +52,7 @@ impl SlashCommand for SearchSlashCommand {
_query: String,
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
_cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Ok(Vec::new()))
}
@@ -54,7 +61,7 @@ impl SlashCommand for SearchSlashCommand {
self: Arc<Self>,
argument: Option<&str>,
workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let Some(workspace) = workspace.upgrade() else {

View File

@@ -2,7 +2,7 @@ use super::{SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Context as _, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use editor::Editor;
use gpui::{AppContext, Task, WeakView};
use gpui::{Task, WeakView};
use language::LspAdapterDelegate;
use std::sync::Arc;
use std::{path::Path, sync::atomic::AtomicBool};
@@ -29,7 +29,7 @@ impl SlashCommand for OutlineSlashCommand {
_query: String,
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
_cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Err(anyhow!("this command does not require argument")))
}
@@ -42,7 +42,7 @@ impl SlashCommand for OutlineSlashCommand {
self: Arc<Self>,
_argument: Option<&str>,
workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let output = workspace.update(cx, |workspace, cx| {

View File

@@ -3,29 +3,35 @@ use super::{
file_command::{build_entry_output_section, codeblock_fence_for_path},
SlashCommand, SlashCommandOutput,
};
use anyhow::{anyhow, Result};
use anyhow::{Context, Result};
use assistant_slash_command::ArgumentCompletion;
use collections::HashMap;
use editor::Editor;
use gpui::{AppContext, Entity, Task, WeakView};
use language::LspAdapterDelegate;
use std::{fmt::Write, sync::Arc};
use gpui::{Entity, Task, WeakView};
use language::{BufferSnapshot, LspAdapterDelegate};
use std::{
fmt::Write,
path::PathBuf,
sync::{atomic::AtomicBool, Arc},
};
use ui::WindowContext;
use workspace::Workspace;
pub(crate) struct TabsSlashCommand;
const ALL_TABS_COMPLETION_ITEM: &str = "all";
impl SlashCommand for TabsSlashCommand {
fn name(&self) -> String {
"tabs".into()
}
fn description(&self) -> String {
"insert open tabs".into()
"insert open tabs (active tab by default)".to_owned()
}
fn menu_text(&self) -> String {
"Insert Open Tabs".into()
"Insert Open Tabs".to_owned()
}
fn requires_argument(&self) -> bool {
@@ -34,85 +40,188 @@ impl SlashCommand for TabsSlashCommand {
fn complete_argument(
self: Arc<Self>,
_query: String,
_cancel: Arc<std::sync::atomic::AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
query: String,
cancel: Arc<AtomicBool>,
workspace: Option<WeakView<Workspace>>,
cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Err(anyhow!("this command does not require argument")))
let all_tabs_completion_item = if ALL_TABS_COMPLETION_ITEM.contains(&query) {
Some(ArgumentCompletion {
label: ALL_TABS_COMPLETION_ITEM.into(),
new_text: ALL_TABS_COMPLETION_ITEM.to_owned(),
run_command: true,
})
} else {
None
};
let tab_items_search = tab_items_for_query(workspace, query, cancel, false, cx);
cx.spawn(|_| async move {
let tab_completion_items =
tab_items_search
.await?
.into_iter()
.filter_map(|(path, ..)| {
let path_string = path.as_deref()?.to_string_lossy().to_string();
Some(ArgumentCompletion {
label: path_string.clone().into(),
new_text: path_string,
run_command: true,
})
});
Ok(all_tabs_completion_item
.into_iter()
.chain(tab_completion_items)
.collect::<Vec<_>>())
})
}
fn run(
self: Arc<Self>,
_argument: Option<&str>,
argument: Option<&str>,
workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let open_buffers = workspace.update(cx, |workspace, cx| {
let mut timestamps_by_entity_id = HashMap::default();
let mut open_buffers = Vec::new();
let tab_items_search = tab_items_for_query(
Some(workspace),
argument.map(ToOwned::to_owned).unwrap_or_default(),
Arc::new(AtomicBool::new(false)),
true,
cx,
);
for pane in workspace.panes() {
let pane = pane.read(cx);
for entry in pane.activation_history() {
timestamps_by_entity_id.insert(entry.entity_id, entry.timestamp);
cx.background_executor().spawn(async move {
let mut sections = Vec::new();
let mut text = String::new();
let mut has_diagnostics = false;
for (full_path, buffer, _) in tab_items_search.await? {
let section_start_ix = text.len();
text.push_str(&codeblock_fence_for_path(full_path.as_deref(), None));
for chunk in buffer.as_rope().chunks() {
text.push_str(chunk);
}
if !text.ends_with('\n') {
text.push('\n');
}
writeln!(text, "```").unwrap();
if write_single_file_diagnostics(&mut text, full_path.as_deref(), &buffer) {
has_diagnostics = true;
}
if !text.ends_with('\n') {
text.push('\n');
}
let section_end_ix = text.len() - 1;
sections.push(build_entry_output_section(
section_start_ix..section_end_ix,
full_path.as_deref(),
false,
None,
));
}
for editor in workspace.items_of_type::<Editor>(cx) {
if let Some(buffer) = editor.read(cx).buffer().read(cx).as_singleton() {
if let Some(timestamp) = timestamps_by_entity_id.get(&editor.entity_id()) {
let snapshot = buffer.read(cx).snapshot();
let full_path = snapshot.resolve_file_path(cx, true);
open_buffers.push((full_path, snapshot, *timestamp));
}
}
}
open_buffers
});
match open_buffers {
Ok(mut open_buffers) => cx.background_executor().spawn(async move {
open_buffers.sort_by_key(|(_, _, timestamp)| *timestamp);
let mut sections = Vec::new();
let mut text = String::new();
let mut has_diagnostics = false;
for (full_path, buffer, _) in open_buffers {
let section_start_ix = text.len();
text.push_str(&codeblock_fence_for_path(full_path.as_deref(), None));
for chunk in buffer.as_rope().chunks() {
text.push_str(chunk);
}
if !text.ends_with('\n') {
text.push('\n');
}
writeln!(text, "```").unwrap();
if write_single_file_diagnostics(&mut text, full_path.as_deref(), &buffer) {
has_diagnostics = true;
}
if !text.ends_with('\n') {
text.push('\n');
}
let section_end_ix = text.len() - 1;
sections.push(build_entry_output_section(
section_start_ix..section_end_ix,
full_path.as_deref(),
false,
None,
));
}
Ok(SlashCommandOutput {
text,
sections,
run_commands_in_text: has_diagnostics,
})
}),
Err(error) => Task::ready(Err(error)),
}
Ok(SlashCommandOutput {
text,
sections,
run_commands_in_text: has_diagnostics,
})
})
}
}
fn tab_items_for_query(
workspace: Option<WeakView<Workspace>>,
mut query: String,
cancel: Arc<AtomicBool>,
use_active_tab_for_empty_query: bool,
cx: &mut WindowContext,
) -> Task<anyhow::Result<Vec<(Option<PathBuf>, BufferSnapshot, usize)>>> {
cx.spawn(|mut cx| async move {
query.make_ascii_lowercase();
let mut open_buffers =
workspace
.context("no workspace")?
.update(&mut cx, |workspace, cx| {
if use_active_tab_for_empty_query && query.trim().is_empty() {
let active_editor = workspace
.active_item(cx)
.context("no active item")?
.downcast::<Editor>()
.context("active item is not an editor")?;
let snapshot = active_editor
.read(cx)
.buffer()
.read(cx)
.as_singleton()
.context("active editor is not a singleton buffer")?
.read(cx)
.snapshot();
let full_path = snapshot.resolve_file_path(cx, true);
return anyhow::Ok(vec![(full_path, snapshot, 0)]);
}
let mut timestamps_by_entity_id = HashMap::default();
let mut open_buffers = Vec::new();
for pane in workspace.panes() {
let pane = pane.read(cx);
for entry in pane.activation_history() {
timestamps_by_entity_id.insert(entry.entity_id, entry.timestamp);
}
}
for editor in workspace.items_of_type::<Editor>(cx) {
if let Some(buffer) = editor.read(cx).buffer().read(cx).as_singleton() {
if let Some(timestamp) =
timestamps_by_entity_id.get(&editor.entity_id())
{
let snapshot = buffer.read(cx).snapshot();
let full_path = snapshot.resolve_file_path(cx, true);
open_buffers.push((full_path, snapshot, *timestamp));
}
}
}
Ok(open_buffers)
})??;
let background_executor = cx.background_executor().clone();
cx.background_executor()
.spawn(async move {
open_buffers.sort_by_key(|(_, _, timestamp)| *timestamp);
let query = query.trim();
if query.is_empty() || query == ALL_TABS_COMPLETION_ITEM {
return Ok(open_buffers);
}
let match_candidates = open_buffers
.iter()
.enumerate()
.filter_map(|(id, (full_path, ..))| {
let path_string = full_path.as_deref()?.to_string_lossy().to_string();
Some(fuzzy::StringMatchCandidate {
id,
char_bag: path_string.as_str().into(),
string: path_string,
})
})
.collect::<Vec<_>>();
let string_matches = fuzzy::match_strings(
&match_candidates,
&query,
true,
usize::MAX,
&cancel,
background_executor,
)
.await;
Ok(string_matches
.into_iter()
.filter_map(|string_match| open_buffers.get(string_match.candidate_id))
.cloned()
.collect())
})
.await
})
}

View File

@@ -9,21 +9,23 @@ use gpui::{AppContext, Task, WeakView};
use language::{CodeLabel, LspAdapterDelegate};
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
use ui::prelude::*;
use workspace::Workspace;
use workspace::{dock::Panel, Workspace};
use crate::DEFAULT_CONTEXT_LINES;
use super::create_label_for_command;
pub(crate) struct TermSlashCommand;
pub(crate) struct TerminalSlashCommand;
const LINE_COUNT_ARG: &str = "--line-count";
impl SlashCommand for TermSlashCommand {
impl SlashCommand for TerminalSlashCommand {
fn name(&self) -> String {
"term".into()
"terminal".into()
}
fn label(&self, cx: &AppContext) -> CodeLabel {
create_label_for_command("term", &[LINE_COUNT_ARG], cx)
create_label_for_command("terminal", &[LINE_COUNT_ARG], cx)
}
fn description(&self) -> String {
@@ -43,10 +45,10 @@ impl SlashCommand for TermSlashCommand {
_query: String,
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
_cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Ok(vec![ArgumentCompletion {
label: LINE_COUNT_ARG.to_string(),
label: LINE_COUNT_ARG.into(),
new_text: LINE_COUNT_ARG.to_string(),
run_command: true,
}]))
@@ -56,7 +58,7 @@ impl SlashCommand for TermSlashCommand {
self: Arc<Self>,
argument: Option<&str>,
workspace: WeakView<Workspace>,
_delegate: Arc<dyn LspAdapterDelegate>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let Some(workspace) = workspace.upgrade() else {
@@ -65,17 +67,17 @@ impl SlashCommand for TermSlashCommand {
let Some(terminal_panel) = workspace.read(cx).panel::<TerminalPanel>(cx) else {
return Task::ready(Err(anyhow::anyhow!("no terminal panel open")));
};
let Some(active_terminal) = terminal_panel
.read(cx)
.pane()
.read(cx)
.active_item()
.and_then(|t| t.downcast::<TerminalView>())
else {
let Some(active_terminal) = terminal_panel.read(cx).pane().and_then(|pane| {
pane.read(cx)
.active_item()
.and_then(|t| t.downcast::<TerminalView>())
}) else {
return Task::ready(Err(anyhow::anyhow!("no active terminal")));
};
let line_count = argument.and_then(|a| parse_argument(a)).unwrap_or(20);
let line_count = argument
.and_then(|a| parse_argument(a))
.unwrap_or(DEFAULT_CONTEXT_LINES);
let lines = active_terminal
.read(cx)

View File

@@ -0,0 +1,76 @@
use crate::prompts::PromptBuilder;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use anyhow::Result;
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
};
use gpui::{Task, WeakView};
use language::LspAdapterDelegate;
use ui::prelude::*;
use workspace::Workspace;
pub(crate) struct WorkflowSlashCommand {
prompt_builder: Arc<PromptBuilder>,
}
impl WorkflowSlashCommand {
pub fn new(prompt_builder: Arc<PromptBuilder>) -> Self {
Self { prompt_builder }
}
}
impl SlashCommand for WorkflowSlashCommand {
fn name(&self) -> String {
"workflow".into()
}
fn description(&self) -> String {
"insert a prompt that opts into the edit workflow".into()
}
fn menu_text(&self) -> String {
"Insert Workflow Prompt".into()
}
fn requires_argument(&self) -> bool {
false
}
fn complete_argument(
self: Arc<Self>,
_query: String,
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Ok(Vec::new()))
}
fn run(
self: Arc<Self>,
_argument: Option<&str>,
_workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let prompt_builder = self.prompt_builder.clone();
cx.spawn(|_cx| async move {
let text = prompt_builder.generate_workflow_prompt()?;
let range = 0..text.len();
Ok(SlashCommandOutput {
text,
sections: vec![SlashCommandOutputSection {
range,
icon: IconName::Route,
label: "Workflow".into(),
}],
run_commands_in_text: false,
})
})
}
}

View File

@@ -0,0 +1,44 @@
use anyhow::Result;
use gpui::AppContext;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
/// Settings for slash commands.
#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)]
pub struct SlashCommandSettings {
/// Settings for the `/docs` slash command.
#[serde(default)]
pub docs: DocsCommandSettings,
/// Settings for the `/project` slash command.
#[serde(default)]
pub project: ProjectCommandSettings,
}
/// Settings for the `/docs` slash command.
#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)]
pub struct DocsCommandSettings {
/// Whether `/docs` is enabled.
#[serde(default)]
pub enabled: bool,
}
/// Settings for the `/project` slash command.
#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)]
pub struct ProjectCommandSettings {
/// Whether `/project` is enabled.
#[serde(default)]
pub enabled: bool,
}
impl Settings for SlashCommandSettings {
const KEY: Option<&'static str> = Some("slash_commands");
type FileContent = Self;
fn load(sources: SettingsSources<Self::FileContent>, _cx: &mut AppContext) -> Result<Self> {
SettingsSources::<Self::FileContent>::json_merge_with(
[sources.default].into_iter().chain(sources.user),
)
}
}

View File

@@ -1,5 +1,6 @@
use collections::HashMap;
use ordered_float::OrderedFloat;
use rope::{Point, Rope, TextSummary};
use std::collections::{BTreeSet, HashMap};
use std::{
cmp,
fmt::{self, Debug},
@@ -64,11 +65,11 @@ impl Debug for Matrix {
}
}
#[derive(Debug)]
pub enum Hunk {
#[derive(Debug, Clone)]
pub enum CharOperation {
Insert { text: String },
Remove { len: usize },
Keep { len: usize },
Delete { bytes: usize },
Keep { bytes: usize },
}
pub struct StreamingDiff {
@@ -103,7 +104,7 @@ impl StreamingDiff {
}
}
pub fn push_new(&mut self, text: &str) -> Vec<Hunk> {
pub fn push_new(&mut self, text: &str) -> Vec<CharOperation> {
self.new.extend(text.chars());
self.scores.resize(self.old.len() + 1, self.new.len() + 1);
@@ -145,7 +146,7 @@ impl StreamingDiff {
hunks
}
fn backtrack(&self, old_text_ix: usize, new_text_ix: usize) -> Vec<Hunk> {
fn backtrack(&self, old_text_ix: usize, new_text_ix: usize) -> Vec<CharOperation> {
let mut pending_insert: Option<Range<usize>> = None;
let mut hunks = Vec::new();
let mut i = old_text_ix;
@@ -185,22 +186,22 @@ impl StreamingDiff {
}
} else {
if let Some(range) = pending_insert.take() {
hunks.push(Hunk::Insert {
hunks.push(CharOperation::Insert {
text: self.new[range].iter().collect(),
});
}
let char_len = self.old[i - 1].len_utf8();
if prev_i == i - 1 && prev_j == j {
if let Some(Hunk::Remove { len }) = hunks.last_mut() {
if let Some(CharOperation::Delete { bytes: len }) = hunks.last_mut() {
*len += char_len;
} else {
hunks.push(Hunk::Remove { len: char_len })
hunks.push(CharOperation::Delete { bytes: char_len })
}
} else if let Some(Hunk::Keep { len }) = hunks.last_mut() {
} else if let Some(CharOperation::Keep { bytes: len }) = hunks.last_mut() {
*len += char_len;
} else {
hunks.push(Hunk::Keep { len: char_len })
hunks.push(CharOperation::Keep { bytes: char_len })
}
}
@@ -209,7 +210,7 @@ impl StreamingDiff {
}
if let Some(range) = pending_insert.take() {
hunks.push(Hunk::Insert {
hunks.push(CharOperation::Insert {
text: self.new[range].iter().collect(),
});
}
@@ -218,74 +219,853 @@ impl StreamingDiff {
hunks
}
pub fn finish(self) -> Vec<Hunk> {
pub fn finish(self) -> Vec<CharOperation> {
self.backtrack(self.old.len(), self.new.len())
}
}
#[cfg(test)]
mod tests {
use std::env;
#[derive(Debug, Clone, PartialEq)]
pub enum LineOperation {
Insert { lines: u32 },
Delete { lines: u32 },
Keep { lines: u32 },
}
use super::*;
use rand::prelude::*;
#[derive(Debug, Default)]
pub struct LineDiff {
inserted_newline_at_end: bool,
/// The extent of kept and deleted text.
old_end: Point,
/// The extent of kept and inserted text.
new_end: Point,
/// Deleted rows, expressed in terms of the old text.
deleted_rows: BTreeSet<u32>,
/// Inserted rows, expressed in terms of the new text.
inserted_rows: BTreeSet<u32>,
buffered_insert: String,
/// After deleting a newline, we buffer deletion until we keep or insert a character.
buffered_delete: usize,
}
#[gpui::test(iterations = 100)]
fn test_random_diffs(mut rng: StdRng) {
let old_text_len = env::var("OLD_TEXT_LEN")
.map(|i| i.parse().expect("invalid `OLD_TEXT_LEN` variable"))
.unwrap_or(10);
let new_text_len = env::var("NEW_TEXT_LEN")
.map(|i| i.parse().expect("invalid `NEW_TEXT_LEN` variable"))
.unwrap_or(10);
let old = util::RandomCharIter::new(&mut rng)
.take(old_text_len)
.collect::<String>();
log::info!("old text: {:?}", old);
let mut diff = StreamingDiff::new(old.clone());
let mut hunks = Vec::new();
let mut new_len = 0;
let mut new = String::new();
while new_len < new_text_len {
let new_chunk_len = rng.gen_range(1..=new_text_len - new_len);
let new_chunk = util::RandomCharIter::new(&mut rng)
.take(new_len)
.collect::<String>();
log::info!("new chunk: {:?}", new_chunk);
new_len += new_chunk_len;
new.push_str(&new_chunk);
let new_hunks = diff.push_new(&new_chunk);
log::info!("hunks: {:?}", new_hunks);
hunks.extend(new_hunks);
impl LineDiff {
pub fn push_char_operations<'a>(
&mut self,
operations: impl IntoIterator<Item = &'a CharOperation>,
old_text: &Rope,
) {
for operation in operations {
self.push_char_operation(operation, old_text);
}
let final_hunks = diff.finish();
log::info!("final hunks: {:?}", final_hunks);
hunks.extend(final_hunks);
}
log::info!("new text: {:?}", new);
let mut old_ix = 0;
let mut new_ix = 0;
let mut patched = String::new();
for hunk in hunks {
match hunk {
Hunk::Keep { len } => {
assert_eq!(&old[old_ix..old_ix + len], &new[new_ix..new_ix + len]);
patched.push_str(&old[old_ix..old_ix + len]);
old_ix += len;
new_ix += len;
pub fn push_char_operation(&mut self, operation: &CharOperation, old_text: &Rope) {
match operation {
CharOperation::Insert { text } => {
self.flush_delete(old_text);
if is_line_start(self.old_end) {
if let Some(newline_ix) = text.rfind('\n') {
let (prefix, suffix) = text.split_at(newline_ix + 1);
self.buffered_insert.push_str(prefix);
self.flush_insert(old_text);
self.buffered_insert.push_str(suffix);
} else {
self.buffered_insert.push_str(&text);
}
} else {
self.buffered_insert.push_str(&text);
if !text.ends_with('\n') {
self.flush_insert(old_text);
}
}
Hunk::Remove { len } => {
old_ix += len;
}
CharOperation::Delete { bytes } => {
self.buffered_delete += bytes;
let common_suffix_len = self.trim_buffered_end(old_text);
self.flush_insert(old_text);
if common_suffix_len > 0 || !is_line_end(self.old_end, old_text) {
self.flush_delete(old_text);
self.keep(common_suffix_len, old_text);
}
Hunk::Insert { text } => {
assert_eq!(text, &new[new_ix..new_ix + text.len()]);
patched.push_str(&text);
new_ix += text.len();
}
CharOperation::Keep { bytes } => {
self.flush_delete(old_text);
self.flush_insert(old_text);
self.keep(*bytes, old_text);
}
}
}
fn flush_insert(&mut self, old_text: &Rope) {
if self.buffered_insert.is_empty() {
return;
}
let new_start = self.new_end;
let lines = TextSummary::from(self.buffered_insert.as_str()).lines;
self.new_end += lines;
if is_line_start(self.old_end) {
if self.new_end.column == 0 {
self.inserted_rows.extend(new_start.row..self.new_end.row);
} else {
self.deleted_rows.insert(self.old_end.row);
self.inserted_rows.extend(new_start.row..=self.new_end.row);
}
} else if is_line_end(self.old_end, old_text) {
if self.buffered_insert.starts_with('\n') {
self.inserted_rows
.extend(new_start.row + 1..=self.new_end.row);
self.inserted_newline_at_end = true;
} else {
if !self.inserted_newline_at_end {
self.deleted_rows.insert(self.old_end.row);
}
self.inserted_rows.extend(new_start.row..=self.new_end.row);
}
} else {
self.deleted_rows.insert(self.old_end.row);
self.inserted_rows.extend(new_start.row..=self.new_end.row);
}
self.buffered_insert.clear();
}
fn flush_delete(&mut self, old_text: &Rope) {
if self.buffered_delete == 0 {
return;
}
let old_start = self.old_end;
self.old_end =
old_text.offset_to_point(old_text.point_to_offset(self.old_end) + self.buffered_delete);
if is_line_end(old_start, old_text) && is_line_end(self.old_end, old_text) {
self.deleted_rows
.extend(old_start.row + 1..=self.old_end.row);
} else if is_line_start(old_start)
&& (is_line_start(self.old_end) && self.old_end < old_text.max_point())
&& self.new_end.column == 0
{
self.deleted_rows.extend(old_start.row..self.old_end.row);
} else {
self.inserted_rows.insert(self.new_end.row);
self.deleted_rows.extend(old_start.row..=self.old_end.row);
}
self.inserted_newline_at_end = false;
self.buffered_delete = 0;
}
fn keep(&mut self, bytes: usize, old_text: &Rope) {
if bytes == 0 {
return;
}
let lines =
old_text.offset_to_point(old_text.point_to_offset(self.old_end) + bytes) - self.old_end;
self.old_end += lines;
self.new_end += lines;
self.inserted_newline_at_end = false;
}
fn trim_buffered_end(&mut self, old_text: &Rope) -> usize {
let old_start_offset = old_text.point_to_offset(self.old_end);
let old_end_offset = old_start_offset + self.buffered_delete;
let new_chars = self.buffered_insert.chars().rev();
let old_chars = old_text
.chunks_in_range(old_start_offset..old_end_offset)
.flat_map(|chunk| chunk.chars().rev());
let mut common_suffix_len = 0;
for (new_ch, old_ch) in new_chars.zip(old_chars) {
if new_ch == old_ch {
common_suffix_len += new_ch.len_utf8();
} else {
break;
}
}
self.buffered_delete -= common_suffix_len;
self.buffered_insert
.truncate(self.buffered_insert.len() - common_suffix_len);
common_suffix_len
}
pub fn finish(&mut self, old_text: &Rope) {
self.flush_insert(old_text);
self.flush_delete(old_text);
let old_start = self.old_end;
self.old_end = old_text.max_point();
self.new_end += self.old_end - old_start;
}
pub fn line_operations(&self) -> Vec<LineOperation> {
let mut ops = Vec::new();
let mut deleted_rows = self.deleted_rows.iter().copied().peekable();
let mut inserted_rows = self.inserted_rows.iter().copied().peekable();
let mut old_row = 0;
let mut new_row = 0;
while deleted_rows.peek().is_some() || inserted_rows.peek().is_some() {
// Check for a run of deleted lines at current old row.
if Some(old_row) == deleted_rows.peek().copied() {
if let Some(LineOperation::Delete { lines }) = ops.last_mut() {
*lines += 1;
} else {
ops.push(LineOperation::Delete { lines: 1 });
}
old_row += 1;
deleted_rows.next();
} else if Some(new_row) == inserted_rows.peek().copied() {
if let Some(LineOperation::Insert { lines }) = ops.last_mut() {
*lines += 1;
} else {
ops.push(LineOperation::Insert { lines: 1 });
}
new_row += 1;
inserted_rows.next();
} else {
// Keep lines until the next deletion, insertion, or the end of the old text.
let lines_to_next_deletion = inserted_rows
.peek()
.copied()
.unwrap_or(self.new_end.row + 1)
- new_row;
let lines_to_next_insertion =
deleted_rows.peek().copied().unwrap_or(self.old_end.row + 1) - old_row;
let kept_lines =
cmp::max(1, cmp::min(lines_to_next_insertion, lines_to_next_deletion));
if kept_lines > 0 {
ops.push(LineOperation::Keep { lines: kept_lines });
old_row += kept_lines;
new_row += kept_lines;
}
}
}
assert_eq!(patched, new);
if old_row < self.old_end.row + 1 {
ops.push(LineOperation::Keep {
lines: self.old_end.row + 1 - old_row,
});
}
ops
}
}
fn is_line_start(point: Point) -> bool {
point.column == 0
}
fn is_line_end(point: Point, text: &Rope) -> bool {
text.line_len(point.row) == point.column
}
#[cfg(test)]
mod tests {
use super::*;
use rand::prelude::*;
use std::env;
#[test]
fn test_delete_first_of_two_lines() {
let old_text = "aaaa\nbbbb";
let char_ops = vec![
CharOperation::Delete { bytes: 5 },
CharOperation::Keep { bytes: 4 },
];
let expected_line_ops = vec![
LineOperation::Delete { lines: 1 },
LineOperation::Keep { lines: 1 },
];
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &expected_line_ops)
);
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(line_ops, expected_line_ops);
}
#[test]
fn test_delete_second_of_two_lines() {
let old_text = "aaaa\nbbbb";
let char_ops = vec![
CharOperation::Keep { bytes: 5 },
CharOperation::Delete { bytes: 4 },
];
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Keep { lines: 1 },
LineOperation::Delete { lines: 1 },
LineOperation::Insert { lines: 1 }
]
);
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &line_ops)
);
}
#[test]
fn test_add_new_line() {
let old_text = "aaaa\nbbbb";
let char_ops = vec![
CharOperation::Keep { bytes: 9 },
CharOperation::Insert {
text: "\ncccc".into(),
},
];
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Keep { lines: 2 },
LineOperation::Insert { lines: 1 }
]
);
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &line_ops)
);
}
#[test]
fn test_delete_line_in_middle() {
let old_text = "aaaa\nbbbb\ncccc";
let char_ops = vec![
CharOperation::Keep { bytes: 5 },
CharOperation::Delete { bytes: 5 },
CharOperation::Keep { bytes: 4 },
];
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Keep { lines: 1 },
LineOperation::Delete { lines: 1 },
LineOperation::Keep { lines: 1 }
]
);
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &line_ops)
);
}
#[test]
fn test_replace_line() {
let old_text = "aaaa\nbbbb\ncccc";
let char_ops = vec![
CharOperation::Keep { bytes: 5 },
CharOperation::Delete { bytes: 4 },
CharOperation::Insert {
text: "BBBB".into(),
},
CharOperation::Keep { bytes: 5 },
];
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Keep { lines: 1 },
LineOperation::Delete { lines: 1 },
LineOperation::Insert { lines: 1 },
LineOperation::Keep { lines: 1 }
]
);
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &line_ops)
);
}
#[test]
fn test_multiple_edits_on_different_lines() {
let old_text = "aaaa\nbbbb\ncccc\ndddd";
let char_ops = vec![
CharOperation::Insert { text: "A".into() },
CharOperation::Keep { bytes: 9 },
CharOperation::Delete { bytes: 5 },
CharOperation::Keep { bytes: 4 },
CharOperation::Insert {
text: "\nEEEE".into(),
},
];
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Delete { lines: 1 },
LineOperation::Insert { lines: 1 },
LineOperation::Keep { lines: 1 },
LineOperation::Delete { lines: 2 },
LineOperation::Insert { lines: 2 },
]
);
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &line_ops)
);
}
#[test]
fn test_edit_at_end_of_line() {
let old_text = "aaaa\nbbbb\ncccc";
let char_ops = vec![
CharOperation::Keep { bytes: 4 },
CharOperation::Insert { text: "A".into() },
CharOperation::Keep { bytes: 10 },
];
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Delete { lines: 1 },
LineOperation::Insert { lines: 1 },
LineOperation::Keep { lines: 2 }
]
);
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &line_ops)
);
}
#[test]
fn test_insert_newline_character() {
let old_text = "aaaabbbb";
let char_ops = vec![
CharOperation::Keep { bytes: 4 },
CharOperation::Insert { text: "\n".into() },
CharOperation::Keep { bytes: 4 },
];
let new_text = apply_char_operations(old_text, &char_ops);
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Delete { lines: 1 },
LineOperation::Insert { lines: 2 }
]
);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &line_ops)
);
}
#[test]
fn test_insert_newline_at_beginning() {
let old_text = "aaaa\nbbbb";
let char_ops = vec![
CharOperation::Insert { text: "\n".into() },
CharOperation::Keep { bytes: 9 },
];
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Insert { lines: 1 },
LineOperation::Keep { lines: 2 }
]
);
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &line_ops)
);
}
#[test]
fn test_delete_newline() {
let old_text = "aaaa\nbbbb";
let char_ops = vec![
CharOperation::Keep { bytes: 4 },
CharOperation::Delete { bytes: 1 },
CharOperation::Keep { bytes: 4 },
];
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Delete { lines: 2 },
LineOperation::Insert { lines: 1 }
]
);
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &line_ops)
);
}
#[test]
fn test_insert_multiple_newlines() {
let old_text = "aaaa\nbbbb";
let char_ops = vec![
CharOperation::Keep { bytes: 5 },
CharOperation::Insert {
text: "\n\n".into(),
},
CharOperation::Keep { bytes: 4 },
];
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Keep { lines: 1 },
LineOperation::Insert { lines: 2 },
LineOperation::Keep { lines: 1 }
]
);
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &line_ops)
);
}
#[test]
fn test_delete_multiple_newlines() {
let old_text = "aaaa\n\n\nbbbb";
let char_ops = vec![
CharOperation::Keep { bytes: 5 },
CharOperation::Delete { bytes: 2 },
CharOperation::Keep { bytes: 4 },
];
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Keep { lines: 1 },
LineOperation::Delete { lines: 2 },
LineOperation::Keep { lines: 1 }
]
);
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &line_ops)
);
}
#[test]
fn test_complex_scenario() {
let old_text = "line1\nline2\nline3\nline4";
let char_ops = vec![
CharOperation::Keep { bytes: 6 },
CharOperation::Insert {
text: "inserted\n".into(),
},
CharOperation::Delete { bytes: 6 },
CharOperation::Keep { bytes: 5 },
CharOperation::Insert {
text: "\nnewline".into(),
},
CharOperation::Keep { bytes: 6 },
];
let line_ops = char_ops_to_line_ops(&old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Keep { lines: 1 },
LineOperation::Delete { lines: 1 },
LineOperation::Insert { lines: 1 },
LineOperation::Keep { lines: 1 },
LineOperation::Insert { lines: 1 },
LineOperation::Keep { lines: 1 }
]
);
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(new_text, "line1\ninserted\nline3\nnewline\nline4");
assert_eq!(
apply_line_operations(old_text, &new_text, &line_ops),
new_text,
);
}
#[test]
fn test_cleaning_up_common_suffix() {
let old_text = concat!(
" for y in 0..size.y() {\n",
" let a = 10;\n",
" let b = 20;\n",
" }",
);
let char_ops = [
CharOperation::Keep { bytes: 8 },
CharOperation::Insert { text: "let".into() },
CharOperation::Insert {
text: " mut".into(),
},
CharOperation::Insert { text: " y".into() },
CharOperation::Insert { text: " =".into() },
CharOperation::Insert { text: " 0".into() },
CharOperation::Insert { text: ";".into() },
CharOperation::Insert { text: "\n".into() },
CharOperation::Insert {
text: " while".into(),
},
CharOperation::Insert { text: " y".into() },
CharOperation::Insert {
text: " < size".into(),
},
CharOperation::Insert { text: ".".into() },
CharOperation::Insert { text: "y".into() },
CharOperation::Insert { text: "()".into() },
CharOperation::Insert { text: " {".into() },
CharOperation::Insert { text: "\n".into() },
CharOperation::Delete { bytes: 23 },
CharOperation::Keep { bytes: 23 },
CharOperation::Keep { bytes: 1 },
CharOperation::Keep { bytes: 23 },
CharOperation::Keep { bytes: 1 },
CharOperation::Keep { bytes: 8 },
CharOperation::Insert {
text: " y".into(),
},
CharOperation::Insert { text: " +=".into() },
CharOperation::Insert { text: " 1".into() },
CharOperation::Insert { text: ";".into() },
CharOperation::Insert { text: "\n".into() },
CharOperation::Insert {
text: " ".into(),
},
CharOperation::Keep { bytes: 1 },
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
LineOperation::Delete { lines: 1 },
LineOperation::Insert { lines: 2 },
LineOperation::Keep { lines: 2 },
LineOperation::Delete { lines: 1 },
LineOperation::Insert { lines: 2 },
]
);
let new_text = apply_char_operations(old_text, &char_ops);
assert_eq!(
new_text,
apply_line_operations(old_text, &new_text, &line_ops)
);
}
#[test]
fn test_random_diffs() {
random_test(|mut rng| {
let old_text_len = env::var("OLD_TEXT_LEN")
.map(|i| i.parse().expect("invalid `OLD_TEXT_LEN` variable"))
.unwrap_or(10);
let old = random_text(&mut rng, old_text_len);
println!("old text: {:?}", old);
let new = randomly_edit(&old, &mut rng);
println!("new text: {:?}", new);
let char_operations = random_streaming_diff(&mut rng, &old, &new);
println!("char operations: {:?}", char_operations);
// Use apply_char_operations to verify the result
let patched = apply_char_operations(&old, &char_operations);
assert_eq!(patched, new);
// Test char_ops_to_line_ops
let line_ops = char_ops_to_line_ops(&old, &char_operations);
println!("line operations: {:?}", line_ops);
let patched = apply_line_operations(&old, &new, &line_ops);
assert_eq!(patched, new);
});
}
fn char_ops_to_line_ops(old_text: &str, char_ops: &[CharOperation]) -> Vec<LineOperation> {
let old_rope = Rope::from(old_text);
let mut diff = LineDiff::default();
for op in char_ops {
diff.push_char_operation(op, &old_rope);
}
diff.finish(&old_rope);
diff.line_operations()
}
fn random_streaming_diff(rng: &mut impl Rng, old: &str, new: &str) -> Vec<CharOperation> {
let mut diff = StreamingDiff::new(old.to_string());
let mut char_operations = Vec::new();
let mut new_len = 0;
while new_len < new.len() {
let mut chunk_len = rng.gen_range(1..=new.len() - new_len);
while !new.is_char_boundary(new_len + chunk_len) {
chunk_len += 1;
}
let chunk = &new[new_len..new_len + chunk_len];
let new_hunks = diff.push_new(chunk);
char_operations.extend(new_hunks);
new_len += chunk_len;
}
char_operations.extend(diff.finish());
char_operations
}
fn random_test<F>(mut test_fn: F)
where
F: FnMut(StdRng),
{
let iterations = env::var("ITERATIONS")
.map(|i| i.parse().expect("invalid `ITERATIONS` variable"))
.unwrap_or(100);
let seed: u64 = env::var("SEED")
.map(|s| s.parse().expect("invalid `SEED` variable"))
.unwrap_or(0);
println!(
"Running test with {} iterations and seed {}",
iterations, seed
);
for i in 0..iterations {
println!("Iteration {}", i + 1);
let rng = StdRng::seed_from_u64(seed + i);
test_fn(rng);
}
}
fn apply_line_operations(old_text: &str, new_text: &str, line_ops: &[LineOperation]) -> String {
let mut result: Vec<&str> = Vec::new();
let old_lines: Vec<&str> = old_text.split('\n').collect();
let new_lines: Vec<&str> = new_text.split('\n').collect();
let mut old_start = 0_usize;
let mut new_start = 0_usize;
for op in line_ops {
match op {
LineOperation::Keep { lines } => {
let old_end = old_start + *lines as usize;
result.extend(&old_lines[old_start..old_end]);
old_start = old_end;
new_start += *lines as usize;
}
LineOperation::Delete { lines } => {
old_start += *lines as usize;
}
LineOperation::Insert { lines } => {
let new_end = new_start + *lines as usize;
result.extend(&new_lines[new_start..new_end]);
new_start = new_end;
}
}
}
result.join("\n")
}
#[test]
fn test_apply_char_operations() {
let old_text = "Hello, world!";
let char_ops = vec![
CharOperation::Keep { bytes: 7 },
CharOperation::Delete { bytes: 5 },
CharOperation::Insert {
text: "Rust".to_string(),
},
CharOperation::Keep { bytes: 1 },
];
let result = apply_char_operations(old_text, &char_ops);
assert_eq!(result, "Hello, Rust!");
}
fn random_text(rng: &mut impl Rng, length: usize) -> String {
util::RandomCharIter::new(rng).take(length).collect()
}
fn randomly_edit(text: &str, rng: &mut impl Rng) -> String {
let mut result = String::from(text);
let edit_count = rng.gen_range(1..=5);
fn random_char_range(text: &str, rng: &mut impl Rng) -> (usize, usize) {
let mut start = rng.gen_range(0..=text.len());
while !text.is_char_boundary(start) {
start -= 1;
}
let mut end = rng.gen_range(start..=text.len());
while !text.is_char_boundary(end) {
end += 1;
}
(start, end)
}
for _ in 0..edit_count {
match rng.gen_range(0..3) {
0 => {
// Insert
let (pos, _) = random_char_range(&result, rng);
let insert_len = rng.gen_range(1..=5);
let insert_text: String = random_text(rng, insert_len);
result.insert_str(pos, &insert_text);
}
1 => {
// Delete
if !result.is_empty() {
let (start, end) = random_char_range(&result, rng);
result.replace_range(start..end, "");
}
}
2 => {
// Replace
if !result.is_empty() {
let (start, end) = random_char_range(&result, rng);
let replace_len = end - start;
let replace_text: String = random_text(rng, replace_len);
result.replace_range(start..end, &replace_text);
}
}
_ => unreachable!(),
}
}
result
}
fn apply_char_operations(old_text: &str, char_ops: &[CharOperation]) -> String {
let mut result = String::new();
let mut old_ix = 0;
for operation in char_ops {
match operation {
CharOperation::Keep { bytes } => {
result.push_str(&old_text[old_ix..old_ix + bytes]);
old_ix += bytes;
}
CharOperation::Delete { bytes } => {
old_ix += bytes;
}
CharOperation::Insert { text } => {
result.push_str(text);
}
}
}
result
}
}

View File

@@ -1,86 +0,0 @@
When the user asks you to suggest edits for a buffer, use a strict template consisting of:
* A markdown code block with the file path as the language identifier.
* The original code that should be replaced
* A separator line (`---`)
* The new text that should replace the original lines
Each code block may only contain an edit for one single contiguous range of text. Use multiple code blocks for multiple edits.
## Example
If you have a buffer with the following lines:
```path/to/file.rs
fn quicksort(arr: &mut [i32]) {
if arr.len() <= 1 {
return;
}
let pivot_index = partition(arr);
let (left, right) = arr.split_at_mut(pivot_index);
quicksort(left);
quicksort(&mut right[1..]);
}
fn partition(arr: &mut [i32]) -> usize {
let last_index = arr.len() - 1;
let pivot = arr[last_index];
let mut i = 0;
for j in 0..last_index {
if arr[j] <= pivot {
arr.swap(i, j);
i += 1;
}
}
arr.swap(i, last_index);
i
}
```
And you want to replace the for loop inside `partition`, output the following.
```edit path/to/file.rs
for j in 0..last_index {
if arr[j] <= pivot {
arr.swap(i, j);
i += 1;
}
}
---
let mut j = 0;
while j < last_index {
if arr[j] <= pivot {
arr.swap(i, j);
i += 1;
}
j += 1;
}
```
If you wanted to insert comments above the partition function, output the following:
```edit path/to/file.rs
fn partition(arr: &mut [i32]) -> usize {
---
// A helper function used for quicksort.
fn partition(arr: &mut [i32]) -> usize {
```
If you wanted to delete the partition function, output the following:
```edit path/to/file.rs
fn partition(arr: &mut [i32]) -> usize {
let last_index = arr.len() - 1;
let pivot = arr[last_index];
let mut i = 0;
for j in 0..last_index {
if arr[j] <= pivot {
arr.swap(i, j);
i += 1;
}
}
arr.swap(i, last_index);
i
}
---
```

View File

@@ -1,6 +1,6 @@
use crate::{
humanize_token_count, prompts::generate_terminal_assistant_prompt, AssistantPanel,
AssistantPanelEvent, LanguageModelCompletionProvider, ModelSelector,
humanize_token_count, prompts::PromptBuilder, AssistantPanel, AssistantPanelEvent,
ModelSelector, DEFAULT_CONTEXT_LINES,
};
use anyhow::{Context as _, Result};
use client::telemetry::Telemetry;
@@ -16,7 +16,9 @@ use gpui::{
Subscription, Task, TextStyle, UpdateGlobal, View, WeakView,
};
use language::Buffer;
use language_model::{LanguageModelRequest, LanguageModelRequestMessage, Role};
use language_model::{
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
};
use settings::Settings;
use std::{
cmp,
@@ -30,8 +32,13 @@ use ui::{prelude::*, IconButtonShape, Tooltip};
use util::ResultExt;
use workspace::{notifications::NotificationId, Toast, Workspace};
pub fn init(fs: Arc<dyn Fs>, telemetry: Arc<Telemetry>, cx: &mut AppContext) {
cx.set_global(TerminalInlineAssistant::new(fs, telemetry));
pub fn init(
fs: Arc<dyn Fs>,
prompt_builder: Arc<PromptBuilder>,
telemetry: Arc<Telemetry>,
cx: &mut AppContext,
) {
cx.set_global(TerminalInlineAssistant::new(fs, prompt_builder, telemetry));
}
const PROMPT_HISTORY_MAX_LEN: usize = 20;
@@ -53,18 +60,24 @@ pub struct TerminalInlineAssistant {
prompt_history: VecDeque<String>,
telemetry: Option<Arc<Telemetry>>,
fs: Arc<dyn Fs>,
prompt_builder: Arc<PromptBuilder>,
}
impl Global for TerminalInlineAssistant {}
impl TerminalInlineAssistant {
pub fn new(fs: Arc<dyn Fs>, telemetry: Arc<Telemetry>) -> Self {
pub fn new(
fs: Arc<dyn Fs>,
prompt_builder: Arc<PromptBuilder>,
telemetry: Arc<Telemetry>,
) -> Self {
Self {
next_assist_id: TerminalInlineAssistId::default(),
assists: HashMap::default(),
prompt_history: VecDeque::default(),
telemetry: Some(telemetry),
fs,
prompt_builder,
}
}
@@ -215,17 +228,18 @@ impl TerminalInlineAssistant {
let assist = self.assists.get(&assist_id).context("invalid assist")?;
let shell = std::env::var("SHELL").ok();
let working_directory = assist
let (latest_output, working_directory) = assist
.terminal
.update(cx, |terminal, cx| {
terminal
.model()
.read(cx)
let terminal = terminal.model().read(cx);
let latest_output = terminal.last_n_non_empty_lines(DEFAULT_CONTEXT_LINES);
let working_directory = terminal
.working_directory()
.map(|path| path.to_string_lossy().to_string())
.map(|path| path.to_string_lossy().to_string());
(latest_output, working_directory)
})
.ok()
.flatten();
.unwrap_or_default();
let context_request = if assist.include_context {
assist.workspace.as_ref().and_then(|workspace| {
@@ -243,7 +257,7 @@ impl TerminalInlineAssistant {
None
};
let prompt = generate_terminal_assistant_prompt(
let prompt = self.prompt_builder.generate_terminal_assistant_prompt(
&assist
.prompt_editor
.clone()
@@ -252,7 +266,8 @@ impl TerminalInlineAssistant {
.prompt(cx),
shell.as_deref(),
working_directory.as_deref(),
);
&latest_output,
)?;
let mut messages = Vec::new();
if let Some(context_request) = context_request {
@@ -261,7 +276,7 @@ impl TerminalInlineAssistant {
messages.push(LanguageModelRequestMessage {
role: Role::User,
content: prompt,
content: vec![prompt.into()],
});
Ok(LanguageModelRequest {
@@ -548,7 +563,7 @@ impl Render for PromptEditor {
.gap_2()
.child(ModelSelector::new(
self.fs.clone(),
IconButton::new("context", IconName::Settings)
IconButton::new("context", IconName::SlidersAlt)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
@@ -556,7 +571,7 @@ impl Render for PromptEditor {
Tooltip::with_meta(
format!(
"Using {}",
LanguageModelCompletionProvider::read_global(cx)
LanguageModelRegistry::read_global(cx)
.active_model()
.map(|model| model.name().0)
.unwrap_or_else(|| "No model selected".into()),
@@ -700,6 +715,9 @@ impl PromptEditor {
fn count_tokens(&mut self, cx: &mut ViewContext<Self>) {
let assist_id = self.id;
let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
return;
};
self.pending_token_count = cx.spawn(|this, mut cx| async move {
cx.background_executor().timer(Duration::from_secs(1)).await;
let request =
@@ -707,18 +725,11 @@ impl PromptEditor {
inline_assistant.request_for_inline_assist(assist_id, cx)
})??;
if let Some(token_count) = cx.update(|cx| {
LanguageModelCompletionProvider::read_global(cx).count_tokens(request, cx)
})? {
let token_count = token_count.await?;
this.update(&mut cx, |this, cx| {
this.token_count = Some(token_count);
cx.notify();
})
} else {
Ok(())
}
let token_count = cx.update(|cx| model.count_tokens(request, cx))?.await?;
this.update(&mut cx, |this, cx| {
this.token_count = Some(token_count);
cx.notify();
})
})
}
@@ -843,7 +854,7 @@ impl PromptEditor {
}
fn render_token_count(&self, cx: &mut ViewContext<Self>) -> Option<impl IntoElement> {
let model = LanguageModelCompletionProvider::read_global(cx).active_model()?;
let model = LanguageModelRegistry::read_global(cx).active_model()?;
let token_count = self.token_count?;
let max_token_count = model.max_token_count();
@@ -985,19 +996,16 @@ impl Codegen {
}
pub fn start(&mut self, prompt: LanguageModelRequest, cx: &mut ModelContext<Self>) {
self.status = CodegenStatus::Pending;
self.transaction = Some(TerminalTransaction::start(self.terminal.clone()));
let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
return;
};
let telemetry = self.telemetry.clone();
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.status = CodegenStatus::Pending;
self.transaction = Some(TerminalTransaction::start(self.terminal.clone()));
self.generation = cx.spawn(|this, mut cx| async move {
let response = response.await;
let model_telemetry_id = model.telemetry_id();
let response = model.stream_completion(prompt, &cx).await;
let generate = async {
let (mut hunks_tx, mut hunks_rx) = mpsc::channel(1);

View File

@@ -0,0 +1,25 @@
### Using the Assistant
Once you have configured a provider, you can interact with the provider's language models in a context editor.
To create a new context editor, use the menu in the top right of the assistant panel and the `New Context` option.
In the context editor, select a model from one of the configured providers, type a message in the `You` block, and submit with `cmd-enter` (or `ctrl-enter` on Linux).
### Inline assistant
When you're in a normal editor, you can use `ctrl-enter` to open the inline assistant.
The inline assistant allows you to send the current selection (or the current line) to a language model and modify the selection with the language model's response.
### Adding Prompts
You can customize the default prompts that are used in new context editor, by opening the `Prompt Library`.
Open the `Prompt Library` using either the menu in the top right of the assistant panel and choosing the `Prompt Library` option, or by using the `assistant: deploy prompt library` command when the assistant panel is focused.
### Viewing past contexts
You view all previous contexts by opening up the `History` tab in the assistant panel.
Open the `History` using the menu in the top right of the assistant panel and choosing the `History`.

View File

@@ -18,7 +18,7 @@ pub fn init(cx: &mut AppContext) {
#[derive(Debug)]
pub struct ArgumentCompletion {
/// The label to display for this completion.
pub label: String,
pub label: CodeLabel,
/// The new text that should be inserted into the command when this completion is accepted.
pub new_text: String,
/// Whether the command should be run when accepting this completion.
@@ -37,7 +37,7 @@ pub trait SlashCommand: 'static + Send + Sync {
query: String,
cancel: Arc<AtomicBool>,
workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
cx: &mut WindowContext,
) -> Task<Result<Vec<ArgumentCompletion>>>;
fn requires_argument(&self) -> bool;
fn run(
@@ -49,7 +49,7 @@ pub trait SlashCommand: 'static + Send + Sync {
//
// It may be that `LspAdapterDelegate` needs a more general name, or
// perhaps another kind of delegate is needed here.
delegate: Arc<dyn LspAdapterDelegate>,
delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>>;
}
@@ -60,7 +60,7 @@ pub type RenderFoldPlaceholder = Arc<
+ Fn(ElementId, Arc<dyn Fn(&mut WindowContext)>, &mut WindowContext) -> AnyElement,
>;
#[derive(Default)]
#[derive(Debug, Default)]
pub struct SlashCommandOutput {
pub text: String,
pub sections: Vec<SlashCommandOutputSection<usize>>,

View File

@@ -56,6 +56,14 @@ impl SlashCommandRegistry {
state.commands.insert(command_name, Arc::new(command));
}
/// Unregisters the provided [`SlashCommand`].
pub fn unregister_command(&self, command: impl SlashCommand) {
let mut state = self.state.write();
let command_name: Arc<str> = command.name().into();
state.featured_commands.remove(&command_name);
state.commands.remove(&command_name);
}
/// Returns the names of registered [`SlashCommand`]s.
pub fn command_names(&self) -> Vec<Arc<str>> {
self.state.read().commands.keys().cloned().collect()

View File

@@ -55,6 +55,8 @@ struct UpdateRequestBody {
installation_id: Option<Arc<str>>,
release_channel: Option<&'static str>,
telemetry: bool,
is_staff: Option<bool>,
destination: &'static str,
}
#[derive(Clone, PartialEq, Eq)]
@@ -575,18 +577,27 @@ async fn download_remote_server_binary(
cx: &AsyncAppContext,
) -> 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 (installation_id, release_channel, telemetry_enabled, is_staff) = cx.update(|cx| {
let telemetry = Client::global(cx).telemetry().clone();
let is_staff = telemetry.is_staff();
let installation_id = telemetry.installation_id();
let release_channel =
ReleaseChannel::try_global(cx).map(|release_channel| release_channel.display_name());
let telemetry = TelemetrySettings::get_global(cx).metrics;
let telemetry_enabled = TelemetrySettings::get_global(cx).metrics;
(installation_id, release_channel, telemetry)
(
installation_id,
release_channel,
telemetry_enabled,
is_staff,
)
})?;
let request_body = AsyncBody::from(serde_json::to_string(&UpdateRequestBody {
installation_id,
release_channel,
telemetry,
telemetry: telemetry_enabled,
is_staff,
destination: "remote",
})?);
let mut response = client.get(&release.url, request_body, true).await?;
@@ -602,19 +613,28 @@ async fn download_release(
) -> 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 (installation_id, release_channel, telemetry_enabled, is_staff) = cx.update(|cx| {
let telemetry = Client::global(cx).telemetry().clone();
let is_staff = telemetry.is_staff();
let installation_id = telemetry.installation_id();
let release_channel =
ReleaseChannel::try_global(cx).map(|release_channel| release_channel.display_name());
let telemetry = TelemetrySettings::get_global(cx).metrics;
let telemetry_enabled = TelemetrySettings::get_global(cx).metrics;
(installation_id, release_channel, telemetry)
(
installation_id,
release_channel,
telemetry_enabled,
is_staff,
)
})?;
let request_body = AsyncBody::from(serde_json::to_string(&UpdateRequestBody {
installation_id,
release_channel,
telemetry,
telemetry: telemetry_enabled,
is_staff,
destination: "local",
})?);
let mut response = client.get(&release.url, request_body, true).await?;

View File

@@ -5,14 +5,13 @@ use clap::Parser;
use cli::{ipc::IpcOneShotServer, CliRequest, CliResponse, IpcHandshake};
use parking_lot::Mutex;
use std::{
convert::Infallible,
env, fs, io,
path::{Path, PathBuf},
process::ExitStatus,
sync::Arc,
thread::{self, JoinHandle},
};
use util::paths::PathLikeWithPosition;
use util::paths::PathWithPosition;
struct Detect;
@@ -54,13 +53,10 @@ struct Args {
}
fn parse_path_with_position(argument_str: &str) -> Result<String, std::io::Error> {
let path_like = PathLikeWithPosition::parse_str::<Infallible>(argument_str, |_, path_str| {
Ok(Path::new(path_str).to_path_buf())
})
.unwrap();
let path = PathWithPosition::parse_str(argument_str);
let curdir = env::current_dir()?;
let canonicalized = path_like.map_path_like(|path| match fs::canonicalize(&path) {
let canonicalized = path.map_path(|path| match fs::canonicalize(&path) {
Ok(path) => Ok(path),
Err(e) => {
if let Some(mut parent) = path.parent() {

View File

@@ -541,9 +541,16 @@ impl Client {
}
pub fn production(cx: &mut AppContext) -> Arc<Self> {
let user_agent = format!(
"Zed/{} ({}; {})",
AppVersion::global(cx),
std::env::consts::OS,
std::env::consts::ARCH
);
let clock = Arc::new(clock::RealSystemClock);
let http = Arc::new(HttpClientWithUrl::new(
&ClientSettings::get_global(cx).server_url,
Some(user_agent),
ProxySettings::get_global(cx).proxy.clone(),
));
Self::new(clock, http.clone(), cx)

View File

@@ -1,5 +1,6 @@
use crate::{Client, Connection, Credentials, EstablishConnectionError, UserStore};
use anyhow::{anyhow, Result};
use chrono::Duration;
use futures::{stream::BoxStream, StreamExt};
use gpui::{BackgroundExecutor, Context, Model, TestAppContext};
use parking_lot::Mutex;
@@ -162,6 +163,11 @@ impl FakeServer {
return Ok(*message.downcast().unwrap());
}
let accepted_tos_at = chrono::Utc::now()
.checked_sub_signed(Duration::hours(5))
.expect("failed to build accepted_tos_at")
.timestamp() as u64;
if message.is::<TypedEnvelope<GetPrivateUserInfo>>() {
self.respond(
message
@@ -172,6 +178,7 @@ impl FakeServer {
metrics_id: "the-metrics-id".into(),
staff: false,
flags: Default::default(),
accepted_tos_at: Some(accepted_tos_at),
},
);
continue;

View File

@@ -1,5 +1,6 @@
use super::{proto, Client, Status, TypedEnvelope};
use anyhow::{anyhow, Context, Result};
use chrono::{DateTime, Utc};
use collections::{hash_map::Entry, HashMap, HashSet};
use feature_flags::FeatureFlagAppExt;
use futures::{channel::mpsc, Future, StreamExt};
@@ -92,7 +93,9 @@ pub struct UserStore {
by_github_login: HashMap<String, u64>,
participant_indices: HashMap<u64, ParticipantIndex>,
update_contacts_tx: mpsc::UnboundedSender<UpdateContacts>,
current_plan: Option<proto::Plan>,
current_user: watch::Receiver<Option<Arc<User>>>,
accepted_tos_at: Option<Option<DateTime<Utc>>>,
contacts: Vec<Arc<Contact>>,
incoming_contact_requests: Vec<Arc<User>>,
outgoing_contact_requests: Vec<Arc<User>>,
@@ -139,6 +142,7 @@ impl UserStore {
let (mut current_user_tx, current_user_rx) = watch::channel();
let (update_contacts_tx, mut update_contacts_rx) = mpsc::unbounded();
let rpc_subscriptions = vec![
client.add_message_handler(cx.weak_model(), Self::handle_update_plan),
client.add_message_handler(cx.weak_model(), Self::handle_update_contacts),
client.add_message_handler(cx.weak_model(), Self::handle_update_invite_info),
client.add_message_handler(cx.weak_model(), Self::handle_show_contacts),
@@ -147,6 +151,8 @@ impl UserStore {
users: Default::default(),
by_github_login: Default::default(),
current_user: current_user_rx,
current_plan: None,
accepted_tos_at: None,
contacts: Default::default(),
incoming_contact_requests: Default::default(),
participant_indices: Default::default(),
@@ -186,9 +192,10 @@ impl UserStore {
} else {
break;
};
let fetch_metrics_id =
let fetch_private_user_info =
client.request(proto::GetPrivateUserInfo {}).log_err();
let (user, info) = futures::join!(fetch_user, fetch_metrics_id);
let (user, info) =
futures::join!(fetch_user, fetch_private_user_info);
cx.update(|cx| {
if let Some(info) = info {
@@ -199,9 +206,17 @@ impl UserStore {
client.telemetry.set_authenticated_user_info(
Some(info.metrics_id.clone()),
staff,
)
);
this.update(cx, |this, _| {
this.set_current_user_accepted_tos_at(
info.accepted_tos_at,
);
})
} else {
anyhow::Ok(())
}
})?;
})??;
current_user_tx.send(user).await.ok();
@@ -280,6 +295,18 @@ impl UserStore {
Ok(())
}
async fn handle_update_plan(
this: Model<Self>,
message: TypedEnvelope<proto::UpdateUserPlan>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
this.current_plan = Some(message.payload.plan());
cx.notify();
})?;
Ok(())
}
fn update_contacts(
&mut self,
message: UpdateContacts,
@@ -657,10 +684,47 @@ impl UserStore {
self.current_user.borrow().clone()
}
pub fn current_plan(&self) -> Option<proto::Plan> {
self.current_plan
}
pub fn watch_current_user(&self) -> watch::Receiver<Option<Arc<User>>> {
self.current_user.clone()
}
pub fn current_user_has_accepted_terms(&self) -> Option<bool> {
self.accepted_tos_at
.map(|accepted_tos_at| accepted_tos_at.is_some())
}
pub fn accept_terms_of_service(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
if self.current_user().is_none() {
return Task::ready(Err(anyhow!("no current user")));
};
let client = self.client.clone();
cx.spawn(move |this, mut cx| async move {
if let Some(client) = client.upgrade() {
let response = client
.request(proto::AcceptTermsOfService {})
.await
.context("error accepting tos")?;
this.update(&mut cx, |this, _| {
this.set_current_user_accepted_tos_at(Some(response.accepted_tos_at))
})
} else {
Err(anyhow!("client not found"))
}
})
}
fn set_current_user_accepted_tos_at(&mut self, accepted_tos_at: Option<u64>) {
self.accepted_tos_at = Some(
accepted_tos_at.and_then(|timestamp| DateTime::from_timestamp(timestamp as i64, 0)),
);
}
fn load_users(
&mut self,
request: impl RequestMessage<Response = UsersResponse>,

View File

@@ -15,6 +15,9 @@ BLOB_STORE_URL = "http://127.0.0.1:9000"
BLOB_STORE_REGION = "the-region"
ZED_CLIENT_CHECKSUM_SEED = "development-checksum-seed"
SEED_PATH = "crates/collab/seed.default.json"
LLM_DATABASE_URL = "postgres://postgres@localhost/zed_llm"
LLM_DATABASE_MAX_CONNECTIONS = 5
LLM_API_SECRET = "llm-secret"
# CLICKHOUSE_URL = ""
# CLICKHOUSE_USER = "default"

View File

@@ -20,6 +20,7 @@ test-support = ["sqlite"]
[dependencies]
anthropic.workspace = true
anyhow.workspace = true
async-stripe.workspace = true
async-tungstenite.workspace = true
aws-config = { version = "1.1.5" }
aws-sdk-s3 = { version = "1.15.0" }
@@ -36,6 +37,7 @@ futures.workspace = true
google_ai.workspace = true
hex.workspace = true
http_client.workspace = true
jsonwebtoken.workspace = true
live_kit_server.workspace = true
log.workspace = true
nanoid.workspace = true
@@ -56,10 +58,12 @@ serde_derive.workspace = true
serde_json.workspace = true
sha2.workspace = true
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "json", "time", "uuid", "any"] }
strum.workspace = true
subtle.workspace = true
rustc-demangle.workspace = true
telemetry_events.workspace = true
text.workspace = true
thiserror.workspace = true
time.workspace = true
tokio.workspace = true
toml.workspace = true
@@ -79,7 +83,6 @@ 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
@@ -88,6 +91,7 @@ fs = { workspace = true, features = ["test-support"] }
git = { workspace = true, features = ["test-support"] }
git_hosting_providers.workspace = true
gpui = { workspace = true, features = ["test-support"] }
hyper.workspace = true
indoc.workspace = true
language = { workspace = true, features = ["test-support"] }
language_model = { workspace = true, features = ["test-support"] }
@@ -116,3 +120,6 @@ util.workspace = true
workspace = { workspace = true, features = ["test-support"] }
worktree = { workspace = true, features = ["test-support"] }
headless.workspace = true
[package.metadata.cargo-machete]
ignored = ["async-stripe"]

View File

@@ -10,7 +10,7 @@ It contains our back-end logic for collaboration, to which we connect from the Z
Before you can run the collab server locally, you'll need to set up a zed Postgres database.
```
```sh
script/bootstrap
```
@@ -32,13 +32,13 @@ To use a different set of admin users, create `crates/collab/seed.json`.
In one terminal, run Zed's collaboration server and the livekit dev server:
```
```sh
foreman start
```
In a second terminal, run two or more instances of Zed.
```
```sh
script/zed-local -2
```
@@ -64,7 +64,7 @@ You can tell what is currently deployed with `./script/what-is-deployed`.
To create a new migration:
```
```sh
./script/create-migration <name>
```

View File

@@ -92,6 +92,18 @@ spec:
secretKeyRef:
name: api
key: token
- name: LLM_API_SECRET
valueFrom:
secretKeyRef:
name: llm-token
key: secret
- name: LLM_DATABASE_URL
valueFrom:
secretKeyRef:
name: llm-database
key: url
- name: LLM_DATABASE_MAX_CONNECTIONS
value: "${LLM_DATABASE_MAX_CONNECTIONS}"
- name: ZED_CLIENT_CHECKSUM_SEED
valueFrom:
secretKeyRef:
@@ -122,11 +134,26 @@ spec:
secretKeyRef:
name: anthropic
key: api_key
- name: ANTHROPIC_STAFF_API_KEY
valueFrom:
secretKeyRef:
name: anthropic
key: staff_api_key
- name: GOOGLE_AI_API_KEY
valueFrom:
secretKeyRef:
name: google-ai
key: api_key
- name: QWEN2_7B_API_KEY
valueFrom:
secretKeyRef:
name: hugging-face
key: api_key
- name: QWEN2_7B_API_URL
valueFrom:
secretKeyRef:
name: hugging-face
key: qwen2_api_url
- name: BLOB_STORE_ACCESS_KEY
valueFrom:
secretKeyRef:

View File

@@ -3,3 +3,4 @@ RUST_LOG=info
INVITE_LINK_PREFIX=https://zed.dev/invites/
AUTO_JOIN_CHANNEL_ID=283
DATABASE_MAX_CONNECTIONS=85
LLM_DATABASE_MAX_CONNECTIONS=25

View File

@@ -2,4 +2,5 @@ ZED_ENVIRONMENT=staging
RUST_LOG=info
INVITE_LINK_PREFIX=https://staging.zed.dev/invites/
DATABASE_MAX_CONNECTIONS=5
LLM_DATABASE_MAX_CONNECTIONS=5
AUTO_JOIN_CHANNEL_ID=8

View File

@@ -12,7 +12,7 @@ metadata:
spec:
type: LoadBalancer
selector:
app: postgrest
app: nginx
ports:
- name: web
protocol: TCP
@@ -24,17 +24,99 @@ apiVersion: apps/v1
kind: Deployment
metadata:
namespace: ${ZED_KUBE_NAMESPACE}
name: postgrest
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: postgrest
app: nginx
template:
metadata:
labels:
app: postgrest
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 8080
protocol: TCP
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
volumes:
- name: nginx-config
configMap:
name: nginx-config
---
apiVersion: v1
kind: ConfigMap
metadata:
namespace: ${ZED_KUBE_NAMESPACE}
name: nginx-config
data:
nginx.conf: |
events {}
http {
server {
listen 8080;
location /app/ {
proxy_pass http://postgrest-app:8080/;
}
location /llm/ {
proxy_pass http://postgrest-llm:8080/;
}
}
}
---
apiVersion: v1
kind: Service
metadata:
namespace: ${ZED_KUBE_NAMESPACE}
name: postgrest-app
spec:
selector:
app: postgrest-app
ports:
- protocol: TCP
port: 8080
targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
namespace: ${ZED_KUBE_NAMESPACE}
name: postgrest-llm
spec:
selector:
app: postgrest-llm
ports:
- protocol: TCP
port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: ${ZED_KUBE_NAMESPACE}
name: postgrest-app
spec:
replicas: 1
selector:
matchLabels:
app: postgrest-app
template:
metadata:
labels:
app: postgrest-app
spec:
containers:
- name: postgrest
@@ -55,3 +137,39 @@ spec:
secretKeyRef:
name: postgrest
key: jwt_secret
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: ${ZED_KUBE_NAMESPACE}
name: postgrest-llm
spec:
replicas: 1
selector:
matchLabels:
app: postgrest-llm
template:
metadata:
labels:
app: postgrest-llm
spec:
containers:
- name: postgrest
image: "postgrest/postgrest"
ports:
- containerPort: 8080
protocol: TCP
env:
- name: PGRST_SERVER_PORT
value: "8080"
- name: PGRST_DB_URI
valueFrom:
secretKeyRef:
name: llm-database
key: url
- name: PGRST_JWT_SECRET
valueFrom:
secretKeyRef:
name: postgrest
key: jwt_secret

View File

@@ -9,7 +9,9 @@ CREATE TABLE "users" (
"connected_once" BOOLEAN NOT NULL DEFAULT false,
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"metrics_id" TEXT,
"github_user_id" INTEGER
"github_user_id" INTEGER,
"accepted_tos_at" TIMESTAMP WITHOUT TIME ZONE,
"github_user_created_at" TIMESTAMP WITHOUT TIME ZONE
);
CREATE UNIQUE INDEX "index_users_github_login" ON "users" ("github_login");
CREATE UNIQUE INDEX "index_invite_code_users" ON "users" ("invite_code");
@@ -416,3 +418,34 @@ CREATE TABLE dev_server_projects (
dev_server_id INTEGER NOT NULL REFERENCES dev_servers(id),
paths TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS billing_customers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
user_id INTEGER NOT NULL REFERENCES users(id),
stripe_customer_id TEXT NOT NULL
);
CREATE UNIQUE INDEX "uix_billing_customers_on_user_id" ON billing_customers (user_id);
CREATE UNIQUE INDEX "uix_billing_customers_on_stripe_customer_id" ON billing_customers (stripe_customer_id);
CREATE TABLE IF NOT EXISTS billing_subscriptions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
billing_customer_id INTEGER NOT NULL REFERENCES billing_customers(id),
stripe_subscription_id TEXT NOT NULL,
stripe_subscription_status TEXT NOT NULL,
stripe_cancel_at TIMESTAMP
);
CREATE INDEX "ix_billing_subscriptions_on_billing_customer_id" ON billing_subscriptions (billing_customer_id);
CREATE UNIQUE INDEX "uix_billing_subscriptions_on_stripe_subscription_id" ON billing_subscriptions (stripe_subscription_id);
CREATE TABLE IF NOT EXISTS processed_stripe_events (
stripe_event_id TEXT PRIMARY KEY,
stripe_event_type TEXT NOT NULL,
stripe_event_created_timestamp INTEGER NOT NULL,
processed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX "ix_processed_stripe_events_on_stripe_event_created_timestamp" ON processed_stripe_events (stripe_event_created_timestamp);

View File

@@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS billing_subscriptions (
id SERIAL PRIMARY KEY,
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
stripe_customer_id TEXT NOT NULL,
stripe_subscription_id TEXT NOT NULL,
stripe_subscription_status TEXT NOT NULL
);
CREATE INDEX "ix_billing_subscriptions_on_user_id" ON billing_subscriptions (user_id);
CREATE INDEX "ix_billing_subscriptions_on_stripe_customer_id" ON billing_subscriptions (stripe_customer_id);
CREATE UNIQUE INDEX "uix_billing_subscriptions_on_stripe_subscription_id" ON billing_subscriptions (stripe_subscription_id);

View File

@@ -0,0 +1,18 @@
CREATE TABLE IF NOT EXISTS billing_customers (
id SERIAL PRIMARY KEY,
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
stripe_customer_id TEXT NOT NULL
);
CREATE UNIQUE INDEX "uix_billing_customers_on_user_id" ON billing_customers (user_id);
CREATE UNIQUE INDEX "uix_billing_customers_on_stripe_customer_id" ON billing_customers (stripe_customer_id);
-- Make `billing_subscriptions` reference `billing_customers` instead of having its
-- own `user_id` and `stripe_customer_id`.
DROP INDEX IF EXISTS "ix_billing_subscriptions_on_user_id";
DROP INDEX IF EXISTS "ix_billing_subscriptions_on_stripe_customer_id";
ALTER TABLE billing_subscriptions DROP COLUMN user_id;
ALTER TABLE billing_subscriptions DROP COLUMN stripe_customer_id;
ALTER TABLE billing_subscriptions ADD COLUMN billing_customer_id INTEGER NOT NULL REFERENCES billing_customers (id) ON DELETE CASCADE;
CREATE INDEX "ix_billing_subscriptions_on_billing_customer_id" ON billing_subscriptions (billing_customer_id);

View File

@@ -0,0 +1,2 @@
ALTER TABLE billing_customers ADD COLUMN last_stripe_event_id TEXT;
ALTER TABLE billing_subscriptions ADD COLUMN last_stripe_event_id TEXT;

View File

@@ -0,0 +1,11 @@
ALTER TABLE billing_customers DROP COLUMN last_stripe_event_id;
ALTER TABLE billing_subscriptions DROP COLUMN last_stripe_event_id;
CREATE TABLE IF NOT EXISTS processed_stripe_events (
stripe_event_id TEXT PRIMARY KEY,
stripe_event_type TEXT NOT NULL,
stripe_event_created_timestamp BIGINT NOT NULL,
processed_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
);
CREATE INDEX "ix_processed_stripe_events_on_stripe_event_created_timestamp" ON processed_stripe_events (stripe_event_created_timestamp);

View File

@@ -0,0 +1 @@
ALTER TABLE billing_subscriptions ADD COLUMN stripe_cancel_at TIMESTAMP WITHOUT TIME ZONE;

View File

@@ -0,0 +1 @@
ALTER TABLE users ADD accepted_tos_at TIMESTAMP WITHOUT TIME ZONE;

View File

@@ -0,0 +1 @@
ALTER TABLE "users" ADD COLUMN "github_user_created_at" TIMESTAMP WITHOUT TIME ZONE;

View File

@@ -0,0 +1,19 @@
create table if not exists providers (
id serial primary key,
name text not null
);
create unique index uix_providers_on_name on providers (name);
create table if not exists models (
id serial primary key,
provider_id integer not null references providers (id) on delete cascade,
name text not null,
max_requests_per_minute integer not null,
max_tokens_per_minute integer not null,
max_tokens_per_day integer not null
);
create unique index uix_models_on_provider_id_name on models (provider_id, name);
create index ix_models_on_provider_id on models (provider_id);
create index ix_models_on_name on models (name);

View File

@@ -0,0 +1,19 @@
create table usage_measures (
id serial primary key,
name text not null
);
create unique index uix_usage_measures_on_name on usage_measures (name);
create table if not exists usages (
id serial primary key,
user_id integer not null,
model_id integer not null references models (id) on delete cascade,
measure_id integer not null references usage_measures (id) on delete cascade,
timestamp timestamp without time zone not null,
buckets bigint[] not null
);
create index ix_usages_on_user_id on usages (user_id);
create index ix_usages_on_model_id on usages (model_id);
create unique index uix_usages_on_user_id_model_id_measure_id on usages (user_id, model_id, measure_id);

View File

@@ -0,0 +1,4 @@
ALTER TABLE models
ALTER COLUMN max_requests_per_minute TYPE bigint,
ALTER COLUMN max_tokens_per_minute TYPE bigint,
ALTER COLUMN max_tokens_per_day TYPE bigint;

View File

@@ -0,0 +1,3 @@
ALTER TABLE models
ADD COLUMN price_per_million_input_tokens integer NOT NULL DEFAULT 0,
ADD COLUMN price_per_million_output_tokens integer NOT NULL DEFAULT 0;

View File

@@ -0,0 +1 @@
alter table usages add column is_staff boolean not null default false;

View File

@@ -0,0 +1,9 @@
create table lifetime_usages (
id serial primary key,
user_id integer not null,
model_id integer not null references models (id) on delete cascade,
input_tokens bigint not null default 0,
output_tokens bigint not null default 0
);
create unique index uix_lifetime_usages_on_user_id_model_id on lifetime_usages (user_id, model_id);

View File

@@ -0,0 +1,7 @@
create table revoked_access_tokens (
id serial primary key,
jti text not null,
revoked_at timestamp without time zone not null default now()
);
create unique index uix_revoked_access_tokens_on_jti on revoked_access_tokens (jti);

View File

@@ -0,0 +1,4 @@
db-uri = "postgres://postgres@localhost/zed_llm"
server-port = 8082
jwt-secret = "the-postgrest-jwt-secret-for-authorization"
log-level = "info"

View File

@@ -1,86 +0,0 @@
use anyhow::{anyhow, Result};
use rpc::proto;
pub fn language_model_request_to_open_ai(
request: proto::CompleteWithLanguageModel,
) -> Result<open_ai::Request> {
Ok(open_ai::Request {
model: open_ai::Model::from_id(&request.model).unwrap_or(open_ai::Model::FourTurbo),
messages: request
.messages
.into_iter()
.map(|message: proto::LanguageModelRequestMessage| {
let role = proto::LanguageModelRole::from_i32(message.role)
.ok_or_else(|| anyhow!("invalid role {}", message.role))?;
let openai_message = match role {
proto::LanguageModelRole::LanguageModelUser => open_ai::RequestMessage::User {
content: message.content,
},
proto::LanguageModelRole::LanguageModelAssistant => {
open_ai::RequestMessage::Assistant {
content: Some(message.content),
tool_calls: Vec::new(),
}
}
proto::LanguageModelRole::LanguageModelSystem => {
open_ai::RequestMessage::System {
content: message.content,
}
}
};
Ok(openai_message)
})
.collect::<Result<Vec<open_ai::RequestMessage>>>()?,
stream: true,
stop: request.stop,
temperature: request.temperature,
tool_choice: None,
tools: Vec::new(),
})
}
pub fn language_model_request_to_google_ai(
request: proto::CompleteWithLanguageModel,
) -> Result<google_ai::GenerateContentRequest> {
Ok(google_ai::GenerateContentRequest {
contents: request
.messages
.into_iter()
.map(language_model_request_message_to_google_ai)
.collect::<Result<Vec<_>>>()?,
generation_config: None,
safety_settings: None,
})
}
pub fn language_model_request_message_to_google_ai(
message: proto::LanguageModelRequestMessage,
) -> Result<google_ai::Content> {
let role = proto::LanguageModelRole::from_i32(message.role)
.ok_or_else(|| anyhow!("invalid role {}", message.role))?;
Ok(google_ai::Content {
parts: vec![google_ai::Part::TextPart(google_ai::TextPart {
text: message.content,
})],
role: match role {
proto::LanguageModelRole::LanguageModelUser => google_ai::Role::User,
proto::LanguageModelRole::LanguageModelAssistant => google_ai::Role::Model,
proto::LanguageModelRole::LanguageModelSystem => google_ai::Role::User,
},
})
}
pub fn count_tokens_request_to_google_ai(
request: proto::CountTokensWithLanguageModel,
) -> Result<google_ai::CountTokensRequest> {
Ok(google_ai::CountTokensRequest {
contents: request
.messages
.into_iter()
.map(language_model_request_message_to_google_ai)
.collect::<Result<Vec<_>>>()?,
})
}

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