Compare commits

..

431 Commits

Author SHA1 Message Date
Peter Tripp
d664f56fac zed 0.161.2 2024-11-13 17:38:59 -05:00
Kyle Kelley
4f51496e09 Allow base64 encoded images to be decoded with or without padding (#20616)
The R kernel doesn't use base64 padding whereas the Python kernel (via
matplotlib) sometimes uses padding. We have to use the `base64` crate's
`Indifferent` mode.

/cherry-pick v0.161.x

Release Notes:

- N/A
2024-11-13 11:31:14 -08:00
Kyle Kelley
71921c7a6f Set up editor actions after workspace not on stack (#20445)
Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad@zed.dev>
2024-11-13 10:45:29 -08:00
Joseph T. Lyons
395a23618c v0.161.x stable 2024-11-13 11:47:38 -05:00
Kyle Kelley
fb6774cfff Fix issue with image output from Jupyter kernels that didn't use base64 padding (#20561)
This upgrades `nbformat` and `runtimelib` to handle jupyter types with
even more validation and flexiblity. This also processes any multiline
string data coming from the kernel, including with image data (like
`image/png`). While I was at it I also fixed a longstanding issue around
images by eliminating all whitespace (something `atob` does) and using
the no pad decoder.

Fixes: #17956

Before:

<img width="741" alt="image"
src="https://github.com/user-attachments/assets/37ec2cae-ce78-4475-aaa3-4d785e4015d0">

After:

<img width="727" alt="image"
src="https://github.com/user-attachments/assets/e2431ba2-048b-4205-9898-54f357795a9c">


Release Notes:

- Fixed issue with image output from REPL kernels that didn't use base64
padding
2024-11-13 11:18:15 -05:00
Joseph T. Lyons
06533d5a68 zed 0.161.1 2024-11-08 12:18:22 -05:00
dhaus67
cd7416bfb4 Update Copilot Chat max_tokens soft limits (#20363)
Closes #20362 

Co-authored-by: Peter Tripp <peter@zed.dev>
2024-11-08 12:13:44 -05:00
Kyle Kelley
e5fc5ea366 Disable repl in non-local projects (#20397)
Release Notes:

- Disable REPL buttons and actions for remote projects and collaboration
(only the host should have access).
2024-11-08 12:13:39 -05:00
Joseph T. Lyons
ca006fbeea Add edit events for assistant panel and inline assist (#20418)
Release Notes:

- N/A
2024-11-08 11:25:58 -05:00
Michael Sloan
c0099afc2e Revert "Use correct context path for focused element in WindowContext::bindings_for_action (#18843)" (#20367)
@JosephTLyons found that this broke display of keybindings in the recent
projects modal.

Release Notes:

- N/A
2024-11-08 17:25:55 +02:00
Thorsten Ball
65532a5f10 project panel: Fix preview tabs not working when enabled (#20416)
PR #20154 introduced a regression and essentially disabled preview tabs
in code.

This fixes it and restores the old preview tabs behavior.

Release Notes:

- Fixed preview tabs being disabled in code, even if they were enabled
in the settings.

Co-authored-by: Piotr <piotr@zed.dev>
2024-11-08 09:48:12 -05:00
Kirill Bulatov
bce4938350 Improve outline panel keyboard navigation (#20385)
Closes https://github.com/zed-industries/zed/issues/20187

Make outline panel more eager to open its entries:

* scroll editor to selected outline entries (before it required an extra
`"outline_panel::Open", { "change_selection": false }` action call)
* make any `Open` action call to behave like `"outline_panel::Open", {
"change_selection": true }` and remove the redundant parameter.
Now opening an entry is equal to double clicking the same entry: the
editor gets scrolled and its selection changes
* add a way to open entries the same way as excerpts are open in multi
buffers (will open the entire file, scroll and place the caret)

* additionally, fix another race issue that caused wrong entry to be
revealed after the selection change

Release Notes:

- Improved outline panel keyboard navigation
2024-11-08 16:12:10 +02:00
Conrad Irwin
1b1a8bad51 Don't introduce more failure 2024-11-06 12:25:58 -07:00
Conrad Irwin
544b1e94c1 fix typo 2024-11-06 11:34:15 -07:00
Conrad Irwin
25901eace8 de-dbg 2024-11-06 11:17:41 -07:00
Conrad Irwin
8db3ea9eda Fix extension tests on release branches
Co-Authored-By: Max <max@zed.dev>
2024-11-06 11:08:45 -07:00
Peter Tripp
15a0b94222 v0.161.x preview 2024-11-06 11:09:02 -05:00
Peter Tripp
815385cc5f Add autoclosing braces for Shell Scripts (#20278)
Added support for autoclosing braces `{}` and single quotes `''` in Shell Scripts
2024-11-06 11:05:59 -05:00
Lars Diederich
eca3424cb5 Update openSUSE dependencies to build on fresh Tumbleweed installation (#20298) 2024-11-06 11:05:13 -05:00
Danilo Leal
71b3633c1b assistant: Remove automatic diagnostic attachment to tab and file commands (#20297)
This PR returns the `/tab` and `/file` commands to their original
behavior of _not_ automatically including diagnostics. This is an
assistant-only change, though, given that we can already pass the
`/diagnostic` command by itself. The inline assistant will still have
the diagnostics baked in to allow prompts such as "Fix this error."

Release Notes:

- Remove automatic diagnostic attachment to tab and file commands in the
assistant panel

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
2024-11-06 11:36:34 -03:00
Antonio Scandurra
21f778c6db Reduce memory footprint for inline transformations (#20296)
Closes https://github.com/zed-industries/zed/issues/18062

This pull request prevents the `scores` matrix for the streaming diff
from growing quadratically.

Previously, we would store rows and columns respectively for all
characters in the old and new text. However, every time we receive a
chunk, we will always advance the position in the matrix to the very
latest character in the new text. This means we can avoid storing scores
for the new characters that were already reported.

Randomized tests still pass and I also made sure that the diffs we
produce are identical.

Release Notes:

- Improved memory footprint for inline transformations
([#18062](https://github.com/zed-industries/zed/issues/18062))
2024-11-06 15:07:16 +01:00
Roshan Padaki
1eb6fb0b5d go: Run ./... tasks against current module (#20190)
In #17108, we updated `go test ./...` to run against the package
directory, to fix cases in which the top-level project is not the go
module root. However, this leads to the confusing behavior of `go test
./...` only running tests in subdirectories of the current package.
Here, we change the behavior to instead walk up the dirtree to find the
closest `go.mod`, and run the `./...` tasks relative to that directory.
This might lead to more predictable behavior for these tasks.

Also see:
https://github.com/zed-industries/zed/pull/19987#issuecomment-2450159099

Release Notes:

- Improved go test and generate `./...` commands to run against the
current go module directory rather than the current package directory
2024-11-06 14:55:57 +01:00
Ilya Sorochan
484e5df2ee Add suggestion for CMake files (#20292)
Release Notes:

- Added NeoCMake extension suggestion for cmake files (`CMakeLists.txt`
and `.cmake`)
2024-11-06 14:51:26 +01:00
Piotr Osiewicz
fef7df667c pane: Update pinned tab counts when unnamed buffer is discarded (#20294)
Closes #19492

Release Notes:

- Fixed a crash that could happen when closing a workspace with pinned
untitled buffers.
2024-11-06 13:58:51 +01:00
Kirill Bulatov
1c84fd1fef Use Zed handler for undo and redo macOS actions (#20293)
Those seem to require a corresponding NSTextView/NSTextField with
explicitly enabled `allowsUndo` property. But Zed does not use any of
these *Text* elements, so there's nothing to allow undo on. Hence, use
the Zed handler, making both actions always enabled instead of being
always disabled.

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

Release Notes:

- Fixed undo and redo macOS menu items being always disabled
([#12335](https://github.com/zed-industries/zed/issues/12335))
2024-11-06 14:34:46 +02:00
Thorsten Ball
b6adab84a0 rope: Index tab locations for each chunk (#20289)
This is a follow-up to #19913 and adds another "index" to the `Chunk`,
this time indexing the location of tabs.

Release Notes:

- N/A

---------

Co-authored-by: Antonio <antonio@zed.dev>
2024-11-06 13:18:30 +01:00
Danilo Leal
d3a49f6d8f assistant: Rename "new context" to "new chat" (#20043)
This PR is only updating UI strings and pieces of the documentation—it
doesn't touch the actual code, where it's still using things such as
`NewContext` and similar terminology for variables, actions, etc.

Release Notes:

- N/A
2024-11-06 07:24:04 -03:00
Patrick Decat
c2cf4c45c0 Fix path to install-mold script in linux script (#20286)
Release Notes:

- N/A
2024-11-06 12:18:31 +02:00
Thorsten Ball
bd03dea296 git: Add support for opening git worktrees (#20164)
This adds support for [git
worktrees](https://matklad.github.io/2024/07/25/git-worktrees.html). It
fixes the errors that show up (git blame not working) and actually adds
support for detecting git changes in a `.git` folder that's outside of
our path (and not even in the ancestor chain of our root path).

(While working on this we discovered that our `.gitignore` handling is
not 100% correct. For example: we do stop processing `.gitignore` files
once we found a `.git` repository and don't go further up the ancestors,
which is correct, but then we also don't take into account the
`excludesFile` that a user might have configured, see:
https://git-scm.com/docs/gitignore)


Closes https://github.com/zed-industries/zed/issues/19842
Closes https://github.com/zed-industries/zed/issues/4670

Release Notes:

- Added support for git worktrees. Zed can now open git worktrees and
the git status in them is correctly handled.

---------

Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Bennet <bennet@zed.dev>
2024-11-06 09:43:39 +01:00
Danilo Leal
3f777f0c68 Fix project panel losing focus after file creation attempt (#20273)
Closes https://github.com/zed-industries/zed/issues/19771

### Before

When pressing <kbd>esc</kbd> after attempting to create a file, the
focus is lost and you don't know where it went.


https://github.com/user-attachments/assets/2ccd82b7-b7d2-49e4-b1c7-1867331ab9dc

### After

Now, after pressing <kbd>esc</kbd>, the focus returns to where it was
before trying to create a new file.


https://github.com/user-attachments/assets/a8eb1cf1-dfef-42eb-9f3d-2ab6200056c4

Release Notes:

- Fix project panel losing focus after file creation attempt
([#19771](https://github.com/zed-industries/zed/issues/19771))

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2024-11-06 09:59:06 +02:00
Conrad Irwin
846aec701f Remoting: Fix opening multiple folders on one server (#20281)
Release Notes:

- Remoting: Fix opening multiple folders on one server
2024-11-05 22:16:38 -07:00
Conrad Irwin
cfce6a8fbf Potentially fix invocation of draft-release-notes (#20265)
Release Notes:

- N/A
2024-11-05 21:46:05 -07:00
Conrad Irwin
803e5d4c9f vim: Fix readonly mode (#20264)
Closes #18854

Release Notes:

- vim: Fixed `r`, `~` etc. appearing to modify readonly buffers
2024-11-05 21:45:45 -07:00
AidanV
c10c35ffda Fix duplicate SshProject's in Remote Projects menu (#20271)
Closes #20269

Release Notes:

- Changes SshConnection to use a BTreeSet of SshProject's instead of a
Vec of SshProject's in order to remove duplicate remote projects from
"settings.json" and the Remote Projects menu.
2024-11-05 21:31:57 -07:00
Conrad Irwin
38b1940251 Re-entrant SendKeystrokes (#20277)
Release Notes:

- Improved `workspace::SendKeystrokes` to support re-binding keys. For
example you can now do: `"x": ["workspace::SendKeystrokes", "\" _ x"]`
in vim mode to ensure that `x` does not clobber your clipboard.
- Improved key binding documentation
2024-11-05 21:18:16 -07:00
Max Brunsfeld
50069a2153 Unbind app menu actions (#20268)
Closes #7544

Release Notes:

- Fixed an issue that prevented removing key bindings for actions used
in the macOS application menu.
2024-11-05 16:18:41 -08:00
Kirill Bulatov
b23835b5a5 Disable sccache during dev extension builds (#20270) 2024-11-06 02:01:56 +02:00
Nathan Sobo
e47b305ca7 Use correct context path for focused element in WindowContext::bindings_for_action (#18843)
Previously, we were reaching in and using the context_stack on the dispatch tree, which was incorrect.

/cc @as-cii 
/cc @ConradIrwin

Release Notes:

- N/A

---------

Co-authored-by: Michael Sloan <michael@zed.dev>
2024-11-05 16:42:50 -07:00
Will Bradley
7931342455 Fix typo in Ansible docs (#20267)
fix typo in Ansible docs.

Release Notes:

- N/A
2024-11-05 16:31:39 -07:00
Kirill Bulatov
282f6241b7 Fix outline panel selection races (#20263)
Follow-up of https://github.com/zed-industries/zed/pull/20211

Release Notes:

- N/A
2024-11-06 00:54:15 +02:00
Conrad Irwin
73bbdd4456 Allow vim macros in visual mode (#20261)
Closes #19764

Release Notes:

- vim: Fixed `q` and `@` in visual mode
2024-11-05 15:52:02 -07:00
Conrad Irwin
236498408f Use zed-style shifted letters (#20254)
Release Notes:

- vim: Fixed some shortcuts to render correctly in Command
2024-11-05 15:51:52 -07:00
Conrad Irwin
86ff6e2c8e vim: Fix paragraphs with softwrap (#20259)
Closes #19778

Release Notes:

- vim: Fixed paragraph object in the presence of softwrap
2024-11-05 15:32:44 -07:00
Conrad Irwin
4bf6fb217e vim: Fix search in the Assistant (#20258)
Closes #17704

Release Notes:

- vim: Fix search in the assistant panel
2024-11-05 15:08:01 -07:00
Mikayla Maki
c527f2e212 Prevent extra line break on long token at start of rewrap (#20256)
Closes #19532

Release Notes:

- Fixed a bug where rewrapping with a long word at the start of the line
would cause a new line to be inserted.

Co-authored-by: Will Bradley <will@zed.dev>
2024-11-05 14:58:07 -07:00
Peter Tripp
47defa2849 docs: Add documentation for configuring clangd in C-only mode (#20255) 2024-11-05 16:43:31 -05:00
Will Bradley
6dfff1b46d Improve rewrap for ideographic writing systems (#20218)
Closes #19733

Before:

https://github.com/user-attachments/assets/5399e8fd-2687-445a-a8ab-023c348aff3f

After:

https://github.com/user-attachments/assets/b4ea5cb6-92ec-49ae-a982-194a1fc68d88

Release Notes:

- improve handling of text wrapping in Rewrap for some ideographic
writing systems

Co-authored-by: Richard <richard@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>
2024-11-05 14:22:21 -07:00
Conrad Irwin
66e06616db Don't write temp files for telemetry logs (#20209)
This still keeps a telemetry.log for the current session, but not one
file per load of zed.

Closes: #20045

Release Notes:

- Fixed a bug where Zed would create a new temporary file on each boot
for telemetry logs
2024-11-05 14:05:51 -07:00
Conrad Irwin
765626a007 Disable /search by default (#20252)
This stops us sending GetCachedEmbeddings requests which frequently time
out
after 10s, and block the collab connection.

Release Notes:

- N/A
2024-11-05 14:05:10 -07:00
Kirill Bulatov
27cdc6cb93 Reuse focused buffer search query in the project search (#20253)
Closes https://github.com/zed-industries/zed/issues/10011

Release Notes:

- Reuse focused buffer search query in the project search
([#10011](https://github.com/zed-industries/zed/issues/10011))
2024-11-05 22:57:08 +02:00
Conrad Irwin
87ba5fd7bc Rebuild SSH installation (#20220)
Closes #ISSUE

This refactors SSH installation to require less shell stuff. We'd like
to
support arbitrary remote hosts, and unfortunately csh/tcsh have quoting
rules
that make it impossible to run multi-line scripts.

The primary changes are:
* The target path now contains the version:
`./zed_server/zed-remote-server-{release_channel}-{version}`
* We do all our processing in a temporary file and `mv` it into place.
* We do fewer calls to `ssh_command` overall. With the previous two
changes we can avoid lock files, and fuser calls. Instead cleanup of old
binaries now happens in `execute_run`.
* We only try to install the remote server when the connection is
established, not on each project open.

This should also put us in a good position if we want to pre-emptively
install new versions when the auto-updater detects an update for the
running version of zed (but that's not wired up yet)

Release Notes:

- Remoting: Fixed remoting when the remote runs `tcsh`
- Remoting: Improved latency of connecting
2024-11-05 13:37:54 -07:00
Kartik Vashistha
7c72929f0b docs: Add Ansible language documentation (#20087)
Co-authored-by: Peter Tripp <peter@zed.dev>
2024-11-05 13:57:15 -05:00
Danilo Leal
9e49894ed6 assistant: Remove the selection action as an extra option (#20234)
This PR should only be merged after
https://github.com/zed-industries/zed/pull/19988. Once the selection
action is added as a "proper" slash command, there's no need to have it
duplicate on the "Add Context" menu anymore. 😄

Release Notes:

- N/A
2024-11-05 15:00:43 -03:00
Tristan Marechaux
2364804f17 assistant: Implement /selection slash command (#19988)
- Closes #18868

## Summary
This PR introduces a new slash command `/selection` to enhance the
usability of the assistant's quote selection feature.

## Changes Made
1. Extracted a function from the `assistant: quote selection` action to
find the selected text and format it as an assistant section.
2. Created a new slash command `/selection` that utilizes the extracted
function to achieve the same effect as the existing `assistant: quote
selection` action.
3. Updated the documentation to include information about the new
`/selection` slash command.


Release Notes:

- Moved the text selection action to a slash command (`/selection`) in
the assistant panel

---------

Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
2024-11-05 18:42:10 +01:00
Peter Tripp
db11a3b554 ci: Update issue templates. Rename defect to bug and improve log formatting (#20246) 2024-11-05 12:07:17 -05:00
Danilo Leal
d0ca49f0e1 Make project search inputs full width (#20242)
Closes https://github.com/zed-industries/zed/issues/13099

This PR main thing is making the inputs in the project search full
width, but it also has some slight design and UI code improvements here
and there, such as extracting the common input styles to its own
variable.

I figure that the reason why the inputs weren't full width before is
just because it'd be hard to reach for the buttons when in a large
monitor with the app maximised _and_ with a single tab open. However, I
do feel like it's common not to have these conditions in place, too,
which make the small inputs too small, like the issue states. At the
very least, we also have the keybindings.

Here's the final result:

| Small window size | Big window size |
|--------|--------|
| <img width="1279" alt="Screenshot 2024-11-05 at 11 18 08"
src="https://github.com/user-attachments/assets/73548300-1ad2-4ed0-b99f-adb3212ac163">
| <img width="2992" alt="Screenshot 2024-11-05 at 11 24 06"
src="https://github.com/user-attachments/assets/3a1ccabd-2350-42f0-8e31-112f27da98a4">
|

Release Notes:

- N/A
2024-11-05 13:04:42 -03:00
Vladimir Varankin
d6fcd9853a keymaps: Fix how single line editor handles new lines in BufferSearch (#19316)
Updates JetBrains keymap to handle the `shift-enter` as the `SelectPrevMatch` action.
2024-11-05 11:00:26 -05:00
Alex Viscreanu
bc3550d991 workspace: Add settings to dim inactive panes and highlight active pane (#18968)
Closes #12529
Closes #8639

Release Notes:

- Added option to dim inactive panes
([#12529](https://github.com/zed-industries/zed/issues/12529))
- Added option to highlight active pane with a border
([#8639](https://github.com/zed-industries/zed/issues/8639))

BREAKING: `active_pane_magnification` value is no longer used, it should
be migrated to `active_pane_modifiers.magnification`


![panes](https://github.com/user-attachments/assets/b19959bc-4c06-4320-be36-412113143af5)

> note: don't know much rust, so I wouldn't be surprised if stuff can be
done much better, happy to update things after the review.
Also, wasn't sure about introducing the new object in the settings, but
it felt better than adding two more keys to the root, let me know what
you think and if there's a better way to do this. Also happy to get
feedback on the text itself, as I didn't spend much thinking how to
document this.
2024-11-05 17:26:07 +02:00
1dNDN
b8501199c2 Fix name of vcpp libs in windows build guide (#20240)
It is not obvious that you need to choose the latest one from the same
libraries, especially in non-English locales

Release Notes:

- N/A
2024-11-05 16:00:22 +02:00
Thorsten Ball
7fb9549098 vim: Fix d shift-g not deleting until EOD if soft-wrap is on (#20160)
This previously didn't work: `d G` would delete to the end of the "first
of the soft-wrapped lines" of the last line.

To fix it, we special case the delete behavior for `shift-g`, which is
what Neovim also seems to do.


Release Notes:

- Fixed `d G` in Vim mode not deleting until the actual end of the
document if soft-wrap is turned on.
2024-11-05 14:47:14 +01:00
Piotr Osiewicz
4097118070 ui: Fix scrollbar content size calculation for non-uniform lists with single element (#20237)
Previously we were always adding the origin coordinate of last item to
the content size, which is incorrect when the list has just one item; in
that case, we should just use the size of that item as the content size
of a list.

Closes #ISSUE

Release Notes:

- N/A
2024-11-05 14:32:13 +01:00
Stanislav Alekseev
a26c0a8537 Fix toolchain detection for worktree-local paths (#20229)
Reimplements `pet::EnvironmentApi`, trying to access the `project_env`
first
Closes #20177 

Release Notes:

- Fixed python toolchain detection when worktree local path is set
2024-11-05 14:25:18 +01:00
Stephan Aßmus
f8bd6c66f4 Fix cursor shape flickering and dead-zone on 1px border around list items in project and outline panels (#20202)
Move click listener to outer div
- Avoids dead area when clicking the 1px border around a list item
- Avoids flickering cursor shape when moving the cursor above the list,
and especially when scrolling the list with a stationary cursor.

Closes #15614

Release Notes:

- Fixed mouse cursor shape flickering in project and outline panels when
crossing items
([#15614](https://github.com/zed-industries/zed/issues/15614))

---------

Co-authored-by: Stephan Aßmus <stephan.assmus@sap.com>
2024-11-05 14:26:20 +02:00
Danilo Leal
02b1e3a3c1 assistant: Adjust the toolbar design (#20101)
This PR's most relevant change is removing the three-dot menu dropdown
from the assistant toolbar. The "Regenerate Title" button is now only
visible on hover and it appears on the far right of the title input.

<img width="700" alt="Screenshot 2024-11-04 at 13 31 37"
src="https://github.com/user-attachments/assets/891703af-7985-4b16-bb5e-d852491abd6f">

Release Notes:

- N/A
2024-11-05 09:07:26 -03:00
Alvaro Parker
83ad28dfe7 Remove duplicate load_system_fontscall (#19374)
Related comment on issue
https://github.com/zed-industries/zed/issues/14222#issuecomment-2418375056

On `crates/gpui/src/platform/linux/text_system.rs` on method
`CosmicTextSystem::new` `load_system_fonts` is being called twice:
```rust
    pub(crate) fn new() -> Self {
        let mut font_system = FontSystem::new();

        // todo(linux) make font loading non-blocking
        font_system.db_mut().load_system_fonts();

        Self(RwLock::new(CosmicTextSystemState {
            font_system,
            swash_cache: SwashCache::new(),
            scratch: ShapeBuffer::default(),
            loaded_fonts_store: Vec::new(),
            font_ids_by_family_cache: HashMap::default(),
            postscript_names: HashMap::default(),
        }))
    }
```

First one on `FontSystem::new()` and second one is explicit on
`font_system.db_mut().load_system_fonts()`. The first call
`FontSystem::new()` is defined as:
```
    pub fn new() -> Self {
        Self::new_with_fonts(core::iter::empty())
    }
```
And `new_with_fonts`: 
```rust
    /// Create a new [`FontSystem`] with a pre-specified set of fonts.
    pub fn new_with_fonts(fonts: impl IntoIterator<Item = fontdb::Source>) -> Self {
        let locale = Self::get_locale();
        log::debug!("Locale: {}", locale);

        let mut db = fontdb::Database::new();

        //TODO: configurable default fonts
        db.set_monospace_family("Fira Mono");
        db.set_sans_serif_family("Fira Sans");
        db.set_serif_family("DejaVu Serif");

        Self::load_fonts(&mut db, fonts.into_iter());

        Self::new_with_locale_and_db(locale, db)
    }
```
Finally `Self::load_fonts(&mut db, fonts.into_iter())` calls
`load_system_fonts`:
```rust
    #[cfg(feature = "std")]
    fn load_fonts(db: &mut fontdb::Database, fonts: impl Iterator<Item = fontdb::Source>) {
        #[cfg(not(target_arch = "wasm32"))]
        let now = std::time::Instant::now();

        db.load_system_fonts();

        for source in fonts {
            db.load_font_source(source);
        }
        ...
```

Release Notes:

- Remove duplicate font loading on Linux
2024-11-05 12:43:22 +02:00
Conrad Irwin
17b9d1976f Fix buffer restoration on ssh projects (#20215)
Closes #20143

Release Notes:

- Remoting: Fixed a panic restoring unsaved untitled buffers over SSH
2024-11-04 23:05:52 -07:00
Kirill Bulatov
3856599853 Improve project search performance (#20211)
Follow-up of https://github.com/zed-industries/zed/pull/20171

Reduces time Zed needs to reach maximum search results by an order of a
magnitude.

Methodology: 
* plugged-in mac with Instruments and Zed open
* Zed is restarted before each measurement, `zed` project is opened, a
*.rs file is opened and rust-analyzer is fully loaded, file is closed
then
* from an "empty" state, a `test` word is searched in the project search
* each version is checked with project panel; and then, separately,
without it
* after we reach maximum test results (the counter stops at `10191+`),
the measurement stops

Zed Dev is compiled and installed with `./script/bundle-mac -li`

------------------------


[measurements.trace.zip](https://github.com/user-attachments/files/17625516/measurements.trace.zip)

Before:

* Zed Nightly with outline panel open

<img width="1113" alt="image"
src="https://github.com/user-attachments/assets/62b29a69-c266-4d46-8c3c-0e9534ca7967">

Took over 30s to load the result set

* Zed Nightly without outline panel

<img width="1109" alt="image"
src="https://github.com/user-attachments/assets/82d8d9d6-e8f2-4e67-af55-3f54a7c1d92d">

Took over 24s to load the result set

* Zed Dev with outline panel open

<img width="1131" alt="image"
src="https://github.com/user-attachments/assets/15605ff8-0787-428e-bbb6-f8496f7e1d43">

Took around 6s to load the result set (the profile was running a bit
longer)

* Zed Dev without outline panel

<img width="1147" alt="image"
src="https://github.com/user-attachments/assets/0715d73e-f41a-4d74-a604-a3a96ad8d585">

Took around 5s to load the result set

---------------------

Improvements in the outline panel:

* https://github.com/zed-industries/zed/pull/20171 ensured we reuse
previous rendered search results from the outline panel
* all search results are now rendered in the background thread
* only the entries that are rendered with gpui are sent to the
background thread for rendering
* FS entries' update logic does nothing before the debounce now

Improvements in the editor:

* cursor update operations are debounced and all calculations start
after the debounce only
* linked edits are now debounced and all work is done after the debounce
only

Further possible improvements:

* we could batch calculations of text coordinates, related to the search
entries: right now, each search match range is expanded around and
clipped, then fitted to the closest surrounding whitespace (if any,
otherwise it's just trimmed).
Each such calculation requires multiple tree traversals, which is
suboptimal and causes more CPU usage than we could use.

* linked edits are always calculated, even if the language settings have
it disabled, or the corresponding language having no corresponding
capabilities

Release Notes:

- Improve large project search performance
2024-11-05 03:49:37 +02:00
Ganesh Gupta
81dd4ca1c9 Fix a typo (#20210)
Release Notes:

- N/A
2024-11-05 02:02:50 +02:00
Piotr Osiewicz
2965119201 project panel: scroll when drag-hovering over the edge of a list (#20207)
Closes [19554](https://github.com/zed-industries/zed/issues/19554)

Release Notes:

- Added auto-scrolling to project panel when a vertical edge of a panel
is hovered with a dragged entry.
2024-11-05 00:56:58 +01:00
Max Brunsfeld
258cf6c746 Add inclusive range scope overrides. Don't auto-close quotes at the ends of line comments (#20206)
Closes #9195
Closes #19787

Release Notes:

- Fixed an issue where single quotation marks were spuriously
auto-closed when typing in line comments
2024-11-04 15:36:39 -08:00
Richard Feldman
369de400be Make rewrapping take tabs more into account (#20196)
Closes #18686


https://github.com/user-attachments/assets/e87b4508-3570-4395-92b4-c5e0e9e19623


Release Notes:

- The Rewrap command now considers the width of each tab character at
the beginning of the line to be the configured tab size.

---------

Co-authored-by: Will <will@zed.dev>
2024-11-04 18:10:40 -05:00
Piotr Osiewicz
dc02894db4 editor: Add scrollbar to info popovers (#20184)
Related to https://github.com/zed-industries/zed/issues/5034

Release Notes:

- Added scrollbar to info popovers in editor.
2024-11-05 00:08:38 +01:00
Patrick Sy
966b18e142 assistant: Fix Gemini 1.5 Pro throwing "missing field 'index' at line N column M" (#20200)
Closes https://github.com/zed-industries/zed/issues/20033

- Fixed deserialization error of `GenerateContentCandidate` where `index` is unexpectedly nil
2024-11-04 17:01:08 -05:00
Elias Müller
2c7e71028d Fix selection extend/shrink on JetBrains keymap (#20199)
JetBrains IDE's use `ctrl-w` and `ctrl-shift-w` on Win/Linux and
`cmd-up` and `cmd-down` on mac to extend/shrink selections.

https://www.jetbrains.com/guide/java/tips/extend-selection/

Release Notes:

- Fixed extend/shrink selection on JetBrains keymap
2024-11-04 16:35:52 -05:00
Kirill Bulatov
24dba07a9b Do not alter soft wrap based on .editorconfig contents (#20198)
Closes https://github.com/zed-industries/zed/issues/20194

Release Notes:

- Fixed Zed incorrectly changing soft wrap based on .editorconfig
contents ([#20194](https://github.com/zed-industries/zed/issues/20194))
2024-11-04 23:31:40 +02:00
Marshall Bowers
16e9b4ceeb typescript: Improve installation checks for vtsls (#20197)
This PR improves the installation checks for `vtsls`.

Previously we were checking the installed version of TypeScript against
the latest available version to determine whether we needed to installed
the `vtsls` language server or not.

However, these are two independent concerns, so we should be checking
individually whether `typescript` or `@vtsls/language-server` need to be
installed/updated.

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

Release Notes:

- typescript: Fixed an issue where `@vtsls/language-server` may not have
been updated to the latest version.
2024-11-04 16:00:51 -05:00
Richard Feldman
bc4bd2e168 Don't conservatively include Suggest Edits token in token count (#20180)
Before: (note the 1.3k in the upper right corner instead of 3 in the
second screenshot)

<img width="459" alt="Screenshot 2024-11-04 at 11 37 58 AM"
src="https://github.com/user-attachments/assets/64c06aff-f7d2-42a4-a767-0d7a4ba0f486">

Now:
<img width="631" alt="Screenshot 2024-11-04 at 11 38 11 AM"
src="https://github.com/user-attachments/assets/22af974d-915a-41e1-9ee0-f0622901e242">

This was intended to be a conservative estimate in case you pressed
Suggest Edits (and therefore might have an unpleasant surprise if you
were under the context limit but Suggest Edits failed with a "too much
context" error message anyway, after the Suggest Edits context got added
for you behind the scenes).

However, in retrospect this design created more [confusion in the common
case](https://github.com/zed-industries/zed/pull/19900#issuecomment-2453456569)
because it made it look like more context had been actually consumed
than what was actually consumed.

This does raise a potential design question for the future: the Suggest
Edits button adds more context at the last minute without ever
communicating that it's going to do that.

In the meantime it seems best to go back to the less-confusing way of
reporting the token counts, especially since only users of the
experimental flag could possibly press Suggest Edits anyway.

Release Notes:

- Fixed issue where initial token count was over-reported as 1.3k
instead of 3 (for the context string "You").
2024-11-04 15:40:10 -05:00
Max Brunsfeld
4d3a18cbdc Fix two auto-indent issues with Markdown and YAML (#20193)
Closes #13376
Closes #13338

Release Notes:

- Fixed unhelpful auto-indent suggestions in markdown.
- Added `auto_indent_on_paste` setting, which can be used on a
per-language basis, to configure whether indentation should be adjusted
when pasting. This setting is enabled by default for languages other
than YAML and Markdown.
2024-11-04 12:29:38 -08:00
Conrad Irwin
cfcbfc1d82 Fix saving files as *.sql on macOS Sequoia (#20195)
Closes #16969

Release Notes:

- Fixed a bug in macOS Sequoia where you can't save a new file as
`*.sql`, it would rename to `.sql.s`. As a side effect you can no longer
save a new file as `*sql.s`. We hope to remove this workaround when the
operating system fixes its bug; in the meantime you can either set
`"use_system_path_prompts": false` in your settings file to skip the
macOS dialogues, or create new files by right clicking in the project
panel.
2024-11-04 13:23:59 -07:00
Avinash Thakur
c9ec235b12 Sort completions by relevance for strong matches (#20145)
Further enhancement:
On exploring VSCode's sorting logic, there are two major distinctions:
* A config option exists to adjust sort priority of snippets. They can
be placed inline (default), top or at bottom of completitions.
* The sorting order sorts by (in order): sort_text (lower case),
sort_text, kind

ref:
6f2d4781e8/src/vs/editor/contrib/suggest/browser/suggest.ts (L338-L383)

Closes #19786

Release Notes:

- Improved sort order in completions to show relevant matches first
([#19786](https://github.com/zed-industries/zed/issues/19786))
2024-11-04 22:23:36 +02:00
Marshall Bowers
8196db6022 settings: Include null in the type for optional settings (#20192)
This PR updates all settings that are defined as `Option`s to include
`null` in their type.

This prevents warnings from being displayed when `null` is used a
default value.

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

Release Notes:

- Updated the settings schema to allow `null` as a value for optional
settings instead of showing a warning.
2024-11-04 14:25:44 -05:00
Piotr Osiewicz
dc5fad52a3 diagnostics: Improve performance with large # of diagnostics (#20189)
Related to: https://github.com/zed-industries/zed/issues/19022

Release Notes:

- Improve editor performance with large # of diagnostics.

---------

Co-authored-by: Conrad <conrad@zed.dev>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-11-04 12:16:02 -07:00
Marshall Bowers
77de20c23a settings: Allow null as a value for font fallback fields (#20186)
This PR updates the `buffer_font_fallbacks` and `ui_font_fallbacks`
settings to allow `null` as a value instead of showing a warning.

Related to https://github.com/zed-industries/zed/issues/18006.

Release Notes:

- Updated the settings schema to allow `null` as a value for
`buffer_font_fallbacks` and `ui_font_fallbacks` instead of showing a
warning.
2024-11-04 12:58:10 -05:00
Nate Butler
e1cb8a66f0 Add pages to theme_preview (#20185)
Added some simple logic + an example of adding pages to the theme
preview. Will be used for organizing theme preview sections.

Release Notes:

- N/A
2024-11-04 12:57:36 -05:00
Marshall Bowers
7025d3f29d Extract outline rendering to outline crate (#20179)
This PR extracts the `render_item` implementation for outlines to the
`outline` crate to help reduce `language`'s dependence on `theme`.

Release Notes:

- N/A
2024-11-04 11:54:37 -05:00
Bennet Bo Fenner
4bbde40267 recent projects: Fix inconsistent keybinding with which window is replaced or not (#20176)
This removes the inconsistency between these two workflows:
- Clicking on the project name in the upper left shows:
<img width="333" alt="image"
src="https://github.com/user-attachments/assets/f54c34b4-67ab-4cbc-85ee-845c41aa4a8c">

- Using `projects: Open recent` shows:
<img width="337" alt="image"
src="https://github.com/user-attachments/assets/b1eb244f-ce28-4e6c-8404-b6cd88caef1e">

---

We now use `enter` to re-use the window and `cmd-enter` to open a new
window in both cases

Closes #16361

Release Notes:

- Fixed an inconsistency in the recent project picker, where different
keybindings would determine whether to reuse the window or not
2024-11-04 17:41:25 +01:00
Bennet Bo Fenner
15e7b67559 markdown preview: Refresh preview when file is changed outside of the editor (#20175)
Closes #20091

Release Notes:

- Fixed an issue where the markdown preview would not update when a
markdown file was changed outside of the editor
2024-11-04 17:23:32 +01:00
Mike Lloyd
f0aeab7d00 Add surround aliases (#20104)
Closes #19417

Release Notes:

- vim : Added `r` and `a` as aliases for `[` and `<` text objects
(copying vim-surround).
- vim: (breaking change) rebound the function argument text object to
`g`.
- vim: Fixed surrounds to allow `b`/`B`/`r`/`a` anywhere you could use
`(`, `{`, `[`, `<`.

---


- vim: Added `b`, `B`, `r`, `s`, `a` as aliases for `()`, `{}`, `[]`,
`<>` in vim surround mode.
- Adds a new `surround_alias` function where aliases are defined.
- This function is used in `find_surround_pairs` to substitute the
chosen text with the alias
- The keymap is also modified to add support for Square and Angle
brackets when changing surrounds. These two were added to follow the
example of Tim Pope's ubiquitous `vim-surround` plugin.
- I had to overwrite the `vim::Argument` keybind in order to do this. I
moved it to use the `g` modifier. I realize this is a breaking change
and will happily move the `vim::AngleBracket` keymap to a different
letter if you'd like to avoid this. I'm just trying to keep with
convention. Ideally, Users would be able to define surround aliases
themselves in the config file but that's a much bigger task than I'm
able to do right now.
- I also added tests for the new aliases.

Thanks for making such a clean and organized codebase. I was able to
find the relevant section of code rather quickly thanks to this.
2024-11-04 09:03:27 -07:00
bangbangsheshotmedown
4bbddcad31 extension: Add support for labelDetails for LSP completions (#20144)
Closes #14278


be7336e92a/src/completion.rs (L419-L429)


be7336e92a/src/completion.rs (L555-L572)


Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-11-04 11:02:19 -05:00
Pablo Portas López
1e944a51ff Delete a.py (#20173)
Release Notes:

- N/A
2024-11-04 10:41:44 -05:00
Kirill Bulatov
d90770c673 Actually reuse previous search entries (#20171)
Release Notes:

- Improved outline panel performance during large project searches
2024-11-04 17:21:09 +02:00
Peter Tripp
6316151a83 docs: Add redirect for /conversations (#20170)
Release Notes:

- N/A
2024-11-04 09:56:07 -05:00
Piotr Osiewicz
49a0a11c4f chore: Remove toolchain section from language settings (#20168)
Closes #ISSUE

Release Notes:

- N/A
2024-11-04 14:13:20 +01:00
Danilo Leal
376a45528d assistant: Improve role button loading state (#20125)
We've received feedback that it wasn't clear how to cancel/interrupt the
LLM while it's generating a response. Additionally, I also had folks
telling me that the loading state was hard to notice—the pulsating
animation is too subtle on its own. This PR attempts to improve both of
these things. The end result is:


![llm](https://github.com/user-attachments/assets/57a94f8a-c254-4011-adc0-7c63ed13daa1)

Release Notes:

- N/A
2024-11-04 10:12:27 -03:00
Jason Lee
20eeb78251 chore: Update BranchListDelegate to use WeakView<Workspace> (#20157) 2024-11-04 12:17:11 +02:00
Jonathan Toledo
67be6ec3b5 copilot: Add support for new models (#19968)
Closes #19963

This PR implements integration with the newly announced GitHub Copilot
LLM models, including:
- Claude 3.5 Sonnet
- o1-mini
- o1-preview

Release Notes:

- N/A

---------

Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
2024-11-04 10:55:20 +01:00
Lilith Iris
070e5914c9 markdown renderer: Add copy icon button for code block (#19312)
Closes #19061

I don't know should i add tooltip or not

Before

![image](https://github.com/user-attachments/assets/0729806b-9b0d-442a-8f71-92c0443f34ef)

After

![image](https://github.com/user-attachments/assets/08eb5178-4139-44e7-a23f-50133233911d)

Release Notes:

- Markdown Preview: Added button to copy code blocks.

---------

Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
2024-11-04 10:40:46 +01:00
Kirill Bulatov
c41a8e33a0 Fix file reloading not populating the history (#20156)
Closes https://github.com/zed-industries/zed/issues/20111
Closes https://github.com/zed-industries/zed/issues/20153

cc @mikayla-maki and @ConradIrwin 

Release Notes:

- Fixed undo stack corruption on external file changes
([#20111](https://github.com/zed-industries/zed/issues/20111))
([#20153](https://github.com/zed-industries/zed/issues/20153))

Co-authored-by: Antonio Scandurra <antonio@zed.dev>
2024-11-04 11:30:15 +02:00
Bennet Bo Fenner
25443a91ca image viewer: Show path in breadcrumbs (#20155)
Closes #10057

<img width="354" alt="image"
src="https://github.com/user-attachments/assets/47afe8fd-c8ac-45af-be9a-9ca8c5e066f6">

Release Notes:

- Show path in breadcrumbs/toolbar when opening an image
2024-11-04 10:18:21 +01:00
Roshan Padaki
8e00caf23b go: Add go-generate runnables and tasks (#19987)
I was missing the `go generate` runnable from other editors so I figured
I'd implement one here! Now, comments of the form `//go:generate` can
prompt for the `go generate <package>` task. Meanwhile, I've also added
a global `go generate ./...` task.

~When making the global task, I noticed that the existing `go test
./...` task runs tests in subdirectories of the CWD of the active
editor, whereas I would really expect it to run all tests across my
project. I have changed to use the latter behavior (run relative to
project root) for both `go generate ./...` and `go test ./...`. Please
let me know if the prior behavior was intended, and I can revert.~

Release Notes:

- Added runnable and tasks for `go generate` commands
2024-11-04 10:13:15 +01:00
Bennet Bo Fenner
9a869f0c5f project panel: Focus editor when single-clicking on file and preview tabs are disabled (#20154)
Closes #4324

Release Notes:

- Fixed an issue where single-clicking on a file in the project panel
would not focus the editor, when preview tabs are disabled
2024-11-04 10:02:55 +01:00
Marc
de2483e132 terminal: Do not show multibuffer hint when in centered pane (#20137)
Co-Authored-by: Bennet <bennet@zed.dev>


![image](https://github.com/user-attachments/assets/581f493e-aa9b-4767-8029-6ab83755336b)

Release Notes:

- Fixed an issue where the multibuffer hint was shown when terminal was
in centered mode

Co-authored-by: Bennet <bennet@zed.dev>
2024-11-04 09:19:48 +01:00
Michael Sloan
95259bf9fe Add michael@zed.dev to .mailmap (#20119)
Release Notes:

- N/A
2024-11-04 09:17:53 +02:00
Henry Barreto
3b76ba6d5a docs: Align soft_wrap reference with naming pattern used (#20080)
Release Notes:

- N/A
2024-11-02 20:29:56 +02:00
Richard Feldman
773a3b335e Expand selections to Replace block boundaries (#20092)
Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Max <max@zed.dev>
2024-11-01 16:10:19 -04:00
Conrad Irwin
b5c38e9a09 Remoting: Fix connecting to servers with long hostnames (#20093)
Closes #20018

Release Notes:

- Remoting: Fixed connecting to hosts with long (>~50 character)
hostnames
2024-11-01 13:52:21 -06:00
Marshall Bowers
273173ec8a Revert "theme: Turn ThemeRegistry into a trait (#20076)" (#20094)
This PR reverts #20076 to turn the `ThemeRegistry` back into a regular
struct again.

It doesn't actually help us by having it behind a trait.

Release Notes:

- N/A
2024-11-01 15:34:20 -04:00
Conrad Irwin
4084ba36f9 Fix clang popovers (#20090)
Closes  #15498

Release Notes:

- Fixed info popups from clangd missing information
2024-11-01 13:28:34 -06:00
Marshall Bowers
770886880f Add new extension crate (#20089)
This PR adds a new `extension` crate, containing some contents extracted
from the `extension_host`.

Right now it contains just the `ExtensionManifest` and
`ExtensionBuilder`, although we may move more of the extension interface
into here.

The introduction of the `extension` crate allows us to depend on it in
the `extension_cli`, thereby eliminating the need for the `no-webrtc`
feature on a number of crates.

Release Notes:

- N/A
2024-11-01 13:20:30 -04:00
Marshall Bowers
ea44c510a3 Rename extension crate to extension_host (#20081)
This PR renames the `extension` crate to `extension_host`.

This is to free up the name so that we can create a smaller-scoped
`extension` crate.

Release Notes:

- N/A
2024-11-01 12:53:02 -04:00
Kirill Bulatov
c8f1969916 Fix the outline panel's focus tracking (#20083)
Closes https://github.com/zed-industries/zed/issues/20073

Release Notes:

- Fixed outline panel navigation
([https://github.com/zed-industries/zed/issues/20073](#20073))

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
2024-11-01 18:01:10 +02:00
Marshall Bowers
a960344301 theme: Remove unused staff parameter for listing themes (#20077)
This PR removes the `staff` parameter for listing themes, as it was not
used.

Release Notes:

- N/A
2024-11-01 10:54:21 -04:00
Marshall Bowers
af9e7f1f96 theme: Turn ThemeRegistry into a trait (#20076)
This PR converts the `ThemeRegistry` type into a trait instead of a
concrete implementation.

This allows for the extension store to depend on an abstraction rather
than the concrete theme registry implementation.

We currently have two `ThemeRegistry` implementations:

- `RealThemeRegistry` — this was previously the `ThemeRegistry` and
contains the real implementation of the registry.
- `VoidThemeRegistry` — a null object that doesn't have any behavior.

Release Notes:

- N/A
2024-11-01 10:19:09 -04:00
renovate[bot]
c04c439d23 Update Rust crate async-compression to v0.4.17 (#19319)
This PR contains the following updates:

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

---

### Release Notes

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

###
[`v0.4.17`](https://redirect.github.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#0417---2024-10-20)

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

##### Fixed

-   Fix occasional panics when consuming from pending buffers.

###
[`v0.4.16`](https://redirect.github.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#0416---2024-10-16)

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

##### Other

- Implement pass-through `AsyncBufRead` on write-based encoders &
decoders.

###
[`v0.4.15`](https://redirect.github.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#0415---2024-10-13)

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

##### Feature

- Implement pass-through `AsyncRead` or `AsyncWrite` where appropriate.
- Relax `AsyncRead`/`AsyncWrite` bounds on `*::{get_ref, get_mut,
get_pin_mut, into_inner}()` methods.

###
[`v0.4.14`](https://redirect.github.com/Nullus157/async-compression/blob/HEAD/CHANGELOG.md#0414---2024-10-10)

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

##### Fixed

- In Tokio-based decoders, attempt to decode from internal state even if
nothing was read.

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-01 16:11:16 +02:00
renovate[bot]
d3cd8f8f14 Update Rust crate proc-macro2 to v1.0.89 (#19326)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [proc-macro2](https://redirect.github.com/dtolnay/proc-macro2) |
dependencies | patch | `1.0.87` -> `1.0.89` |

---

### Release Notes

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

###
[`v1.0.89`](https://redirect.github.com/dtolnay/proc-macro2/releases/tag/1.0.89)

[Compare
Source](https://redirect.github.com/dtolnay/proc-macro2/compare/1.0.88...1.0.89)

- Ensure OUT_DIR is left with deterministic contents after build script
execution
([#&#8203;474](https://redirect.github.com/dtolnay/proc-macro2/issues/474))

###
[`v1.0.88`](https://redirect.github.com/dtolnay/proc-macro2/releases/tag/1.0.88)

[Compare
Source](https://redirect.github.com/dtolnay/proc-macro2/compare/1.0.87...1.0.88)

- Return accurate line and column from `Span::start` and `Span::end`
inside proc macros on nightly
([#&#8203;472](https://redirect.github.com/dtolnay/proc-macro2/issues/472))

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-01 16:10:31 +02:00
renovate[bot]
cd8d776fe1 Update Rust crate profiling to v1.0.16 (#19334)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [profiling](https://redirect.github.com/aclysma/profiling) |
workspace.dependencies | patch | `1.0.15` -> `1.0.16` |

---

### Release Notes

<details>
<summary>aclysma/profiling (profiling)</summary>

###
[`v1.0.16`](https://redirect.github.com/aclysma/profiling/blob/HEAD/CHANGELOG.md#1016)

[Compare
Source](https://redirect.github.com/aclysma/profiling/compare/v1.0.15...v1.0.16)

-   Address warnings from upstream rustc changes
-   Update puffin to 0.19.1
-   Update tracing-tracy to 0.11.3 and tracing-subscriber to 0.3
-   Implement finish_frame! for tracing
-   Add fuction_scope!() as an alternative to the function proc macro
- Avoid local variable names that don't start with an underscore
introduced into a function's namespace

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-01 16:10:21 +02:00
renovate[bot]
6de2330253 Update Rust crate pathdiff to v0.2.2 (#19325)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [pathdiff](https://redirect.github.com/Manishearth/pathdiff) |
workspace.dependencies | patch | `0.2.1` -> `0.2.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:eyJjcmVhdGVkSW5WZXIiOiIzOC4xMjAuMSIsInVwZGF0ZWRJblZlciI6IjM4LjEyMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-01 16:10:09 +02:00
renovate[bot]
0e264b5a68 Update cloudflare/wrangler-action digest to b2a0191 (#19645)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[cloudflare/wrangler-action](https://redirect.github.com/cloudflare/wrangler-action)
| action | digest | `9681c29` -> `b2a0191` |

---

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-01 16:07:36 +02:00
renovate[bot]
1af5304074 Update Rust crate flume to v0.11.1 (#19641)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [flume](https://redirect.github.com/zesterer/flume) | dependencies |
patch | `0.11.0` -> `0.11.1` |

---

### Release Notes

<details>
<summary>zesterer/flume (flume)</summary>

###
[`v0.11.1`](https://redirect.github.com/zesterer/flume/blob/HEAD/CHANGELOG.md#0111---2024-10-19)

##### Added

-   `SendSink::sender`
- `SendFut`, `SendSink`, `RecvFut`, `RecvStream`, `WeakSender`, `Iter`,
`TryIter`, and `IntoIter` now implement `Debug`
-   Docs now show required features

##### Removed

##### Changed

-   `WeakSender` is now `Clone`
- `spin` feature no longer uses `std::thread::sleep` for locking except
on Unix-like operating systems and Windows
- Flume is now in [casual maintenance
mode](https://casuallymaintained.tech/).

##### Fixed

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-01 16:06:24 +02:00
renovate[bot]
dde692eb88 Update Rust crate libc to v0.2.161 (#19650)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

##### Fixed

- OpenBSD: fix `FNM_PATHNAME` and `FNM_NOESCAPE` values
[#&#8203;3983](https://redirect.github.com/rust-lang/libc/pull/3983)

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

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

##### Added

- Android: add `PR_GET_NAME` and `PR_SET_NAME`
[#&#8203;3941](https://redirect.github.com/rust-lang/libc/pull/3941)
- Apple: add `F_TRANSFEREXTENTS`
[#&#8203;3925](https://redirect.github.com/rust-lang/libc/pull/3925)
- Apple: add `mach_error_string`
[#&#8203;3913](https://redirect.github.com/rust-lang/libc/pull/3913)
- Apple: add additional `pthread` APIs
[#&#8203;3846](https://redirect.github.com/rust-lang/libc/pull/3846)
- Apple: add the `LOCAL_PEERTOKEN` socket option
[#&#8203;3929](https://redirect.github.com/rust-lang/libc/pull/3929)
- BSD: add `RTF_*`, `RTA_*`, `RTAX_*`, and `RTM_*` definitions
[#&#8203;3714](https://redirect.github.com/rust-lang/libc/pull/3714)
- Emscripten: add `AT_EACCESS`
[#&#8203;3911](https://redirect.github.com/rust-lang/libc/pull/3911)
- Emscripten: add `getgrgid`, `getgrnam`, `getgrnam_r` and `getgrgid_r`
[#&#8203;3912](https://redirect.github.com/rust-lang/libc/pull/3912)
- Emscripten: add `getpwnam_r` and `getpwuid_r`
[#&#8203;3906](https://redirect.github.com/rust-lang/libc/pull/3906)
- FreeBSD: add `POLLRDHUP`
[#&#8203;3936](https://redirect.github.com/rust-lang/libc/pull/3936)
- Haiku: add `arc4random`
[#&#8203;3945](https://redirect.github.com/rust-lang/libc/pull/3945)
- Illumos: add `ptsname_r`
[#&#8203;3867](https://redirect.github.com/rust-lang/libc/pull/3867)
- Linux: add `fanotify` interfaces
[#&#8203;3695](https://redirect.github.com/rust-lang/libc/pull/3695)
- Linux: add `tcp_info`
[#&#8203;3480](https://redirect.github.com/rust-lang/libc/pull/3480)
- Linux: add additional AF_PACKET options
[#&#8203;3540](https://redirect.github.com/rust-lang/libc/pull/3540)
- Linux: make Elf constants always available
[#&#8203;3938](https://redirect.github.com/rust-lang/libc/pull/3938)
- Musl x86: add `iopl` and `ioperm`
[#&#8203;3720](https://redirect.github.com/rust-lang/libc/pull/3720)
- Musl: add `posix_spawn` chdir functions
[#&#8203;3949](https://redirect.github.com/rust-lang/libc/pull/3949)
- Musl: add `utmpx.h` constants
[#&#8203;3908](https://redirect.github.com/rust-lang/libc/pull/3908)
- NetBSD: add `sysctlnametomib`, `CLOCK_THREAD_CPUTIME_ID` and
`CLOCK_PROCESS_CPUTIME_ID`
[#&#8203;3927](https://redirect.github.com/rust-lang/libc/pull/3927)
- Nuttx: initial support
[#&#8203;3909](https://redirect.github.com/rust-lang/libc/pull/3909)
- RTEMS: add `getentropy`
[#&#8203;3973](https://redirect.github.com/rust-lang/libc/pull/3973)
- RTEMS: initial support
[#&#8203;3866](https://redirect.github.com/rust-lang/libc/pull/3866)
- Solarish: add `POLLRDHUP`, `POSIX_FADV_*`, `O_RSYNC`, and
`posix_fallocate`
[#&#8203;3936](https://redirect.github.com/rust-lang/libc/pull/3936)
- Unix: add `fnmatch.h`
[#&#8203;3937](https://redirect.github.com/rust-lang/libc/pull/3937)
- VxWorks: add riscv64 support
[#&#8203;3935](https://redirect.github.com/rust-lang/libc/pull/3935)
- VxWorks: update constants related to the scheduler
[#&#8203;3963](https://redirect.github.com/rust-lang/libc/pull/3963)

##### Changed

- Redox: change `ino_t` to be `c_ulonglong`
[#&#8203;3919](https://redirect.github.com/rust-lang/libc/pull/3919)

##### Fixed

- ESP-IDF: fix mismatched constants and structs
[#&#8203;3920](https://redirect.github.com/rust-lang/libc/pull/3920)
- FreeBSD: fix `struct stat` on FreeBSD 12+
[#&#8203;3946](https://redirect.github.com/rust-lang/libc/pull/3946)

##### Other

- CI: Fix CI for FreeBSD 15
[#&#8203;3950](https://redirect.github.com/rust-lang/libc/pull/3950)
- Docs: link to `windows-sys`
[#&#8203;3915](https://redirect.github.com/rust-lang/libc/pull/3915)

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-01 16:06:16 +02:00
renovate[bot]
cec72b837e Update Rust crate linkme to v0.3.29 (#19657)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

- Add UEFI target support
([#&#8203;100](https://redirect.github.com/dtolnay/linkme/issues/100),
thanks [@&#8203;Javagedes](https://redirect.github.com/Javagedes))

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-01 16:06:04 +02:00
Piotr Osiewicz
95842c7987 gpui: Add scroll anchors (#19894)
## Problem statement
I want to add keyboard navigation support to SSH modal. Doing so is
possible in current landscape, but not particularly ergonomic;
`gpui::ScrollHandle` has `scroll_to_item` API that takes an index of the
item you want to scroll to. The problem is, however, that it only works
with it's immediate children - thus in order to support scrolling via
keyboard you have to bend your UI to have a particular layout. Even when
your list of items is perfectly flat, having decorations inbetween items
is problematic as they are also children of the list, which means that
you either have to maintain the mapping to devise a correct index of an
item that you want to scroll to, or you have to make the decoration a
part of the list item itself, which might render the scrolling imprecise
(you might e.g. not want to scroll to a header, but to a button beneath
it).

## The solution
This PR adds `ScrollAnchor`, a new kind of handle to the gpui. It has a
similar role to that of a ScrollHandle, but instead of tracking how far
along an item has been scrolled, it tracks position of an element
relative to the parent to which a given scroll handle belongs. In short,
it allows us to persist the position of an element in a list of items
and scroll to it even if it's not an immediate children of a container
whose scroll position is tracked via an associated scroll handle.
Additionally this PR adds a new kind of the container to the UI crate
that serves as a convenience wrapper for using ScrollAnchors. This
container provides handlers for `menu::SelectNext` and
`menu::SelectPrev` and figures out which item should be focused next.

Release Notes:

- Improve keyboard navigation in ssh modal
2024-11-01 14:47:46 +01:00
Kirill Bulatov
183e3664cc Mention spectre-mitigated libs component in the Windows docs (#20069)
Closes https://github.com/zed-industries/zed/issues/20066

Release Notes:

- N/A
2024-11-01 13:43:35 +02:00
Yury Zhuravlev
08b124c8d4 Add possibility to build without musl (#19813)
Closes #19803 

Ad



Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2024-11-01 09:25:45 +02:00
Conrad Irwin
ea08026cd0 vim: Make window shortcuts work in other contexts (#20058)
Closes #18552

Release Notes:

- vim: Extended `ctrl-w` to work in non-editor contexts (like markdown
preview, or screen shares)
2024-10-31 23:49:43 -06:00
Conrad Irwin
daa9939c03 vim: o should scroll (#20054)
Closes: #19684

Release Notes:

- vim: Fixed `o` not scrolling new head into view
2024-10-31 23:26:04 -06:00
Conrad Irwin
f757e5a6c3 vim: Add :noh[lsearch] (#20056)
Closes: #18590

Release Notes:

- vim: Add :noh[lsearch]
2024-10-31 23:25:59 -06:00
Conrad Irwin
ecb874db62 vim: Fix gU$ (#20057)
Closes: #19380

Release Notes:

- vim: Fixed `gu$` missing last character of the line
2024-10-31 23:25:54 -06:00
Conrad Irwin
75f1862268 vim: Add (half of) ctrl-v/ctrl-q (#19585)
Release Notes:

- vim: Add `ctrl-v`/`ctrl-q` to type any unicode code point. For example
`ctrl-v escape` inserts an escape character(U+001B), or `ctrl-v u 1 0 E
2` types ტ (U+10E2). As in vim `ctrl-v ctrl-j` inserts U+0000 not
U+000A. Zed does not yet implement insertion of the vim-specific
representation of the typed keystroke for other keystrokes.
- vim: Add `ctrl-shift-v` as an alias for paste on Linux
2024-10-31 23:25:42 -06:00
Conrad Irwin
f8ab86f930 Simplify line normalization (#19712)
Release Notes:

- Added \u2028 and \u2029 to invisible characters. Previously these were
treated as \n.
2024-10-31 22:24:24 -06:00
Conrad Irwin
155854d9a9 Fix trigger release? (#20053)
Release Notes:

- N/A
2024-10-31 20:45:11 -06:00
Kyle Kelley
5b6578247f Upgrade nbformat and runtimelib (#20050)
Fixes an issue on load of notebooks that have `text/*` output in
`Vec<String>` rather than `String`. This ensures that Markdown output
will render correctly.

<img width="1306" alt="image"
src="https://github.com/user-attachments/assets/0bcc7dc8-527f-4067-a916-3ae569ea197d">


Release Notes:

- N/A
2024-10-31 17:58:36 -07:00
Boris Cherny
b87c4a1e13 assistant: Add health telemetry (#19928)
This PR adds a bit of telemetry for Anthropic models, in order to
understand model health. With this logging, we can monitor and diagnose
dips in performance, for example due to model rollouts.

Release Notes:

- N/A

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2024-10-31 16:21:26 -07:00
Conrad Irwin
a0988508f0 SSHHELL escaping.... (#20046)
Closes #20027 
Closes #19976 (again)

Release Notes:

- Remoting: Fixed remotes with non-sh/bash/zsh default shells
- Remoting: Fixed remotes running busybox's version of gunzip
2024-10-31 16:10:03 -06:00
Nate Butler
a347c4def7 Add theme preview (#20039)
This PR adds a theme preview tab to help get an at a glance overview of
the styles in a theme.

![CleanShot 2024-10-31 at 11 27
18@2x](https://github.com/user-attachments/assets/798e97cf-9f80-4994-b2fd-ac1dcd58e4d9)

You can open it using `debug: open theme preview`.

The next major theme preview PR will move this into it's own crate, as
it will grow substantially as we add content.

Next for theme preview:

- Update layout to two columns, with controls on the right for selecting
theme, layer/elevation-index, etc.
- Cover more UI elements in preview
- Display theme colors in a more helpful way
- Add syntax & markdown previews


Release Notes:

- Added a way to preview the current theme's styles with the `debug:
open theme preview` command.
2024-10-31 11:40:38 -04:00
renovate[bot]
9c77bcc827 Update actions/setup-node digest to 39370e3 (#19979)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/setup-node](https://redirect.github.com/actions/setup-node) |
action | digest | `0a44ba7` -> `39370e3` |

---

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-31 11:28:37 -04:00
Marshall Bowers
8d1f377bf0 assistant: Add example streaming slash command (#20034)
This PR adds a `/streaming-example` slash command for the purposes of
showcasing streaming during development.

This slash command is only available to staff and isn't intended to be
shipped to the general public.

Release Notes:

- N/A
2024-10-31 10:53:47 -04:00
Kirill Bulatov
f766f6ceae Do less work when revealing entries in the outline panel (#20031)
Before this change, we were trying to determine current element before
debouncing, causing a lot of extra work on caret movement. Now, we only
do this for the task that managed to wait the entire debounce period.

Closes https://github.com/zed-industries/zed/issues/19817
Closes https://github.com/zed-industries/zed/issues/14235

Release Notes:

- Fixed outline panel-related performance issues when selections change
in the large document
([#19817](https://github.com/zed-industries/zed/issues/19817)),
([#14235](https://github.com/zed-industries/zed/issues/14235))
2024-10-31 16:29:19 +02:00
Kyle Kelley
9dad897d49 Clean up notebook item creation in project (#20030)
* Implement `clone_on_split` to allow splitting a notebook into another
pane

* Switched to `tab_content` in `impl Item for NotebookEditor` to show
both the notebook name and an icon

* Added placeholder methods and TODOs for future work, such as saving,
reloading, and search functionality within the notebook editor.

* Started moving more core `Model` bits into `NotebookItem`, including
pulling the language of the notebook (which affects every code cell)

* Loaded notebook asynchronously using `fs`

Release Notes:

- N/A

---------

Co-authored-by: Mikayla <mikayla@zed.dev>
2024-10-31 07:01:46 -07:00
Thorsten Ball
5b6401519b activity indicator: Reset formatting failure on click (#20029)
Release Notes:

- N/A
2024-10-31 14:33:36 +01:00
Thorsten Ball
293e080f03 tasks: Add editor: Spawn Nearest Task action (#19901)
This spawns the runnable task that that's closest to the cursor.

One thing missing right now is that it doesn't find tasks that are
attached to non-outline symbols, such as subtests in Go.

Release Notes:

- Added a new reveal option for tasks: `"no_focus"`. If used, the tasks
terminal panel will be opened and shown, but not focused.
- Added a new `editor: spawn nearest task` action that spawns the task
with a run indicator icon nearest to the cursor. It can be configured to
also use a `reveal` strategy. Example:
```json
{
  "context": "EmptyPane || SharedScreen || vim_mode == normal",
  "bindings": {
    ", r t": ["editor::SpawnNearestTask", { "reveal": "no_focus" }],
  }
}
```


Demo:



https://github.com/user-attachments/assets/0d1818f0-7ae4-4200-8c3e-0ed47550c298

---------

Co-authored-by: Bennet <bennet@zed.dev>
2024-10-31 14:25:57 +01:00
Auf keinen Fall Jens
633b665379 Option to insert comment character(s) at the beginning of the line(s) (#19746)
Closes #19459


This PR adds the optional setting to insert comment character(s) at the
beginning of the line(s) instead of after the indentation. It can be
enabled via keybindings:

```
"ctrl-/": ["editor::ToggleComments", { "ignore_indent": true }]
```

As suggested by @notpeter in #19459, this is implemented in
`toggle_comments` (editor.rs) taking the existing `advance_downwards`
option as example.

There's also a test case for the setting, which mimics the test case for
the regular comment toggling behavior.

---

I am not entirely happy with the name `ignore_indent`. The default would
be a double negative now `ignore_indent=false`. A positive wording would
probably easier to understand, but I could not think of anything
concise. `insert_at_line_start` or just `at_line_start` might work, but
didn't convince me either. That said, I am happy to change the name if
there are better ideas.

---

Release Notes:

- Added optional setting to insert comment character(s) at the beginning
of the line(s) instead of after the indentation. It can be used by
changing the default mapping to toggle comments like this: `"ctrl-/":
["editor::ToggleComments", { "ignore_indent": true }]`
2024-10-31 09:39:57 +01:00
Thorsten Ball
7fd334fddb proto: Remove unused UpdateUserSettings message (#20005)
Release Notes:

- N/A
2024-10-31 09:36:18 +01:00
Thorsten Ball
10226a3992 docs: Document inline blame options (#20006)
Release Notes:

- N/A
2024-10-31 09:36:05 +01:00
Peter Tripp
383e868af0 docs: SSH no longer requires Zed Preview (#20003) 2024-10-30 23:28:13 -04:00
Conrad Irwin
40802d91d4 SSH installation refactor (#19991)
This also cleans up logic for deciding how to do things.

Release Notes:

- Remoting: If downloading the binary on the remote fails, fall back to
uploading it.

---------

Co-authored-by: Mikayala <mikayla@zed.dev>
2024-10-30 16:20:11 -07:00
Danilo Leal
6d5784daa6 Adjust design of the slash command picker (#19973)
This PR removes the quote selection icon button from the footer and adds
it in the picker, and adds an icon field to each command entry. Final
result looks like:


https://github.com/user-attachments/assets/d177f1c1-b6f6-4652-9434-f6291b279e34

Release Notes:

- N/A
2024-10-30 19:42:42 -03:00
Conrad Irwin
f80eb264fb Robustify download on remote (#19983)
Closes #19976
Closes #19972

We now prefer curl to wget (as it supports socks5:// proxies) and pass
-f to
curl so it fails; and use sh instead of bash, which should have more
consistent
behaviour across systems

Release Notes:

- SSH Remoting: make downloading binary on remote more reliable.

---------

Co-authored-by: Will <will@zed.dev>
2024-10-30 15:17:50 -07:00
Conrad Irwin
3d956ca68b Fail download if download fails (#19990)
Co-Authored-By: Mikayla <mikayla@zed.dev>

Release Notes:

- Remoting: Fixes a bug where we could cache an HTML error page as a
binary

Co-authored-by: Mikayla <mikayla@zed.dev>
2024-10-30 14:50:41 -07:00
Kyle Kelley
7ce131aaf8 Trim whitespace from base64 encoded image data before decoding it (#19977)
Closes #17956
Closes #16330

This fix is for both REPL (released) and notebook (unreleased)

<img width="1210" alt="image"
src="https://github.com/user-attachments/assets/bd046f0f-3ad1-4c25-b3cb-114e008c2a69">

Release Notes:

- Fixed image support in REPL for certain versions of matplotlib that
included preceding and/or trailing whitespace in the base64 image data
2024-10-30 12:32:17 -07:00
Jen Stehlik
60be47d115 Update Gleam icon (#19978)
Improves upon: https://github.com/zed-industries/zed/pull/19887

Implements the feedback by @PixelJanitor to make the icon follow the
design guidelines.

Release Notes:

- Improved Gleam icon
2024-10-30 15:29:32 -04:00
Conrad Irwin
bd187883da Migration to remove dev servers (#19639)
Depends on #19638

Release Notes:

- None
2024-10-30 11:55:55 -06:00
Conrad Irwin
4f9217bca0 Support zed://ssh (#19970)
Closes: #15070

Release Notes:

- Added support for `zed://ssh/<connnection>/<path>`
2024-10-30 11:28:25 -06:00
Conrad Irwin
ce5222f1df Add KeyContextView (#19872)
Release Notes:

- Added `cmd-shift-p debug: Open Key Context View` to help debug custom
key bindings



https://github.com/user-attachments/assets/de273c97-5b27-45aa-9ff1-f943b0ed7dfe
2024-10-30 11:26:54 -06:00
Kirill Bulatov
cf7b0c8971 Add scrollbars to outline panel (#19969)
Part of https://github.com/zed-industries/zed/issues/15324


![image](https://github.com/user-attachments/assets/4f32d585-9bd2-46be-8234-3658a71906ee)

Repeats the approach used in the project panel.

Release Notes:

- Added scrollbars to outline panel

---------

Co-authored-by: Nate Butler <nate@zed.dev>
2024-10-30 19:09:14 +02:00
renovate[bot]
7bc4cb9868 Update Rust crate hyper to v0.14.31 (#19323)
This PR contains the following updates:

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

---

### Release Notes

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

###
[`v0.14.31`](https://redirect.github.com/hyperium/hyper/releases/tag/v0.14.31)

[Compare
Source](https://redirect.github.com/hyperium/hyper/compare/v0.14.30...v0.14.31)

#### Bug Fixes

- **http1:** improve performance of parsing sequentially partial
messages
([97b595e](97b595e589))

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-30 12:11:07 -04:00
Gherman
f84f3ffeb7 docs: Add linkedProjects section to Rust docs (#19954)
Related to #19897

Adds a section about multi-project workspaces and how to configure
rust-analyzer to diagnose them even if the cargo workspace does not list
them

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-10-30 11:43:44 -04:00
Richard Feldman
c564a4a26c Require /file or /tab when using Suggest Edits (#19960)
Now if you try to do Suggest Edits without a file context, you see this
(and it doesn't run the query).

<img width="635" alt="Screenshot 2024-10-30 at 10 51 24 AM"
src="https://github.com/user-attachments/assets/a3997ba6-98a9-4bfa-81b6-1d8579c26fd7">


Release Notes:

- N/A

---------

Co-authored-by: Antonio <antonio@zed.dev>
2024-10-30 11:38:43 -04:00
Marshall Bowers
515fd7b75f git_hosting_providers: Fix support for GitLab remotes containing subgroups (#19962)
This PR fixes the support for GitLab remote URLs containing subgroups.

Reported in
https://github.com/zed-industries/zed/issues/18012#issuecomment-2446206256.

Release Notes:

- N/A
2024-10-30 11:16:44 -04:00
Peter Tripp
662a4440cc v0.161.x dev 2024-10-30 11:06:39 -04:00
Marshall Bowers
5dee43b05c dart: Extract to zed-extensions/dart repository (#19959)
This PR extracts the Dart extension to the
[zed-extensions/dart](https://github.com/zed-extensions/dart)
repository.

Release Notes:

- N/A
2024-10-30 11:00:06 -04:00
Antonio Scandurra
c8003c0697 Take a mutable context when resolving selections (#19948)
This is a behavior-preserving change, but lays the groundwork for
expanding selections when the cursor lands inside of a "replace" block.

Release Notes:

- N/A
2024-10-30 15:21:51 +01:00
Lukas Geiger
83e2889d63 Fix notebook cell-height when soft-wrapping lines (#19933) 2024-10-30 07:12:32 -07:00
Kirill Bulatov
d49cd0019f Log prettier errors on failures (#19951)
Closes https://github.com/zed-industries/zed/issues/11987

Release Notes:

- Fixed prettier not reporting failures in the status panel on
formatting and installation errors
2024-10-30 14:49:47 +02:00
Thorsten Ball
0ba40bdfb8 remote dev: Always upload binary in development mode (#19953)
Release Notes:

- N/A
2024-10-30 13:41:28 +01:00
Thorsten Ball
f6cd97f6fd remote dev: Allow canceling language server work in editor (#19946)
Release Notes:

- Added ability to cancel language server work in remote development.

Demo:



https://github.com/user-attachments/assets/c9ca91a5-617f-4886-a458-87c563c5a247
2024-10-30 13:27:11 +01:00
Thorsten Ball
774a8bf039 inline blame: Fix default setting for inline blame (#19943)
Follow-up to #19759. Fixes the default value. cc @pjtatlow 😄 

Release Notes:

- N/A
2024-10-30 11:40:04 +01:00
Antonio Scandurra
4431ef1870 Speed up point translation in the Rope (#19913)
This pull request introduces an index of Unicode codepoints, newlines
and UTF-16 codepoints.

Benchmarks worth a thousand words:

```
push/4096               time:   [467.06 µs 470.07 µs 473.24 µs]
                        thrpt:  [8.2543 MiB/s 8.3100 MiB/s 8.3635 MiB/s]
                 change:
                        time:   [-4.1462% -3.0990% -2.0527%] (p = 0.00 < 0.05)
                        thrpt:  [+2.0957% +3.1981% +4.3255%]
                        Performance has improved.
Found 3 outliers among 100 measurements (3.00%)
  1 (1.00%) low mild
  2 (2.00%) high mild
push/65536              time:   [1.4650 ms 1.4796 ms 1.4922 ms]
                        thrpt:  [41.885 MiB/s 42.242 MiB/s 42.664 MiB/s]
                 change:
                        time:   [-3.2871% -2.3489% -1.4555%] (p = 0.00 < 0.05)
                        thrpt:  [+1.4770% +2.4054% +3.3988%]
                        Performance has improved.
Found 6 outliers among 100 measurements (6.00%)
  3 (3.00%) low severe
  3 (3.00%) low mild

append/4096             time:   [729.00 ns 730.57 ns 732.14 ns]
                        thrpt:  [5.2103 GiB/s 5.2215 GiB/s 5.2327 GiB/s]
                 change:
                        time:   [-81.884% -81.836% -81.790%] (p = 0.00 < 0.05)
                        thrpt:  [+449.16% +450.53% +452.01%]
                        Performance has improved.
Found 11 outliers among 100 measurements (11.00%)
  3 (3.00%) low mild
  6 (6.00%) high mild
  2 (2.00%) high severe
append/65536            time:   [504.44 ns 505.58 ns 506.77 ns]
                        thrpt:  [120.44 GiB/s 120.72 GiB/s 121.00 GiB/s]
                 change:
                        time:   [-94.833% -94.807% -94.782%] (p = 0.00 < 0.05)
                        thrpt:  [+1816.3% +1825.8% +1835.5%]
                        Performance has improved.
Found 4 outliers among 100 measurements (4.00%)
  3 (3.00%) high mild
  1 (1.00%) high severe

slice/4096              time:   [29.661 µs 29.733 µs 29.816 µs]
                        thrpt:  [131.01 MiB/s 131.38 MiB/s 131.70 MiB/s]
                 change:
                        time:   [-48.833% -48.533% -48.230%] (p = 0.00 < 0.05)
                        thrpt:  [+93.161% +94.298% +95.440%]
                        Performance has improved.
slice/65536             time:   [588.00 µs 590.22 µs 592.17 µs]
                        thrpt:  [105.54 MiB/s 105.89 MiB/s 106.29 MiB/s]
                 change:
                        time:   [-45.599% -45.347% -45.099%] (p = 0.00 < 0.05)
                        thrpt:  [+82.147% +82.971% +83.821%]
                        Performance has improved.
Found 2 outliers among 100 measurements (2.00%)
  1 (1.00%) low severe
  1 (1.00%) high mild

bytes_in_range/4096     time:   [3.8630 µs 3.8811 µs 3.8994 µs]
                        thrpt:  [1001.8 MiB/s 1006.5 MiB/s 1011.2 MiB/s]
                 change:
                        time:   [+0.0600% +0.6000% +1.1833%] (p = 0.03 < 0.05)
                        thrpt:  [-1.1695% -0.5964% -0.0600%]
                        Change within noise threshold.
bytes_in_range/65536    time:   [98.178 µs 98.545 µs 98.931 µs]
                        thrpt:  [631.75 MiB/s 634.23 MiB/s 636.60 MiB/s]
                 change:
                        time:   [-0.6513% +0.7537% +2.2265%] (p = 0.30 > 0.05)
                        thrpt:  [-2.1780% -0.7481% +0.6555%]
                        No change in performance detected.
Found 11 outliers among 100 measurements (11.00%)
  8 (8.00%) high mild
  3 (3.00%) high severe

chars/4096              time:   [878.91 ns 879.45 ns 880.06 ns]
                        thrpt:  [4.3346 GiB/s 4.3376 GiB/s 4.3403 GiB/s]
                 change:
                        time:   [+9.1679% +9.4000% +9.6304%] (p = 0.00 < 0.05)
                        thrpt:  [-8.7844% -8.5923% -8.3979%]
                        Performance has regressed.
Found 8 outliers among 100 measurements (8.00%)
  1 (1.00%) low severe
  1 (1.00%) low mild
  3 (3.00%) high mild
  3 (3.00%) high severe
chars/65536             time:   [15.615 µs 15.691 µs 15.757 µs]
                        thrpt:  [3.8735 GiB/s 3.8899 GiB/s 3.9087 GiB/s]
                 change:
                        time:   [+5.4902% +5.9345% +6.4044%] (p = 0.00 < 0.05)
                        thrpt:  [-6.0190% -5.6021% -5.2045%]
                        Performance has regressed.
Found 2 outliers among 100 measurements (2.00%)
  2 (2.00%) low mild

clip_point/4096         time:   [29.677 µs 29.835 µs 30.019 µs]
                        thrpt:  [130.13 MiB/s 130.93 MiB/s 131.63 MiB/s]
                 change:
                        time:   [-46.306% -45.866% -45.436%] (p = 0.00 < 0.05)
                        thrpt:  [+83.272% +84.728% +86.240%]
                        Performance has improved.
Found 11 outliers among 100 measurements (11.00%)
  3 (3.00%) high mild
  8 (8.00%) high severe
clip_point/65536        time:   [1.5933 ms 1.6116 ms 1.6311 ms]
                        thrpt:  [38.318 MiB/s 38.782 MiB/s 39.226 MiB/s]
                 change:
                        time:   [-30.388% -29.598% -28.717%] (p = 0.00 < 0.05)
                        thrpt:  [+40.286% +42.040% +43.653%]
                        Performance has improved.
Found 3 outliers among 100 measurements (3.00%)
  3 (3.00%) high mild


running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 7 filtered out; finished in 0.00s

point_to_offset/4096    time:   [14.493 µs 14.591 µs 14.707 µs]
                        thrpt:  [265.61 MiB/s 267.72 MiB/s 269.52 MiB/s]
                 change:
                        time:   [-71.990% -71.787% -71.588%] (p = 0.00 < 0.05)
                        thrpt:  [+251.96% +254.45% +257.01%]
                        Performance has improved.
Found 9 outliers among 100 measurements (9.00%)
  5 (5.00%) high mild
  4 (4.00%) high severe
point_to_offset/65536   time:   [700.72 µs 713.75 µs 727.26 µs]
                        thrpt:  [85.939 MiB/s 87.566 MiB/s 89.194 MiB/s]
                 change:
                        time:   [-61.778% -61.015% -60.256%] (p = 0.00 < 0.05)
                        thrpt:  [+151.61% +156.51% +161.63%]
                        Performance has improved.
```

Calling `Rope::chars` got slightly slower but I don't think it's a big
issue (we don't really call `chars` for an entire `Rope`).

In a future pull request, I want to use the tab index (which we're not
yet using) and the char index to make `TabMap` a lot faster.

Release Notes:

- N/A
2024-10-30 10:59:03 +01:00
Mikayla Maki
b3f0ba1430 Implement panic reporting saving and uploads (#19932)
TODO: 
- [x] check that the app version is well formatted for zed.dev

Release Notes:

- N/A

---------

Co-authored-by: Trace <violet.white.batt@gmail.com>
2024-10-29 23:54:00 -07:00
Nate Butler
a5f52f0f04 Use theme families to refine user themes (#19936)
This PR changes the way we load user themes into the ThemeRegistry. 

Rather than directly pass a theme family's themes to
`insert_user_themes`, instead we use the new `refine_theme_family ` and
`ThemeFamily::refine_theme`.

This PR should have net zero change to themes today, but sets up
enabling theme variables. We need to do it this way so each theme has
access to it's family when it is refined.

Release Notes:

- N/A
2024-10-29 22:30:58 -04:00
Nate Butler
63524a2354 Add missing full-size styles for panes (#19935)
As we don't use scrolling flex layouts directly in panes that often, the
methods that would normally be applied to containers that should fill
the space weren't applied here.

Should help un-stuck #19872's layout issue, but I'm merging this change
separately in case it creates some other layout issue in panes.

Release Notes:

- N/A
2024-10-29 21:16:45 -04:00
Marshall Bowers
90edb7189f git_hosting_providers: Clean up tests (#19927)
This PR cleans up the tests for the various Git hosting providers.

These tests had rotted a bit over time, to the point that some of them
weren't even testing what they claimed anymore.

Release Notes:

- N/A
2024-10-29 17:24:32 -04:00
Max Brunsfeld
518f6b529b Fix missing diagnostic and text highlights after blocks (#19920)
Release Notes:

- Fixed an issue where diagnostic underlines and certain text highlights
were not rendered correctly below block decorations such as the inline
assistant prompt.

Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Richard <richard@zed.dev>
2024-10-29 13:47:43 -07:00
Conrad Irwin
fb97e462de Better handle interrupted connections for shared SSH (#19925)
Co-Authored-By: Mikayla <mikayla@zed.dev>
2024-10-29 16:43:34 -04:00
Marshall Bowers
5b7fa05a87 Make Git remote URL parsing more robust (#19924)
This PR improves the parsing of Git remote URLs in order to make
features that depend on them more robust.

Previously we were just treating these as plain strings and doing
one-off shotgun parsing to massage them into the right format. This
meant that we weren't accounting for edge cases in URL structure.

One of these cases was HTTPS Git URLs containing a username, which can
arise when using GitHub Enterprise (see
https://github.com/zed-industries/zed/issues/11160).

We now have a `RemoteUrl` typed to represent a parsed Git remote URL and
use the `Url` parser to parse it.

Release Notes:

- Improved the parsing of Git remote URLs to support additional
scenarios.
2024-10-29 16:19:05 -04:00
Conrad Irwin
d310a1269f SSH Remoting: Fix diagnostic summary syncing (#19923)
Co-Authored-By: Mikayla <mikayla@zed.dev>

Release Notes:

- SSH Remoting: Fix diagnostics summary over collab

Co-authored-by: Mikayla <mikayla@zed.dev>
2024-10-29 13:02:32 -07:00
Mikayla Maki
9818835c9d Fix the log spam from the BlameBuffer request (#19921)
Release Notes:

- N/A
2024-10-29 13:02:21 -07:00
Mikayla Maki
f3b7f5944d Fix a rare crash on startup (#19922)
Release Notes:

- Fixed a rare crash that could happen when certain SQL statements are
prepared
2024-10-29 12:30:55 -07:00
Conrad Irwin
fc5cde9434 Fix quotes in Rust (#19914)
Release Notes:

- (preview only) Fixed quote-autoclose in Rust
2024-10-29 12:35:34 -06:00
Kyle Kelley
6ea4662326 Initial Notebook UI structure (#19756)
This is the start of a notebook UI for Zed. 

`🔔 Note: This won't be useable yet when it is merged! Read below. 🔔`

This is going to be behind a feature flag so that we can merge this
initial PR and then make follow up PRs. Release notes will be produced
in a future PR.

Minimum checklist for merging this:

* [x] All functionality behind the `notebooks` feature flag (with env
var opt out)
* [x] Open notebook files in the workspace
* [x] Remove the "Open Notebook" button from title bar
* [x] Incorporate text style refinements for cell editors
* [x] Rely on `nbformat` crate for parsing the notebook into our
in-memory format
* [x] Move notebook to a `gpui::List`
* [x] Hook up output rendering


Release Notes:

- N/A

---------

Co-authored-by: Nate Butler <iamnbutler@gmail.com>
Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
2024-10-29 11:30:07 -07:00
Joseph T. Lyons
9d12308d06 Fix Julia icon extension lookup (#19916)
Release Notes:

- Fixed a bug where the Julia icon was not displayed for Julia files.
2024-10-29 14:07:54 -04:00
Richard Feldman
21137d2ba7 Delete /workflow (#19900)
This a separate PR from https://github.com/zed-industries/zed/pull/19705
so we can revert it more easily if we want it back later.

Release Notes:

- Added "Suggest Edit" button to the assistant panel if
`"enable_experimental_live_diffs": true` is set in the `"assistant"`
section of `settings.json`. This button takes the place of the previous
`/workflow` command, but it is experimental and may change!

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
2024-10-29 13:57:33 -04:00
Conrad Irwin
273cb1921f Fix wrong UpdateWorktree chunk size being used in release mode (#19912)
Release Notes:

- Fixed slowness when collaborating

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-10-29 11:22:41 -06:00
Nathan Sobo
cfa20ff221 Sketch in assistant edit button (#19705)
Add an edit button to the assistant. This is totally hacked in for now,
just to see how this would feel rendered simply in the UI.

![CleanShot 2024-10-24 at 16 26
14@2x](https://github.com/user-attachments/assets/e630d078-78b7-42d7-93f1-cf61c00bd20e)

cc @as-cii @danilo-leal 

Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
2024-10-29 13:21:10 -04:00
Mikayla Maki
759d136fe6 Update a few doc comments (#19911)
Release Notes:

- N/A
2024-10-29 10:09:49 -07:00
Marshall Bowers
322aa41ad6 Add support for self-hosted GitLab instances for Git permalinks (#19909)
This PR adds support for self-hosted GitLab instances when generating
Git permalinks.

If the `origin` Git remote contains `gitlab` in the URL hostname we will
then attempt to register it as a self-hosted GitLab instance.

A note on this: I don't think relying on specific keywords is going to
be a suitable long-term solution to detection. In reality the
self-hosted instance could be hosted anywhere (e.g.,
`vcs.my-company.com`), so we will ultimately need a way to have the user
indicate which Git provider they are using (perhaps via a setting).

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

Release Notes:

- Added support for self-hosted GitLab instances when generating Git
permalinks.
- The instance URL must have `gitlab` somewhere in the host in order to
be recognized.
2024-10-29 12:31:51 -04:00
Max Brunsfeld
3e2f1d733c Fix horizontal scroll caused by diagnostic block width error (#19856)
Previously, when scrolling the diagnostics view with the mouse, we'd get
a spurious horizontal scroll (even if the content was not overflowing
horizontally) due to an error in the widths of the diagnostic blocks.

Release Notes:

- Fixed an issue where the project diagnostics view spuriously allowed
horizontal scrolling by a small amount.
2024-10-29 09:16:38 -07:00
Michael Sloan
3fed738d2f Use same logic for skipping single instance check on Linux as on Mac/Win (#19446)
Release Notes:

- Linux: Now skips check which exits with "zed is already running" when
in development mode or when run with `zed-local`, matching the behavior
on Mac and Windows

Co-authored-by: Nathan Sobo <nathan@zed.dev>
2024-10-29 09:12:34 -07:00
Kirill Bulatov
5893e85708 Ensure shared ssh project propagates buffer changes to all participants (#19907)
Fixed the bug when shared ssh project did not account for client
changing things in their buffers.
Also ensures Prettier formatting workflow works for both ssh project
owner and ssh project clients.

Release Notes:

- N/A

---------

Co-authored-by: Conrad Irwin <conrad@zed.dev>
2024-10-29 17:24:10 +02:00
Joseph T. Lyons
1356665ed3 Update community links page url (#19899)
See:

- https://github.com/zed-industries/zed.dev/pull/786

Release Notes:

- N/A
2024-10-29 10:13:26 -04:00
Jen Stehlik
9739da8de3 Add Gleam icon (#19887)
I took a shot at creating an icon version of the Gleam logo in response
to https://github.com/zed-industries/zed/pull/19529

Release Notes:

- Added an icon for Gleam files.


![image](https://github.com/user-attachments/assets/97432ded-342f-4d87-8eb2-dc9145513d8c)

<img width="231" alt="Screenshot 2024-10-29 at 9 46 33 AM"
src="https://github.com/user-attachments/assets/c957c98f-3da0-4b92-bc21-2a5adca1daa3">

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-10-29 09:54:21 -04:00
Joseph T. Lyons
249c8a4d96 Remove community content from docs and point to zed.dev (#19895)
The community content now lives on zed.dev, discoverable via the navbar
`resources` menu.

See:

- https://github.com/zed-industries/zed.dev/pull/783

Release Notes:

- N/A
2024-10-29 09:44:58 -04:00
Thorsten Ball
f919fa92de remote servers: Fix title from alpha to beta (#19889)
Discussed this in Slack yesterday. We use `beta` because that's what we
use in the docs as well.

Release Notes:

- N/A
2024-10-29 13:38:30 +01:00
Bennet Bo Fenner
21b58643fa vsc menu: Fix issue when switching branch while non-visible worktree is open (#19888)
Fixes a regression introduced in #19755

<img width="935" alt="Screenshot 2024-10-29 at 12 13 04"
src="https://github.com/user-attachments/assets/7699b8da-631d-4932-89a8-bc5d7f2546f1">

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

Release Notes:

- Fixed an issue where the branch switcher would show an error, when
opening a file outside of the project

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-10-29 12:27:30 +01:00
Bennet Bo Fenner
6a0bcca9ec ssh remoting: Hide share button while connecting to project (#19885)
Co-Authored-by: Thorsten <thorsten@zed.dev>

Release Notes:

- N/A

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-10-29 12:13:21 +01:00
PJ Tatlow
84328c303b Include commit summary in inline Git blame (#19759)
Closes #19758

Release Notes:

- Added feature to show commit summary as part of the inline Git blame

---------

Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
2024-10-29 11:35:31 +01:00
Bennet Bo Fenner
f7b2b41df9 ssh remoting: Check nightly version correctly by comparing commit SHA (#19884)
This ensures that we detect if a new nightly version of the remote
server is available.
Previously we would always mark a version as matching if they had the
same semantic version.
However, for nightly versions we also need to check if they have the
same commit SHA.

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

Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-10-29 11:32:55 +01:00
Bennet Bo Fenner
7a6b6435c4 languages: Enable grammar loading when compiling with test feature (#19881)
This ensures that `cargo tests -p languages` will not fail with a
confusing error message.

Follow up to #19821

We opted to check the `test` feature flag instead of defining a runtime
flag, because we only want to include the `tree-sitter-*` dependencies
in some cases, which is not possible with a runtime flag.

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

Release Notes:

- N/A

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-10-29 11:00:44 +01:00
Bennet Bo Fenner
bdb54decdc ssh remoting: Show the host's GitHub name in the titlebar when sharing an SSH project (#19844)
The name (GitHub name) of the host was not displayed when sharing an ssh
project.

Previously we assumed that the a collaborator is a host if the
`replica_id` of the collaborator was `0`,
but for ssh project the `replica_id` is actually `1`.

<img width="329" alt="Screenshot 2024-10-28 at 18 16 30"
src="https://github.com/user-attachments/assets/c0151e12-a96f-4f38-aec1-4ed5475a9eaf">


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

Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-10-29 09:52:54 +01:00
Bennet Bo Fenner
b5c41eeb98 Future-proof indent guides settings for panels (#19878)
This PR ensures that we do not have to break the indent guides settings
for the project/outline panel. In the future we might want to have a
more granular way to control when to show indent guides, or control
other indent guide properties, like its width.

Release Notes:

- N/A
2024-10-29 09:52:36 +01:00
Conrad Irwin
719a7f7890 Fix block cursor on graphemes (#19867)
Release Notes:

- Fixed block cursor rendering only first char of multii-char graphemes.
2024-10-28 21:05:24 -06:00
Joseph T. Lyons
1b84fee708 restore editor::UnfoldRecursive binding (#19865) 2024-10-28 22:32:59 -04:00
Conrad Irwin
58e5d4ff02 Reland invisibles (#19846)
Release Notes:

- Show invisibles in the editor

Relands #19298 

Trying to quantify a performance impact, it doesn't seem to impact much
visible in Instruments or in a micro-benchmark of Editor#layout_lines.
We're still taking a few hundred micro-seconds (+/- a lot) every time.
The ascii file has just ascii, where as the cc file has one control
character per line.

<img width="1055" alt="Screenshot 2024-10-28 at 12 14 53"
src="https://github.com/user-attachments/assets/1c382063-bb19-4e92-bbba-ed5e7c02309f">
<img width="1020" alt="Screenshot 2024-10-28 at 12 15 07"
src="https://github.com/user-attachments/assets/1789f65e-5f83-4c32-be47-7748c62c3703">
2024-10-28 20:27:09 -06:00
Mikayla Maki
85ff03cde0 Add more context to the save new file path picker (#19863)
Release Notes:

- N/A

Co-authored-by: Conrad <conrad@zed.dev>
2024-10-28 17:21:41 -07:00
Conrad Irwin
a3f0bb4547 SSH Remoting: Document manual binary management (#19862)
Release Notes:

- N/A
2024-10-28 17:10:08 -06:00
Marshall Bowers
93b20008e0 Add support for Doxygen doc comments in C++ (#19858)
This PR adds support for Doxygen-style doc comments in C++.

<img width="962" alt="Screenshot 2024-10-28 at 5 38 34 PM"
src="https://github.com/user-attachments/assets/57d0fa4b-07c1-4b71-899a-fba78e822e8f">

https://www.doxygen.nl/manual/docblocks.html

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

Release Notes:

- C++: Added support for Doxygen-style doc comments starting with `/// `
or `//! `.
2024-10-28 17:44:23 -04:00
ClanEver
188a893fd0 python: Enhance syntax highlighting for type hints (#18185)
Release Notes:

- Python: Improved syntax highlighting for type hints.

# Before

![image](https://github.com/user-attachments/assets/876a69ab-a572-4d1b-af99-e6f85f249ea6)

# After

![image](https://github.com/user-attachments/assets/4fb98a9b-bc5d-4799-b535-057047884383)

---
Why manual recursion?
- Due to tree-sitter grammar not supporting recursion in query
(https://github.com/tree-sitter-grammars/tree-sitter-lua/issues/24),
currently only manual recursion is possible (refer to
https://github.com/projekt0n/github-nvim-theme/pull/250/files).

<br/>

Unable to highlight when simple structures appear before complex
structures, example:
```python
def t() -> str | dict[int, dict[int, dict[int, str]]]:
    pass
```
Because complex structures are parsed as `subscript` rather than
`generic_type` by tree-sitter in this case ☹

<br/>

Related:

- https://github.com/zed-industries/zed/issues/14715
- [Union Type (Python
Doc)](https://docs.python.org/3/library/stdtypes.html#union-type)
- [Type parameter lists (Python
Doc)](https://docs.python.org/3/reference/compound_stmts.html#type-parameter-lists)

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-10-28 16:56:59 -04:00
Piotr Osiewicz
052b746fbd language_selector: Fix debug_assert firing off on context menu creation for LSP view (#19854)
Closes #ISSUE

Release Notes:

- N/A
2024-10-28 21:55:38 +01:00
Mikayla Maki
80f89059aa Fix mouse clicks on remote-open-folder UI (#19851)
Also change Zed's standard style to use
`.track_focus(&self.focus_handle(cx))`, instead of
`.track_focus(&self.focus_handle)`, to catch these kinds of errors more
easily in the future.

Release Notes:

- N/A

---------

Co-authored-by: Conrad <conrad@zed.dev>
2024-10-28 12:55:55 -07:00
Mikayla Maki
826d83edfe Fix backtrace spam on remote server (#19850)
Release Notes:

- N/A

Co-authored-by: conrad <conrad@zed.dev>
2024-10-28 12:28:42 -07:00
Joseph T. Lyons
f5d5fab2c8 Improve fold_at_level performance (#19845)
Just spotted a tiny error that was causing us to continue looking for
nested folds 1 layer deeper than any fold already found at the target
level. We shouldn't continue to seek for a deeper fold after the fold at
the target level is found.

Tested on a debug build and used `editor.rs` as the source material:

```
Old

Level 1 fold:
[crates/editor/src/editor.rs:10777:9] counter = 2806
[crates/editor/src/editor.rs:10778:9] time_elapsed = 320.570792ms

Level 2 fold:
[crates/editor/src/editor.rs:10777:9] counter = 5615
[crates/editor/src/editor.rs:10778:9] time_elapsed = 497.4305ms

Level 3 fold:
[crates/editor/src/editor.rs:10777:9] counter = 7528
[crates/editor/src/editor.rs:10778:9] time_elapsed = 619.818334ms

New

Level 1 fold:
[crates/editor/src/editor.rs:10776:9] counter = 543
[crates/editor/src/editor.rs:10777:9] time_elapsed = 139.115625ms

Level 2 fold:
[crates/editor/src/editor.rs:10776:9] counter = 2806
[crates/editor/src/editor.rs:10777:9] time_elapsed = 312.560416ms

Level 3 fold:
[crates/editor/src/editor.rs:10776:9] counter = 5615
[crates/editor/src/editor.rs:10777:9] time_elapsed = 498.873292ms
```

Release Notes:

- N/A
2024-10-28 13:37:28 -04:00
Thorsten Ball
fab2f22a89 remote project: Fix project reference leak when waiting for prompt reply (#19838)
When the language server gave us a prompt and we'd close the window, we
wouldn't release the `project` until the next `flush_effects` call that
came in when opening a window.

With this change, we no longer hold a strong reference to the project in
the future. Fixes the leak and makes sure we clean up the SSH connection
when closing a window.

Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-10-28 17:07:30 +01:00
Marshall Bowers
a451bcc3c4 collab: Exempt staff from LLM usage limits (#19836)
This PR updates the usage limit check to exempt Zed staff members from
usage limits.

We previously had some affordances for the rate limits, but hadn't yet
updated it for the usage-based billing.

Release Notes:

- N/A
2024-10-28 11:45:18 -04:00
Marshall Bowers
5e9ff3e313 dart: Bump to v0.1.2 (#19835)
This PR bumps the Dart extension to v0.1.2.

Changes:

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

Release Notes:

- N/A
2024-10-28 11:36:44 -04:00
Thorsten Ball
cc81f19c68 remote server: Fix error log about inability to open buffer (#19824)
Turns out that we used client-side `fs` to check whether something is a
directory or not, which obviously doesn't work with SSH projects.

Release Notes:

- N/A

---------

Co-authored-by: Bennet <bennet@zed.dev>
2024-10-28 16:35:37 +01:00
Ömer Sinan Ağacan
5e89fba681 dart: Add support for documentation comments (#19592)
Closes #19590

Release Notes:

- N/A
---

I'm unable to test this because rebuilding Zed with the changes does not
seem to use the changes. If maintainers could let me know how to test
these changes I'd like to verify that this really fixes #19590.

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-10-28 11:20:04 -04:00
Thorsten Ball
67eb652bf1 remote servers: Always dismiss modal (#19831)
We display the errors in another window anyway and if the connection
takes a while it looks like a bug that the modal stays open.

Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-10-28 16:12:37 +01:00
Bennet Bo Fenner
e0ea9a9ab5 Remove leftover comments from previous PR (#19820)
Co-Authored-by: Thorsten <thorsten@zed.dev>

Removes some leftover comments from #19766 

Release Notes:

- N/A

Co-authored-by: Thorsten <thorsten@zed.dev>
2024-10-28 16:00:38 +01:00
xdBronch
ff29a34298 zig: Account for doctests in outline (#19776)
zig has a feature called
[doctests](https://ziglang.org/documentation/master/#Doctests) where
instead of providing a string as the name of a test you use an
identifier so that the test is "tied" to it and can be used in
documentation. this wasnt accounted for so any tests using this were
unnamed in the outline

Release Notes:

- N/A
2024-10-28 10:49:40 -04:00
Thorsten Ball
6686f66949 ollama: Ensure only single task fetches models (#19830)
Before this change, we'd see a ton of requests from the Ollama provider
trying to fetch models:

```
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: https://api.zed.dev/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
[2024-10-28T15:00:52+01:00 DEBUG reqwest::connect] starting new connection: http://localhost:11434/
```

Turns out we'd send a request on *every* change to settings.

Now, with this change, we only send a single request.

Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-10-28 15:40:50 +01:00
David Soria Parra
8a96ea25c4 context_servers: Support tools (#19548)
This PR depends on #19547 

This PR adds support for tools from context servers. Context servers are
free to expose tools that Zed can pass to models. When called by the
model, Zed forwards the request to context servers. This allows for some
interesting techniques. Context servers can easily expose tools such as
querying local databases, reading or writing local files, reading
resources over authenticated APIs (e.g. kubernetes, asana, etc).

This is currently experimental. 

Things to discuss
* I want to still add a confirm dialog asking people if a server is
allows to use the tool. Should do this or just use the tool and assume
trustworthyness of context servers?
* Can we add tool use behind a local setting flag?

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-10-28 10:37:58 -04:00
Piotr Osiewicz
cdddb4d360 Add language toolchains (#19576)
This PR adds support for selecting toolchains for a given language (e.g.
Rust toolchains or Python virtual environments) with support for SSH
projects provided out of the box. For Python we piggy-back off of
[PET](https://github.com/microsoft/python-environment-tools), a library
maintained by Microsoft.
Closes #16421
Closes #7646

Release Notes:

- Added toolchain selector to the status bar (with initial support for
Python virtual environments)
2024-10-28 15:34:03 +01:00
Thorsten Ball
03bd95405b docs: Add diagram to remote development docs (#19827)
Release Notes:

- N/A
2024-10-28 14:14:51 +01:00
Kirill Bulatov
177dfdf900 Declare RUSTFLAGS env var for all CI jobs (#19826)
Follow-up of https://github.com/zed-industries/zed/pull/19149 

Makes RUSTFLAGS propagation uniform, to ensure all `cargo ...` jobs get
the same RUSTFLAGS env set.

Release Notes:

- N/A
2024-10-28 14:53:40 +02:00
Thorsten Ball
2ab0b3b819 remote server: Fix language servers not starting (#19821)
PR #19653 change the code in this diff, which lead to the remote_server
binary trying to load language grammars, which in turn failed, and
stopped languages from being loaded correctly.

That then lead to language servers not starting up.

This change reintroduces what #19653 removed, so that we don't load the
grammar on the remote_server, by ignoring the grammar name from the
config. The tests still all work.


Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-10-28 11:02:46 +01:00
Bennet Bo Fenner
888fec9299 outline panel: Add indent guides (#19719)
See #12673

| File | Search |
|--------|--------|
| <img width="302" alt="image"
src="https://github.com/user-attachments/assets/44b8d5f9-8446-41b5-8c0f-e438050f0ac9">
| <img width="301" alt="image"
src="https://github.com/user-attachments/assets/a2e6f77b-6d3b-4f1c-8fcb-16bd35274807">
|



Release Notes:

- Added indent guides to the outline panel
2024-10-28 09:54:18 +01:00
Bennet Bo Fenner
e86b096b92 docs: Add indent_guides setting to project panel docs (#19819)
Follow up to #18260 

Release Notes:

- N/A
2024-10-28 09:45:19 +01:00
Mikayla Maki
ffe36c9beb Remove hosted projects (#19754)
Release Notes:

- N/A
2024-10-27 19:44:21 -07:00
Kirill Bulatov
2d16d2d036 Fixed outline panel panicking on filtering (#19811)
Closes https://github.com/zed-industries/zed/issues/19732

Release Notes:

- Fixed outline panel panicking on filtering
([#19732](https://github.com/zed-industries/zed/issues/19732))
2024-10-28 02:31:02 +02:00
Mikayla Maki
c69da2df70 Add support for git branches on remote projects (#19755)
Release Notes:

- Fixed a bug where the branch switcher could not be used remotely.
2024-10-27 15:50:54 -07:00
CharlesChen0823
5506669b06 windows: Fix more windows platform test (#19802)
Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2024-10-27 19:15:23 +02:00
Bennet Bo Fenner
b13940720a markdown preview: Ignore inline HTML tags in text (#19804)
Follow up to #19785

This PR ensures that we explicitly ignore inline HTML tags so that we
can still extract the text between the tags and show them to the user

Release Notes:

- N/A
2024-10-27 14:34:59 +01:00
Peter Tripp
db61711753 ci: Don't run GitHub Actions workflows on forks (#19789)
- Closes: https://github.com/zed-industries/zed/issues/19351

Release Notes:

- N/A
2024-10-27 09:04:52 -04:00
Joseph T. Lyons
c12a9f2673 Add fold_at_level test (#19800) 2024-10-26 21:57:55 -04:00
Kirill Bulatov
2e32f1c8a1 Restore horizontal scrollbar checks (#19767)
Closes https://github.com/zed-industries/zed/issues/19637

Follow-up of https://github.com/zed-industries/zed/pull/18927 , restores
the condition that removed the horizontal scrollbar when panel's items
are not long enough.

Release Notes:

- Fixed horizontal scrollbar not being hidden
([#19637](https://github.com/zed-industries/zed/issues/19637))
2024-10-26 21:57:22 +03:00
Bennet Bo Fenner
03a1c8d2b8 markdown preview: Fix infinite loop in parser when parsing list items (#19785)
Release Notes:

- Fixed an issue with the markdown parser when opening a markdown
preview file that contained HTML tags inside a list item
2024-10-26 14:59:46 +02:00
Kirill Bulatov
d7a277607b Fix a few Windows tests (#19773) 2024-10-26 03:32:22 +03:00
Thorsten Ball
fc8a72cdd8 WIP: ssh remoting: Add upload_binary field to SshConnections (#19748)
This removes the old `remote_server { "download_binary_on_host": bool }`
field and replaces it with a `upload_binary: bool` on every
`ssh_connection`.


@ConradIrwin it compiles, it connects, but I haven't tested it really
yet

Release Notes:

- N/A

---------

Co-authored-by: Conrad <conrad@zed.dev>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-10-25 17:32:54 -06:00
Kirill Bulatov
1acebb3c47 Remove another false-positive Danger message (#19769)
Follow-up of https://github.com/zed-industries/zed/pull/19151

Ignores any URLs aftrer `Release Notes:` (if present) and after
`Follow-up of` and `Part of` words.


Release Notes:

- N/A
2024-10-26 01:55:46 +03:00
Conrad Irwin
78ed0c9312 vim: Copy comment to new lines with o/O (#19766)
Co-Authored-By: Kurt Wolf <kurtwolfbuilds@gmail.com>

Closes: #4691

Closes #ISSUE

Release Notes:

- vim: o/O now respect `extend_comment_on_newline`
2024-10-25 16:47:44 -06:00
Conrad Irwin
98d2e5fe73 Quote fixes (#19765)
Closes #19372

Release Notes:

- Fixed autoclosing quotes when the string is already open.
- Added autoclosing of rust multiline strings

---------

Co-authored-by: Kurt Wolf <kurtwolfbuilds@gmail.com>
2024-10-25 16:28:08 -06:00
Max Brunsfeld
4325819075 Fix more failure cases of assistant edits (#19653)
* Make `description` optional (since we describe it as optional in the
prompt, and we're currently not showing it)
* Fix fuzzy location bug that neglected the cost of deleting prefixes of
the query.
* Make auto-indent work for single-line edits. Previously, auto-indent
would not occur when overwriting a single line (without inserting or
deleting a newline)

Release Notes:

- N/A
2024-10-25 14:30:34 -07:00
Marshall Bowers
c19c89e6df collab: Include checkout_complete query parameter after checking out (#19763)
This PR updates the checkout flow to include the `?checkout_complete=1`
query parameter after successfully checking out.

We'll use this on the account page to adapt the UI accordingly.

Release Notes:

- N/A
2024-10-25 16:29:20 -04:00
Joseph T. Lyons
507929cb79 Add editor: fold at level <level> commands (#19750)
Closes https://github.com/zed-industries/zed/issues/5142

Note that I only moved the cursor to the top of the file so it wouldn't
jump - the commands work no matter where you are in the file.


https://github.com/user-attachments/assets/78c74ca6-5c17-477c-b5d1-97c5665e44b0

Also, is VS Code doing this right thing here? or is it busted?


https://github.com/user-attachments/assets/8c503b50-9671-4221-b9f8-1e692fe8cd9a

Release Notes:

- Added `editor: fold at level <level>` commands. macOS: `cmd-k,
cmd-<number>`, Linux: `ctrl-k, ctrl-<number>`.

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2024-10-25 16:08:37 -04:00
Max Brunsfeld
7d0a7aff44 Fix condition for re-using highlights when seeking buffer chunks iterator (#19760)
Fixes a syntax highlighting regression introduced in
https://github.com/zed-industries/zed/pull/19531, which caused syntax
highlighting to be missing after any block.

Release Notes:

- N/A
2024-10-25 12:37:03 -07:00
Kirill Bulatov
92ba18342c Properly deserialize active pane in the workspace (#19744)
Without setting the active pane metadata, no center pane events are
emitted on start before the pane is focused manually, which breaks
deserialization of other components like outline panel, which should
show the active pane's active item outlines on start.

Release Notes:

- N/A

Co-authored-by: Thorsten Ball <thorsten@zed.dev>
2024-10-25 22:04:09 +03:00
Kirill Bulatov
6de5ace116 Update outline panel representation when a theme is changed (#19747)
Release Notes:

- N/A
2024-10-25 22:04:02 +03:00
Richard Feldman
c9db1b9a7b Add keybindings for accepting hunks (#19749)
I went with Cmd-Shift-Y on macOS (Ctrl-Shift-Y on Linux) for "yes accept
this individual hunk" - both are currently unused.

I went with Cmd-Shift-A on macOS (Ctrl-Alt-A on Linux) for "accept all
hunks" - both are unused. (Ctrl-Shift-A on Linux was taken, as is
Ctrl-Alt-Y, so although the pairing of Ctrl-Shift-Y and Ctrl-Alt-A isn't
necessarily obvious, the letters seem intuitive - "yes" and "all" - and
those key combinations don't conflict with anything.)

Release Notes:

- Added keybindings for applying hunks in Proposed Changes
<img width="247" alt="Screenshot 2024-10-25 at 12 47 00 PM"
src="https://github.com/user-attachments/assets/d6355621-ba80-4ee2-8918-b7239a4d29be">
2024-10-25 14:10:04 -04:00
Nathan Sobo
24cb694494 Update placeholder text with key bindings to focus context panel and navigate history (#19447)
Hopefully, this will help people understand how easy it is to add
context to an inline transformation.

![CleanShot 2024-10-18 at 22 41
00@2x](https://github.com/user-attachments/assets/c09c1d89-3df2-4079-9849-9de7ac63c003)

@as-cii @maxdeviant @rtfeldman could somebody update this to display the
actual correct key bindings and ship it. I have them hard coded for now.

Release Notes:

- Updated placeholder text with key bindings to focus context panel and
navigate history.

---------

Co-authored-by: Richard Feldman <oss@rtfeldman.com>
2024-10-25 14:09:21 -04:00
Conrad Irwin
85bdd9329b Revert "Show invisibles in editor (#19298)" (#19752)
Closes: #19714

This reverts commit 6dcec47235.

Release Notes:

- (preview only) Fixes a crash when rendering invisibles
2024-10-25 11:59:22 -06:00
Kirill Bulatov
d40ea8fc81 Make macOS bundle script compatible with GNU sed (#19745)
Closes https://github.com/zed-industries/zed/issues/19742

Release Notes:

- N/A
2024-10-25 19:04:38 +03:00
Marshall Bowers
5f9a1482f1 assistant: Make /file emit events as they occur (#19743)
This PR updates the `/file` command to emit its `SlashCommandEvent`s in
a way that can actually be streamed.

Previously it was buffering up all of the events and then returning them
all at once.

Note that we still don't yet support streaming in the context editor on
`main`, so there won't be any visible changes just yet.

Release Notes:

- N/A
2024-10-25 11:02:27 -04:00
Thorsten Ball
5c2238c7a5 ssh remoting: Use matching versions of remote server binary (#19740)
This changes the download logic to not fetch the latest version, but to
fetch the version matching the current version of Zed.


Release Notes:

- Changed the update logic of the SSH remote server to not fetch the
latest version for a current channel, but to fetch the version matching
the current Zed version. If Zed is updated, the server is updated too.
If the server is newer than the Zed version an error will be displayed.
2024-10-25 16:27:36 +02:00
Piotr Osiewicz
5769065f27 project panel: Persist full filename when renaming auto-folded entries (#19728)
This fixes a debug-only panic when processing filenames. The underflow
that happens in Preview/Stable shouldn't cause any issues (other than
maybe unmarking an entry in the project panel).

/cc @notpeter

Closes #ISSUE

Release Notes:

- N/A
2024-10-25 13:47:01 +02:00
Thorsten Ball
0173479d18 ssh remoting: Lock file becomes stale if connection drops & no update if binary is running (#19724)
Release Notes:

- Changed the update process of the remote server binary to not attempt
an update if we can detect that the current binary is used by another
process.
- Changed the update process of the remote server binary to mark the
lock file as stale in case the SSH connection of the process that
created the lock file isn't open anymore.
2024-10-25 12:42:31 +02:00
Max Brunsfeld
08a3c54bac Allow editor blocks to replace ranges of text (#19531)
This PR adds the ability for editor blocks to replace lines of text, but
does not yet use that feature anywhere. We'll update assistant patches
to use replace blocks on another branch:
https://github.com/zed-industries/zed/tree/assistant-patch-replace-blocks

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Richard Feldman <richard@zed.dev>
Co-authored-by: Marshall Bowers <marshall@zed.dev>
Co-authored-by: Nathan Sobo <nathan@zed.dev>
2024-10-25 12:29:25 +02:00
Piotr Osiewicz
3617873431 project panel: Fix interactions with auto-folded directories (#19723)
Closes https://github.com/zed-industries/zed/issues/19566

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2024-10-25 12:13:21 +02:00
Bennet Bo Fenner
6eb6788201 image viewer: Reuse existing tabs (#19717)
Co-authored-by: Kirill <kirill@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>

Fixes #9896

Release Notes:

- Fixed an issue where clicking on an image inside the project panel
would not re-use an existing image tab

Co-authored-by: Kirill <kirill@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>
2024-10-25 09:34:50 +02:00
Conrad Irwin
ebc3031fd9 Inline initialization (#19711)
This restores all the init behaviour into main again. This means we
never need to call init_ui (and so we can't call it more than once).

Release Notes:

- (Nightly only) fixes a panic when using the cli to open another file
in a running zed.
2024-10-24 20:43:40 -06:00
Danilo Leal
42a7402cc5 assistant: Use a labeled button for the slash command menu (#19703)
This should help a bit more the discoverability of the slash commands.

Release Notes:

- N/A
2024-10-24 19:37:42 -03:00
Danilo Leal
6cd5c9e32f assistant: Tweak the model selector design (#19704)
Exploring using the UI font for it, as it is more common for dropdowns
and popovers throughout the app. Feeling like it makes it lighter and
also shorter in width!

| Before | After |
|--------|--------|
| <img width="1296" alt="Screenshot 2024-10-24 at 16 39 04"
src="https://github.com/user-attachments/assets/0412f922-77a9-4d83-adf9-5632534d6c5b">
| <img width="1296" alt="Screenshot 2024-10-24 at 16 38 26"
src="https://github.com/user-attachments/assets/8bf52ba7-fda7-4437-b53e-903c282f2931">
|

Release Notes:

- N/A
2024-10-24 19:37:09 -03:00
Conrad Irwin
d45b830412 SSH connection pooling (#19692)
Co-Authored-By: Max <max@zed.dev>

Closes #ISSUE

Release Notes:

- SSH Remoting: Reuse connections across hosts

---------

Co-authored-by: Max <max@zed.dev>
2024-10-24 14:37:54 -06:00
Conrad Irwin
3a9c071e6e Fix partial downloads of ssh remote server (#19700)
Release Notes:

- SSH Remoting: fix a bug where inerrrupting ssh connecting could leave
your local binary cached in an invalid state
2024-10-24 14:37:02 -06:00
Marshall Bowers
ca861bb1bb ui: Fix swapped element background colors (#19701)
This PR fixes an issue introduced in
https://github.com/zed-industries/zed/pull/18768 where the element
backgrounds colors for `ElevationIndex::ElevatedSurface` and
`ElevationIndex::Surface` were swapped.

Release Notes:

- N/A
2024-10-24 16:34:45 -04:00
Kirill Bulatov
454d3dd52b Fix ssh project history (#19683)
Use `Fs` instead of `std::fs` and do entry existence checks better:
* first, check the worktree entry existence without any FS checks
* then, only for local cases, use `Fs` to check for abs_path existence
of items, in case those came from single-filed worktrees that got closed
and removed.

Remote entries do not get file existence checks, so might try opening
previously removed buffers for now.

Release Notes:

- N/A
2024-10-24 21:49:07 +03:00
Peter Tripp
3ec015b325 docs: Example theme_overrides for docstrings as italic (#19694) 2024-10-24 14:37:57 -04:00
Mikayla Maki
02718284ef Remove dev servers (#19638)
TODO:

- [ ] Check that workspace migration worked
- [ ] Add server migrations and make sure SeaORM files are in sync
(maybe?)

Release Notes:

- N/A

---------

Co-authored-by: Conrad <conrad@zed.dev>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-10-24 12:14:03 -06:00
Marshall Bowers
b5f816dde5 assistant: Add implementation for /delta argument completion (#19693)
This PR fixes a panic that could occur when trying to complete arguments
for the `/delta` slash command.

We were using `unimplemented!()` instead of providing a default no-op
implementation like we do for other slash commands that do not support
completing arguments.

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

Release Notes:

- Fixed a panic that could occur when trying to complete arguments with
the `/delta` command.
2024-10-24 13:39:06 -04:00
Antonio Scandurra
499e1459eb Fix crash in collab when sending worktree updates (#19678)
This pull request does a couple of things:

- In 29c2df73e1, we introduced a safety
guard that prevents this crash from happening again in the future by
returning an error instead of panicking when the payload is too large.
- In 3e7a2e5c30, we introduced chunking
for updates coming from SSH servers (previously, we were sending the
whole changeset and initial set of paths in their entirety).
- In 122b5b4, we introduced a panic hook that sends panics to Axiom.

For posterity, this is how we figured out what the panic was:

```
kubectl logs current-pod-name --previous --namespace=production
```

Release Notes:

- N/A

---------

Co-authored-by: Thorsten <thorsten@zed.dev>
Co-authored-by: Bennet <bennet@zed.dev>
Co-authored-by: Kirill <kirill@zed.dev>
2024-10-24 15:57:24 +02:00
Danilo Leal
b5aea548a8 ssh: Capitalize error and connection strings (#19675)
Another tiny PR for the sake of consistency :)

Release Notes:

- N/A
2024-10-24 09:43:35 -03:00
Danilo Leal
3c6a505166 docs: Add tweaks to the Remote Development page (#19674)
Just making just we also add the other keybinding to open the Remote
Projects dialog and capitalize every "SSH" mention for consistency. Tiny
stuff!

Release Notes:

- N/A
2024-10-24 09:35:59 -03:00
Thorsten Ball
efc4d3efdf ssh remoting: Fix wrong working directory for SSH terminals (#19672)
Before this change, we would save the working directory *on the client*
of each shell that was running in a terminal.

While it's technically right, it's wrong in all of these cases where
`working_directory` was used:

- in inline assistant
- when resolving file paths in the terminal output
- when serializing the current working dir and deserializing it on
restart

Release Notes:

- Fixed terminals opened on remote hosts failing to deserialize with an
error message after restarting Zed.
2024-10-24 13:52:26 +02:00
Bennet Bo Fenner
4214ed927f project panel: Add indent guides (#18260)
See #12673



https://github.com/user-attachments/assets/94079afc-a851-4206-9c9b-4fad3542334e



TODO:
- [x] Make active indent guides work for autofolded directories
- [x] Figure out which theme colors to use
- [x] Fix horizontal scrolling
- [x] Make indent guides easier to click
- [x] Fix selected background flashing when hovering over entry/indent
guide
- [x] Docs

Release Notes:

- Added indent guides to the project panel
2024-10-24 13:07:20 +02:00
Zhang
e040b200bc project_panel: Make up/down in file rename editor not select items (#19670)
Closes #19017 

Release Notes:

- Fixed project panel bug when renaming files where up/down keys could
select other files.
2024-10-24 12:15:42 +02:00
Thorsten Ball
1dba50f42f ssh remoting: Fix version check (#19668)
This snuck in when Bennet and I were debugging why our connection to the
SSH host would break. We suspected that somewhere something was logging
to STDOUT and, I guess, we changed all `println!` to `eprintln!`.

Now, two weeks later, I'm sitting here, wondering why the version check
doesn't work anymore. The server always reports a version of `""`.

Turns out we take the command's STDOUT and not STDERR, which is correct.

But it also turns out we started to print the version to STDERR, which
breaks the version check.

One-character bug & one-character fix.

Release Notes:

- N/A
2024-10-24 11:37:32 +02:00
renovate[bot]
0ffc92ab65 Update actions/checkout digest to 11bd719 (#19636)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://redirect.github.com/actions/checkout) |
action | digest | `eef6144` -> `11bd719` |

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC4xMjAuMSIsInVwZGF0ZWRJblZlciI6IjM4LjEyMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 21:57:06 -04:00
Marshall Bowers
d30361537e assistant: Update SlashCommand trait with streaming return type (#19652)
This PR updates the `SlashCommand` trait to use a streaming return type.

This change is just at the trait layer. The goal here is to decouple
changing the trait's API while preserving behavior on either side.

The `SlashCommandOutput` type now has two methods for converting two and
from a stream to use in cases where we're not yet doing streaming.

On the `SlashCommand` implementer side, the implements can call
`to_event_stream` to produce a stream of events based off the
`SlashCommandOutput`.

On the slash command consumer side we use
`SlashCommandOutput::from_event_stream` to convert a stream of events
back into a `SlashCommandOutput`.

The `/file` slash command has been updated to emit `SlashCommandEvent`s
directly in order for it to work properly.

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
2024-10-23 21:26:50 -04:00
renovate[bot]
510c71d41b Pin crate-ci/typos action to 8e6a428 (#19635)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [crate-ci/typos](https://redirect.github.com/crate-ci/typos) | action
| pinDigest | -> `8e6a428` |

---

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 19:00:28 -04:00
renovate[bot]
013d2d52fd Update Rust crate anyhow to v1.0.91 (#19640)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [anyhow](https://redirect.github.com/dtolnay/anyhow) |
workspace.dependencies | patch | `1.0.89` -> `1.0.91` |

---

### Release Notes

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

###
[`v1.0.91`](https://redirect.github.com/dtolnay/anyhow/releases/tag/1.0.91)

[Compare
Source](https://redirect.github.com/dtolnay/anyhow/compare/1.0.90...1.0.91)

- Ensure OUT_DIR is left with deterministic contents after build script
execution
([#&#8203;388](https://redirect.github.com/dtolnay/anyhow/issues/388))

###
[`v1.0.90`](https://redirect.github.com/dtolnay/anyhow/releases/tag/1.0.90)

[Compare
Source](https://redirect.github.com/dtolnay/anyhow/compare/1.0.89...1.0.90)

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-24 00:45:58 +03:00
Conrad Irwin
eee91f3f1b docs: Update SSH docs (#19339)
Update of the SSH remoting docs

Release Notes:

- N/A
2024-10-23 14:25:27 -06:00
Kirill Bulatov
e87d5e145d Use zstd without dynamic linking due to musl usage (#19627)
Due to leaning towards `musl` builds, unit features for `zstd` and link
it statically too for Zed.


bfe1e34f59/zstd-safe/zstd-sys/build.rs (L260)
shows that `ZSTD_SYS_USE_PKG_CONFIG` env var can be used to return this
behavior.

Release Notes:

- N/A
2024-10-23 22:19:06 +03:00
Peter Tripp
291af664e1 Switch to Anthropic -latest tags (#19615)
- Closes: https://github.com/zed-industries/zed/issues/19609

Switches us to using `-latest` tags with Anthropic models instead of
pinning to a specific date version.
See: [Anthropic Model
Docs](https://docs.anthropic.com/en/docs/about-claude/models)

This is a no-op for:
- Claude 3 Opus (`claude-3-opus-20240229`)
- Claude 3 Sonnet (`claude-3-sonnet-20240229`)
- Claude 3 Haiku (`claude-3-haiku-20240307`)

For Claude 3.5 Sonnet this will update us from
`claude-3-5-sonnet-20240620` to `claude-3-5-sonnet-20241022`. We will
also pickup any subsequent model updates automatically when Anthropic
updates the `latest` tag.

This matches the behavior for OpenAI where use `gpt-4o` as the
model_name and not `gpt-4o-2024-08-06`.
2024-10-23 15:13:52 -04:00
Marshall Bowers
9c0dba4ce1 Add a SlashCommandResult type alias (#19633)
This PR adds a new `SlashCommandResult` type alias.

We're going to be changing what slash commands can return in order to
support streaming, so having this type alias in place will make that
switch a bit more neat.

Release Notes:

- N/A
2024-10-23 14:32:43 -04:00
Peter Tripp
8bfd27b00b docs: Improve Markdown trailing whitespace section (#19630) 2024-10-23 13:23:01 -04:00
Joseph T. Lyons
c6f08dea89 v0.160.x dev 2024-10-23 13:12:28 -04:00
Piotr Osiewicz
9dfe4a30bb languages: Do not expose unnecessary captures from tasks (#19625)
This tackles an issue with us exposing unnecessary env variables in
environment which are not actually needed for tasks themselves (and may
have little utility), yet come into the way of ssh remoting.

/cc @ConradIrwin 

Release Notes:

- N/A
2024-10-23 18:54:08 +02:00
Marshall Bowers
69b12f4e33 semantic_index: Disable embeddings index for non-staff (#19618)
This PR disables the embeddings index for non-staff users.

Release Notes:

- N/A
2024-10-23 12:34:51 -04:00
Danilo Leal
c3860804ff ssh: Ensure long server names (and nicknames) truncate (#19621)
Just polishing the UI a bit more. One drawback of this, though, is that
if you _do_ have a big nickname or server name, with this current
solution, you won't be able to see it. Ideally, we should be able to
hover over it and see it in a tooltip, but the `div` still doesn't
support that out of the box.

| Main modal | Modal header |
|--------|--------|
| <img width="1136" alt="Screenshot 2024-10-23 at 12 49 18"
src="https://github.com/user-attachments/assets/ed5f0222-faa1-49bd-b249-2f22497566d8">
| <img width="1136" alt="Screenshot 2024-10-23 at 12 49 23"
src="https://github.com/user-attachments/assets/5a464b12-99e8-4934-aa6a-c9c4c40ea4d4">
|

Release Notes:

- N/A
2024-10-23 13:31:03 -03:00
Thorsten Ball
6a0c19fcf9 ssh remoting: Log server version check result (#19622)
Release Notes:

- N/A
2024-10-23 18:30:48 +02:00
Tom Zaspel
622c266160 docs: Add documentation for auto_install_extensions setting (#19559)
Improved: Documenation for (un)installing extensions automatically.

Signed-off-by: Tom Zaspel <tom@zaspel.it>
Co-authored-by: Peter Tripp <peter@zed.dev>
2024-10-23 11:57:13 -04:00
Thorsten Ball
dc4396b79c ssh remoting: Fix overflowing host in titlebar (#19619)
Release Notes:

- N/A

Co-authored-by: Danilo <danilo@zed.dev>
2024-10-23 18:37:30 +03:00
Kirill Bulatov
1a59b6413b Fill the rest of the prompt data when sending to the client (#19616)
Follow-up of https://github.com/zed-industries/zed/pull/19587

Release Notes:

- N/A
2024-10-23 18:33:57 +03:00
Jamie Bowers
992155c60c Update example node settings in default.json to use the correct key for node path (#19607)
Hey team!

I was investigating the new node settings added in #18172, and when
trying to set the node path I noticed that the example settings in
`default.json` use the wrong key for the `node_path` - it should be
`path` instead.

See
[here](19eebcd349/crates/zed/src/main.rs (L488))
for where the setting is used.

Release Notes:

- N/A
2024-10-23 18:33:31 +03:00
Thorsten Ball
e633f62eaf ssh remoting: Fix opening settings file (#19614)
We have to do `workspace.with_local_workspace`, otherwise we'll try to
open the settings on the remote host.


Release Notes:

- N/A
2024-10-23 16:56:39 +02:00
Kirill Bulatov
b85af0e533 Fall back to handling the abs path for external worktree entries (#19612)
Certain files like Rust stdlib ones can be opened by cmd-clicking on
terminal, editor contents, etc.

Those files will not belong to the current worktree, so a fake worktree,
with a single file, invisible (i.e. its dir(s) will not be shown in the
UI such as project panel), will be created on the file opening.

When the file is closed, the worktree is closed and removed along the
way, so those worktrees are considered ephemeral and their ids are not
stored in the database.
This causes issues on reopening such files when they are closed. 

The PR makes Zed to fall back to opening the file by abs path when it's
not in the project metadata, but has the abs path stored in history or
in the opened items DB data.

Release Notes:

- Handle external worktree entries [re]open better
2024-10-23 17:34:23 +03:00
Thorsten Ball
bce1b7a10a ssh remoting: Add setting to download binary on host (#19606)
This adds the following optional setting:

```json
{
  "remote_server": {
    "download_on_host": false
  }
}
```
Right now, it's **off by default** because I haven't tested it enough.

Release Notes:

- N/A
2024-10-23 16:06:16 +02:00
Peter Tripp
c292bdd2ca docs: Improve language server configuration dotted/nested notation example (#19608) 2024-10-23 09:41:27 -04:00
Danilo Leal
1cb9f64917 ssh: Clean up bits of the main modal UI (#19604)
This PR main relevant change is removing the logic we had inserted for
keyboard nav scroll as that was unreliable; we need to figure out a
better solution still. I'm also removing the visible on hover behavior
for the scrollbar as that was making us lose the click and drag feature
the component has. Lastly, I added a bit of right-margin in the delete
icon button so that's not too crammed with the scrollbar.

Release Notes:

- N/A
2024-10-23 09:49:44 -03:00
Piotr Osiewicz
1bded42b2a ssh: Dismiss file picker when new window is opened (#19600)
Closes #ISSUE

Release Notes:

- N/A
2024-10-23 13:57:50 +02:00
Thorsten Ball
7f64f0454d ssh remoting: Ensure only single instance can upload binary (#19597)
This creates a `<binary_path>.lock` file and checks for it whenever
uploading a binary.

Parameters:
- Wait for other instance to finish: 10m
- Mark lockfile as stale and ignore it after: 10m
- When waiting on another process, check every 5seconds

We can tweak all of them.

Ideally we'd have a value in the lockfile, that lets us detect whether
the other process is still there, but I haven't found something stable
yet:

- We don't have a stable PID on the server side when we run multiple
commands
- The `ControlPath` is on the client side

Release Notes:

- N/A
2024-10-23 13:18:27 +02:00
Vitaly Slobodin
375bc88f95 lsp_log: Add server capabilities view (#19448)
Hello, this PR adds a new view to the LSP servers menu for
displaying an LSP server capabilities.

When I work on LSP stuff, quite often I need to check what capabilities
an LSP server has. Currently there is no built-in way for checking that
in Zed, and I have to use [`LSP
DevTools`](https://lsp-devtools.readthedocs.io) project. LSP DevTools
works OK but it works as a proxy between the client and the server, so
setting it up is not that easy in Zed. Zed already has many goodies for
LSP like tracing and RPC messages, so I thought that a simple view with
server capabilities could be useful too. Thanks!

## Some screenshots:

### Ruby LSP

![CleanShot 2024-10-19 at 07 44
38@2x](https://github.com/user-attachments/assets/22c97b49-c539-4e39-a5f1-1c926347abca)


### New menu entry:

![CleanShot 2024-10-19 at 07 45
08@2x](https://github.com/user-attachments/assets/d3903d6e-c09a-40e2-b042-1abde490987d)


Release Notes:

- N/A
2024-10-23 12:53:49 +02:00
wannacu
d53a86b01d project_panel: Fix the confusing display when files or directories have the same name in a case-insensitive comparison (#19211)
- Closes #18851

Release Notes:

- Fixed directory name comparison on Linux not considering the case
([#18851](https://github.com/zed-industries/zed/issues/18851))
2024-10-23 10:37:57 +03:00
Kevin Wang
6c7e79eff6 Cap the size of the Supermaven states buffer (#19246)
Caps the size of the Supermaven states buffer to 1000 elements.
Previously, the buffer would grow unbounded so for long sessions the
number of states that the Supermaven autocomplete provider maintains can
be quite large. In practice, states that are sufficiently old are so
unlikely to be visited again that we can regenerate the completion.
Thus, we can cap the buffer to 1000 elements.

Release Notes:

- N/A
2024-10-23 10:36:14 +03:00
Mikayla Maki
d0bc84eb33 Fix remoting things (#19587)
- Fixes modal closing when using the remote modal folder 
- Fixes a bug with local terminals where they could open in / instead of
~
- Fixes a bug where SSH connections would continue running after their
window is closed
- Hides SSH Terminal process details from Zed UI
- Implement `cmd-o` for remote projects
- Implement LanguageServerPromptRequest for remote LSPs

Release Notes:

- N/A
2024-10-23 00:14:43 -07:00
Joseph T. Lyons
fabc14355c Update telemetry docs to not point to specific lines of code (#19583)
The old links pointed to specific lines that were no longer the correct
lines. Let's skip pointing the reader of the docs to a specific line.

Release Notes:

- N/A
2024-10-23 00:36:43 -04:00
Conrad Irwin
07e086b41e Don't upload local settings to ssh remotes (#19577)
Closes: #18618

Release Notes:

- (breaking) SSH Remoting: stop uploading local settings to the remote.
2024-10-22 20:11:05 -06:00
Mikayla Maki
48674ec54c Add paths to connection headers (#19579)
Closes #ISSUE

<img width="562" alt="Screenshot 2024-10-22 at 3 21 22 PM"
src="https://github.com/user-attachments/assets/51afd8e8-b635-4d69-9463-2ccdaabeb955">

<img width="844" alt="Screenshot 2024-10-22 at 3 22 35 PM"
src="https://github.com/user-attachments/assets/ae57c0f8-396c-485d-b5e2-14558da98a18">

Release Notes:

- N/A

---------

Co-authored-by: Trace <violet.white.batt@gmail.com>
2024-10-22 16:50:33 -07:00
Finn Evers
fc7874e64e Fix GitHub link for upstream markdown grammer (#19580)
This PR removes an extra slash from the Github link to the upstream
Tree-sitter markdown grammer introduced in #19570 in the cargo-files.

Release Notes:

- N/A
2024-10-22 19:06:34 -04:00
Marshall Bowers
dcb0da0a7d collab: Return free tier usage from GET /billing/monthly_spend (#19578)
This PR updates the `GET /billing/monthly_spend` endpoint to also return
information about the free tier usage.

Release Notes:

- N/A
2024-10-22 18:06:54 -04:00
Conrad Irwin
a9f48bd9d1 Try to build with musl on CI (#19571)
- Closes #19092 
- Closes #15411

Release Notes:

- SSH Remoting: Build with musl (adds support for machines with old, or
no glibc)
2024-10-22 16:00:00 -06:00
Mikayla Maki
33197608ed Make closing the SSH modal equivalent to cancelling the SSH connection task (#19568)
TODO: 
- [x] Fix focus not being on the modal initially

Release Notes:

- N/A

---------

Co-authored-by: Trace <violet.white.batt@gmail.com>
2024-10-22 14:50:55 -07:00
Kirill Bulatov
f951410ef0 Use upstream tree-sitter-markdown (#19570)
After
https://github.com/tree-sitter-grammars/tree-sitter-markdown/pull/163 ,
no need to use zed-industries fork for tree-sitter-markdown

Release Notes:

- N/A
2024-10-23 00:41:01 +03:00
Peter Tripp
47ade2f9f9 Check for rsync before downloading updates (#19392) 2024-10-22 17:12:26 -04:00
Peter Tripp
263e143d1b macos: Add services menu (#16959) 2024-10-22 17:08:19 -04:00
Marshall Bowers
21a44d74bd collab: Prevent users from ending up with multiple active billing subscriptions (#19574)
This PR adds some safeguards to ensure that users do not end up with
multiple active billing subscriptions.

We now do the following:

1. When initiating a checkout, we first make sure the user does not
already have an active subscription.
2. When creating subscriptions in response to Stripe events, we ensure
that we don't already have an active subscription.

Release Notes:

- N/A
2024-10-22 17:01:59 -04:00
Derek Briggs
efd485cbb8 Add file icons for Zig, Julia, SCSS, HCL, Nix, and Roc (#19529)
Release Notes:

- Added file icons for Zig, Julia, SCSS, HCL, Nix, and Roc files.

<img width="733" alt="image"
src="https://github.com/user-attachments/assets/37602be7-173f-40d1-8177-3d35b11d4208">

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-10-22 16:03:58 -04:00
Kirill Bulatov
7a6550c1d1 Do not allow drag and drop of FS entries into the remote projects (#19565)
Release Notes:

- N/A
2024-10-22 22:34:54 +03:00
Danilo Leal
23ad470daf ssh: Add UI refinements to the remote modals (#19558)
This PR polishes spacing, borders, header design, font size, etc.

Release Notes:

- N/A
2024-10-22 16:33:59 -03:00
Conrad Irwin
6dcec47235 Show invisibles in editor (#19298)
Release Notes:

- Added highlighting for "invisible" unicode characters

Closes #16310

---------

Co-authored-by: dovakin0007 <dovakin0007@gmail.com>
Co-authored-by: dovakin0007 <73059450+dovakin0007@users.noreply.github.com>
2024-10-22 13:23:13 -06:00
Conrad Irwin
e93d62680d Improve disconnected modal for SSH (#19567)
Closes #ISSUE

Release Notes:

- SSH Remoting: made reconnect modal more robust
2024-10-22 13:22:27 -06:00
Marshall Bowers
5dbf68ddc4 editor: Save base buffers when applying changes from their branches (#19562)
This PR makes it so that when we apply changes within a branch
buffer—currently just the edits buffer—we save the underlying buffer.

This also fixes an issue where new files created via edits were not
properly flushed to disk.

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
2024-10-22 14:10:11 -04:00
Joseph T. Lyons
d8d84bf5d4 Tell users they can close a stale issue if they can't repro (#19563)
A user commented on this issue to let us know they couldn't reproduce,
which would keep it open for another 6 months. Best we can do is tell
them what to do if they can't repro, which is to close the issue
themselves.

- https://github.com/zed-industries/zed/issues/10671

Release Notes:

- N/A
2024-10-22 13:46:21 -04:00
Mikayla Maki
6f6893a93a Fix reversed prompt in #19533 (#19538)
Release Notes:

- N/A
2024-10-22 10:33:42 -07:00
Joseph T. Lyons
7ae25d10c8 Fix comment when marking issues as stale (#19561)
Oops

<img width="928" alt="image"
src="https://github.com/user-attachments/assets/a16ea5cd-c02d-4add-9b58-853de9aa5ba7">

Release Notes:

- N/A
2024-10-22 13:29:48 -04:00
Thorsten Ball
d80683f2bf ssh remoting: Fix heartbeat timer and exit conditions (#19557)
This contains a bunch of smallish but nasty fixes:

- Heartbeat timer was never reset after first heartbeat
- Use same return value when stderr is closed as when stdout is closed
- Always check proxy process status since it should also be done when we
get to this point (either it died and our task stopped, or our task
stopped and we dropped the process handle and it was killed on drop)
- make error messages less wrongly-specific


Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>
2024-10-22 18:45:56 +02:00
Adam Wolff
ab98d4889b assistant: Add tracking fields to Codegen for alternative assists (#19467)
Added some tracking fields to Codegen and CodegenAlternatives in order
to collect data for experimentation in a fork.

Release Notes:

- N/A
2024-10-22 12:45:01 -04:00
Richard Feldman
ce11ca9d49 Don't suggest assistant code actions if it's disabled (#19553)
Closes #19155

Release Notes:

- Fixed code action for "Fix with assistant" appearing when assistant
was disabled.
2024-10-22 12:36:36 -04:00
Kirill Bulatov
edda149d75 Do not remove worktrees after the headless server removal (#19556)
Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad@zed.dev>
2024-10-22 19:27:50 +03:00
renovate[bot]
291ca2c32c Update Rust crate wasmtime-wasi to v24.0.1 (#19338)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [wasmtime-wasi](https://redirect.github.com/bytecodealliance/wasmtime)
| workspace.dependencies | patch | `24.0.0` -> `24.0.1` |

---

### Release Notes

<details>
<summary>bytecodealliance/wasmtime (wasmtime-wasi)</summary>

###
[`v24.0.1`](https://redirect.github.com/bytecodealliance/wasmtime/releases/tag/v24.0.1)

[Compare
Source](https://redirect.github.com/bytecodealliance/wasmtime/compare/v24.0.0...v24.0.1)

#### 24.0.1

Released 2024-10-09.

##### Fixed

- Fix a runtime crash when combining tail-calls with host imports that
capture a
    stack trace or trap.

[GHSA-q8hx-mm92-4wvg](https://redirect.github.com/bytecodealliance/wasmtime/security/advisories/GHSA-q8hx-mm92-4wvg)

- Fix a race condition could lead to WebAssembly control-flow integrity
and type
    safety violations.

[GHSA-7qmx-3fpx-r45m](https://redirect.github.com/bytecodealliance/wasmtime/security/advisories/GHSA-7qmx-3fpx-r45m)

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-22 11:39:35 -04:00
Danilo Leal
3ba2af289b ssh: Expose server address in the title bar (#19549)
This PR exposes the server address (or the nickname, if there is one) on
the title bar and in all modals that have the SSH header. The title bar
tooltip meta description still shows the original server address
(regardless of a nickname existing in this case), though.

<img width="600" alt="Screenshot 2024-10-22 at 10 58 36"
src="https://github.com/user-attachments/assets/64a94d9f-798b-44a4-9dee-6056886535bb">

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2024-10-22 12:39:00 -03:00
David Soria Parra
d8d8c908ed context_servers: Update protocol (#19547)
We sadly have to change the underlying protocol once again. This will
likely be the last change to the core protocol without correctly
handling older versions. From here on out, we want to get better with
version handling. To do so, we introduce the notion of a string protocol
version to be explicit of when the underlying protocol last changed.

The change also changes the return values of prompts. For now we only
allow User messages from servers to match the current behaviour. We will
change this once #19222 lands which will allow slash commands to insert
user and assistant messages.

Release Notes:

- N/A
2024-10-22 11:19:32 -04:00
Adam Wolff
680b3dd80b Refine AI context summary prompt (#19530)
Release Notes:

- Improved prompt for generating context editor summaries.
2024-10-22 11:02:04 -04:00
Peter Tripp
270e13bb9a docs: Document upstream JSONC Prettier issues (#19552) 2024-10-22 10:43:21 -04:00
Thorsten Ball
b3aa7055e4 ssh remoting: Daemonize server process properly by forking and closing stdio (#19544)
This fixes the `ssh` proxy process not being notified when the proxy
process dies. Turns out that the server would have stdout/stderr/stdin
connected to the grand-parent ssh process connected to it and as long as
the server kept running (even once it was daemonized into the
background) the grand-parent ssh process wouldn't exit.

That in turn meant that the Zed client wasn't notified when the proxy
process died.

Release Notes:

- N/A

---------

Co-authored-by: Bennet <bennet@zed.dev>
2024-10-22 16:36:40 +02:00
Joseph T. Lyons
f16461d7d0 Fix Discord link (#19551)
We can't seem to generate invite links that dumps a user directly into a
specific channel, so just adding a general invite link and then pointing
them to the Windows channel name.

Release Notes:

- N/A
2024-10-22 10:36:05 -04:00
Danilo Leal
970f8db5c4 ssh: Don't erase the connection input on escape key (#19550)
This PR changes the behavior of pressing the `Esc` key on the connection
input. Now, if you hit it, the address inserted into the input won't be
erased. Effectively, escape now only cancels the connection process
instead of doing both (clearing the input _and_ cancelling the
connection).

Release Notes:

- N/A

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2024-10-22 11:28:39 -03:00
Piotr Osiewicz
bc9086c9af ssh: Fix file picker not getting focus when it's opened for the first time (#19546)
Release Notes:

- N/A

Co-authored-by: Danilo <danilo@zed.dev>
2024-10-22 16:11:59 +02:00
Thorsten Ball
a367c6de6e ssh remote: Only send a single FlushBufferedMessages (#19541)
Release Notes:

- N/A

---------

Co-authored-by: Bennet <bennet@zed.dev>
2024-10-22 12:04:15 +02:00
Thorsten Ball
27d1a566d0 ssh remoting: Emit Disconnected when reconnected exhausted (#19540)
Release Notes:

- N/A

---------

Co-authored-by: Bennet <bennet@zed.dev>
2024-10-22 11:35:27 +02:00
Conrad Irwin
4f52077d97 Better error handling for SSH (#19533)
Before this change we sometimes showed errors inline, sometimes in
alerts.
Sometimes we closed the window, someimtes we didn't.

Now they always show as prompts and we never close windows.

Co-Authored-By: Mikayla <mikayla@zed.dev>

Release Notes:

- SSH Remoting: Improve error handling
2024-10-21 22:17:42 -07:00
Conrad Irwin
6e485453d0 Rename dev servers to remote projects in UI (#19527)
Release Notes:

- N/A

Co-authored-by: Mikayla <mikayla@zed.dev>
2024-10-21 16:50:25 -06:00
Conrad Irwin
9bae93cd39 SSH Remoting: Fix yes/no/fingerprint prompt (#19526)
Release Notes:

- SSH Remoting: fix SSH fingerprint prompt

Co-authored-by: Mikayla <mikayla@zed.dev>
2024-10-21 15:28:22 -06:00
Marshall Bowers
1a4b253ee5 collab: Add support for a custom monthly allowance for LLM usage (#19525)
This PR adds support for setting a monthly LLM usage allowance for
certain users.

Release Notes:

- N/A
2024-10-21 17:12:33 -04:00
Vladimir Varankin
89f6b65ee6 docs: Add missing link to jsonnet.md in the summary (#19522)
This is a fixup for #19410.

Apparently, `mdbook` requires a reference to the document from the
`SUMMARY.md`. This fixes the 404
(https://zed.dev/docs/languages/jsonnet.html) and also adds the Jsonnet
to the docs' navigation.

Release Notes:

- N/A
2024-10-21 15:56:56 -04:00
Bruno Calza
a2c6b4ad2f Fix empty keystroke with simulated IME (#19414)
Closes #19181

When the keystroke was empty ("") the `ime_key` was converted from
`None` to `Some("")` when `with_simulated_ime` was called. That was
leading to not intentional behavior when an empty keystroke was combined
with `shift-up` in a keybinding `["workspace::SendKeystrokes", "shift-up
"]`.

By adding a `key.is_empty()` we make sure the `ime_key` keeps as `None`.

This was manually tested. 

Release Notes:

- Fixed empty keystroke with simulated ime

Signed-off-by: Bruno Calza <brunoangelicalza@gmail.com>
2024-10-21 13:44:05 -06:00
Conrad Irwin
6b7d85b769 SSH Remoting: Fix Save As (#19517)
Co-Authored-By: Mikayla <mikayla@zed.dev>

Closes #ISSUE

Release Notes:

- SSH Remoting: Fix SaveAs to pick the file on the remote

Co-authored-by: Mikayla <mikayla@zed.dev>
2024-10-21 13:41:48 -06:00
Max Brunsfeld
cb3eb75712 Fix block-wise autoindent when editing adjacent ranges (#19521)
This fixes problems where auto-indent wasn't working correctly for
assistant edits.

Release Notes:

- Fixed a bug where auto-indent didn't work correctly when pasting with
multiple cursors on adjacent lines

Co-authored-by: Marshall <marshall@zed.dev>
2024-10-21 12:41:41 -07:00
Mikayla Maki
bae85d858e SSH Remoting: Fix reload/save race (#19519)
Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-10-21 11:23:19 -07:00
Marshall Bowers
755fd695f5 editor: Move hunk controls to the right (#19515)
This PR moves the hunk controls over to the right so we can see how they
feel over there.

Git:

<img width="1068" alt="Screenshot 2024-10-21 at 10 27 34 AM"
src="https://github.com/user-attachments/assets/71556e22-024a-4bdf-8a99-fe28430b9155">

Live diffs:

<img width="1060" alt="Screenshot 2024-10-21 at 10 27 28 AM"
src="https://github.com/user-attachments/assets/681ff409-dc55-4b63-87d7-7e39016417d2">


Release Notes:

- Moved hunk controls to the right of the header.
2024-10-21 10:45:54 -04:00
Daniel Eichman
74e25c11f1 Fix incorrect checkbox placement in Markdown preview (#19383)
- Closes #12515

Before fix:
<img width="1506" alt="Screenshot 2024-10-17 at 09 50 19"
src="https://github.com/user-attachments/assets/250f50cb-0119-4b96-bc9b-7258aa83247c">

After fix:
<img width="1027" alt="Screenshot 2024-10-17 at 09 52 36"
src="https://github.com/user-attachments/assets/c2eb7e4a-3c03-466c-b215-7fcc22eed024">

Testing:
- Manual testing 
- Added unit test

Test results, these tests fail on the main branch for my setup as well,
I have docker running but still had some failures:
```
failures:
    tests::integration_tests::test_context_collaboration_with_reconnect
    tests::integration_tests::test_formatting_buffer
    tests::integration_tests::test_fs_operations
    tests::integration_tests::test_git_branch_name
    tests::integration_tests::test_git_diff_base_change
    tests::integration_tests::test_git_status_sync
    tests::integration_tests::test_join_after_restart
    tests::integration_tests::test_join_call_after_screen_was_shared
    tests::integration_tests::test_joining_channels_and_calling_multiple_users_simultaneously
    tests::integration_tests::test_leaving_project
    tests::integration_tests::test_leaving_worktree_while_opening_buffer
    tests::integration_tests::test_local_settings
    tests::integration_tests::test_lsp_hover
    tests::integration_tests::test_mute_deafen
    tests::integration_tests::test_open_buffer_while_getting_definition_pointing_to_it
    tests::integration_tests::test_pane_split_left
    tests::integration_tests::test_prettier_formatting_buffer
    tests::integration_tests::test_preview_tabs
    tests::integration_tests::test_project_reconnect
    tests::integration_tests::test_project_search
    tests::integration_tests::test_project_symbols
    tests::integration_tests::test_propagate_saves_and_fs_changes
    tests::integration_tests::test_references
    tests::integration_tests::test_reloading_buffer_manually
    tests::integration_tests::test_right_click_menu_behind_collab_panel
    tests::integration_tests::test_room_location
    tests::integration_tests::test_room_uniqueness
    tests::integration_tests::test_server_restarts
    tests::integration_tests::test_unshare_project
    tests::notification_tests::test_notifications
    tests::random_project_collaboration_tests::test_random_project_collaboration
    tests::remote_editing_collaboration_tests::test_sharing_an_ssh_remote_project

test result: FAILED. 156 passed; 32 failed; 0 ignored; 0 measured; 0 filtered out; finished in 100.98s
```
Comments:
I do not have a ton of rust knowledge, so very open to feedback. TYSM

Release Notes:

- Fix Incorrect checkbox placement in Markdown preview

---------

Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
2024-10-21 15:57:49 +02:00
Michael Sloan
b355a6f449 Fix markdown preview handling of empty list items (#19449)
Before this change, `parse_block` was consuming events that it doesn't
handle. This was fine in its use in `parse_document`, but in its use in
`parse_list` this broke when there is an empty list item, causing it to
consume list end tags / list item starts / etc.

Release Notes:

- Fixed markdown preview rendering of empty list items.
2024-10-21 15:13:26 +02:00
reslear
6341ad2f7a docs: Correct link to Vue extension (#19508)
Closes -

Release Notes:

- N/A
2024-10-21 08:12:10 -04:00
Kirill Bulatov
d3cb08bf35 Support .editorconfig (#19455)
Closes https://github.com/zed-industries/zed/issues/8534
Supersedes https://github.com/zed-industries/zed/pull/16349

Potential concerns:
* we do not follow up to the `/` when looking for `.editorconfig`, only
up to the worktree root.
Seems fine for most of the cases, and the rest should be solved
generically later, as the same issue exists for settings.json
* `fn language` in `AllLanguageSettings` is very hot, called very
frequently during rendering. We accumulate and parse all `.editorconfig`
file contents beforehand, but have to go over globs and match these
against the path given + merge the properties still.
This does not seem to be very bad, but needs more testing and
potentially some extra caching.


Release Notes:

- Added .editorconfig support

---------

Co-authored-by: Ulysse Buonomo <buonomo.ulysse@gmail.com>
2024-10-21 13:05:30 +03:00
renovate[bot]
d95a4f8671 Update swatinem/rust-cache digest to 82a92a6 (#19318)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [swatinem/rust-cache](https://redirect.github.com/swatinem/rust-cache)
| action | digest | `23bce25` -> `82a92a6` |

---

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-21 11:10:45 +03:00
Alvaro Gaona
44dc693d30 docs: Update C# and C++ configuration pages (#19500)
- Update C# configuration page.
- Fix typo in C++ configuration page.

Release Notes:

- N/A
2024-10-21 11:07:12 +03:00
Conrad Irwin
92c29be74c SSH Remoting: Fix reconnects (#19485)
Before this change messages could be lost on reconnect, now they will
not be.

Release Notes:

- SSH Remoting: make reconnects smoother

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-10-19 23:14:19 -06:00
Kirill Bulatov
1ae30f5813 Show project panel symlink icons for remote clients (#19464) 2024-10-19 19:44:47 +03:00
Antonio Scandurra
e8207288e5 Refold updated patch only if the patch was already folded (#19462)
Release Notes:

- N/A
2024-10-19 17:10:50 +02:00
Nathan Sobo
781fff220c Fix merging of an update of a symbol with an insert_before operation before the same symbol (#19450)
When we insert before some text and then update that same text, we need
to preserve and concatenate the new text associated with both
operations.

Release Notes:

- N/A
2024-10-19 08:36:21 -06:00
Max Brunsfeld
d209eab058 Combine excerpt footer and header into a single block (#19441)
This simplifies rendering of excerpt headers and footers, and removes
the need to store a `BlockDisposition` on these boundary blocks. It's a
step toward implementing "replace blocks", which we want to use in the
assistant panel.

We've also cleaned up the way heights are specified for headers and
footers and fixed some visual asymmetries between the "expand upward"
and "expand downward" buttons.

Release Notes:

- N/A

---------

Co-authored-by: Richard <richard@zed.dev>
2024-10-18 17:58:07 -07:00
Vitaly Slobodin
3e0c5c10b7 lsp: Handle unregistration "textDocument/rename" from a server (#19427)
Hi. While working on https://github.com/zed-industries/zed/pull/19230 I
noticed that some servers send a request to unregistered the
`textDocument/rename` capability. I thought it would be good to handle
that message in Zed:

```plaintext
[2024-10-18T21:25:07+02:00 WARN  project::lsp_store] unhandled capability unregistration: Unregistration { id: "biome_rename", method: "textDocument/rename" }
```

So this pull request implements that. Thanks.

Release Notes:

- N/A
2024-10-19 00:52:17 +02:00
Mikayla Maki
8a912726d7 Fix flakey SSH connection (#19439)
Fixes a bug due to the `select!` macro tossing futures that had
partially read messages, causing us to desync our message reading with
the input stream.

Release Notes:

- N/A

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: conrad <conrad@zed.dev>
2024-10-18 15:41:43 -07:00
Marshall Bowers
30e081b3f7 elixir: Bump to v0.1.1 (#19437)
This PR bumps the Elixir extension to v0.1.1.

Changes:

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

Release Notes:

- N/A
2024-10-18 18:31:08 -04:00
Conrad Irwin
a5492b3ea6 Revert "SSH reconnect reliability (#19398)" (#19440)
This reverts commit 98ecb43b2d.

Tests fail on main?!

Closes #ISSUE

Release Notes:

- N/A
2024-10-18 15:08:56 -07:00
Marshall Bowers
47380001cc remote: Fix formatting (#19438)
This PR fixes some formatting issues from #19398 that slipped past CI,
somehow.

Release Notes:

- N/A
2024-10-18 17:31:41 -04:00
Conrad Irwin
98ecb43b2d SSH reconnect reliability (#19398)
Release Notes:

- SSH Remoting: Fix message reliability across restarts

---------

Co-authored-by: Nathan <nathan@zed.dev>
2024-10-18 15:28:08 -06:00
Valentine Briese
be474a6d6f docs: Update info on JDTLS install for Java (#19436) 2024-10-18 17:12:34 -04:00
Marshall Bowers
b44bed0115 collab: Unconditionally execute billing checks (#19432)
This PR removes the conditional checks around the billing-related
enforcement for LLM completions.

These were just in place to prevent executing any billing code before we
had rolled it out. Now that it is rolled out, we don't need this
conditional execution anymore.

Release Notes:

- N/A
2024-10-18 15:55:28 -04:00
Joseph T. Lyons
11a82e3347 Do not run CI on changes to community action config files (#19430)
Release Notes:

- N/A
2024-10-18 15:36:20 -04:00
Joseph T. Lyons
be81e29b0f Prefer users to open new issues if closed stale issue is valid (#19428)
I no longer want to have to keep my ears and eyes open for GitHub
notifications that relate to someone requesting a closed stale issue be
reopened. As a community maintainer, I can get hundreds or even
thousands of notifications a week, and a lot of those are about activity
on closed issues. If everyone following an issue did not react fast
enough (7 days) to keep an issue flagged as `stale` open, let's instruct
them to open new issues, so we are forced to see it during next triage.

Release Notes:

- N/A
2024-10-18 15:04:26 -04:00
Valentine Briese
6a463be1ae docs: Direct Java extension users to JDTLS initialization options (#19401)
Continuation of #19390
2024-10-18 15:02:42 -04:00
Vladimir Varankin
64a6e9cafb docs: Outline Jsonnet language (#19410)
This PR adds a basic documentation about the Jsonnet language support.

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
2024-10-18 14:20:15 -04:00
Marshall Bowers
fa738ee5e1 vue: Extract to zed-extensions/vue repository (#19426)
This PR extracts the Vue extension to the
[zed-extensions/vue](https://github.com/zed-extensions/vue) repository.

Release Notes:

- N/A
2024-10-18 14:08:32 -04:00
Marshall Bowers
15449cdf30 svelte: Extract to zed-extensions/svelte repository (#19425)
This PR extracts the Svelte extension to the
[zed-extensions/svelte](https://github.com/zed-extensions/svelte)
repository.

Release Notes:

- N/A
2024-10-18 13:36:07 -04:00
Shish
2db9090a2f remote: Polish for connection progress & error dialogs (#19379)
Before/after:

![err1-before](https://github.com/user-attachments/assets/43d959b3-c9d9-45dd-938e-42d34ec1cfc5)

![err1-after](https://github.com/user-attachments/assets/311d53e0-752c-4eb8-9816-64b1970c228d)

Before/after (I feel like text-wrapping would be more useful than
text-ellipsis here, but I don't see any wrap function):

![err2-before](https://github.com/user-attachments/assets/1626cda9-bf06-43fe-9b7d-3ec64f4db08a)

![err2-after](https://github.com/user-attachments/assets/749a6950-1409-4e75-808e-a1a96dbfc87e)

Before/after:

![prog-before](https://github.com/user-attachments/assets/f5f5a171-db42-4797-bab0-ad71c750bb20)

![prog-after](https://github.com/user-attachments/assets/b52a7694-36f6-4f7a-8a90-ceb223f12ec1)

Release Notes:

- N/A
2024-10-18 11:09:52 -06:00
Thomas
34b8655bf6 Improve increment/decrement with leading zeros in vim mode (#18362)
- Closes: 18360

Release Notes:

- Added support for incrementing and decrementing numbers with leading
zeros
2024-10-18 11:03:34 -06:00
张小白
5b745a82e1 reqwest_client: Fix socks proxy settings (#19123)
Closes #19362

This pull request includes several updates to the `reqwest_client` crate
and its dependencies. The most important changes involve adding support
for SOCKS proxies, improving error handling for proxy URIs, and adding
tests for proxy functionality.

### Dependency Updates:
*
[`Cargo.toml`](diffhunk://#diff-2e9d962a08321605940b5a657135052fbcef87b5e360662bb527c96d9a615542L394-R401):
Added support for SOCKS proxies in the `reqwest` dependency by including
the `socks` feature.

### Code Improvements:
*
[`crates/reqwest_client/src/reqwest_client.rs`](diffhunk://#diff-8e036b034e987390be2f57373864b75d6983f0cf84e85c43793eb431d13538f3L47-R52):
Improved error handling when parsing proxy URIs by logging errors
instead of directly panicking.

### Testing Enhancements:
*
[`crates/reqwest_client/src/reqwest_client.rs`](diffhunk://#diff-8e036b034e987390be2f57373864b75d6983f0cf84e85c43793eb431d13538f3R274-R317):
Added tests to verify the handling of various proxy URIs, including
valid and invalid cases.

Release Notes:

- N/A
2024-10-18 09:57:00 -07:00
renovate[bot]
c59a75db1d Update actions/upload-artifact digest to b4b15b8 (#19310)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/upload-artifact](https://redirect.github.com/actions/upload-artifact)
| action | digest | `604373d` -> `b4b15b8` |

---

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-18 12:41:36 -04:00
David Baldwin
b3c93130ec elixir: Support describe, test, setup, setup_all in outlines (#19135)
Closes #9894 

Release Notes:

- N/A

### Before


![2024-10-12T204848@2x](https://github.com/user-attachments/assets/84b7f123-8845-4e6d-b1b1-444e54ea6599)

### After


![2024-10-12T204749@2x](https://github.com/user-attachments/assets/67fdcead-bad3-4967-9ac4-0b85f1da7bca)
2024-10-18 12:39:24 -04:00
Marshall Bowers
73a6c542f3 vue: Bump to v0.1.1 (#19421)
This PR bumps the Vue extension to v0.1.1.

Changes:

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

Release Notes:

- N/A
2024-10-18 11:56:16 -04:00
Marshall Bowers
2cd6c19873 svelte: Bump to v0.2.1 (#19420)
This PR bumps the Svelte extension to v0.2.1.

Changes:

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

Release Notes:

- N/A
2024-10-18 11:37:51 -04:00
Marshall Bowers
6f24c1da79 vue: Support lang attribute for style tag injections (#19419)
This PR adds support for injecting languages into `<style>` tags using
`<style lang="...">` in Vue.

Extracted from https://github.com/zed-industries/zed/pull/18052.

Release Notes:

- N/A

Co-authored-by: Albert Marashi <albert@lumina.earth>
2024-10-18 11:36:30 -04:00
Marshall Bowers
5508832ba6 svelte: Adjust block keyword highlighting (#19418)
This PR adjusts the highlights for `{#each ...}` and `{#if ...}`
expression blocks to use keyword highlighting.

Extracted from https://github.com/zed-industries/zed/pull/18052.

Release Notes:

- N/A

Co-authored-by: Albert Marashi <albert@lumina.earth>
2024-10-18 11:25:03 -04:00
Marshall Bowers
35f2f2aac4 Treat .postcss files as CSS (#19416)
This PR makes it so `.postcss` files are recognized as CSS.

The `tree-sitter-css` grammar has basic support for PostCSS:
https://github.com/tree-sitter/tree-sitter-css/issues/17#issuecomment-1830349808.

Closes #18051.

Release Notes:

- `.postcss` files are now recognized as CSS.
2024-10-18 11:11:29 -04:00
Peter Tripp
9e27b6694a keymap: Add cmd-o 'Go to symbol' for JetBrains MacOS key bindings (#19415) 2024-10-18 10:12:39 -04:00
CharlesChen0823
f5124c21d1 scrollbar: Fix horizontal scrollbar display overflow last item (#19403)
currently, the laste item display is overflow by scrollbar, cause it
cannot clickable.
So remain one item height for display horizontal scrollbar,.
![Screenshot 2024-10-18
192352](https://github.com/user-attachments/assets/d686f0e8-93f8-426e-8816-6f00ed17a599)



Release Notes:

- N/A
2024-10-18 14:07:50 +02:00
Thorsten Ball
ea460014ab ssh remoting: Undo the spawning of message handlers (#19409)
I suspect that this might have something to do with saving files not
being possible after a while.

I want to merge this and bump nightly.

Release Notes:

- N/A
2024-10-18 10:48:11 +02:00
Peter Tripp
5168fc27a1 docs: More Java extension documentation (#19390)
Follow up of: https://github.com/zed-industries/zed/pull/19113
2024-10-17 20:21:45 -04:00
Marshall Bowers
2bcf9fc490 Add client::zed_urls module for constructing zed.dev URLs (#19391)
This PR adds a new `zed_urls` module to the `client` crate.

This module contains functions for constructing URLs to Zed properties,
such as zed.dev.

The URLs produced by this module will respect the server URL set via
settings or the `ZED_SERVER_URL` environment variable. This allows them
to correctly reflect the current environment (such as when testing Zed
against a local collab/zed.dev).

Release Notes:

- N/A
2024-10-17 16:18:35 -04:00
Marshall Bowers
3c32f01a8f paths: Update doc comment (#19388)
This PR updates a doc comment to use proper capitalization.

Also removes an unneeded `return`.

Release Notes:

- N/A
2024-10-17 15:36:29 -04:00
Marshall Bowers
9d61cd5120 ci: Check spelling separately from formatting (#19385)
This PR moves the spelling check out of the `check_style` action, which
we can leave for just checking formatting.

We can't use the `crates-ci-typos` action as-is on the macOS runners due
to the absence of `wget`.

Release Notes:

- N/A
2024-10-17 13:47:23 -04:00
Max Brunsfeld
411f64b374 Restructure assistant edits to show all changes in a proposed-change editor (#18240)
This changes the `/workflow` command so that instead of emitting edits
in separate steps, the user is presented with a single tab, with an
editable diff that they can apply to the buffer.

Todo

* Assistant panel
* [x] Show a patch title and a list of changed files in a block
decoration
* [x] Don't store resolved patches as state on Context. Resolve on
demand.
    * [ ] Better presentation of patches in the panel
    * [ ] Show a spinner while patch is streaming in
* Patches
* [x] Preserve leading whitespace in new text, auto-indent insertions
    * [x] Ensure patch title is very short, to fit better in tab
* [x] Improve patch location resolution, prefer skipping whitespace over
skipping `}`
    * [x] Ensure patch edits are auto-indented properly
* [ ] Apply `Update` edits via a diff between the old and new text, to
get fine-grained edits.
* Proposed changes editor
    * [x] Show patch title in the tab
    * [x] Add a toolbar with an "Apply all" button
* [x] Make `open excerpts` open the corresponding location in the base
buffer (https://github.com/zed-industries/zed/pull/18591)
* [x] Add an apply button above every hunk
(https://github.com/zed-industries/zed/pull/18592)
* [x] Expand all diff hunks by default
(https://github.com/zed-industries/zed/pull/18598)
    * [x] Fix https://github.com/zed-industries/zed/issues/18589
* [x] Syntax highlighting doesn't work until the buffer is edited
(https://github.com/zed-industries/zed/pull/18648)
* [x] Disable LSP interaction in Proposed Changes editor
(https://github.com/zed-industries/zed/pull/18945)
* [x] No auto-indent? (https://github.com/zed-industries/zed/pull/18984)
* Prompt
    * [ ] make sure old_text is unique

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Richard <richard@zed.dev>
Co-authored-by: Marshall <marshall@zed.dev>
Co-authored-by: Nate Butler <iamnbutler@gmail.com>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
2024-10-17 13:18:13 -04:00
Peter Tripp
4ae2f93086 ci: Improve GitHub Action modularity (#18861)
- Closes: https://github.com/zed-industries/zed/issues/19351
- Switch to using the official [typos GitHub Action](https://github.com/crate-ci/typos/blob/master/docs/github-action.md)
- Move the typos check into `actions/check_style`
- Move Squawk Postgres migration check out of `actions/check_style` file into ci.yml
- `actions/check_style` can now be run on stateless/linux runners (previous required self-hosted MacOS runner)
- ci.yml: Split old `style` into checks into those that can run statelessly (linux) and everything else into a new `migration` group which benefit from the full git checkout available on the MacOS runners.
- ci.yml: Move `Check unused dependencies` from style to `linux_tests`
- Add `if: github.repository_owner == 'zed-industries'` to all jobs so they won't try and run on GitHub forks.
2024-10-17 12:50:19 -04:00
Conrad Irwin
65fb2782eb Always have cmd-o open a local project (#19376)
Release Notes:

- Fixed `cmd-o` in an SSH project to always open a local project
2024-10-17 10:36:53 -06:00
Thorsten Ball
e6b9a8ef9b ssh remoting: Handle OpenNewBuffer request (#19373)
Release Notes:

- N/A
2024-10-17 17:49:17 +02:00
Elliot Thomas
398d0396b6 workspace: Fix inconsistent paths order serialization (#19232)
Release Notes:

- Fixed inconsistent serialization of workspace paths order
2024-10-17 17:38:28 +02:00
Finn Evers
e9e4c770ca Update all occurrences of option_as_meta to new default value (#19369)
This PR is a quick follow-up to #19364 which updates some left-out
occurrences of `option_as_meta` to the new default value (`false`).
2024-10-17 11:21:07 -04:00
Thorsten Ball
4be9da2641 remote ssh: Make "get permalink to line" work (#19366)
This makes the `editor: copy permalink to line` and `editor: copy
permalink to line` actions work in SSH remote projects.

Previously it would only work in local projects.

Demo:


https://github.com/user-attachments/assets/a8012152-b631-4b34-9ff2-e4d033c97dee




Release Notes:

- N/A
2024-10-17 17:07:42 +02:00
Thorsten Ball
c186e99a3d ssh remote: Reset missed heartbeats on connection activity (#19368)
Ran into this this morning. At least I suspect I ran into it. In any
case: we need to reset the missed hearbeats to 0 in case we got any
connection activity.

Release Notes:

- N/A
2024-10-17 17:07:34 +02:00
Peter Tripp
4df882c295 Make terminal.option_as_meta=false in default settings (#19364)
- This reverts the change I made in https://github.com/zed-industries/zed/pull/15535 which set `option_as_meta` to `true` in the default settings.
- `true` is a reasonable default for US Keyboards, but is terrible for many others which rely on `alt+<key>` for totally normal keystroke combinations.
2024-10-17 10:31:35 -04:00
Marshall Bowers
17f2929b4c collab: Anchor new subscription's billing cycle to the first of the month (#19367)
This PR makes it so new subscriptions will have their billing cycle
anchored to the first of the month.

When someone signs up today, they will be billed starting on the first
of next month.

Release Notes:

- N/A

Co-authored-by: Antonio <antontio@zed.dev>
Co-authored-by: Richard <richard@zed.dev>
2024-10-17 10:18:12 -04:00
Peter Tripp
5ad392035e Support uppercase extensions in image preview (#19304) 2024-10-17 08:48:18 -04:00
Antonio Scandurra
8c910540ed Subtract FREE_TIER_MONTHLY_SPENDING_LIMIT from reported monthly spend (#19358)
Release Notes:

- N/A
2024-10-17 13:09:50 +02:00
Antonio Scandurra
455f241c6a Introduce a new /billing/monthly_spend API (#19354)
Fixes https://github.com/zed-industries/zed/issues/19353

Release Notes:

- N/A
2024-10-17 12:34:25 +02:00
Antonio Scandurra
498ecd6404 Fetch more than one page when polling stripe events (#19343)
This fixes a bug that was causing most users to be unable to use the
LLMs via Zed. It was caused by not using pagination and, instead, always
querying the very first page of stripe events.

Note that we're also allowing processing events generated in the last 24
hours (before, this was only 1 hour). I did this so that we can process
the backlog of events that the aforementioned bug was skipping.

Release Notes:

- N/A
2024-10-17 09:47:25 +02:00
Thorsten Ball
3216de7eb5 ssh remoting: Do not print error backtrace on non-zero exit (#19290)
Closes #ISSUE


Release Notes:

- N/A
2024-10-17 09:41:16 +02:00
renovate[bot]
57369b5a54 Update Rust crate tree-sitter-elixir to v0.3.1 (#19335) 2024-10-17 08:44:51 +03:00
Heavysnowjakarta
f9d4272e13 docs: Java extension settings (#19113)
Co-authored-by: Peter Tripp <peter@zed.dev>
2024-10-17 00:04:59 -04:00
Conrad Irwin
378a2cf9d8 Allow passing args to ssh (#19336)
This is useful for passing a custom identity file, jump hosts, etc.

Unlike with the v1 feature, we won't support `gh`/`gcloud` ssh wrappers
(yet?). I think the right way of supporting those would be to let
extensions provide remote projects.

Closes #19118

Release Notes:

- SSH remoting: restored ability to set arguments for SSH
2024-10-16 21:09:31 -06:00
Conrad Irwin
f1d01d59ac Simplify PR template (#19337)
Release Notes:

- N/A
2024-10-16 20:22:08 -06:00
Danilo Leal
78093b8e76 ssh: Clean up title bar indicator icon (#19328)
This PR cleans up the custom icon with indicator implementation in favor
of `IconWithIndicator`, which we already had. It seems like it isn't
super used still, but it's good to try to enforce some consistency
either way. I checked my changes against the REPL stuff (one instance
where its used) and everything's looking good so far. As far as SSH,
nothing has visually changed; we just have less code for this thing now.

<img width="800" alt="Screenshot 2024-10-17 at 2 15 47 AM"
src="https://github.com/user-attachments/assets/5c146757-501e-4242-b145-a576a8f289b5">

---

Release Notes:

- N/A
2024-10-16 22:25:27 -03:00
Danilo Leal
a41e973782 ssh: Remove server count from modal header (#19329)
The server count was something that existed since the remote development
implementation and we just kept it there without a lot of critical
thinking. However, it doesn't feel like it's particularly useful yet,
which means that, at least for now, we could clean it up more and wait
for further feedback to add it back, if ever requested.

Release Notes:

- N/A
2024-10-16 22:25:15 -03:00
Danilo Leal
9a3d8733ce ssh: Use system prompt for the server removal action (#19332)
This PR replaces a toast for the system prompt to confirm the action of
removing a server from the remote list. The alert dialog component is
the right choice here as we want to have a modal action that forces
choice. This should make it easier to convert to a nativa alert dialog
in the future, as well as for other platforms.

<img width="800" alt="Screenshot 2024-10-17 at 3 01 41 AM"
src="https://github.com/user-attachments/assets/7bb1210a-54bf-40da-a85a-f269484825a1">

Release Notes:

- N/A
2024-10-16 22:25:03 -03:00
Conrad Irwin
c888101e4b SSH remoting: Don't panic when opening root, open ~ instead (#19322)
Release Notes:

- Fixed a panic when doing `zed ssh://server/`
2024-10-16 17:17:20 -06:00
Conrad Irwin
0c04fb9862 SSH remoting: better error message for projects (#19320)
Before this, if no project paths were opened you were in a wierd UI
state where
most things didn't work because the project was ssh, but no
files/folders were open.

Release Notes:

- Fixed error handling when no project paths could be opened
2024-10-16 17:16:56 -06:00
Marshall Bowers
f6fad3b09e collab: Remove lifetime spending limit in favor of LLM usage billing (#19321)
This PR removes the lifetime spending limit that was added in #16780.

We had previously added this as a way to prevent runaway usage, but now
that we have a cap on free usage per month with paid access after that,
we don't need this check anymore.

Release Notes:

- N/A
2024-10-16 18:14:07 -04:00
renovate[bot]
6614feff97 Pin astral-sh/setup-uv action to f3bcaeb (#19309)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [astral-sh/setup-uv](https://redirect.github.com/astral-sh/setup-uv) |
action | pinDigest | -> `f3bcaeb` |

---

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-16 17:20:39 -04:00
Conrad Irwin
08b1545c85 Show a user-visible error message if saving fails (#19311)
Release Notes:

- Added a user-visible error message when a manual save fails.
2024-10-16 15:17:38 -06:00
Marshall Bowers
fedd177b08 collab: Add context to errors syncing billing events to Stripe (#19315)
This PR adds context to errors that occur when trying to sync billing
events to Stripe.

Release Notes:

- N/A
2024-10-16 17:09:26 -04:00
renovate[bot]
4288096ca1 Update Rust crate tree-sitter-cpp to v0.23.1 (#18974)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[tree-sitter-cpp](https://redirect.github.com/tree-sitter/tree-sitter-cpp)
| workspace.dependencies | patch | `0.23.0` -> `0.23.1` |

---

### Release Notes

<details>
<summary>tree-sitter/tree-sitter-cpp (tree-sitter-cpp)</summary>

###
[`v0.23.1`](https://redirect.github.com/tree-sitter/tree-sitter-cpp/compare/v0.23.0...v0.23.1)

[Compare
Source](https://redirect.github.com/tree-sitter/tree-sitter-cpp/compare/v0.23.0...v0.23.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:eyJjcmVhdGVkSW5WZXIiOiIzOC4xMTQuMCIsInVwZGF0ZWRJblZlciI6IjM4LjExNC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-16 23:21:23 +03:00
renovate[bot]
256c31a5d9 Update Rust crate tree-sitter-c to v0.23.1 (#18958)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [tree-sitter-c](https://redirect.github.com/tree-sitter/tree-sitter-c)
| workspace.dependencies | patch | `0.23.0` -> `0.23.1` |

---

### Release Notes

<details>
<summary>tree-sitter/tree-sitter-c (tree-sitter-c)</summary>

###
[`v0.23.1`](https://redirect.github.com/tree-sitter/tree-sitter-c/compare/v0.23.0...v0.23.1)

[Compare
Source](https://redirect.github.com/tree-sitter/tree-sitter-c/compare/v0.23.0...v0.23.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:eyJjcmVhdGVkSW5WZXIiOiIzOC4xMTQuMCIsInVwZGF0ZWRJblZlciI6IjM4LjExNC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-16 23:19:10 +03:00
David Soria Parra
c8b6ad9666 Context Servers: Protocol fixes and UI improvements (#19087)
This PR does two things. It fixes some minor inconsistencies in the
protocol. This is mostly about handling JSON RPC notifications correctly
and skipping fields when set to None.

Second part is about improving the rendering of context server commands,
by passing on the description
of the command to the slash command UI and showing the name of the
argument as a CodeLabel.

Release Notes:

- N/A
2024-10-16 13:07:15 -07:00
Peter Tripp
0e22c9f275 docs: Add C++ clangd example arguments (#19308) 2024-10-16 16:07:05 -04:00
Kirill Bulatov
56f69be2e7 Do not allow [re]running ssh tasks when not connected to the server (#19306)
Release Notes:

- N/A
2024-10-16 22:57:39 +03:00
Kirill Bulatov
02f63e49ed Resolve proto hints with empty resolve data (#19274)
Fixed ssh remoting not showing a lot of hints


Release Notes:

- N/A
2024-10-16 21:50:51 +03:00
Kirill Bulatov
3dcc638537 Better handle shell for remote ssh projects (#19297)
Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad@zed.dev>
2024-10-16 21:49:54 +03:00
Marshall Bowers
d35b646dbb assistant: Direct user to account page to subscribe for more LLM usage (#19300)
This PR updates the location where we send the user to subscribe for
more LLM usage to the account page.

Release Notes:

- Updated the URL to the account page when subscribing to LLM usage.
2024-10-16 14:03:42 -04:00
张小白
338bf3fd28 windows: Fix window not displaying correctly on launch (#19124)
Closes #18705 (comment)

This PR fixes the issue where the Zed window was not displaying
correctly on launch. Now, when Zed is closed in a maximized state, it
will reopen in a maximized state.

On macOS, when a window is created but not yet visible, calling `zoom`
or `toggle_fullscreen` will still affect the hidden window. However,
this behavior is different on Windows, so special handling is required.

Also, since #18705 hasn't been reviewed yet, I'm not sure if this PR
should be merged now or if it should wait until #18705 is reviewed
first.


Release Notes:

- N/A
2024-10-16 10:29:42 -07:00
Matin Aniss
879a2ea06f gpui: Replace redundant code in animation (#19273)
Just a small change to replace some redundant code in the animation
element.

Release Notes:

- N/A
2024-10-16 10:26:26 -07:00
Piotr Osiewicz
7a5003bea2 ssh: Do not look up dev servers when rendering the default mode (#19295)
This should help with the bug where there's a mismatch between
connection count and the list showing empty state.

Closes #ISSUE

Release Notes:

- N/A
2024-10-16 18:53:05 +02:00
Joseph T. Lyons
f8f3f369f6 v0.159.x dev 2024-10-16 12:47:57 -04:00
583 changed files with 32257 additions and 20911 deletions

View File

@@ -15,6 +15,13 @@ body:
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
validations:
required: true
- type: textarea
attributes:
label: |

View File

@@ -2,7 +2,7 @@ name: Bug Report
description: |
Use this template for **non-crash-related** bug reports.
Tip: open this issue template from within Zed with the `file bug report` command palette action.
labels: ["admin read", "triage", "defect"]
labels: ["admin read", "triage", "bug"]
body:
- type: checkboxes
attributes:
@@ -38,9 +38,12 @@ body:
Linux: `~/.local/share/zed/logs/Zed.log` or $XDG_DATA_HOME
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
value: |
<details><summary>Zed.log</summary><pre>
<!-- Click below this line and paste or drag-and-drop your log-->
<details><summary>Zed.log</summary>
<!-- Click above this line and paste or drag-and-drop your log--></pre></details>
<!-- Click below this line and paste or drag-and-drop your log-->
```
```
<!-- Click above this line and paste or drag-and-drop your log--></details>
validations:
required: false

View File

@@ -1,7 +1,7 @@
name: Crash Report
description: |
Use this template for crash reports.
labels: ["admin read", "triage", "defect", "panic / crash"]
labels: ["admin read", "triage", "bug", "panic / crash"]
body:
- type: checkboxes
attributes:
@@ -31,9 +31,12 @@ body:
Linux: `~/.local/share/zed/logs/Zed.log` or $XDG_DATA_HOME
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
value: |
<details><summary>Zed.log</summary><pre>
<!-- Click below this line and paste or drag-and-drop your log-->
<details><summary>Zed.log</summary>
<!-- Click above this line and paste or drag-and-drop your log--></pre></details>
<!-- Click below this line and paste or drag-and-drop your log-->
```
```
<!-- Click above this line and paste or drag-and-drop your log--></details>
validations:
required: false

View File

@@ -7,9 +7,3 @@ runs:
- name: cargo fmt
shell: bash -euxo pipefail {0}
run: cargo fmt --all -- --check
- name: Find modified migrations
shell: bash -euxo pipefail {0}
run: |
export SQUAWK_GITHUB_TOKEN=${{ github.token }}
. ./script/squawk

View File

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

View File

@@ -2,14 +2,4 @@ Closes #ISSUE
Release Notes:
- 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
- N/A *or* Added/Fixed/Improved ...

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0

View File

@@ -18,7 +18,7 @@ jobs:
- buildjet-16vcpu-ubuntu-2204
steps:
- name: Checkout code
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
ref: ${{ github.event.inputs.branch }}
ssh-key: ${{ secrets.ZED_BOT_DEPLOY_KEY }}
@@ -43,6 +43,8 @@ jobs:
esac
which cargo-set-version > /dev/null || cargo install cargo-edit
output=$(cargo set-version -p zed --bump patch 2>&1 | sed 's/.* //')
export GIT_COMMITTER_NAME="Zed Bot"
export GIT_COMMITTER_EMAIL="hi@zed.dev"
git commit -am "Bump to $output for @$GITHUB_ACTOR" --author "Zed Bot <hi@zed.dev>"
git tag v${output}${tag_suffix}
git push origin HEAD v${output}${tag_suffix}

View File

@@ -13,7 +13,8 @@ on:
branches:
- "**"
paths-ignore:
- "docs/**"
- "docs/**/*"
- ".github/workflows/community_*"
concurrency:
# Allow only one workflow per any non-`main` branch.
@@ -24,38 +25,31 @@ env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1
RUSTFLAGS: "-D warnings"
jobs:
style:
migration_checks:
name: Check Postgres and Protobuf migrations, mergability
if: github.repository_owner == 'zed-industries'
timeout-minutes: 60
name: Check formatting and spelling
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
fetch-depth: 0
fetch-depth: 0 # fetch full history
- name: Remove untracked files
run: git clean -df
- name: Check spelling
run: script/check-spelling
- name: Run style checks
uses: ./.github/actions/check_style
- name: Check unused dependencies
uses: bnjbvr/cargo-machete@main
- name: Check licenses are present
run: script/check-licenses
- name: Check license generation
run: script/generate-licenses /tmp/zed_licenses_output
- name: Find modified migrations
shell: bash -euxo pipefail {0}
run: |
export SQUAWK_GITHUB_TOKEN=${{ github.token }}
. ./script/squawk
- name: Ensure fresh merge
shell: bash -euxo pipefail {0}
@@ -77,36 +71,64 @@ jobs:
input: "crates/proto/proto/"
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=${BUF_BASE_BRANCH},subdir=crates/proto/proto/"
style:
timeout-minutes: 60
name: Check formatting and spelling
if: github.repository_owner == 'zed-industries'
runs-on:
- buildjet-8vcpu-ubuntu-2204
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Run style checks
uses: ./.github/actions/check_style
- name: Check for typos
uses: crate-ci/typos@8e6a4285bcbde632c5d79900a7779746e8b7ea3f # v1.24.6
with:
config: ./typos.toml
macos_tests:
timeout-minutes: 60
name: (macOS) Run Clippy and tests
if: github.repository_owner == 'zed-industries'
runs-on:
- self-hosted
- test
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
- name: cargo clippy
run: ./script/clippy
- name: Check unused dependencies
uses: bnjbvr/cargo-machete@main
- name: Check licenses
run: |
script/check-licenses
script/generate-licenses /tmp/zed_licenses_output
- name: Run tests
uses: ./.github/actions/run_tests
- name: Build collab
run: RUSTFLAGS="-D warnings" cargo build -p collab
run: cargo build -p collab
- name: Build other binaries and features
run: |
RUSTFLAGS="-D warnings" cargo build --workspace --bins --all-features
cargo build --workspace --bins --all-features
cargo check -p gpui --features "macos-blade"
RUSTFLAGS="-D warnings" cargo build -p remote_server
cargo build -p remote_server
linux_tests:
timeout-minutes: 60
name: (Linux) Run Clippy and tests
if: github.repository_owner == 'zed-industries'
runs-on:
- buildjet-16vcpu-ubuntu-2204
steps:
@@ -114,12 +136,12 @@ jobs:
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
- name: Cache dependencies
uses: swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2
uses: swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-provider: "buildjet"
@@ -134,11 +156,12 @@ jobs:
uses: ./.github/actions/run_tests
- name: Build Zed
run: RUSTFLAGS="-D warnings" cargo build -p zed
run: cargo build -p zed
build_remote_server:
timeout-minutes: 60
name: (Linux) Build Remote Server
if: github.repository_owner == 'zed-industries'
runs-on:
- buildjet-16vcpu-ubuntu-2204
steps:
@@ -146,12 +169,12 @@ jobs:
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
- name: Cache dependencies
uses: swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2
uses: swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-provider: "buildjet"
@@ -160,21 +183,25 @@ jobs:
run: ./script/remote-server && ./script/install-mold 2.34.0
- name: Build Remote Server
run: RUSTFLAGS="-D warnings" cargo build -p remote_server
run: cargo build -p remote_server
# todo(windows): Actually run the tests
windows_tests:
timeout-minutes: 60
name: (Windows) Run Clippy and tests
if: github.repository_owner == 'zed-industries'
runs-on: hosted-windows-1
steps:
# more info here:- https://github.com/rust-lang/cargo/issues/13020
- name: Enable longer pathnames for git
run: git config --system core.longpaths true
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
- name: Cache dependencies
uses: swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2
uses: swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-provider: "github"
@@ -184,7 +211,7 @@ jobs:
run: cargo xtask clippy
- name: Build Zed
run: $env:RUSTFLAGS="-D warnings"; cargo build
run: cargo build
bundle-mac:
timeout-minutes: 60
@@ -205,12 +232,12 @@ jobs:
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
steps:
- name: Install Node
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
with:
node-version: "18"
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
# We need to fetch more than one commit so that `script/draft-release-notes`
# is able to diff between the current and previous tag.
@@ -233,7 +260,7 @@ jobs:
run: |
mkdir -p target/
# Ignore any errors that occur while drafting release notes to not fail the build.
script/draft-release-notes "$version" "$channel" > target/release-notes.md || true
script/draft-release-notes "$RELEASE_VERSION" "$RELEASE_CHANNEL" > target/release-notes.md || true
- name: Generate license file
run: script/generate-licenses
@@ -248,20 +275,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@604373da6381bf24206979c74d06a550515601b9 # v4
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # 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@604373da6381bf24206979c74d06a550515601b9 # v4
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # 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@604373da6381bf24206979c74d06a550515601b9 # v4
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # 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
@@ -295,7 +322,7 @@ jobs:
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
@@ -312,7 +339,7 @@ jobs:
run: script/bundle-linux
- name: Upload Linux bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # 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
@@ -342,7 +369,7 @@ jobs:
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
@@ -359,7 +386,7 @@ jobs:
run: script/bundle-linux
- name: Upload Linux bundle to workflow run if main branch or specific label
uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # 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
@@ -367,7 +394,6 @@ jobs:
- name: Upload app bundle to release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
if: ${{ env.RELEASE_CHANNEL == 'preview' || env.RELEASE_CHANNEL == 'stable' }}
with:
draft: true
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}

View File

@@ -14,17 +14,17 @@ jobs:
stale-issue-message: >
Hi there! 👋
We're working to clean up our issue tracker by closing older issues that might not be relevant anymore. Are you able to reproduce this issue in the latest version of Zed? If so, please let us know by commenting on this issue and we will keep it open; otherwise, we'll close it in 7 days. Feel free to open a new issue if you're seeing this message after the issue has been closed.
We're working to clean up our issue tracker by closing older issues that might not be relevant anymore. If you are able to reproduce this issue in the latest version of Zed, please let us know by commenting on this issue, and we will keep it open. If you can't reproduce it, feel free to close the issue yourself. Otherwise, we'll close it in 7 days.
Thanks for your help!
close-issue-message: "This issue was closed due to inactivity. If you're still experiencing this problem, feel free to ping a Zed team member to reopen this issue or open a new one."
close-issue-message: "This issue was closed due to inactivity. If you're still experiencing this problem, please open a new issue with a link to this issue."
# We will increase `days-before-stale` to 365 on or after Jan 24th,
# 2024. This date marks one year since migrating issues from
# 'community' to 'zed' repository. The migration added activity to all
# issues, preventing 365 days from working until then.
days-before-stale: 180
days-before-close: 7
any-of-issue-labels: "defect,panic / crash"
any-of-issue-labels: "bug,panic / crash"
operations-per-run: 1000
ascending: true
enable-statistics: true

View File

@@ -10,9 +10,9 @@ jobs:
runs-on: ubuntu-latest
if: github.repository_owner == 'zed-industries'
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up uv
uses: astral-sh/setup-uv@v3
uses: astral-sh/setup-uv@f3bcaebff5eace81a1c062af9f9011aae482ca9d # v3
with:
version: "latest"
enable-cache: true

View File

@@ -10,9 +10,9 @@ jobs:
runs-on: ubuntu-latest
if: github.repository_owner == 'zed-industries'
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up uv
uses: astral-sh/setup-uv@v3
uses: astral-sh/setup-uv@f3bcaebff5eace81a1c062af9f9011aae482ca9d # v3
with:
version: "latest"
enable-cache: true

View File

@@ -14,14 +14,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9
- name: Setup Node
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
with:
node-version: "20"
cache: "pnpm"

View File

@@ -8,11 +8,12 @@ on:
jobs:
deploy-docs:
name: Deploy Docs
if: github.repository_owner == 'zed-industries'
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
@@ -36,28 +37,28 @@ jobs:
mdbook build ./docs --dest-dir=../target/deploy/docs/
- name: Deploy Docs
uses: cloudflare/wrangler-action@9681c2997648301493e78cacbfb790a9f19c833f # v3
uses: cloudflare/wrangler-action@b2a0191ce60d21388e1a8dcc968b4e9966f938e1 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy target/deploy --project-name=docs
- name: Deploy Install
uses: cloudflare/wrangler-action@9681c2997648301493e78cacbfb790a9f19c833f # v3
uses: cloudflare/wrangler-action@b2a0191ce60d21388e1a8dcc968b4e9966f938e1 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: r2 object put -f script/install.sh zed-open-source-website-assets/install.sh
- name: Deploy Docs Workers
uses: cloudflare/wrangler-action@9681c2997648301493e78cacbfb790a9f19c833f # v3
uses: cloudflare/wrangler-action@b2a0191ce60d21388e1a8dcc968b4e9966f938e1 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy .cloudflare/docs-proxy/src/worker.js
- name: Deploy Install Workers
uses: cloudflare/wrangler-action@9681c2997648301493e78cacbfb790a9f19c833f # v3
uses: cloudflare/wrangler-action@b2a0191ce60d21388e1a8dcc968b4e9966f938e1 # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

View File

@@ -17,7 +17,7 @@ jobs:
- test
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
fetch-depth: 0
@@ -36,7 +36,7 @@ jobs:
needs: style
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
fetch-depth: 0
@@ -71,7 +71,7 @@ jobs:
run: doctl registry login
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
@@ -97,7 +97,7 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false

View File

@@ -11,10 +11,11 @@ on:
jobs:
check_formatting:
name: "Check formatting"
if: github.repository_owner == 'zed-industries'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
@@ -29,5 +30,8 @@ jobs:
false
}
- name: Check spelling
run: script/check-spelling docs/
- name: Check for Typos with Typos-CLI
uses: crate-ci/typos@8e6a4285bcbde632c5d79900a7779746e8b7ea3f # v1.24.6
with:
config: ./typos.toml
files: ./docs/

View File

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

View File

@@ -22,12 +22,12 @@ jobs:
- buildjet-16vcpu-ubuntu-2204
steps:
- name: Install Node
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
with:
node-version: "18"
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false

View File

@@ -23,7 +23,7 @@ jobs:
- test
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
fetch-depth: 0
@@ -44,7 +44,7 @@ jobs:
needs: style
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
@@ -70,12 +70,12 @@ jobs:
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
steps:
- name: Install Node
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
with:
node-version: "18"
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
@@ -109,7 +109,7 @@ jobs:
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
@@ -149,7 +149,7 @@ jobs:
ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
clean: false
@@ -182,7 +182,7 @@ jobs:
- bundle-linux-arm
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0

View File

@@ -60,6 +60,8 @@ Max Brunsfeld <maxbrunsfeld@gmail.com>
Max Brunsfeld <maxbrunsfeld@gmail.com> <max@zed.dev>
Max Linke <maxlinke88@gmail.com>
Max Linke <maxlinke88@gmail.com> <kain88-de@users.noreply.github.com>
Michael Sloan <michael@zed.dev>
Michael Sloan <michael@zed.dev> <mgsloan@google.com>
Mikayla Maki <mikayla@zed.dev>
Mikayla Maki <mikayla@zed.dev> <mikayla.c.maki@gmail.com>
Mikayla Maki <mikayla@zed.dev> <mikayla.c.maki@icloud.com>

View File

@@ -1,3 +1,3 @@
# Code of Conduct
The Code of Conduct for this repository can be found online at [zed.dev/docs/code-of-conduct](https://zed.dev/docs/code-of-conduct).
The Code of Conduct for this repository can be found online at [zed.dev/code-of-conduct](https://zed.dev/code-of-conduct).

View File

@@ -2,7 +2,7 @@
Thanks for your interest in contributing to Zed, the collaborative platform that is also a code editor!
All activity in Zed forums is subject to our [Code of Conduct](https://zed.dev/docs/code-of-conduct). Additionally, contributors must sign our [Contributor License Agreement](https://zed.dev/cla) before their contributions can be merged.
All activity in Zed forums is subject to our [Code of Conduct](https://zed.dev/code-of-conduct). Additionally, contributors must sign our [Contributor License Agreement](https://zed.dev/cla) before their contributions can be merged.
## Contribution ideas

834
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,6 @@ members = [
"crates/context_servers",
"crates/copilot",
"crates/db",
"crates/dev_server_projects",
"crates/diagnostics",
"crates/docs_preprocessor",
"crates/editor",
@@ -31,6 +30,7 @@ members = [
"crates/extension",
"crates/extension_api",
"crates/extension_cli",
"crates/extension_host",
"crates/extensions_ui",
"crates/feature_flags",
"crates/feedback",
@@ -45,7 +45,6 @@ members = [
"crates/google_ai",
"crates/gpui",
"crates/gpui_macros",
"crates/headless",
"crates/html_to_markdown",
"crates/http_client",
"crates/image_viewer",
@@ -119,6 +118,7 @@ members = [
"crates/theme_selector",
"crates/time_format",
"crates/title_bar",
"crates/toolchain_selector",
"crates/ui",
"crates/ui_input",
"crates/ui_macros",
@@ -139,7 +139,6 @@ members = [
"extensions/astro",
"extensions/clojure",
"extensions/csharp",
"extensions/dart",
"extensions/deno",
"extensions/elixir",
"extensions/elm",
@@ -158,12 +157,10 @@ members = [
"extensions/ruff",
"extensions/slash-commands-example",
"extensions/snippets",
"extensions/svelte",
"extensions/terraform",
"extensions/test-extension",
"extensions/toml",
"extensions/uiua",
"extensions/vue",
"extensions/zig",
#
@@ -203,10 +200,10 @@ command_palette_hooks = { path = "crates/command_palette_hooks" }
context_servers = { path = "crates/context_servers" }
copilot = { path = "crates/copilot" }
db = { path = "crates/db" }
dev_server_projects = { path = "crates/dev_server_projects" }
diagnostics = { path = "crates/diagnostics" }
editor = { path = "crates/editor" }
extension = { path = "crates/extension" }
extension_host = { path = "crates/extension_host" }
extensions_ui = { path = "crates/extensions_ui" }
feature_flags = { path = "crates/feature_flags" }
feedback = { path = "crates/feedback" }
@@ -221,7 +218,6 @@ go_to_line = { path = "crates/go_to_line" }
google_ai = { path = "crates/google_ai" }
gpui = { path = "crates/gpui", default-features = false, features = ["http_client"]}
gpui_macros = { path = "crates/gpui_macros" }
headless = { path = "crates/headless" }
html_to_markdown = { path = "crates/html_to_markdown" }
http_client = { path = "crates/http_client" }
image_viewer = { path = "crates/image_viewer" }
@@ -296,6 +292,7 @@ theme_importer = { path = "crates/theme_importer" }
theme_selector = { path = "crates/theme_selector" }
time_format = { path = "crates/time_format" }
title_bar = { path = "crates/title_bar" }
toolchain_selector = { path = "crates/toolchain_selector" }
ui = { path = "crates/ui" }
ui_input = { path = "crates/ui_input" }
ui_macros = { path = "crates/ui_macros" }
@@ -349,6 +346,7 @@ ctor = "0.2.6"
dashmap = "6.0"
derive_more = "0.99.17"
dirs = "4.0"
ec4rs = "1.1"
emojis = "0.6.1"
env_logger = "0.11"
exec = "0.3.1"
@@ -374,6 +372,7 @@ linkify = "0.10.0"
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
markup5ever_rcdom = "0.3.0"
nanoid = "0.4"
nbformat = "0.5.0"
nix = "0.29"
num-format = "0.4.4"
once_cell = "1.19.0"
@@ -381,6 +380,12 @@ ordered-float = "2.1.1"
palette = { version = "0.7.5", default-features = false, features = ["std"] }
parking_lot = "0.12.1"
pathdiff = "0.2"
pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "ffcbf3f28c46633abd5448a52b1f396c322e0d6c" }
postage = { version = "0.5", features = ["futures-traits"] }
pretty_assertions = "1.3.0"
profiling = "1"
@@ -389,11 +394,19 @@ prost-build = "0.9"
prost-types = "0.9"
pulldown-cmark = { version = "0.12.0", default-features = false }
rand = "0.8.5"
rayon = "1.8"
regex = "1.5"
repair_json = "0.1.0"
reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "fd110f6998da16bbca97b6dddda9be7827c50e29", default-features = false, features = ["charset", "http2", "macos-system-configuration", "rustls-tls-native-roots", "stream"]}
reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "fd110f6998da16bbca97b6dddda9be7827c50e29", default-features = false, features = [
"charset",
"http2",
"macos-system-configuration",
"rustls-tls-native-roots",
"socks",
"stream",
] }
rsa = "0.9.6"
runtimelib = { version = "0.15", default-features = false, features = [
runtimelib = { version = "0.19.0", default-features = false, features = [
"async-dispatcher-runtime",
] }
rustc-demangle = "0.1.23"
@@ -453,7 +466,7 @@ tree-sitter-diff = "0.1.0"
tree-sitter-html = "0.20"
tree-sitter-jsdoc = "0.23"
tree-sitter-json = "0.23"
tree-sitter-md = { git = "https://github.com/zed-industries/tree-sitter-markdown", rev = "4cfa6aad6b75052a5077c80fd934757d9267d81b" }
tree-sitter-md = { git = "https://github.com/tree-sitter-grammars/tree-sitter-markdown", rev = "9a23c1a96c0513d8fc6520972beedd419a973539" }
tree-sitter-python = "0.23"
tree-sitter-regex = "0.23"
tree-sitter-ruby = "0.23"
@@ -463,6 +476,7 @@ tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml",
unicase = "2.6"
unindent = "0.1.7"
unicode-segmentation = "1.10"
unicode-script = "0.5.7"
url = "2.2"
uuid = { version = "1.1.2", features = ["v4", "v5", "serde"] }
wasmparser = "0.215"

1
assets/icons/diff.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-diff"><path d="M12 3v14"/><path d="M5 10h14"/><path d="M5 21h14"/></svg>

After

Width:  |  Height:  |  Size: 275 B

View File

@@ -58,6 +58,7 @@
"gitignore": "vcs",
"gitkeep": "vcs",
"gitmodules": "vcs",
"gleam": "gleam",
"go": "go",
"gql": "graphql",
"graphql": "graphql",
@@ -65,6 +66,7 @@
"h": "c",
"handlebars": "code",
"hbs": "template",
"hcl": "hcl",
"heex": "elixir",
"heic": "image",
"heif": "image",
@@ -82,6 +84,7 @@
"j2k": "image",
"java": "java",
"jfif": "image",
"jl": "julia",
"jp2": "image",
"jpeg": "image",
"jpg": "image",
@@ -116,6 +119,7 @@
"myd": "storage",
"myi": "storage",
"nim": "nim",
"nix": "nix",
"nu": "terminal",
"odp": "document",
"ods": "document",
@@ -128,6 +132,7 @@
"php": "php",
"plist": "template",
"png": "image",
"postcss": "css",
"ppt": "document",
"pptx": "document",
"prettierignore": "prettier",
@@ -142,12 +147,15 @@
"rb": "ruby",
"rebar.config": "erlang",
"rkt": "code",
"roc": "roc",
"rs": "rust",
"rtf": "document",
"sass": "sass",
"sav": "storage",
"sc": "scala",
"scala": "scala",
"scm": "code",
"scss": "sass",
"sdf": "storage",
"sh": "terminal",
"sql": "storage",
@@ -181,6 +189,7 @@
"yaml": "settings",
"yml": "settings",
"yrl": "erlang",
"zig": "zig",
"zlogin": "terminal",
"zsh": "terminal",
"zsh_aliases": "terminal",
@@ -256,6 +265,9 @@
"fsharp": {
"icon": "icons/file_icons/fsharp.svg"
},
"gleam": {
"icon": "icons/file_icons/gleam.svg"
},
"go": {
"icon": "icons/file_icons/go.svg"
},
@@ -265,6 +277,9 @@
"haskell": {
"icon": "icons/file_icons/haskell.svg"
},
"hcl": {
"icon": "icons/file_icons/hcl.svg"
},
"heroku": {
"icon": "icons/file_icons/heroku.svg"
},
@@ -277,6 +292,9 @@
"javascript": {
"icon": "icons/file_icons/javascript.svg"
},
"julia": {
"icon": "icons/file_icons/julia.svg"
},
"kotlin": {
"icon": "icons/file_icons/kotlin.svg"
},
@@ -292,6 +310,9 @@
"nim": {
"icon": "icons/file_icons/nim.svg"
},
"nix": {
"icon": "icons/file_icons/nix.svg"
},
"ocaml": {
"icon": "icons/file_icons/ocaml.svg"
},
@@ -316,12 +337,18 @@
"react": {
"icon": "icons/file_icons/react.svg"
},
"roc": {
"icon": "icons/file_icons/roc.svg"
},
"ruby": {
"icon": "icons/file_icons/ruby.svg"
},
"rust": {
"icon": "icons/file_icons/rust.svg"
},
"sass": {
"icon": "icons/file_icons/sass.svg"
},
"scala": {
"icon": "icons/file_icons/scala.svg"
},
@@ -360,6 +387,9 @@
},
"vue": {
"icon": "icons/file_icons/vue.svg"
},
"zig": {
"icon": "icons/file_icons/zig.svg"
}
}
}

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.3848 9.30444C7.3848 9.30444 7.53254 10.2646 8.53248 10.0882C9.53242 9.91193 9.36378 8.95549 9.36378 8.95549" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.54155 5.54157C6.12355 4.90104 6.01688 2.62541 7.22875 2.3985C8.44063 2.17158 9.19097 4.33148 9.91982 4.6814C10.6487 5.03133 12.8517 4.3028 13.4381 5.38734C14.0244 6.47188 12.1395 7.95973 12.026 8.64088C11.9126 9.32203 13.3614 11.2416 12.4675 12.1701C11.5736 13.0986 9.73005 11.7545 8.90486 11.8834C8.07966 12.0123 6.79244 13.9095 5.67367 13.3502C4.55491 12.7909 5.16702 10.5455 4.82437 9.87612C4.48171 9.20673 2.34028 8.54978 2.4525 7.35049C2.56471 6.15121 4.95956 6.1821 5.54155 5.54157Z" stroke="#FF7676" stroke-opacity="0.52" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.54155 5.54157C6.12355 4.90104 6.01688 2.62541 7.22875 2.3985C8.44063 2.17158 9.19097 4.33148 9.91982 4.6814C10.6487 5.03133 12.8517 4.3028 13.4381 5.38734C14.0244 6.47188 12.1395 7.95973 12.026 8.64088C11.9126 9.32203 13.3614 11.2416 12.4675 12.1701C11.5736 13.0986 9.73005 11.7545 8.90486 11.8834C8.07966 12.0123 6.79244 13.9095 5.67367 13.3502C4.55491 12.7909 5.16702 10.5455 4.82437 9.87612C4.48171 9.20673 2.34028 8.54978 2.4525 7.35049C2.56471 6.15121 4.95956 6.1821 5.54155 5.54157Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="6.25098" cy="7.75" r="0.75" fill="black"/>
<circle cx="10.1035" cy="7.25" r="0.75" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 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 fill-rule="evenodd" clip-rule="evenodd" d="M7.11466 3.11809C7.21859 3.37393 7.09545 3.66558 6.83961 3.76952L4.31181 4.79643C4.1233 4.87302 4 5.05619 4 5.25967V11.5C4 11.7761 3.77614 12 3.5 12H2.5C2.22386 12 2 11.7761 2 11.5V4.41827C2 3.90959 2.30825 3.45164 2.77953 3.26018L6.08686 1.91658C6.34269 1.81265 6.63434 1.93579 6.73828 2.19163L7.11466 3.11809ZM10.5 1.99999C10.7761 1.99999 11 2.22384 11 2.49999V10.5C11 10.7761 10.7761 11 10.5 11H9.5C9.22386 11 9 10.7761 9 10.5V9.49999C9 9.22384 8.77614 8.99999 8.5 8.99999H7.5C7.22386 8.99999 7 9.22384 7 9.49999V13.5C7 13.7761 6.77614 14 6.5 14H5.5C5.22386 14 5 13.7761 5 13.5V5.53124C5 5.25509 5.22386 5.03124 5.5 5.03124H6.5C6.77614 5.03124 7 5.25509 7 5.53124V6.49999C7 6.77613 7.22386 6.99999 7.5 6.99999H8.5C8.77614 6.99999 9 6.77613 9 6.49999V2.49999C9 2.22384 9.22386 1.99999 9.5 1.99999H10.5ZM13.5 4.03124C13.7761 4.03124 14 4.2551 14 4.53124L14 11.5847C14 12.0859 13.7006 12.5386 13.2394 12.7349L9.99399 14.1159C9.7399 14.224 9.44626 14.1057 9.33813 13.8516L8.94658 12.9315C8.83845 12.6774 8.95678 12.3837 9.21087 12.2756L11.6958 11.2182C11.8802 11.1397 12 10.9586 12 10.7581L12 4.53124C12 4.2551 12.2238 4.03124 12.5 4.03124L13.5 4.03124Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="8" cy="5" r="2.75" fill="black"/>
<circle cx="4.75" cy="11" r="2.75" fill="black" fill-opacity="0.5"/>
<circle cx="11.25" cy="11" r="2.75" fill="black" fill-opacity="0.75"/>
</svg>

After

Width:  |  Height:  |  Size: 289 B

View File

@@ -0,0 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.00005 4.76556L4.76569 2.74996M6.00005 4.76556L3.75 4.76563M6.00005 4.76556L7.25006 4.7656" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M10.0232 11.2311L11.2675 13.2406M10.0232 11.2311L12.2732 11.2199M10.0232 11.2311L8.7732 11.2373" stroke="black" stroke-opacity="0.5" stroke-width="1.5" stroke-linecap="round"/>
<path d="M9.99025 4.91551L10.9985 2.77781M9.99025 4.91551L8.75599 3.03419M9.99025 4.91551L10.6759 5.9607" stroke="black" stroke-opacity="0.5" stroke-width="1.5" stroke-linecap="round"/>
<path d="M6.0323 11.1009L5.03465 13.2436M6.0323 11.1009L7.27585 12.9761M6.0323 11.1009L5.34151 10.0592" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M11.883 8.19023L14.2466 8.19287M11.883 8.19023L13.0602 6.27268M11.883 8.19023L11.229 9.25547" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M4.12354 7.8356L1.76002 7.84465M4.12354 7.8356L2.95585 9.75894M4.12354 7.8356L4.7723 6.76713" stroke="black" stroke-opacity="0.5" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,7 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.51497 2.02702L1.92042 1.95067C1.69543 1.94589 1.57917 2.21756 1.73796 2.37702L6.24865 6.9068C6.42388 7.08277 6.72071 6.92326 6.67067 6.68002L5.75454 2.22659C5.73103 2.11231 5.63161 2.02949 5.51497 2.02702Z" fill="black" fill-opacity="0.5"/>
<path d="M8.05816 7.38492L12.1366 8.02844C12.3704 8.06532 12.5198 7.78697 12.3599 7.61255L7.30439 2.09814C7.13336 1.91159 6.82522 2.06811 6.87499 2.31624L7.852 7.18714C7.87257 7.28971 7.95483 7.36862 8.05816 7.38492Z" fill="black"/>
<path d="M9.0952 10.9797L11.3824 9.35081C11.564 9.22151 11.4983 8.93722 11.2785 8.90058L8.496 8.43683C8.31974 8.40746 8.17047 8.56712 8.21162 8.74101L8.70689 10.8337C8.74777 11.0064 8.95062 11.0827 9.0952 10.9797Z" fill="black" fill-opacity="0.5"/>
<path d="M5.10282 13.9632L7.59108 12.4532C7.68331 12.3972 7.72923 12.2884 7.70498 12.1832L6.75736 8.07484C6.699 7.8218 6.34133 7.81448 6.27266 8.06491L4.73201 13.6834C4.67223 13.9014 4.90954 14.0805 5.10282 13.9632Z" fill="black"/>
<path d="M11.3183 4.89351L13.1588 7.03149L15.535 6.14302C15.7099 6.07761 15.754 5.85043 15.6161 5.72438L13.7222 3.99219L11.4546 4.48614C11.2695 4.52645 11.1947 4.74995 11.3183 4.89351Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 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 fill-rule="evenodd" clip-rule="evenodd" d="M6.92096 7.00668C7.87408 7.83549 10.0987 7.48203 10.9376 7.06254C12.8751 6.09381 13.9407 4.39379 12.6407 2.90629C11.0157 1.04692 6.24221 2.49998 4.89844 3.40625C3.55467 4.31252 2.67972 5.53126 2.89071 7.1719C3.1017 8.81254 4.68758 9.7422 6.03128 10.3203C5.38786 10.5616 3.8517 11.0388 3.3125 11.7188C2.71341 12.4742 3.04343 14 4.51577 14C7.15639 14 7.59539 11.1486 7.14847 10.4375C7.88773 10.1295 8.49597 9.96169 9.40138 9.77081C9.63831 9.72087 9.65457 9.46395 9.41295 9.44827C8.80252 9.40864 7.30567 9.8489 6.92096 9.97657C5.78909 9.35157 4.51016 7.93818 4.59378 6.87501C4.68676 5.6928 5.27676 5.07603 6.84508 4.21876C8.01705 3.57813 10.258 3.10695 11.25 3.62501C12.6563 4.35936 10.7875 5.75599 9.92969 6.32031C9.28179 6.74656 8.21971 6.77513 7.22979 6.61435C6.99371 6.576 6.74048 6.84974 6.92096 7.00668ZM5.6719 12.4643C6.35508 11.9894 6.45471 11.1076 6.29955 10.8844C5.76663 11.0874 4.36593 11.9102 4.75111 12.4643C4.90628 12.6875 5.31358 12.7134 5.6719 12.4643Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.25 12H11C10.794 12 10.6764 11.7648 10.8 11.6L11.925 10.1C11.9722 10.037 12.0463 10 12.125 10H12.75C12.8881 10 13 9.88807 13 9.75V6.25C13 6.11193 12.8881 6 12.75 6H12.4045C12.2187 6 12.0978 5.80442 12.1809 5.6382L12.9309 4.1382C12.9732 4.0535 13.0598 4 13.1545 4H14.25C14.3881 4 14.5 4.11193 14.5 4.25V11.75C14.5 11.8881 14.3881 12 14.25 12Z" fill="black"/>
<path d="M1.75 4H5C5.20601 4 5.32361 4.23519 5.2 4.4L4.075 5.9C4.02779 5.96295 3.95369 6 3.875 6H3.25C3.11193 6 3 6.11193 3 6.25V9.75C3 9.88807 3.11193 10 3.25 10H3.59549C3.78134 10 3.90221 10.1956 3.8191 10.3618L3.0691 11.8618C3.02675 11.9465 2.94018 12 2.84549 12H1.75C1.61193 12 1.5 11.8881 1.5 11.75V4.25C1.5 4.11193 1.61193 4 1.75 4Z" fill="black"/>
<path d="M7.55748 6H5.95006C5.74177 6 5.62482 5.76022 5.75306 5.59609L6.92493 4.09609C6.97231 4.03544 7.04498 4 7.12194 4H9.93075C9.97607 4 10.0205 3.98769 10.0594 3.96437L11.6408 3.0155C11.8641 2.88154 12.1179 3.13555 11.9837 3.3587L8.22612 9.6083C8.12629 9.77433 8.24508 9.98591 8.43881 9.98712L10.0039 9.9969C10.2092 9.99818 10.3255 10.2327 10.2023 10.3969L9.075 11.9C9.02779 11.963 8.95369 12 8.875 12H6.55383C6.51835 12 6.48328 12.0076 6.45094 12.0222L4.32473 12.9824C4.10122 13.0833 3.88113 12.8356 4.00771 12.6255L7.77161 6.37903C7.87201 6.2124 7.75202 6 7.55748 6Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

7
assets/icons/list_x.svg Normal file
View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.33333 8H3" stroke="#FBF1C7" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.6667 4H3" stroke="#FBF1C7" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.6667 12H3" stroke="#FBF1C7" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.6667 6.66663L11 9.33329" stroke="#FBF1C7" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 6.66663L13.6667 9.33329" stroke="#FBF1C7" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 579 B

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 21V12M7 12H3M7 12H11" stroke="black" stroke-width="2" stroke-linecap="round"/>
<path d="M21 19L16 19L16 14" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.99987 5.07027L7.49915 4.20467L7.99987 5.07027ZM6.04652 5.25026C5.63245 5.61573 5.59305 6.24766 5.95851 6.66173C6.32398 7.0758 6.95592 7.1152 7.36999 6.74974L6.04652 5.25026ZM11.9999 5C15.8659 5 18.9999 8.13401 18.9999 12H20.9999C20.9999 7.02944 16.9705 3 11.9999 3V5ZM18.9999 12C18.9999 14.2101 17.9768 16.1806 16.3744 17.4651L17.6254 19.0256C19.6809 17.3779 20.9999 14.8426 20.9999 12H18.9999ZM8.5006 5.93588C9.5292 5.34086 10.7232 5 11.9999 5V3C10.3623 3 8.82395 3.4383 7.49915 4.20467L8.5006 5.93588ZM7.36999 6.74974C7.71803 6.44255 8.09667 6.16954 8.5006 5.93588L7.49915 4.20467C6.9797 4.50515 6.49329 4.85593 6.04652 5.25026L7.36999 6.74974Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 979 B

1
assets/icons/wand.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-wand"><path d="M15 4V2"/><path d="M15 16v-2"/><path d="M8 9h2"/><path d="M20 9h2"/><path d="M17.8 11.8 19 13"/><path d="M15 9h.01"/><path d="M17.8 6.2 19 5"/><path d="m3 21 9-9"/><path d="M12.2 6.2 11 5"/></svg>

After

Width:  |  Height:  |  Size: 414 B

View File

@@ -313,6 +313,15 @@
"ctrl-k ctrl-l": "editor::ToggleFold",
"ctrl-k ctrl-[": "editor::FoldRecursive",
"ctrl-k ctrl-]": "editor::UnfoldRecursive",
"ctrl-k ctrl-1": ["editor::FoldAtLevel", { "level": 1 }],
"ctrl-k ctrl-2": ["editor::FoldAtLevel", { "level": 2 }],
"ctrl-k ctrl-3": ["editor::FoldAtLevel", { "level": 3 }],
"ctrl-k ctrl-4": ["editor::FoldAtLevel", { "level": 4 }],
"ctrl-k ctrl-5": ["editor::FoldAtLevel", { "level": 5 }],
"ctrl-k ctrl-6": ["editor::FoldAtLevel", { "level": 6 }],
"ctrl-k ctrl-7": ["editor::FoldAtLevel", { "level": 7 }],
"ctrl-k ctrl-8": ["editor::FoldAtLevel", { "level": 8 }],
"ctrl-k ctrl-9": ["editor::FoldAtLevel", { "level": 9 }],
"ctrl-k ctrl-0": "editor::FoldAll",
"ctrl-k ctrl-j": "editor::UnfoldAll",
"ctrl-space": "editor::ShowCompletions",
@@ -505,6 +514,13 @@
"ctrl-enter": "assistant::InlineAssist"
}
},
{
"context": "ProposedChangesEditor",
"bindings": {
"ctrl-shift-y": "editor::ApplyDiffHunk",
"ctrl-alt-a": "editor::ApplyAllDiffHunks"
}
},
{
"context": "Editor && jupyter && !ContextEditor",
"bindings": {
@@ -516,6 +532,7 @@
"context": "ContextEditor > Editor",
"bindings": {
"ctrl-enter": "assistant::Assist",
"ctrl-shift-enter": "assistant::Edit",
"ctrl-s": "workspace::Save",
"ctrl->": "assistant::QuoteSelection",
"ctrl-<": "assistant::InsertIntoEditor",
@@ -547,9 +564,11 @@
"ctrl-alt-c": "outline_panel::CopyPath",
"alt-ctrl-shift-c": "outline_panel::CopyRelativePath",
"alt-ctrl-r": "outline_panel::RevealInFileManager",
"space": ["outline_panel::Open", { "change_selection": false }],
"space": "outline_panel::Open",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev"
"shift-up": "menu::SelectPrev",
"alt-enter": "editor::OpenExcerpts",
"ctrl-k enter": "editor::OpenExcerptsSplit"
}
},
{

View File

@@ -201,6 +201,7 @@
"context": "ContextEditor > Editor",
"bindings": {
"cmd-enter": "assistant::Assist",
"cmd-shift-enter": "assistant::Edit",
"cmd-s": "workspace::Save",
"cmd->": "assistant::QuoteSelection",
"cmd-<": "assistant::InsertIntoEditor",
@@ -350,6 +351,15 @@
"cmd-k cmd-l": "editor::ToggleFold",
"cmd-k cmd-[": "editor::FoldRecursive",
"cmd-k cmd-]": "editor::UnfoldRecursive",
"cmd-k cmd-1": ["editor::FoldAtLevel", { "level": 1 }],
"cmd-k cmd-2": ["editor::FoldAtLevel", { "level": 2 }],
"cmd-k cmd-3": ["editor::FoldAtLevel", { "level": 3 }],
"cmd-k cmd-4": ["editor::FoldAtLevel", { "level": 4 }],
"cmd-k cmd-5": ["editor::FoldAtLevel", { "level": 5 }],
"cmd-k cmd-6": ["editor::FoldAtLevel", { "level": 6 }],
"cmd-k cmd-7": ["editor::FoldAtLevel", { "level": 7 }],
"cmd-k cmd-8": ["editor::FoldAtLevel", { "level": 8 }],
"cmd-k cmd-9": ["editor::FoldAtLevel", { "level": 9 }],
"cmd-k cmd-0": "editor::FoldAll",
"cmd-k cmd-j": "editor::UnfoldAll",
"ctrl-space": "editor::ShowCompletions",
@@ -538,6 +548,13 @@
"ctrl-enter": "assistant::InlineAssist"
}
},
{
"context": "ProposedChangesEditor",
"bindings": {
"cmd-shift-y": "editor::ApplyDiffHunk",
"cmd-shift-a": "editor::ApplyAllDiffHunks"
}
},
{
"context": "PromptEditor",
"bindings": {
@@ -560,9 +577,11 @@
"cmd-alt-c": "outline_panel::CopyPath",
"alt-cmd-shift-c": "outline_panel::CopyRelativePath",
"alt-cmd-r": "outline_panel::RevealInFileManager",
"space": ["outline_panel::Open", { "change_selection": false }],
"space": "outline_panel::Open",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev"
"shift-up": "menu::SelectPrev",
"alt-enter": "editor::OpenExcerpts",
"cmd-k enter": "editor::OpenExcerptsSplit"
}
},
{

View File

@@ -25,8 +25,8 @@
"alt-j": ["editor::SelectNext", { "replace_newest": false }],
"alt-shift-j": ["editor::SelectPrevious", { "replace_newest": false }],
"ctrl-/": ["editor::ToggleComments", { "advance_downwards": true }],
"alt-up": "editor::SelectLargerSyntaxNode",
"alt-down": "editor::SelectSmallerSyntaxNode",
"ctrl-w": "editor::SelectLargerSyntaxNode",
"ctrl-shift-w": "editor::SelectSmallerSyntaxNode",
"shift-alt-up": "editor::MoveLineUp",
"shift-alt-down": "editor::MoveLineDown",
"ctrl-alt-l": "editor::Format",

View File

@@ -24,8 +24,8 @@
"ctrl-g": ["editor::SelectNext", { "replace_newest": false }],
"ctrl-cmd-g": ["editor::SelectPrevious", { "replace_newest": false }],
"cmd-/": ["editor::ToggleComments", { "advance_downwards": true }],
"alt-up": "editor::SelectLargerSyntaxNode",
"alt-down": "editor::SelectSmallerSyntaxNode",
"cmd-up": "editor::SelectLargerSyntaxNode",
"cmd-down": "editor::SelectSmallerSyntaxNode",
"shift-alt-up": "editor::MoveLineUp",
"shift-alt-down": "editor::MoveLineDown",
"cmd-alt-l": "editor::Format",
@@ -34,7 +34,7 @@
"cmd-]": "pane::GoForward",
"alt-f7": "editor::FindAllReferences",
"cmd-alt-f7": "editor::FindAllReferences",
"cmd-b": "editor::GoToDefinition",
"cmd-b": "editor::GoToDefinition", // Conflicts with workspace::ToggleLeftDock
"cmd-alt-b": "editor::GoToDefinitionSplit",
"cmd-shift-b": "editor::GoToTypeDefinition",
"cmd-alt-shift-b": "editor::GoToTypeDefinitionSplit",
@@ -58,13 +58,20 @@
"alt-enter": "editor::ToggleCodeActions"
}
},
{
"context": "BufferSearchBar > Editor",
"bindings": {
"shift-enter": "search::SelectPrevMatch"
}
},
{
"context": "Workspace",
"bindings": {
"cmd-shift-o": "file_finder::Toggle",
"cmd-shift-a": "command_palette::Toggle",
"shift shift": "command_palette::Toggle",
"cmd-alt-o": "project_symbols::Toggle",
"cmd-alt-o": "project_symbols::Toggle", // JetBrains: Go to Symbol
"cmd-o": "project_symbols::Toggle", // JetBrains: Go to Class
"cmd-1": "workspace::ToggleLeftDock",
"cmd-6": "diagnostics::Deploy"
}

View File

@@ -127,6 +127,9 @@
"shift-h": "vim::WindowTop",
"shift-m": "vim::WindowMiddle",
"shift-l": "vim::WindowBottom",
"q": "vim::ToggleRecord",
"shift-q": "vim::ReplayLastRecording",
"@": ["vim::PushOperator", "ReplayRegister"],
// z commands
"z enter": ["workspace::SendKeystrokes", "z t ^"],
"z -": ["workspace::SendKeystrokes", "z b ^"],
@@ -137,14 +140,14 @@
"z .": ["workspace::SendKeystrokes", "z z ^"],
"z b": "editor::ScrollCursorBottom",
"z a": "editor::ToggleFold",
"z A": "editor::ToggleFoldRecursive",
"z shift-a": "editor::ToggleFoldRecursive",
"z c": "editor::Fold",
"z C": "editor::FoldRecursive",
"z shift-c": "editor::FoldRecursive",
"z o": "editor::UnfoldLines",
"z O": "editor::UnfoldRecursive",
"z shift-o": "editor::UnfoldRecursive",
"z f": "editor::FoldSelectedRanges",
"z M": "editor::FoldAll",
"z R": "editor::UnfoldAll",
"z shift-m": "editor::FoldAll",
"z shift-r": "editor::UnfoldAll",
"shift-z shift-q": ["pane::CloseActiveItem", { "saveIntent": "skip" }],
"shift-z shift-z": ["pane::CloseActiveItem", { "saveIntent": "saveAll" }],
// Count support
@@ -157,51 +160,6 @@
"7": ["vim::Number", 7],
"8": ["vim::Number", 8],
"9": ["vim::Number", 9],
// window related commands (ctrl-w X)
"ctrl-w": null,
"ctrl-w left": ["workspace::ActivatePaneInDirection", "Left"],
"ctrl-w right": ["workspace::ActivatePaneInDirection", "Right"],
"ctrl-w up": ["workspace::ActivatePaneInDirection", "Up"],
"ctrl-w down": ["workspace::ActivatePaneInDirection", "Down"],
"ctrl-w h": ["workspace::ActivatePaneInDirection", "Left"],
"ctrl-w l": ["workspace::ActivatePaneInDirection", "Right"],
"ctrl-w k": ["workspace::ActivatePaneInDirection", "Up"],
"ctrl-w j": ["workspace::ActivatePaneInDirection", "Down"],
"ctrl-w ctrl-h": ["workspace::ActivatePaneInDirection", "Left"],
"ctrl-w ctrl-l": ["workspace::ActivatePaneInDirection", "Right"],
"ctrl-w ctrl-k": ["workspace::ActivatePaneInDirection", "Up"],
"ctrl-w ctrl-j": ["workspace::ActivatePaneInDirection", "Down"],
"ctrl-w shift-left": ["workspace::SwapPaneInDirection", "Left"],
"ctrl-w shift-right": ["workspace::SwapPaneInDirection", "Right"],
"ctrl-w shift-up": ["workspace::SwapPaneInDirection", "Up"],
"ctrl-w shift-down": ["workspace::SwapPaneInDirection", "Down"],
"ctrl-w shift-h": ["workspace::SwapPaneInDirection", "Left"],
"ctrl-w shift-l": ["workspace::SwapPaneInDirection", "Right"],
"ctrl-w shift-k": ["workspace::SwapPaneInDirection", "Up"],
"ctrl-w shift-j": ["workspace::SwapPaneInDirection", "Down"],
"ctrl-w g t": "pane::ActivateNextItem",
"ctrl-w ctrl-g t": "pane::ActivateNextItem",
"ctrl-w g shift-t": "pane::ActivatePrevItem",
"ctrl-w ctrl-g shift-t": "pane::ActivatePrevItem",
"ctrl-w w": "workspace::ActivateNextPane",
"ctrl-w ctrl-w": "workspace::ActivateNextPane",
"ctrl-w p": "workspace::ActivatePreviousPane",
"ctrl-w ctrl-p": "workspace::ActivatePreviousPane",
"ctrl-w shift-w": "workspace::ActivatePreviousPane",
"ctrl-w ctrl-shift-w": "workspace::ActivatePreviousPane",
"ctrl-w v": "pane::SplitVertical",
"ctrl-w ctrl-v": "pane::SplitVertical",
"ctrl-w s": "pane::SplitHorizontal",
"ctrl-w shift-s": "pane::SplitHorizontal",
"ctrl-w ctrl-s": "pane::SplitHorizontal",
"ctrl-w c": "pane::CloseAllItems",
"ctrl-w ctrl-c": "pane::CloseAllItems",
"ctrl-w q": "pane::CloseAllItems",
"ctrl-w ctrl-q": "pane::CloseAllItems",
"ctrl-w o": "workspace::CloseInactiveTabsAndPanes",
"ctrl-w ctrl-o": "workspace::CloseInactiveTabsAndPanes",
"ctrl-w n": "workspace::NewFileSplitHorizontal",
"ctrl-w ctrl-n": "workspace::NewFileSplitHorizontal",
"ctrl-w d": "editor::GoToDefinitionSplit",
"ctrl-w g d": "editor::GoToDefinitionSplit",
"ctrl-w shift-d": "editor::GoToTypeDefinitionSplit",
@@ -251,9 +209,6 @@
"\"": ["vim::PushOperator", "Register"],
"g q": ["vim::PushOperator", "Rewrap"],
"g w": ["vim::PushOperator", "Rewrap"],
"q": "vim::ToggleRecord",
"shift-q": "vim::ReplayLastRecording",
"@": ["vim::PushOperator", "ReplayRegister"],
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-pageup": "pane::ActivatePrevItem",
"insert": "vim::InsertBefore",
@@ -279,7 +234,7 @@
"bindings": {
":": "vim::VisualCommand",
"u": "vim::ConvertToLowerCase",
"U": "vim::ConvertToUpperCase",
"shift-u": "vim::ConvertToUpperCase",
"o": "vim::OtherEnd",
"shift-o": "vim::OtherEnd",
"d": "vim::VisualDelete",
@@ -303,8 +258,8 @@
"g ctrl-x": ["vim::Decrement", { "step": true }],
"shift-i": "vim::InsertBefore",
"shift-a": "vim::InsertAfter",
"g I": "vim::VisualInsertFirstNonWhiteSpace",
"g A": "vim::VisualInsertEndOfLine",
"g shift-i": "vim::VisualInsertFirstNonWhiteSpace",
"g shift-a": "vim::VisualInsertEndOfLine",
"shift-j": "vim::JoinLines",
"r": ["vim::PushOperator", "Replace"],
"ctrl-c": ["vim::SwitchMode", "Normal"],
@@ -339,6 +294,10 @@
"ctrl-t": "vim::Indent",
"ctrl-d": "vim::Outdent",
"ctrl-k": ["vim::PushOperator", { "Digraph": {} }],
"ctrl-v": ["vim::PushOperator", { "Literal": {} }],
"ctrl-shift-v": "editor::Paste", // note: this is *very* similar to ctrl-v in vim, but ctrl-shift-v on linux is the typical shortcut for paste when ctrl-v is already in use.
"ctrl-q": ["vim::PushOperator", { "Literal": {} }],
"ctrl-shift-q": ["vim::PushOperator", { "Literal": {} }],
"ctrl-r": ["vim::PushOperator", "Register"],
"insert": "vim::ToggleReplace"
}
@@ -357,6 +316,10 @@
"ctrl-c": "vim::NormalBefore",
"ctrl-[": "vim::NormalBefore",
"ctrl-k": ["vim::PushOperator", { "Digraph": {} }],
"ctrl-v": ["vim::PushOperator", { "Literal": {} }],
"ctrl-shift-v": "editor::Paste", // note: this is *very* similar to ctrl-v in vim, but ctrl-shift-v on linux is the typical shortcut for paste when ctrl-v is already in use.
"ctrl-q": ["vim::PushOperator", { "Literal": {} }],
"ctrl-shift-q": ["vim::PushOperator", { "Literal": {} }],
"backspace": "vim::UndoReplace",
"tab": "vim::Tab",
"enter": "vim::Enter",
@@ -371,7 +334,9 @@
"escape": "vim::ClearOperators",
"ctrl-c": "vim::ClearOperators",
"ctrl-[": "vim::ClearOperators",
"ctrl-k": ["vim::PushOperator", { "Digraph": {} }]
"ctrl-k": ["vim::PushOperator", { "Digraph": {} }],
"ctrl-v": ["vim::PushOperator", { "Literal": {} }],
"ctrl-q": ["vim::PushOperator", { "Literal": {} }]
}
},
{
@@ -399,12 +364,14 @@
"b": "vim::Parentheses",
"[": "vim::SquareBrackets",
"]": "vim::SquareBrackets",
"r": "vim::SquareBrackets",
"{": "vim::CurlyBrackets",
"}": "vim::CurlyBrackets",
"shift-b": "vim::CurlyBrackets",
"<": "vim::AngleBrackets",
">": "vim::AngleBrackets",
"a": "vim::Argument"
"a": "vim::AngleBrackets",
"g": "vim::Argument"
}
},
{
@@ -485,6 +452,49 @@
"c": "vim::CurrentLine"
}
},
{
"context": "vim_mode == literal",
"bindings": {
"ctrl-@": ["vim::Literal", ["ctrl-@", "\u0000"]],
"ctrl-a": ["vim::Literal", ["ctrl-a", "\u0001"]],
"ctrl-b": ["vim::Literal", ["ctrl-b", "\u0002"]],
"ctrl-c": ["vim::Literal", ["ctrl-c", "\u0003"]],
"ctrl-d": ["vim::Literal", ["ctrl-d", "\u0004"]],
"ctrl-e": ["vim::Literal", ["ctrl-e", "\u0005"]],
"ctrl-f": ["vim::Literal", ["ctrl-f", "\u0006"]],
"ctrl-g": ["vim::Literal", ["ctrl-g", "\u0007"]],
"ctrl-h": ["vim::Literal", ["ctrl-h", "\u0008"]],
"ctrl-i": ["vim::Literal", ["ctrl-i", "\u0009"]],
"ctrl-j": ["vim::Literal", ["ctrl-j", "\u000A"]],
"ctrl-k": ["vim::Literal", ["ctrl-k", "\u000B"]],
"ctrl-l": ["vim::Literal", ["ctrl-l", "\u000C"]],
"ctrl-m": ["vim::Literal", ["ctrl-m", "\u000D"]],
"ctrl-n": ["vim::Literal", ["ctrl-n", "\u000E"]],
"ctrl-o": ["vim::Literal", ["ctrl-o", "\u000F"]],
"ctrl-p": ["vim::Literal", ["ctrl-p", "\u0010"]],
"ctrl-q": ["vim::Literal", ["ctrl-q", "\u0011"]],
"ctrl-r": ["vim::Literal", ["ctrl-r", "\u0012"]],
"ctrl-s": ["vim::Literal", ["ctrl-s", "\u0013"]],
"ctrl-t": ["vim::Literal", ["ctrl-t", "\u0014"]],
"ctrl-u": ["vim::Literal", ["ctrl-u", "\u0015"]],
"ctrl-v": ["vim::Literal", ["ctrl-v", "\u0016"]],
"ctrl-w": ["vim::Literal", ["ctrl-w", "\u0017"]],
"ctrl-x": ["vim::Literal", ["ctrl-x", "\u0018"]],
"ctrl-y": ["vim::Literal", ["ctrl-y", "\u0019"]],
"ctrl-z": ["vim::Literal", ["ctrl-z", "\u001A"]],
"ctrl-[": ["vim::Literal", ["ctrl-[", "\u001B"]],
"ctrl-\\": ["vim::Literal", ["ctrl-\\", "\u001C"]],
"ctrl-]": ["vim::Literal", ["ctrl-]", "\u001D"]],
"ctrl-^": ["vim::Literal", ["ctrl-^", "\u001E"]],
"ctrl-_": ["vim::Literal", ["ctrl-_", "\u001F"]],
"escape": ["vim::Literal", ["escape", "\u001B"]],
"enter": ["vim::Literal", ["enter", "\u000D"]],
"tab": ["vim::Literal", ["tab", "\u0009"]],
// zed extensions:
"backspace": ["vim::Literal", ["backspace", "\u0008"]],
"delete": ["vim::Literal", ["delete", "\u007F"]]
}
},
{
"context": "BufferSearchBar && !in_replace",
"bindings": {
@@ -493,7 +503,57 @@
}
},
{
"context": "EmptyPane || SharedScreen",
"context": "ProjectPanel || CollabPanel || OutlinePanel || ChatPanel || VimControl || EmptyPane || SharedScreen || MarkdownPreview || KeyContextView",
"bindings": {
// window related commands (ctrl-w X)
"ctrl-w": null,
"ctrl-w left": ["workspace::ActivatePaneInDirection", "Left"],
"ctrl-w right": ["workspace::ActivatePaneInDirection", "Right"],
"ctrl-w up": ["workspace::ActivatePaneInDirection", "Up"],
"ctrl-w down": ["workspace::ActivatePaneInDirection", "Down"],
"ctrl-w h": ["workspace::ActivatePaneInDirection", "Left"],
"ctrl-w l": ["workspace::ActivatePaneInDirection", "Right"],
"ctrl-w k": ["workspace::ActivatePaneInDirection", "Up"],
"ctrl-w j": ["workspace::ActivatePaneInDirection", "Down"],
"ctrl-w ctrl-h": ["workspace::ActivatePaneInDirection", "Left"],
"ctrl-w ctrl-l": ["workspace::ActivatePaneInDirection", "Right"],
"ctrl-w ctrl-k": ["workspace::ActivatePaneInDirection", "Up"],
"ctrl-w ctrl-j": ["workspace::ActivatePaneInDirection", "Down"],
"ctrl-w shift-left": ["workspace::SwapPaneInDirection", "Left"],
"ctrl-w shift-right": ["workspace::SwapPaneInDirection", "Right"],
"ctrl-w shift-up": ["workspace::SwapPaneInDirection", "Up"],
"ctrl-w shift-down": ["workspace::SwapPaneInDirection", "Down"],
"ctrl-w shift-h": ["workspace::SwapPaneInDirection", "Left"],
"ctrl-w shift-l": ["workspace::SwapPaneInDirection", "Right"],
"ctrl-w shift-k": ["workspace::SwapPaneInDirection", "Up"],
"ctrl-w shift-j": ["workspace::SwapPaneInDirection", "Down"],
"ctrl-w g t": "pane::ActivateNextItem",
"ctrl-w ctrl-g t": "pane::ActivateNextItem",
"ctrl-w g shift-t": "pane::ActivatePrevItem",
"ctrl-w ctrl-g shift-t": "pane::ActivatePrevItem",
"ctrl-w w": "workspace::ActivateNextPane",
"ctrl-w ctrl-w": "workspace::ActivateNextPane",
"ctrl-w p": "workspace::ActivatePreviousPane",
"ctrl-w ctrl-p": "workspace::ActivatePreviousPane",
"ctrl-w shift-w": "workspace::ActivatePreviousPane",
"ctrl-w ctrl-shift-w": "workspace::ActivatePreviousPane",
"ctrl-w v": "pane::SplitVertical",
"ctrl-w ctrl-v": "pane::SplitVertical",
"ctrl-w s": "pane::SplitHorizontal",
"ctrl-w shift-s": "pane::SplitHorizontal",
"ctrl-w ctrl-s": "pane::SplitHorizontal",
"ctrl-w c": "pane::CloseAllItems",
"ctrl-w ctrl-c": "pane::CloseAllItems",
"ctrl-w q": "pane::CloseAllItems",
"ctrl-w ctrl-q": "pane::CloseAllItems",
"ctrl-w o": "workspace::CloseInactiveTabsAndPanes",
"ctrl-w ctrl-o": "workspace::CloseInactiveTabsAndPanes",
"ctrl-w n": "workspace::NewFileSplitHorizontal",
"ctrl-w ctrl-n": "workspace::NewFileSplitHorizontal"
}
},
{
"context": "EmptyPane || SharedScreen || MarkdownPreview || KeyContextView",
"bindings": {
":": "command_palette::Toggle",
"g /": "pane::DeploySearch"

View File

@@ -1,312 +0,0 @@
<task_description>
# Code Change Workflow
Your task is to guide the user through code changes using a series of steps. Each step should describe a high-level change, which can consist of multiple edits to distinct locations in the codebase.
## Output Example
Provide output as XML, with the following format:
<step>
Update the Person struct to store an age
```rust
struct Person {
// existing fields...
age: u8,
height: f32,
// existing fields...
}
impl Person {
fn age(&self) -> u8 {
self.age
}
}
```
<edit>
<path>src/person.rs</path>
<operation>insert_before</operation>
<search>height: f32,</search>
<description>Add the age field</description>
</edit>
<edit>
<path>src/person.rs</path>
<operation>insert_after</operation>
<search>impl Person {</search>
<description>Add the age getter</description>
</edit>
</step>
## Output Format
First, each `<step>` must contain a written description of the change that should be made. The description should begin with a high-level overview, and can contain markdown code blocks as well. The description should be self-contained and actionable.
After the description, each `<step>` must contain one or more `<edit>` tags, each of which refer to a specific range in a source file. Each `<edit>` tag must contain the following child tags:
### `<path>` (required)
This tag contains the path to the file that will be changed. It can be an existing path, or a path that should be created.
### `<search>` (optional)
This tag contains a search string to locate in the source file, e.g. `pub fn baz() {`. If not provided, the new content will be inserted at the top of the file. Make sure to produce a string that exists in the source file and that isn't ambiguous. When there's ambiguity, add more lines to the search to eliminate it.
### `<description>` (required)
This tag contains a single-line description of the edit that should be made at the given location.
### `<operation>` (required)
This tag indicates what type of change should be made, relative to the given location. It can be one of the following:
- `update`: Rewrites the specified string entirely based on the given description.
- `create`: Creates a new file with the given path based on the provided description.
- `insert_before`: Inserts new text based on the given description before the specified search string.
- `insert_after`: Inserts new text based on the given description after the specified search string.
- `delete`: Deletes the specified string from the containing file.
<guidelines>
- There's no need to describe *what* to do, just *where* to do it.
- Only reference locations that actually exist (unless you're creating a file).
- If creating a file, assume any subsequent updates are included at the time of creation.
- Don't create and then update a file. Always create new files in one hot.
- Prefer multiple edits to smaller regions, as opposed to one big edit to a larger region.
- Don't produce edits that intersect each other. In that case, merge them into a bigger edit.
- Never nest an edit with another edit. Never include CDATA. All edits are leaf nodes.
- Descriptions are required for all edits except delete.
- When generating multiple edits, ensure the descriptions are specific to each individual operation.
- Avoid referring to the search string in the description. Focus on the change to be made, not the location where it's made. That's implicit with the `search` string you provide.
- Don't generate multiple edits at the same location. Instead, combine them together in a single edit with a succinct combined description.
- Always ensure imports are added if you're referencing symbols that are not in scope.
</guidelines>
Here are some concrete examples.
<example>
<message role="user">
```rs src/shapes.rs
pub mod rectangle;
pub mod circle;
```
```rs src/shapes/rectangle.rs
pub struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
pub fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
}
```
```rs src/shapes/circle.rs
pub struct Circle {
radius: f64,
}
impl Circle {
pub fn new(radius: f64) -> Self {
Circle { radius }
}
}
```
Update all shapes to store their origin as an (x, y) tuple and implement Display.
</message>
<message role="assistant">
We'll need to update both the rectangle and circle modules.
<step>
Add origin fields to both shape types.
```rust
struct Rectangle {
// existing fields ...
origin: (f64, f64),
}
```
```rust
struct Circle {
// existing fields ...
origin: (f64, f64),
}
```
<edit>
<path>src/shapes/rectangle.rs</path>
<operation>insert_before</operation>
<search>
width: f64,
height: f64,
</search>
<description>Add the origin field to Rectangle</description>
</edit>
<edit>
<path>src/shapes/circle.rs</path>
<operation>insert_before</operation>
<search>
radius: f64,
</search>
<description>Add the origin field to Circle</description>
</edit>
<step>
Update both shape's constructors to take an origin.
<edit>
<path>src/shapes/rectangle.rs</path>
<operation>update</operation>
<search>
fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
</search>
<description>Update the Rectangle new function to take an origin</description>
</edit>
<edit>
<path>src/shapes/circle.rs</path>
<operation>update</operation>
<search>
fn new(radius: f64) -> Self {
Circle { radius }
}
</search>
<description>Update the Circle new function to take an origin</description>
</edit>
</step>
<step>
Implement Display for both shapes
<edit>
<path>src/shapes/rectangle.rs</path>
<operation>insert_before</operation>
<search>
struct Rectangle {
</search>
<description>Add an import for the `std::fmt` module</description>
</edit>
<edit>
<path>src/shapes/rectangle.rs</path>
<operation>insert_after</operation>
<search>
Rectangle { width, height }
}
}
</search>
<description>Add a Display implementation for Rectangle</description>
</edit>
<edit>
<path>src/shapes/circle.rs</path>
<operation>insert_before</operation>
<search>
struct Circle {
</search>
<description>Add an import for the `std::fmt` module</description>
</edit>
<edit>
<path>src/shapes/circle.rs</path>
<operation>insert_after</operation>
<search>
Circle { radius }
}
}
</search>
<description>Add a Display implementation for Circle</description>
</edit>
</step>
</message>
</example>
<example number=2>
<message role="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) {
todo!()
}
}
```
Let's print all the user information and delete the email field.
</message>
<message role="assistant">
<step>
Update the 'print_info' method to print user information
```rust
impl User {
// ... other methods ...
pub fn print_info(&self) {
println!("Name: {name}, Age: {age}", name = self.name, age = self.age);
}
}
```
<edit>
<path>src/user.rs</path>
<operation>update</operation>
<search>
pub fn print_info(&self) {
todo!()
}
</search>
<description>Print all the user information</description>
</edit>
</step>
<step>
Remove the 'email' field from the User struct
<edit>
<path>src/user.rs</path>
<operation>delete</operation>
<search>
email: String,
</search>
</edit>
<edit>
<path>src/user.rs</path>
<operation>update</operation>
<symbol>
fn new(name: String, age: u32, email: String) -> Self {
User { name, age, email }
}
</symbol>
<description>Remove email parameter from new method</description>
</edit>
</step>
</message>
</example>
You should think step by step. When possible, produce smaller, coherent logical steps as opposed to one big step that combines lots of heterogeneous edits.
</task_description>

View File

@@ -1,496 +0,0 @@
<overview>
Your task is to map a step from a workflow to locations in source code where code needs to be changed to fulfill that step.
Given a workflow containing background context plus a series of <step> tags, you will resolve *one* of these step tags to resolve to one or more locations in the code.
With each location, you will produce a brief, one-line description of the changes to be made.
<guidelines>
- There's no need to describe *what* to do, just *where* to do it.
- Only reference locations that actually exist (unless you're creating a file).
- If creating a file, assume any subsequent updates are included at the time of creation.
- Don't create and then update a file. Always create new files in 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.
- 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.
- To add imports respond with a suggestion where the `"symbol"` key is set to `"#imports"`
</guidelines>
</overview>
<examples>
<example>
<workflow_context>
<message role="user">
```rs src/rectangle.rs
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
}
```
We need to add methods to calculate the area and perimeter of the rectangle. Can you help with that?
</message>
<message role="assistant">
Sure, I can help with that!
<step>Add new methods 'calculate_area' and 'calculate_perimeter' to the Rectangle struct</step>
<step>Implement the 'Display' trait for the Rectangle struct</step>
</message>
</workflow_context>
<step_to_resolve>
Add new methods 'calculate_area' and 'calculate_perimeter' to the Rectangle struct
</step_to_resolve>
<incorrect_output reason="NEVER append multiple children at the same location.">
{
"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"
}
]
}
</incorrect_output>
<correct_output>
{
"title": "Add Rectangle methods",
"suggestions": [
{
"kind": "AppendChild",
"path": "src/shapes.rs",
"symbol": "impl Rectangle",
"description": "Add calculate area and perimeter methods"
}
]
}
</correct_output>
<step_to_resolve>
Implement the 'Display' trait for the Rectangle struct
</step_to_resolve>
<output>
{
"title": "Implement Display for Rectangle",
"suggestions": [
{
"kind": "InsertSiblingAfter",
"path": "src/shapes.rs",
"symbol": "impl Rectangle",
"description": "Implement Display trait for Rectangle"
}
]
}
</output>
<example>
<workflow_context>
<message role="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);
}
}
```
</message>
<message role="assistant">
Certainly!
<step>Update the 'print_info' method to use formatted output</step>
<step>Remove the 'email' field from the User struct</step>
</message>
</workflow_context>
<step_to_resolve>
Update the 'print_info' method to use formatted output
</step_to_resolve>
<output>
{
"title": "Use formatted output",
"suggestions": [
{
"kind": "Update",
"path": "src/user.rs",
"symbol": "impl User pub fn print_info",
"description": "Use formatted output"
}
]
}
</output>
<step_to_resolve>
Remove the 'email' field from the User struct
</step_to_resolve>
<output>
{
"title": "Remove email field",
"suggestions": [
{
"kind": "Delete",
"path": "src/user.rs",
"symbol": "struct User email"
}
]
}
</output>
</example>
<example>
<workflow_context>
<message role="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);
}
}
```
</message>
<message role="assistant">
<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>
</message>
</workflow_context>
<step_to_resolve>
Add a 'use std::fmt;' statement at the beginning of the file
</step_to_resolve>
<output>
{
"title": "Add use std::fmt statement",
"suggestions": [
{
"kind": "PrependChild",
"path": "src/vehicle.rs",
"symbol": "#imports",
"description": "Add 'use std::fmt' statement"
}
]
}
</output>
<step_to_resolve>
Add a new method 'start_engine' in the Vehicle impl block
</step_to_resolve>
<output>
{
"title": "Add start_engine method",
"suggestions": [
{
"kind": "InsertSiblingAfter",
"path": "src/vehicle.rs",
"symbol": "impl Vehicle fn new",
"description": "Add start_engine method"
}
]
}
</output>
</example>
<example>
<workflow_context>
<message role="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;
}
}
```
</message>
<message role="assistant">
<step>Make salary an f32</step>
<step>Remove the 'department' field and update the 'print_details' method</step>
</message>
</workflow_context>
<step_to_resolve>
Make salary an f32
</step_to_resolve>
<incorrect_output reason="NEVER include suggestions on a parent symbol and one of its children in the same suggestions block.">
{
"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"
}
]
}
</incorrect_output>
<correct_output>
{
"title": "Change salary to f32",
"suggestions": [
{
"kind": "Update",
"path": "src/employee.rs",
"symbol": "struct Employee salary",
"description": "Change the type to an f32"
}
]
}
</correct_output>
<step_to_resolve>
Remove the 'department' field and update the 'print_details' method
</step_to_resolve>
<output>
{
"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"
}
]
}
</output>
</example>
<example>
<workflow_context>
<message role="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() }
}
}
```
</message>
<message role="assistant">
<step>Add a 'level' field to Player and update the 'new' method</step>
</message>
</workflow_context>
<step_to_resolve>
Add a 'level' field to Player and update the 'new' method
</step_to_resolve>
<output>
{
"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"
}
]
}
</output>
</example>
<example>
<workflow_context>
<message role="user">
```rs src/config.rs
use std::collections::HashMap;
struct Config {
settings: HashMap<String, String>,
}
impl Config {
fn new() -> Self {
Config { settings: HashMap::new() }
}
}
```
</message>
<message role="assistant">
<step>Add a 'load_from_file' method to Config and import necessary modules</step>
</message>
</workflow_context>
<step_to_resolve>
Add a 'load_from_file' method to Config and import necessary modules
</step_to_resolve>
<output>
{
"title": "Add load_from_file method",
"suggestions": [
{
"kind": "PrependChild",
"path": "src/config.rs",
"symbol": "#imports",
"description": "Import std::fs and std::io modules"
},
{
"kind": "AppendChild",
"path": "src/config.rs",
"symbol": "impl Config",
"description": "Add load_from_file method"
}
]
}
</output>
</example>
<example>
<workflow_context>
<message role="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, &[])
}
}
```
</message>
<message role="assistant">
<step>Add error handling to the 'query' method and create a custom error type</step>
</message>
</workflow_context>
<step_to_resolve>
Add error handling to the 'query' method and create a custom error type
</step_to_resolve>
<output>
{
"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"
}
]
}
</output>
</example>
</examples>
Now generate the suggestions for the following step:
<workflow_context>
{{{workflow_context}}}
</workflow_context>
<step_to_resolve>
{{{step_to_resolve}}}
</step_to_resolve>

View File

@@ -0,0 +1,206 @@
<task_description>
The user of a code editor wants to make a change to their codebase.
You must describe the change using the following XML structure:
- <patch> - A group of related code changes.
Child tags:
- <title> (required) - A high-level description of the changes. This should be as short
as possible, possibly using common abbreviations.
- <edit> (1 or more) - An edit to make at a particular range within a file.
Includes the following child tags:
- <path> (required) - The path to the file that will be changed.
- <description> (optional) - An arbitrarily-long comment that describes the purpose
of this edit.
- <old_text> (optional) - An excerpt from the file's current contents that uniquely
identifies a range within the file where the edit should occur. If this tag is not
specified, then the entire file will be used as the range.
- <new_text> (required) - The new text to insert into the file.
- <operation> (required) - The type of change that should occur at the given range
of the file. Must be one of the following values:
- `update`: Replaces the entire range with the new text.
- `insert_before`: Inserts the new text before the range.
- `insert_after`: Inserts new text after the range.
- `create`: Creates a new file with the given path and the new text.
- `delete`: Deletes the specified range from the file.
<guidelines>
- Never provide multiple edits whose ranges intersect each other. Instead, merge them into one edit.
- Prefer multiple edits to smaller, disjoint ranges, rather than one edit to a larger range.
- There's no need to escape angle brackets within XML tags.
- Always ensure imports are added if you're referencing symbols that are not in scope.
</guidelines>
Here are some concrete examples.
<example>
<message role="user">
```rs src/shapes.rs
pub mod rectangle;
pub mod circle;
```
```rs src/shapes/rectangle.rs
pub struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
pub fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
}
```
```rs src/shapes/circle.rs
pub struct Circle {
radius: f64,
}
impl Circle {
pub fn new(radius: f64) -> Self {
Circle { radius }
}
}
```
Update all shapes to store their origin as an (x, y) tuple and implement Display.
</message>
<message role="assistant">
We'll need to update both the rectangle and circle modules.
<patch>
<title>Add origins and display impls to shapes</title>
<edit>
<path>src/shapes/rectangle.rs</path>
<description>Add the origin field to Rectangle struct</description>
<operation>insert_after</operation>
<old_text>
pub struct Rectangle {
</old_text>
<new_text>
origin: (f64, f64),
</new_text>
</edit>
<edit>
<path>src/shapes/rectangle.rs</path>
<operation>update</operation>
<old_text>
fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
</old_text>
<new_text>
fn new(origin: (f64, f64), width: f64, height: f64) -> Self {
Rectangle { origin, width, height }
}
</new_text>
</edit>
<edit>
<path>src/shapes/circle.rs</path>
<description>Add the origin field to Circle struct</description>
<operation>insert_after</operation>
<old_text>
pub struct Circle {
radius: f64,
</old_text>
<new_text>
origin: (f64, f64),
</new_text>
</edit>
<edit>
<path>src/shapes/circle.rs</path>
<operation>update</operation>
<old_text>
fn new(radius: f64) -> Self {
Circle { radius }
}
</old_text>
<new_text>
fn new(origin: (f64, f64), radius: f64) -> Self {
Circle { origin, radius }
}
</new_text>
</edit>
</step>
<edit>
<path>src/shapes/rectangle.rs</path>
<operation>insert_before</operation>
<old_text>
struct Rectangle {
</old_text>
<new_text>
use std::fmt;
</new_text>
</edit>
<edit>
<path>src/shapes/rectangle.rs</path>
<description>
Add a manual Display implementation for Rectangle.
Currently, this is the same as a derived Display implementation.
</description>
<operation>insert_after</operation>
<old_text>
Rectangle { width, height }
}
}
</old_text>
<new_text>
impl fmt::Display for Rectangle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.format_struct(f, "Rectangle")
.field("origin", &self.origin)
.field("width", &self.width)
.field("height", &self.height)
.finish()
}
}
</new_text>
</edit>
<edit>
<path>src/shapes/circle.rs</path>
<operation>insert_before</operation>
<old_text>
struct Circle {
</old_text>
<new_text>
use std::fmt;
</new_text>
</edit>
<edit>
<path>src/shapes/circle.rs</path>
<operation>insert_after</operation>
<old_text>
Circle { radius }
}
}
</old_text>
<new_text>
impl fmt::Display for Rectangle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.format_struct(f, "Rectangle")
.field("origin", &self.origin)
.field("width", &self.width)
.field("height", &self.height)
.finish()
}
}
</new_text>
</edit>
</patch>
</message>
</example>
</task_description>

View File

@@ -68,9 +68,17 @@
"ui_font_size": 16,
// How much to fade out unused code.
"unnecessary_code_fade": 0.3,
// The factor to grow the active pane by. Defaults to 1.0
// which gives the same size as all other panes.
"active_pane_magnification": 1.0,
// Active pane styling settings.
"active_pane_modifiers": {
// The factor to grow the active pane by. Defaults to 1.0
// which gives the same size as all other panes.
"magnification": 1.0,
// Inset border size of the active pane, in pixels.
"border_size": 0.0,
// Opacity of the inactive panes. 0 means transparent, 1 means opaque.
// Values are clamped to the [0.0, 1.0] range.
"inactive_opacity": 1.0
},
// The direction that you want to split panes horizontally. Defaults to "up"
"pane_split_direction_horizontal": "up",
// The direction that you want to split panes horizontally. Defaults to "left"
@@ -152,7 +160,7 @@
"show_signature_help_after_edits": true,
// Whether to show wrap guides (vertical rulers) in the editor.
// Setting this to true will show a guide at the 'preferred_line_length' value
// if softwrap is set to 'preferred_line_length', and will show any
// if 'soft_wrap' is set to 'preferred_line_length', and will show any
// additional guides as specified by the 'wrap_guides' setting.
"show_wrap_guides": true,
// Character counts at which to show wrap guides in the editor.
@@ -174,6 +182,8 @@
// bracket, brace, single or double quote characters.
// For example, when you select text and type (, Zed will surround the text with ().
"use_auto_surround": true,
// Whether indentation of pasted content should be adjusted based on the context.
"auto_indent_on_paste": true,
// Controls how the editor handles the autoclosed characters.
// When set to `false`(default), skipping over and auto-removing of the closing characters
// happen only for auto-inserted characters.
@@ -369,6 +379,17 @@
/// 5. Never show the scrollbar:
/// "never"
"show": null
},
// Settings related to indent guides in the project panel.
"indent_guides": {
// When to show indent guides in the project panel.
// This setting can take two values:
//
// 1. Always show indent guides:
// "always"
// 2. Never show indent guides:
// "never"
"show": "always"
}
},
"outline_panel": {
@@ -392,7 +413,35 @@
"auto_reveal_entries": true,
/// Whether to fold directories automatically
/// when a directory has only one directory inside.
"auto_fold_dirs": true
"auto_fold_dirs": true,
// Settings related to indent guides in the outline panel.
"indent_guides": {
// When to show indent guides in the outline panel.
// This setting can take two values:
//
// 1. Always show indent guides:
// "always"
// 2. Never show indent guides:
// "never"
"show": "always"
},
/// Scrollbar-related settings
"scrollbar": {
/// When to show the scrollbar in the project panel.
/// This setting can take four values:
///
/// 1. null (default): Inherit editor settings
/// 2. Show the scrollbar if there's important information or
/// follow the system's configured behavior (default):
/// "auto"
/// 3. Match the system's configured behavior:
/// "system"
/// 4. Always show the scrollbar:
/// "always"
/// 5. Never show the scrollbar:
/// "never"
"show": null
}
},
"collaboration_panel": {
// Whether to show the collaboration panel button in the status bar.
@@ -436,7 +485,7 @@
"default_width": 640,
// Default height when the assistant is docked to the bottom.
"default_height": 320,
// The default model to use when creating new contexts.
// The default model to use when creating new chats.
"default_model": {
// The provider to use.
"provider": "zed.dev",
@@ -613,6 +662,12 @@
// Sets a delay after which the inline blame information is shown.
// Delay is restarted with every cursor movement.
// "delay_ms": 600
//
// Whether or not do display the git commit summary on the same line.
// "show_commit_summary": false
//
// The minimum column number to show the inline blame information at
// "min_column": 0
}
},
// Configuration for how direnv configuration should be loaded. May take 2 values:
@@ -712,10 +767,10 @@
// May take 2 values:
// 1. Rely on default platform handling of option key, on macOS
// this means generating certain unicode characters
// "option_to_meta": false,
// "option_as_meta": false,
// 2. Make the option keys behave as a 'meta' key, e.g. for emacs
// "option_to_meta": true,
"option_as_meta": true,
// "option_as_meta": true,
"option_as_meta": false,
// Whether or not selecting text in the terminal will automatically
// copy to the system clipboard.
"copy_on_select": false,
@@ -803,7 +858,7 @@
/// You can override this to use a version of node that is not in $PATH with:
/// {
/// "node": {
/// "node_path": "/path/to/node"
/// "path": "/path/to/node"
/// "npm_path": "/path/to/npm" (defaults to node_path/../npm)
/// }
/// }
@@ -1099,13 +1154,13 @@
// }
"command_aliases": {},
// ssh_connections is an array of ssh connections.
// By default this setting is null, which disables the direct ssh connection support.
// You can configure these from `project: Open Remote` in the command palette.
// Zed's ssh support will pull configuration from your ~/.ssh too.
// Examples:
// [
// {
// "host": "example-box",
// // "port": 22, "username": "test", "args": ["-i", "/home/user/.ssh/id_rsa"]
// "projects": [
// {
// "paths": ["/home/user/code/zed"]
@@ -1113,7 +1168,7 @@
// ]
// }
// ]
"ssh_connections": null,
"ssh_connections": [],
// Configures the Context Server Protocol binaries
//
// Examples:

View File

@@ -16,6 +16,7 @@
"allow_concurrent_runs": false,
// What to do with the terminal pane and tab, after the command was started:
// * `always` — always show the terminal pane, add and focus the corresponding task's tab in it (default)
// * `no_focus` — always show the terminal pane, add/reuse the task's tab there, but don't focus it
// * `never` — avoid changing current terminal pane focus, but still add/reuse the task's tab there
"reveal": "always",
// What to do with the terminal pane and tab, after the command had finished:

View File

@@ -16,13 +16,14 @@ doctest = false
anyhow.workspace = true
auto_update.workspace = true
editor.workspace = true
extension.workspace = true
extension_host.workspace = true
futures.workspace = true
gpui.workspace = true
language.workspace = true
project.workspace = true
smallvec.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
[dev-dependencies]

View File

@@ -1,6 +1,6 @@
use auto_update::{AutoUpdateStatus, AutoUpdater, DismissErrorMessage};
use editor::Editor;
use extension::ExtensionStore;
use extension_host::ExtensionStore;
use futures::StreamExt;
use gpui::{
actions, percentage, Animation, AnimationExt as _, AppContext, CursorStyle, EventEmitter,
@@ -13,7 +13,8 @@ use language::{
use project::{EnvironmentErrorMessage, LanguageServerProgress, Project, WorktreeId};
use smallvec::SmallVec;
use std::{cmp::Reverse, fmt::Write, sync::Arc, time::Duration};
use ui::{prelude::*, ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle};
use ui::{prelude::*, ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
use util::truncate_and_trailoff;
use workspace::{item::ItemHandle, StatusItemView, Workspace};
actions!(activity_indicator, [ShowErrorMessage]);
@@ -351,7 +352,10 @@ impl ActivityIndicator {
.into_any_element(),
),
message: format!("Formatting failed: {}. Click to see logs.", failure),
on_click: Some(Arc::new(|_, cx| {
on_click: Some(Arc::new(|indicator, cx| {
indicator.project.update(cx, |project, cx| {
project.reset_last_formatting_failure(cx);
});
cx.dispatch_action(Box::new(workspace::OpenLog));
})),
});
@@ -446,6 +450,8 @@ impl ActivityIndicator {
impl EventEmitter<Event> for ActivityIndicator {}
const MAX_MESSAGE_LEN: usize = 50;
impl Render for ActivityIndicator {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let result = h_flex()
@@ -456,6 +462,7 @@ impl Render for ActivityIndicator {
return result;
};
let this = cx.view().downgrade();
let truncate_content = content.message.len() > MAX_MESSAGE_LEN;
result.gap_2().child(
PopoverMenu::new("activity-indicator-popover")
.trigger(
@@ -464,7 +471,21 @@ impl Render for ActivityIndicator {
.id("activity-indicator-status")
.gap_2()
.children(content.icon)
.child(Label::new(content.message).size(LabelSize::Small))
.map(|button| {
if truncate_content {
button
.child(
Label::new(truncate_and_trailoff(
&content.message,
MAX_MESSAGE_LEN,
))
.size(LabelSize::Small),
)
.tooltip(move |cx| Tooltip::text(&content.message, cx))
} else {
button.child(Label::new(content.message).size(LabelSize::Small))
}
})
.when_some(content.on_click, |this, handler| {
this.on_click(cx.listener(move |this, _, cx| {
handler(this, cx);

View File

@@ -97,6 +97,7 @@ language = { workspace = true, features = ["test-support"] }
language_model = { workspace = true, features = ["test-support"] }
languages = { workspace = true, features = ["test-support"] }
log.workspace = true
pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
rand.workspace = true
serde_json_lenient.workspace = true

View File

@@ -6,6 +6,7 @@ mod context;
pub mod context_store;
mod inline_assistant;
mod model_selector;
mod patch;
mod prompt_library;
mod prompts;
mod slash_command;
@@ -14,7 +15,6 @@ pub mod slash_command_settings;
mod streaming_diff;
mod terminal_inline_assistant;
mod tools;
mod workflow;
pub use assistant_panel::{AssistantPanel, AssistantPanelEvent};
use assistant_settings::AssistantSettings;
@@ -35,29 +35,32 @@ use language_model::{
LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
};
pub(crate) use model_selector::*;
pub use patch::*;
pub use prompts::PromptBuilder;
use prompts::PromptLoadingParams;
use semantic_index::{CloudEmbeddingProvider, SemanticDb};
use serde::{Deserialize, Serialize};
use settings::{update_settings_file, Settings, SettingsStore};
use slash_command::search_command::SearchSlashCommandFeatureFlag;
use slash_command::{
auto_command, cargo_workspace_command, context_server_command, default_command, delta_command,
diagnostics_command, docs_command, fetch_command, file_command, now_command, project_command,
prompt_command, search_command, symbols_command, tab_command, terminal_command,
workflow_command,
prompt_command, search_command, selection_command, symbols_command, tab_command,
terminal_command,
};
use std::path::PathBuf;
use std::sync::Arc;
pub(crate) use streaming_diff::*;
use util::ResultExt;
pub use workflow::*;
use crate::slash_command::streaming_example_command;
use crate::slash_command_settings::SlashCommandSettings;
actions!(
assistant,
[
Assist,
Edit,
Split,
CopyCode,
CycleMessageRole,
@@ -210,21 +213,23 @@ pub fn init(
});
}
cx.spawn(|mut cx| {
let client = client.clone();
async move {
let embedding_provider = CloudEmbeddingProvider::new(client.clone());
let semantic_index = SemanticDb::new(
paths::embeddings_dir().join("semantic-index-db.0.mdb"),
Arc::new(embedding_provider),
&mut cx,
)
.await?;
if cx.has_flag::<SearchSlashCommandFeatureFlag>() {
cx.spawn(|mut cx| {
let client = client.clone();
async move {
let embedding_provider = CloudEmbeddingProvider::new(client.clone());
let semantic_index = SemanticDb::new(
paths::embeddings_dir().join("semantic-index-db.0.mdb"),
Arc::new(embedding_provider),
&mut cx,
)
.await?;
cx.update(|cx| cx.set_global(semantic_index))
}
})
.detach();
cx.update(|cx| cx.set_global(semantic_index))
}
})
.detach();
}
context_store::init(&client.clone().into());
prompt_library::init(cx);
@@ -297,25 +302,64 @@ fn register_context_server_handlers(cx: &mut AppContext) {
return;
};
if let Some(prompts) = protocol.list_prompts().await.log_err() {
for prompt in prompts
.into_iter()
.filter(context_server_command::acceptable_prompt)
{
log::info!(
"registering context server command: {:?}",
prompt.name
);
context_server_registry.register_command(
server.id.clone(),
prompt.name.as_str(),
);
slash_command_registry.register_command(
context_server_command::ContextServerSlashCommand::new(
&server, prompt,
),
true,
);
if protocol.capable(context_servers::protocol::ServerCapability::Prompts) {
if let Some(prompts) = protocol.list_prompts().await.log_err() {
for prompt in prompts
.into_iter()
.filter(context_server_command::acceptable_prompt)
{
log::info!(
"registering context server command: {:?}",
prompt.name
);
context_server_registry.register_command(
server.id.clone(),
prompt.name.as_str(),
);
slash_command_registry.register_command(
context_server_command::ContextServerSlashCommand::new(
&server, prompt,
),
true,
);
}
}
}
})
.detach();
}
},
);
cx.update_model(
&manager,
|manager: &mut context_servers::manager::ContextServerManager, cx| {
let tool_registry = ToolRegistry::global(cx);
let context_server_registry = ContextServerRegistry::global(cx);
if let Some(server) = manager.get_server(server_id) {
cx.spawn(|_, _| async move {
let Some(protocol) = server.client.read().clone() else {
return;
};
if protocol.capable(context_servers::protocol::ServerCapability::Tools) {
if let Some(tools) = protocol.list_tools().await.log_err() {
for tool in tools.tools {
log::info!(
"registering context server tool: {:?}",
tool.name
);
context_server_registry.register_tool(
server.id.clone(),
tool.name.as_str(),
);
tool_registry.register_tool(
tools::context_server_tool::ContextServerTool::new(
server.id.clone(),
tool
),
);
}
}
}
})
@@ -333,6 +377,14 @@ fn register_context_server_handlers(cx: &mut AppContext) {
context_server_registry.unregister_command(&server_id, &command_name);
}
}
if let Some(tools) = context_server_registry.get_tools(server_id) {
let tool_registry = ToolRegistry::global(cx);
for tool_name in tools {
tool_registry.unregister_tool_by_name(&tool_name);
context_server_registry.unregister_tool(&server_id, &tool_name);
}
}
}
},
)
@@ -388,17 +440,15 @@ fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut
slash_command_registry
.register_command(cargo_workspace_command::CargoWorkspaceSlashCommand, true);
slash_command_registry.register_command(prompt_command::PromptSlashCommand, true);
slash_command_registry.register_command(selection_command::SelectionCommand, 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(fetch_command::FetchSlashCommand, false);
slash_command_registry.register_command(fetch_command::FetchSlashCommand, false);
if let Some(prompt_builder) = prompt_builder {
slash_command_registry.register_command(
workflow_command::WorkflowSlashCommand::new(prompt_builder.clone()),
true,
);
cx.observe_flag::<project_command::ProjectSlashCommandFeatureFlag, _>({
let slash_command_registry = slash_command_registry.clone();
move |is_enabled, _cx| {
@@ -424,6 +474,19 @@ fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut
})
.detach();
cx.observe_flag::<streaming_example_command::StreamingExampleSlashCommandFeatureFlag, _>({
let slash_command_registry = slash_command_registry.clone();
move |is_enabled, _cx| {
if is_enabled {
slash_command_registry.register_command(
streaming_example_command::StreamingExampleSlashCommand,
false,
);
}
}
})
.detach();
update_slash_commands_from_settings(cx);
cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
.detach();

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@ use std::sync::Arc;
use ::open_ai::Model as OpenAiModel;
use anthropic::Model as AnthropicModel;
use feature_flags::FeatureFlagAppExt;
use fs::Fs;
use gpui::{AppContext, Pixels};
use language_model::provider::open_ai;
@@ -61,6 +62,13 @@ pub struct AssistantSettings {
pub default_model: LanguageModelSelection,
pub inline_alternatives: Vec<LanguageModelSelection>,
pub using_outdated_settings_version: bool,
pub enable_experimental_live_diffs: bool,
}
impl AssistantSettings {
pub fn are_live_diffs_enabled(&self, cx: &AppContext) -> bool {
cx.is_staff() || self.enable_experimental_live_diffs
}
}
/// Assistant panel settings
@@ -238,6 +246,7 @@ impl AssistantSettingsContent {
}
}),
inline_alternatives: None,
enable_experimental_live_diffs: None,
},
VersionedAssistantSettingsContent::V2(settings) => settings.clone(),
},
@@ -257,6 +266,7 @@ impl AssistantSettingsContent {
.to_string(),
}),
inline_alternatives: None,
enable_experimental_live_diffs: None,
},
}
}
@@ -373,6 +383,7 @@ impl Default for VersionedAssistantSettingsContent {
default_height: None,
default_model: None,
inline_alternatives: None,
enable_experimental_live_diffs: None,
})
}
}
@@ -399,10 +410,14 @@ pub struct AssistantSettingsContentV2 {
///
/// Default: 320
default_height: Option<f32>,
/// The default model to use when creating new contexts.
/// The default model to use when creating new chats.
default_model: Option<LanguageModelSelection>,
/// Additional models with which to generate alternatives when performing inline assists.
inline_alternatives: Option<Vec<LanguageModelSelection>>,
/// Enable experimental live diffs in the assistant panel.
///
/// Default: false
enable_experimental_live_diffs: Option<bool>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
@@ -483,11 +498,11 @@ pub struct LegacyAssistantSettingsContent {
///
/// Default: 320
pub default_height: Option<f32>,
/// The default OpenAI model to use when creating new contexts.
/// The default OpenAI model to use when creating new chats.
///
/// Default: gpt-4-1106-preview
pub default_open_ai_model: Option<OpenAiModel>,
/// OpenAI API base URL to use when creating new contexts.
/// OpenAI API base URL to use when creating new chats.
///
/// Default: https://api.openai.com/v1
pub openai_api_url: Option<String>,
@@ -525,7 +540,10 @@ impl Settings for AssistantSettings {
);
merge(&mut settings.default_model, value.default_model);
merge(&mut settings.inline_alternatives, value.inline_alternatives);
// merge(&mut settings.infer_context, value.infer_context); TODO re-enable this once we ship context inference
merge(
&mut settings.enable_experimental_live_diffs,
value.enable_experimental_live_diffs,
);
}
Ok(settings)
@@ -584,6 +602,7 @@ mod tests {
dock: None,
default_width: None,
default_height: None,
enable_experimental_live_diffs: None,
}),
)
},

View File

@@ -2,12 +2,13 @@
mod context_tests;
use crate::{
prompts::PromptBuilder, slash_command::SlashCommandLine, MessageId, MessageStatus,
WorkflowStep, WorkflowStepEdit, WorkflowStepResolution, WorkflowSuggestionGroup,
prompts::PromptBuilder,
slash_command::{file_command::FileCommandMetadata, SlashCommandLine},
AssistantEdit, AssistantPatch, AssistantPatchStatus, MessageId, MessageStatus,
};
use anyhow::{anyhow, Context as _, Result};
use assistant_slash_command::{
SlashCommandOutput, SlashCommandOutputSection, SlashCommandRegistry,
SlashCommandOutput, SlashCommandOutputSection, SlashCommandRegistry, SlashCommandResult,
};
use assistant_tool::ToolRegistry;
use client::{self, proto, telemetry::Telemetry};
@@ -15,17 +16,15 @@ use clock::ReplicaId;
use collections::{HashMap, HashSet};
use feature_flags::{FeatureFlag, FeatureFlagAppExt};
use fs::{Fs, RemoveOptions};
use futures::{
future::{self, Shared},
FutureExt, StreamExt,
};
use futures::{future::Shared, FutureExt, StreamExt};
use gpui::{
AppContext, AsyncAppContext, Context as _, EventEmitter, Model, ModelContext, RenderImage,
SharedString, Subscription, Task,
AppContext, Context as _, EventEmitter, Model, ModelContext, RenderImage, SharedString,
Subscription, Task,
};
use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};
use language_model::{
logging::report_assistant_event,
provider::cloud::{MaxMonthlySpendReachedError, PaymentRequiredError},
LanguageModel, LanguageModelCacheConfiguration, LanguageModelCompletionEvent,
LanguageModelImage, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
@@ -38,7 +37,7 @@ use project::Project;
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use std::{
cmp::{self, max, Ordering},
cmp::{max, Ordering},
fmt::Debug,
iter, mem,
ops::Range,
@@ -69,6 +68,14 @@ impl ContextId {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RequestType {
/// Request a normal chat response from the model.
Chat,
/// Add a preamble to the message, which tells the model to return a structured response that suggests edits.
SuggestEdits,
}
#[derive(Clone, Debug)]
pub enum ContextOperation {
InsertMessage {
@@ -300,7 +307,7 @@ pub enum ContextEvent {
MessagesEdited,
SummaryChanged,
StreamedCompletion,
WorkflowStepsUpdated {
PatchesUpdated {
removed: Vec<Range<language::Anchor>>,
updated: Vec<Range<language::Anchor>>,
},
@@ -454,13 +461,14 @@ pub struct XmlTag {
#[derive(Copy, Clone, Debug, strum::EnumString, PartialEq, Eq, strum::AsRefStr)]
#[strum(serialize_all = "snake_case")]
pub enum XmlTagKind {
Step,
Patch,
Title,
Edit,
Path,
Search,
Within,
Operation,
Description,
OldText,
NewText,
Operation,
}
pub struct Context {
@@ -490,7 +498,7 @@ pub struct Context {
_subscriptions: Vec<Subscription>,
telemetry: Option<Arc<Telemetry>>,
language_registry: Arc<LanguageRegistry>,
workflow_steps: Vec<WorkflowStep>,
patches: Vec<AssistantPatch>,
xml_tags: Vec<XmlTag>,
project: Option<Model<Project>>,
prompt_builder: Arc<PromptBuilder>,
@@ -506,7 +514,7 @@ impl ContextAnnotation for PendingSlashCommand {
}
}
impl ContextAnnotation for WorkflowStep {
impl ContextAnnotation for AssistantPatch {
fn range(&self) -> &Range<language::Anchor> {
&self.range
}
@@ -591,7 +599,7 @@ impl Context {
telemetry,
project,
language_registry,
workflow_steps: Vec::new(),
patches: Vec::new(),
xml_tags: Vec::new(),
prompt_builder,
};
@@ -929,48 +937,49 @@ impl Context {
self.summary.as_ref()
}
pub(crate) fn workflow_step_containing(
pub(crate) fn patch_containing(
&self,
offset: usize,
position: Point,
cx: &AppContext,
) -> Option<&WorkflowStep> {
) -> Option<&AssistantPatch> {
let buffer = self.buffer.read(cx);
let index = self
.workflow_steps
.binary_search_by(|step| {
let step_range = step.range.to_offset(&buffer);
if offset < step_range.start {
Ordering::Greater
} else if offset > step_range.end {
Ordering::Less
} else {
Ordering::Equal
}
})
.ok()?;
Some(&self.workflow_steps[index])
let index = self.patches.binary_search_by(|patch| {
let patch_range = patch.range.to_point(&buffer);
if position < patch_range.start {
Ordering::Greater
} else if position > patch_range.end {
Ordering::Less
} else {
Ordering::Equal
}
});
if let Ok(ix) = index {
Some(&self.patches[ix])
} else {
None
}
}
pub fn workflow_step_ranges(&self) -> impl Iterator<Item = Range<language::Anchor>> + '_ {
self.workflow_steps.iter().map(|step| step.range.clone())
pub fn patch_ranges(&self) -> impl Iterator<Item = Range<language::Anchor>> + '_ {
self.patches.iter().map(|patch| patch.range.clone())
}
pub(crate) fn workflow_step_for_range(
pub(crate) fn patch_for_range(
&self,
range: &Range<language::Anchor>,
cx: &AppContext,
) -> Option<&WorkflowStep> {
) -> Option<&AssistantPatch> {
let buffer = self.buffer.read(cx);
let index = self.workflow_step_index_for_range(range, buffer).ok()?;
Some(&self.workflow_steps[index])
let index = self.patch_index_for_range(range, buffer).ok()?;
Some(&self.patches[index])
}
fn workflow_step_index_for_range(
fn patch_index_for_range(
&self,
tagged_range: &Range<text::Anchor>,
buffer: &text::BufferSnapshot,
) -> Result<usize, usize> {
self.workflow_steps
self.patches
.binary_search_by(|probe| probe.range.cmp(&tagged_range, buffer))
}
@@ -982,6 +991,20 @@ impl Context {
&self.slash_command_output_sections
}
pub fn contains_files(&self, cx: &AppContext) -> bool {
let buffer = self.buffer.read(cx);
self.slash_command_output_sections.iter().any(|section| {
section.is_valid(buffer)
&& section
.metadata
.as_ref()
.and_then(|metadata| {
serde_json::from_value::<FileCommandMetadata>(metadata.clone()).ok()
})
.is_some()
})
}
pub fn pending_tool_uses(&self) -> Vec<&PendingToolUse> {
self.pending_tool_uses_by_id.values().collect()
}
@@ -1018,8 +1041,6 @@ impl Context {
language::BufferEvent::Edited => {
self.count_remaining_tokens(cx);
self.reparse(cx);
// Use `inclusive = true` to invalidate a step when an edit occurs
// at the start/end of a parsed step.
cx.emit(ContextEvent::MessagesEdited);
}
_ => {}
@@ -1031,7 +1052,9 @@ impl Context {
}
pub(crate) fn count_remaining_tokens(&mut self, cx: &mut ModelContext<Self>) {
let request = self.to_completion_request(cx);
// Assume it will be a Chat request, even though that takes fewer tokens (and risks going over the limit),
// because otherwise you see in the UI that your empty message has a bunch of tokens already used.
let request = self.to_completion_request(RequestType::Chat, cx);
let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
return;
};
@@ -1174,7 +1197,7 @@ impl Context {
}
let request = {
let mut req = self.to_completion_request(cx);
let mut req = self.to_completion_request(RequestType::Chat, cx);
// Skip the last message because it's likely to change and
// therefore would be a waste to cache.
req.messages.pop();
@@ -1248,8 +1271,8 @@ impl Context {
let mut removed_slash_command_ranges = Vec::new();
let mut updated_slash_commands = Vec::new();
let mut removed_steps = Vec::new();
let mut updated_steps = Vec::new();
let mut removed_patches = Vec::new();
let mut updated_patches = Vec::new();
while let Some(mut row_range) = row_ranges.next() {
while let Some(next_row_range) = row_ranges.peek() {
if row_range.end >= next_row_range.start {
@@ -1273,11 +1296,11 @@ impl Context {
&mut removed_slash_command_ranges,
cx,
);
self.reparse_workflow_steps_in_range(
self.reparse_patches_in_range(
start..end,
&buffer,
&mut updated_steps,
&mut removed_steps,
&mut updated_patches,
&mut removed_patches,
cx,
);
}
@@ -1289,10 +1312,10 @@ impl Context {
});
}
if !updated_steps.is_empty() || !removed_steps.is_empty() {
cx.emit(ContextEvent::WorkflowStepsUpdated {
removed: removed_steps,
updated: updated_steps,
if !updated_patches.is_empty() || !removed_patches.is_empty() {
cx.emit(ContextEvent::PatchesUpdated {
removed: removed_patches,
updated: updated_patches,
});
}
}
@@ -1354,7 +1377,7 @@ impl Context {
removed.extend(removed_commands.map(|command| command.source_range));
}
fn reparse_workflow_steps_in_range(
fn reparse_patches_in_range(
&mut self,
range: Range<text::Anchor>,
buffer: &BufferSnapshot,
@@ -1369,41 +1392,32 @@ impl Context {
self.xml_tags
.splice(intersecting_tags_range.clone(), new_tags);
// Find which steps intersect the changed range.
let intersecting_steps_range =
self.indices_intersecting_buffer_range(&self.workflow_steps, range.clone(), cx);
// Find which patches intersect the changed range.
let intersecting_patches_range =
self.indices_intersecting_buffer_range(&self.patches, range.clone(), cx);
// Reparse all tags after the last unchanged step before the change.
// Reparse all tags after the last unchanged patch before the change.
let mut tags_start_ix = 0;
if let Some(preceding_unchanged_step) =
self.workflow_steps[..intersecting_steps_range.start].last()
if let Some(preceding_unchanged_patch) =
self.patches[..intersecting_patches_range.start].last()
{
tags_start_ix = match self.xml_tags.binary_search_by(|tag| {
tag.range
.start
.cmp(&preceding_unchanged_step.range.end, buffer)
.cmp(&preceding_unchanged_patch.range.end, buffer)
.then(Ordering::Less)
}) {
Ok(ix) | Err(ix) => ix,
};
}
// Rebuild the edit suggestions in the range.
let mut new_steps = self.parse_steps(tags_start_ix, range.end, buffer);
if let Some(project) = self.project() {
for step in &mut new_steps {
Self::resolve_workflow_step_internal(step, &project, cx);
}
}
updated.extend(new_steps.iter().map(|step| step.range.clone()));
let removed_steps = self
.workflow_steps
.splice(intersecting_steps_range, new_steps);
// Rebuild the patches in the range.
let new_patches = self.parse_patches(tags_start_ix, range.end, buffer, cx);
updated.extend(new_patches.iter().map(|patch| patch.range.clone()));
let removed_patches = self.patches.splice(intersecting_patches_range, new_patches);
removed.extend(
removed_steps
.map(|step| step.range)
removed_patches
.map(|patch| patch.range)
.filter(|range| !updated.contains(&range)),
);
}
@@ -1464,60 +1478,95 @@ impl Context {
tags
}
fn parse_steps(
fn parse_patches(
&mut self,
tags_start_ix: usize,
buffer_end: text::Anchor,
buffer: &BufferSnapshot,
) -> Vec<WorkflowStep> {
let mut new_steps = Vec::new();
let mut pending_step = None;
let mut edit_step_depth = 0;
cx: &AppContext,
) -> Vec<AssistantPatch> {
let mut new_patches = Vec::new();
let mut pending_patch = None;
let mut patch_tag_depth = 0;
let mut tags = self.xml_tags[tags_start_ix..].iter().peekable();
'tags: while let Some(tag) = tags.next() {
if tag.range.start.cmp(&buffer_end, buffer).is_gt() && edit_step_depth == 0 {
if tag.range.start.cmp(&buffer_end, buffer).is_gt() && patch_tag_depth == 0 {
break;
}
if tag.kind == XmlTagKind::Step && tag.is_open_tag {
edit_step_depth += 1;
let edit_start = tag.range.start;
let mut edits = Vec::new();
let mut step = WorkflowStep {
range: edit_start..edit_start,
leading_tags_end: tag.range.end,
trailing_tag_start: None,
if tag.kind == XmlTagKind::Patch && tag.is_open_tag {
patch_tag_depth += 1;
let patch_start = tag.range.start;
let mut edits = Vec::<Result<AssistantEdit>>::new();
let mut patch = AssistantPatch {
range: patch_start..patch_start,
title: String::new().into(),
edits: Default::default(),
resolution: None,
resolution_task: None,
status: crate::AssistantPatchStatus::Pending,
};
while let Some(tag) = tags.next() {
step.trailing_tag_start.get_or_insert(tag.range.start);
if tag.kind == XmlTagKind::Patch && !tag.is_open_tag {
patch_tag_depth -= 1;
if patch_tag_depth == 0 {
patch.range.end = tag.range.end;
if tag.kind == XmlTagKind::Step && !tag.is_open_tag {
// step.trailing_tag_start = Some(tag.range.start);
edit_step_depth -= 1;
if edit_step_depth == 0 {
step.range.end = tag.range.end;
step.edits = edits.into();
new_steps.push(step);
// Include the line immediately after this <patch> tag if it's empty.
let patch_end_offset = patch.range.end.to_offset(buffer);
let mut patch_end_chars = buffer.chars_at(patch_end_offset);
if patch_end_chars.next() == Some('\n')
&& patch_end_chars.next().map_or(true, |ch| ch == '\n')
{
let messages = self.messages_for_offsets(
[patch_end_offset, patch_end_offset + 1],
cx,
);
if messages.len() == 1 {
patch.range.end = buffer.anchor_before(patch_end_offset + 1);
}
}
edits.sort_unstable_by(|a, b| {
if let (Ok(a), Ok(b)) = (a, b) {
a.path.cmp(&b.path)
} else {
Ordering::Equal
}
});
patch.edits = edits.into();
patch.status = AssistantPatchStatus::Ready;
new_patches.push(patch);
continue 'tags;
}
}
if tag.kind == XmlTagKind::Title && tag.is_open_tag {
let content_start = tag.range.end;
while let Some(tag) = tags.next() {
if tag.kind == XmlTagKind::Title && !tag.is_open_tag {
let content_end = tag.range.start;
patch.title =
trimmed_text_in_range(buffer, content_start..content_end)
.into();
break;
}
}
}
if tag.kind == XmlTagKind::Edit && tag.is_open_tag {
let mut path = None;
let mut search = None;
let mut old_text = None;
let mut new_text = None;
let mut operation = None;
let mut description = None;
while let Some(tag) = tags.next() {
if tag.kind == XmlTagKind::Edit && !tag.is_open_tag {
edits.push(WorkflowStepEdit::new(
edits.push(AssistantEdit::new(
path,
operation,
search,
old_text,
new_text,
description,
));
break;
@@ -1526,7 +1575,8 @@ impl Context {
if tag.is_open_tag
&& [
XmlTagKind::Path,
XmlTagKind::Search,
XmlTagKind::OldText,
XmlTagKind::NewText,
XmlTagKind::Operation,
XmlTagKind::Description,
]
@@ -1538,15 +1588,18 @@ impl Context {
if tag.kind == kind && !tag.is_open_tag {
let tag = tags.next().unwrap();
let content_end = tag.range.start;
let mut content = buffer
.text_for_range(content_start..content_end)
.collect::<String>();
content.truncate(content.trim_end().len());
let content = trimmed_text_in_range(
buffer,
content_start..content_end,
);
match kind {
XmlTagKind::Path => path = Some(content),
XmlTagKind::Operation => operation = Some(content),
XmlTagKind::Search => {
search = Some(content).filter(|s| !s.is_empty())
XmlTagKind::OldText => {
old_text = Some(content).filter(|s| !s.is_empty())
}
XmlTagKind::NewText => {
new_text = Some(content).filter(|s| !s.is_empty())
}
XmlTagKind::Description => {
description =
@@ -1561,162 +1614,28 @@ impl Context {
}
}
pending_step = Some(step);
patch.edits = edits.into();
pending_patch = Some(patch);
}
}
if let Some(mut pending_step) = pending_step {
pending_step.range.end = text::Anchor::MAX;
new_steps.push(pending_step);
}
new_steps
}
pub fn resolve_workflow_step(
&mut self,
tagged_range: Range<text::Anchor>,
cx: &mut ModelContext<Self>,
) -> Option<()> {
let index = self
.workflow_step_index_for_range(&tagged_range, self.buffer.read(cx))
.ok()?;
let step = &mut self.workflow_steps[index];
let project = self.project.as_ref()?;
step.resolution.take();
Self::resolve_workflow_step_internal(step, project, cx);
None
}
fn resolve_workflow_step_internal(
step: &mut WorkflowStep,
project: &Model<Project>,
cx: &mut ModelContext<'_, Context>,
) {
step.resolution_task = Some(cx.spawn({
let range = step.range.clone();
let edits = step.edits.clone();
let project = project.clone();
|this, mut cx| async move {
let suggestion_groups =
Self::compute_step_resolution(project, edits, &mut cx).await;
this.update(&mut cx, |this, cx| {
let buffer = this.buffer.read(cx).text_snapshot();
let ix = this.workflow_step_index_for_range(&range, &buffer).ok();
if let Some(ix) = ix {
let step = &mut this.workflow_steps[ix];
let resolution = suggestion_groups.map(|suggestion_groups| {
let mut title = String::new();
for mut chunk in buffer.text_for_range(
step.leading_tags_end
..step.trailing_tag_start.unwrap_or(step.range.end),
) {
if title.is_empty() {
chunk = chunk.trim_start();
}
if let Some((prefix, _)) = chunk.split_once('\n') {
title.push_str(prefix);
break;
} else {
title.push_str(chunk);
}
}
WorkflowStepResolution {
title,
suggestion_groups,
}
});
step.resolution = Some(Arc::new(resolution));
cx.emit(ContextEvent::WorkflowStepsUpdated {
removed: vec![],
updated: vec![range],
})
}
})
.ok();
}
}));
}
async fn compute_step_resolution(
project: Model<Project>,
edits: Arc<[Result<WorkflowStepEdit>]>,
cx: &mut AsyncAppContext,
) -> Result<HashMap<Model<Buffer>, Vec<WorkflowSuggestionGroup>>> {
let mut suggestion_tasks = Vec::new();
for edit in edits.iter() {
let edit = edit.as_ref().map_err(|e| anyhow!("{e}"))?;
suggestion_tasks.push(edit.resolve(project.clone(), cx.clone()));
}
// Expand the context ranges of each suggestion and group suggestions with overlapping context ranges.
let suggestions = future::try_join_all(suggestion_tasks).await?;
let mut suggestions_by_buffer = HashMap::default();
for (buffer, suggestion) in suggestions {
suggestions_by_buffer
.entry(buffer)
.or_insert_with(Vec::new)
.push(suggestion);
}
let mut suggestion_groups_by_buffer = HashMap::default();
for (buffer, mut suggestions) in suggestions_by_buffer {
let mut suggestion_groups = Vec::<WorkflowSuggestionGroup>::new();
let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot())?;
// Sort suggestions by their range so that earlier, larger ranges come first
suggestions.sort_by(|a, b| a.range().cmp(&b.range(), &snapshot));
// Merge overlapping suggestions
suggestions.dedup_by(|a, b| b.try_merge(a, &snapshot));
// Create context ranges for each suggestion
for suggestion in suggestions {
let context_range = {
let suggestion_point_range = suggestion.range().to_point(&snapshot);
let start_row = suggestion_point_range.start.row.saturating_sub(5);
let end_row =
cmp::min(suggestion_point_range.end.row + 5, snapshot.max_point().row);
let start = snapshot.anchor_before(Point::new(start_row, 0));
let end =
snapshot.anchor_after(Point::new(end_row, snapshot.line_len(end_row)));
start..end
};
if let Some(last_group) = suggestion_groups.last_mut() {
if last_group
.context_range
.end
.cmp(&context_range.start, &snapshot)
.is_ge()
{
// Merge with the previous group if context ranges overlap
last_group.context_range.end = context_range.end;
last_group.suggestions.push(suggestion);
} else {
// Create a new group
suggestion_groups.push(WorkflowSuggestionGroup {
context_range,
suggestions: vec![suggestion],
});
}
if let Some(mut pending_patch) = pending_patch {
let patch_start = pending_patch.range.start.to_offset(buffer);
if let Some(message) = self.message_for_offset(patch_start, cx) {
if message.anchor_range.end == text::Anchor::MAX {
pending_patch.range.end = text::Anchor::MAX;
} else {
// Create the first group
suggestion_groups.push(WorkflowSuggestionGroup {
context_range,
suggestions: vec![suggestion],
});
let message_end = buffer.anchor_after(message.offset_range.end - 1);
pending_patch.range.end = message_end;
}
} else {
pending_patch.range.end = text::Anchor::MAX;
}
suggestion_groups_by_buffer.insert(buffer, suggestion_groups);
new_patches.push(pending_patch);
}
Ok(suggestion_groups_by_buffer)
new_patches
}
pub fn pending_command_for_position(
@@ -1784,7 +1703,7 @@ impl Context {
pub fn insert_command_output(
&mut self,
command_range: Range<language::Anchor>,
output: Task<Result<SlashCommandOutput>>,
output: Task<SlashCommandResult>,
ensure_trailing_newline: bool,
expand_result: bool,
cx: &mut ModelContext<Self>,
@@ -1795,19 +1714,13 @@ impl Context {
let command_range = command_range.clone();
async move {
let output = output.await;
let output = match output {
Ok(output) => SlashCommandOutput::from_event_stream(output).await,
Err(err) => Err(err),
};
this.update(&mut cx, |this, cx| match output {
Ok(mut output) => {
// Ensure section ranges are valid.
for section in &mut output.sections {
section.range.start = section.range.start.min(output.text.len());
section.range.end = section.range.end.min(output.text.len());
while !output.text.is_char_boundary(section.range.start) {
section.range.start -= 1;
}
while !output.text.is_char_boundary(section.range.end) {
section.range.end += 1;
}
}
output.ensure_valid_section_ranges();
// Ensure there is a newline after the last section.
if ensure_trailing_newline {
@@ -1972,7 +1885,11 @@ impl Context {
})
}
pub fn assist(&mut self, cx: &mut ModelContext<Self>) -> Option<MessageAnchor> {
pub fn assist(
&mut self,
request_type: RequestType,
cx: &mut ModelContext<Self>,
) -> Option<MessageAnchor> {
let model_registry = LanguageModelRegistry::read_global(cx);
let provider = model_registry.active_provider()?;
let model = model_registry.active_model()?;
@@ -1985,7 +1902,7 @@ impl Context {
// Compute which messages to cache, including the last one.
self.mark_cache_anchors(&model.cache_configuration(), false, cx);
let mut request = self.to_completion_request(cx);
let mut request = self.to_completion_request(request_type, cx);
if cx.has_flag::<ToolUseFeatureFlag>() {
let tool_registry = ToolRegistry::global(cx);
@@ -2041,6 +1958,7 @@ impl Context {
});
match event {
LanguageModelCompletionEvent::StartMessage { .. } => {}
LanguageModelCompletionEvent::Stop(reason) => {
stop_reason = reason;
}
@@ -2146,23 +2064,28 @@ impl Context {
None
};
if let Some(telemetry) = this.telemetry.as_ref() {
let language_name = this
.buffer
.read(cx)
.language()
.map(|language| language.name());
telemetry.report_assistant_event(AssistantEvent {
let language_name = this
.buffer
.read(cx)
.language()
.map(|language| language.name());
report_assistant_event(
AssistantEvent {
conversation_id: Some(this.id.0.clone()),
kind: AssistantKind::Panel,
phase: AssistantPhase::Response,
message_id: None,
model: model.telemetry_id(),
model_provider: model.provider_id().to_string(),
response_latency,
error_message,
language_name: language_name.map(|name| name.to_proto()),
});
}
},
this.telemetry.clone(),
cx.http_client(),
model.api_key(cx),
cx.background_executor(),
);
if let Ok(stop_reason) = result {
match stop_reason {
@@ -2187,7 +2110,11 @@ impl Context {
Some(user_message)
}
pub fn to_completion_request(&self, cx: &AppContext) -> LanguageModelRequest {
pub fn to_completion_request(
&self,
request_type: RequestType,
cx: &AppContext,
) -> LanguageModelRequest {
let buffer = self.buffer.read(cx);
let mut contents = self.contents(cx).peekable();
@@ -2276,6 +2203,25 @@ impl Context {
completion_request.messages.push(request_message);
}
if let RequestType::SuggestEdits = request_type {
if let Ok(preamble) = self.prompt_builder.generate_suggest_edits_prompt() {
let last_elem_index = completion_request.messages.len();
completion_request
.messages
.push(LanguageModelRequestMessage {
role: Role::User,
content: vec![MessageContent::Text(preamble)],
cache: false,
});
// The preamble message should be sent right before the last actual user message.
completion_request
.messages
.swap(last_elem_index, last_elem_index.saturating_sub(1));
}
}
completion_request
}
@@ -2315,11 +2261,11 @@ impl Context {
let mut updated = Vec::new();
let mut removed = Vec::new();
for range in ranges {
self.reparse_workflow_steps_in_range(range, &buffer, &mut updated, &mut removed, cx);
self.reparse_patches_in_range(range, &buffer, &mut updated, &mut removed, cx);
}
if !updated.is_empty() || !removed.is_empty() {
cx.emit(ContextEvent::WorkflowStepsUpdated { removed, updated })
cx.emit(ContextEvent::PatchesUpdated { removed, updated })
}
}
@@ -2590,11 +2536,12 @@ impl Context {
return;
}
let mut request = self.to_completion_request(cx);
let mut request = self.to_completion_request(RequestType::Chat, cx);
request.messages.push(LanguageModelRequestMessage {
role: Role::User,
content: vec![
"Summarize the context into a short title without punctuation.".into(),
"Generate a concise 3-7 word title for this conversation, omitting punctuation"
.into(),
],
cache: false,
});
@@ -2605,7 +2552,7 @@ impl Context {
let mut messages = stream.await?;
let mut replaced = !replace_old;
while let Some(message) = messages.next().await {
while let Some(message) = messages.stream.next().await {
let text = message?;
let mut lines = text.lines();
this.update(&mut cx, |this, cx| {
@@ -2825,6 +2772,24 @@ impl Context {
}
}
fn trimmed_text_in_range(buffer: &BufferSnapshot, range: Range<text::Anchor>) -> String {
let mut is_start = true;
let mut content = buffer
.text_for_range(range)
.map(|mut chunk| {
if is_start {
chunk = chunk.trim_start_matches('\n');
if !chunk.is_empty() {
is_start = false;
}
}
chunk
})
.collect::<String>();
content.truncate(content.trim_end().len());
content
}
#[derive(Debug, Default)]
pub struct ContextVersion {
context: clock::Global,

View File

@@ -1,13 +1,12 @@
use super::{MessageCacheMetadata, WorkflowStepEdit};
use super::{AssistantEdit, MessageCacheMetadata};
use crate::{
assistant_panel, prompt_library, slash_command::file_command, CacheStatus, Context,
ContextEvent, ContextId, ContextOperation, MessageId, MessageStatus, PromptBuilder,
WorkflowStepEditKind,
assistant_panel, prompt_library, slash_command::file_command, AssistantEditKind, CacheStatus,
Context, ContextEvent, ContextId, ContextOperation, MessageId, MessageStatus, PromptBuilder,
};
use anyhow::Result;
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandRegistry,
SlashCommandRegistry, SlashCommandResult,
};
use collections::HashSet;
use fs::FakeFs;
@@ -15,6 +14,7 @@ use gpui::{AppContext, Model, SharedString, Task, TestAppContext, WeakView};
use language::{Buffer, BufferSnapshot, LanguageRegistry, LspAdapterDelegate};
use language_model::{LanguageModelCacheConfiguration, LanguageModelRegistry, Role};
use parking_lot::Mutex;
use pretty_assertions::assert_eq;
use project::Project;
use rand::prelude::*;
use serde_json::json;
@@ -478,7 +478,15 @@ async fn test_slash_commands(cx: &mut TestAppContext) {
#[gpui::test]
async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
cx.update(prompt_library::init);
let settings_store = cx.update(SettingsStore::test);
let mut settings_store = cx.update(SettingsStore::test);
cx.update(|cx| {
settings_store
.set_user_settings(
r#"{ "assistant": { "enable_experimental_live_diffs": true } }"#,
cx,
)
.unwrap()
});
cx.set_global(settings_store);
cx.update(language::init);
cx.update(Project::init_settings);
@@ -520,7 +528,7 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
»",
cx,
);
expect_steps(
expect_patches(
&context,
"
@@ -539,17 +547,17 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
one
two
«
<step»",
<patch»",
cx,
);
expect_steps(
expect_patches(
&context,
"
one
two
<step",
<patch",
&[],
cx,
);
@@ -563,36 +571,24 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
one
two
<step«>
Add a second function
```rust
fn two() {}
```
<patch«>
<edit>»",
cx,
);
expect_steps(
expect_patches(
&context,
"
one
two
«<step>
Add a second function
```rust
fn two() {}
```
«<patch>
<edit>»",
&[&[]],
cx,
);
// The full suggestion is added
// The full patch is added
edit(
&context,
"
@@ -600,52 +596,47 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
one
two
<step>
Add a second function
```rust
fn two() {}
```
<patch>
<edit>«
<description>add a `two` function</description>
<path>src/lib.rs</path>
<operation>insert_after</operation>
<search>fn one</search>
<description>add a `two` function</description>
<old_text>fn one</old_text>
<new_text>
fn two() {}
</new_text>
</edit>
</step>
</patch>
also,»",
cx,
);
expect_steps(
expect_patches(
&context,
"
one
two
«<step>
Add a second function
```rust
fn two() {}
```
«<patch>
<edit>
<description>add a `two` function</description>
<path>src/lib.rs</path>
<operation>insert_after</operation>
<search>fn one</search>
<description>add a `two` function</description>
<old_text>fn one</old_text>
<new_text>
fn two() {}
</new_text>
</edit>
</step>»
</patch>
»
also,",
&[&[WorkflowStepEdit {
&[&[AssistantEdit {
path: "src/lib.rs".into(),
kind: WorkflowStepEditKind::InsertAfter {
search: "fn one".into(),
description: "add a `two` function".into(),
kind: AssistantEditKind::InsertAfter {
old_text: "fn one".into(),
new_text: "fn two() {}".into(),
description: Some("add a `two` function".into()),
},
}]],
cx,
@@ -659,52 +650,47 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
one
two
<step>
Add a second function
```rust
fn two() {}
```
<patch>
<edit>
<description>add a `two` function</description>
<path>src/lib.rs</path>
<operation>insert_after</operation>
<search>«fn zero»</search>
<description>add a `two` function</description>
<old_text>«fn zero»</old_text>
<new_text>
fn two() {}
</new_text>
</edit>
</step>
</patch>
also,",
cx,
);
expect_steps(
expect_patches(
&context,
"
one
two
«<step>
Add a second function
```rust
fn two() {}
```
«<patch>
<edit>
<description>add a `two` function</description>
<path>src/lib.rs</path>
<operation>insert_after</operation>
<search>fn zero</search>
<description>add a `two` function</description>
<old_text>fn zero</old_text>
<new_text>
fn two() {}
</new_text>
</edit>
</step>»
</patch>
»
also,",
&[&[WorkflowStepEdit {
&[&[AssistantEdit {
path: "src/lib.rs".into(),
kind: WorkflowStepEditKind::InsertAfter {
search: "fn zero".into(),
description: "add a `two` function".into(),
kind: AssistantEditKind::InsertAfter {
old_text: "fn zero".into(),
new_text: "fn two() {}".into(),
description: Some("add a `two` function".into()),
},
}]],
cx,
@@ -715,27 +701,24 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
context.cycle_message_roles(HashSet::from_iter([assistant_message_id]), cx);
context.cycle_message_roles(HashSet::from_iter([assistant_message_id]), cx);
});
expect_steps(
expect_patches(
&context,
"
one
two
<step>
Add a second function
```rust
fn two() {}
```
<patch>
<edit>
<description>add a `two` function</description>
<path>src/lib.rs</path>
<operation>insert_after</operation>
<search>fn zero</search>
<description>add a `two` function</description>
<old_text>fn zero</old_text>
<new_text>
fn two() {}
</new_text>
</edit>
</step>
</patch>
also,",
&[],
@@ -746,34 +729,32 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
context.update(cx, |context, cx| {
context.cycle_message_roles(HashSet::from_iter([assistant_message_id]), cx);
});
expect_steps(
expect_patches(
&context,
"
one
two
«<step>
Add a second function
```rust
fn two() {}
```
«<patch>
<edit>
<description>add a `two` function</description>
<path>src/lib.rs</path>
<operation>insert_after</operation>
<search>fn zero</search>
<description>add a `two` function</description>
<old_text>fn zero</old_text>
<new_text>
fn two() {}
</new_text>
</edit>
</step>»
</patch>
»
also,",
&[&[WorkflowStepEdit {
&[&[AssistantEdit {
path: "src/lib.rs".into(),
kind: WorkflowStepEditKind::InsertAfter {
search: "fn zero".into(),
description: "add a `two` function".into(),
kind: AssistantEditKind::InsertAfter {
old_text: "fn zero".into(),
new_text: "fn two() {}".into(),
description: Some("add a `two` function".into()),
},
}]],
cx,
@@ -792,34 +773,32 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
cx,
)
});
expect_steps(
expect_patches(
&deserialized_context,
"
one
two
«<step>
Add a second function
```rust
fn two() {}
```
«<patch>
<edit>
<description>add a `two` function</description>
<path>src/lib.rs</path>
<operation>insert_after</operation>
<search>fn zero</search>
<description>add a `two` function</description>
<old_text>fn zero</old_text>
<new_text>
fn two() {}
</new_text>
</edit>
</step>»
</patch>
»
also,",
&[&[WorkflowStepEdit {
&[&[AssistantEdit {
path: "src/lib.rs".into(),
kind: WorkflowStepEditKind::InsertAfter {
search: "fn zero".into(),
description: "add a `two` function".into(),
kind: AssistantEditKind::InsertAfter {
old_text: "fn zero".into(),
new_text: "fn two() {}".into(),
description: Some("add a `two` function".into()),
},
}]],
cx,
@@ -834,48 +813,58 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
cx.executor().run_until_parked();
}
fn expect_steps(
#[track_caller]
fn expect_patches(
context: &Model<Context>,
expected_marked_text: &str,
expected_suggestions: &[&[WorkflowStepEdit]],
expected_suggestions: &[&[AssistantEdit]],
cx: &mut TestAppContext,
) {
context.update(cx, |context, cx| {
let expected_marked_text = expected_marked_text.unindent();
let (expected_text, expected_ranges) = marked_text_ranges(&expected_marked_text, false);
let expected_marked_text = expected_marked_text.unindent();
let (expected_text, _) = marked_text_ranges(&expected_marked_text, false);
let (buffer_text, ranges, patches) = context.update(cx, |context, cx| {
context.buffer.read_with(cx, |buffer, _| {
assert_eq!(buffer.text(), expected_text);
let ranges = context
.workflow_steps
.patches
.iter()
.map(|entry| entry.range.to_offset(buffer))
.collect::<Vec<_>>();
let marked = generate_marked_text(&expected_text, &ranges, false);
assert_eq!(
marked,
expected_marked_text,
"unexpected suggestion ranges. actual: {ranges:?}, expected: {expected_ranges:?}"
);
let suggestions = context
.workflow_steps
.iter()
.map(|step| {
step.edits
.iter()
.map(|edit| {
let edit = edit.as_ref().unwrap();
WorkflowStepEdit {
path: edit.path.clone(),
kind: edit.kind.clone(),
}
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
assert_eq!(suggestions, expected_suggestions);
});
(
buffer.text(),
ranges,
context
.patches
.iter()
.map(|step| step.edits.clone())
.collect::<Vec<_>>(),
)
})
});
assert_eq!(buffer_text, expected_text);
let actual_marked_text = generate_marked_text(&expected_text, &ranges, false);
assert_eq!(actual_marked_text, expected_marked_text);
assert_eq!(
patches
.iter()
.map(|patch| {
patch
.iter()
.map(|edit| {
let edit = edit.as_ref().unwrap();
AssistantEdit {
path: edit.path.clone(),
kind: edit.kind.clone(),
}
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>(),
expected_suggestions
);
}
}
@@ -1108,7 +1097,8 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
text: output_text,
sections,
run_commands_in_text: false,
})),
}
.to_event_stream())),
true,
false,
cx,
@@ -1427,11 +1417,12 @@ impl SlashCommand for FakeSlashCommand {
_workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
_cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
Task::ready(Ok(SlashCommandOutput {
text: format!("Executed fake command: {}", self.0),
sections: vec![],
run_commands_in_text: false,
}))
}
.to_event_stream()))
}
}

View File

@@ -1,7 +1,7 @@
use crate::{
assistant_settings::AssistantSettings, humanize_token_count, prompts::PromptBuilder,
AssistantPanel, AssistantPanelEvent, CharOperation, CycleNextInlineAssist,
CyclePreviousInlineAssist, LineDiff, LineOperation, ModelSelector, StreamingDiff,
CyclePreviousInlineAssist, LineDiff, LineOperation, ModelSelector, RequestType, StreamingDiff,
};
use anyhow::{anyhow, Context as _, Result};
use client::{telemetry::Telemetry, ErrorExt};
@@ -9,7 +9,7 @@ use collections::{hash_map, HashMap, HashSet, VecDeque};
use editor::{
actions::{MoveDown, MoveUp, SelectAll},
display_map::{
BlockContext, BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
BlockContext, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
ToDisplayPoint,
},
Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorElement, EditorEvent, EditorMode,
@@ -21,9 +21,7 @@ use fs::Fs;
use futures::{
channel::mpsc,
future::{BoxFuture, LocalBoxFuture},
join,
stream::{self, BoxStream},
SinkExt, Stream, StreamExt,
join, SinkExt, Stream, StreamExt,
};
use gpui::{
anchored, deferred, point, AnyElement, AppContext, ClickEvent, EventEmitter, FocusHandle,
@@ -32,7 +30,8 @@ use gpui::{
};
use language::{Buffer, IndentKind, Point, Selection, TransactionId};
use language_model::{
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
logging::report_assistant_event, LanguageModel, LanguageModelRegistry, LanguageModelRequest,
LanguageModelRequestMessage, LanguageModelTextStream, Role,
};
use multi_buffer::MultiBufferRow;
use parking_lot::Mutex;
@@ -54,7 +53,7 @@ use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
use terminal_view::terminal_panel::TerminalPanel;
use text::{OffsetRangeExt, ToPoint as _};
use theme::ThemeSettings;
use ui::{prelude::*, CheckboxWithLabel, IconButtonShape, Popover, Tooltip};
use ui::{prelude::*, text_for_action, CheckboxWithLabel, IconButtonShape, Popover, Tooltip};
use util::{RangeExt, ResultExt};
use workspace::{notifications::NotificationId, ItemHandle, Toast, Workspace};
@@ -82,33 +81,13 @@ pub struct InlineAssistant {
assists: HashMap<InlineAssistId, InlineAssist>,
assists_by_editor: HashMap<WeakView<Editor>, EditorInlineAssists>,
assist_groups: HashMap<InlineAssistGroupId, InlineAssistGroup>,
assist_observations: HashMap<
InlineAssistId,
(
async_watch::Sender<AssistStatus>,
async_watch::Receiver<AssistStatus>,
),
>,
confirmed_assists: HashMap<InlineAssistId, Model<CodegenAlternative>>,
prompt_history: VecDeque<String>,
prompt_builder: Arc<PromptBuilder>,
telemetry: Option<Arc<Telemetry>>,
telemetry: Arc<Telemetry>,
fs: Arc<dyn Fs>,
}
pub enum AssistStatus {
Idle,
Started,
Stopped,
Finished,
}
impl AssistStatus {
pub fn is_done(&self) -> bool {
matches!(self, Self::Stopped | Self::Finished)
}
}
impl Global for InlineAssistant {}
impl InlineAssistant {
@@ -123,11 +102,10 @@ impl InlineAssistant {
assists: HashMap::default(),
assists_by_editor: HashMap::default(),
assist_groups: HashMap::default(),
assist_observations: HashMap::default(),
confirmed_assists: HashMap::default(),
prompt_history: VecDeque::default(),
prompt_builder,
telemetry: Some(telemetry),
telemetry,
fs,
}
}
@@ -210,11 +188,16 @@ impl InlineAssistant {
initial_prompt: Option<String>,
cx: &mut WindowContext,
) {
let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx);
let (snapshot, initial_selections) = editor.update(cx, |editor, cx| {
(
editor.buffer().read(cx).snapshot(cx),
editor.selections.all::<Point>(cx),
)
});
let mut selections = Vec::<Selection<Point>>::new();
let mut newest_selection = None;
for mut selection in editor.read(cx).selections.all::<Point>(cx) {
for mut selection in initial_selections {
if selection.end > selection.start {
selection.start.column = 0;
// If the selection ends at the start of the line, we don't want to include it.
@@ -257,19 +240,18 @@ impl InlineAssistant {
};
codegen_ranges.push(start..end);
if let Some(telemetry) = self.telemetry.as_ref() {
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
telemetry.report_assistant_event(AssistantEvent {
conversation_id: None,
kind: AssistantKind::Inline,
phase: AssistantPhase::Invoked,
model: model.telemetry_id(),
model_provider: model.provider_id().to_string(),
response_latency: None,
error_message: None,
language_name: buffer.language().map(|language| language.name().to_proto()),
});
}
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
self.telemetry.report_assistant_event(AssistantEvent {
conversation_id: None,
kind: AssistantKind::Inline,
phase: AssistantPhase::Invoked,
message_id: None,
model: model.telemetry_id(),
model_provider: model.provider_id().to_string(),
response_latency: None,
error_message: None,
language_name: buffer.language().map(|language| language.name().to_proto()),
});
}
}
@@ -467,15 +449,14 @@ impl InlineAssistant {
let assist_blocks = vec![
BlockProperties {
style: BlockStyle::Sticky,
position: range.start,
placement: BlockPlacement::Above(range.start),
height: prompt_editor_height,
render: build_assist_editor_renderer(prompt_editor),
disposition: BlockDisposition::Above,
priority: 0,
},
BlockProperties {
style: BlockStyle::Sticky,
position: range.end,
placement: BlockPlacement::Below(range.end),
height: 0,
render: Box::new(|cx| {
v_flex()
@@ -485,7 +466,6 @@ impl InlineAssistant {
.border_color(cx.theme().status().info_border)
.into_any_element()
}),
disposition: BlockDisposition::Below,
priority: 0,
},
];
@@ -589,10 +569,13 @@ impl InlineAssistant {
return;
};
let editor = editor.read(cx);
if editor.selections.count() == 1 {
let selection = editor.selections.newest::<usize>(cx);
let buffer = editor.buffer().read(cx).snapshot(cx);
if editor.read(cx).selections.count() == 1 {
let (selection, buffer) = editor.update(cx, |editor, cx| {
(
editor.selections.newest::<usize>(cx),
editor.buffer().read(cx).snapshot(cx),
)
});
for assist_id in &editor_assists.assist_ids {
let assist = &self.assists[assist_id];
let assist_range = assist.range.to_offset(&buffer);
@@ -617,10 +600,13 @@ impl InlineAssistant {
return;
};
let editor = editor.read(cx);
if editor.selections.count() == 1 {
let selection = editor.selections.newest::<usize>(cx);
let buffer = editor.buffer().read(cx).snapshot(cx);
if editor.read(cx).selections.count() == 1 {
let (selection, buffer) = editor.update(cx, |editor, cx| {
(
editor.selections.newest::<usize>(cx),
editor.buffer().read(cx).snapshot(cx),
)
});
let mut closest_assist_fallback = None;
for assist_id in &editor_assists.assist_ids {
let assist = &self.assists[assist_id];
@@ -766,33 +752,6 @@ impl InlineAssistant {
pub fn finish_assist(&mut self, assist_id: InlineAssistId, undo: bool, cx: &mut WindowContext) {
if let Some(assist) = self.assists.get(&assist_id) {
if let Some(telemetry) = self.telemetry.as_ref() {
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
let language_name = assist.editor.upgrade().and_then(|editor| {
let multibuffer = editor.read(cx).buffer().read(cx);
let ranges = multibuffer.range_to_buffer_ranges(assist.range.clone(), cx);
ranges
.first()
.and_then(|(buffer, _, _)| buffer.read(cx).language())
.map(|language| language.name())
});
telemetry.report_assistant_event(AssistantEvent {
conversation_id: None,
kind: AssistantKind::Inline,
phase: if undo {
AssistantPhase::Rejected
} else {
AssistantPhase::Accepted
},
model: model.telemetry_id(),
model_provider: model.provider_id().to_string(),
response_latency: None,
error_message: None,
language_name: language_name.map(|name| name.to_proto()),
});
}
}
let assist_group_id = assist.group_id;
if self.assist_groups[&assist_group_id].linked {
for assist_id in self.unlink_assist_group(assist_group_id, cx) {
@@ -827,25 +786,47 @@ impl InlineAssistant {
}
}
let active_alternative = assist.codegen.read(cx).active_alternative().clone();
let message_id = active_alternative.read(cx).message_id.clone();
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
let language_name = assist.editor.upgrade().and_then(|editor| {
let multibuffer = editor.read(cx).buffer().read(cx);
let ranges = multibuffer.range_to_buffer_ranges(assist.range.clone(), cx);
ranges
.first()
.and_then(|(buffer, _, _)| buffer.read(cx).language())
.map(|language| language.name())
});
report_assistant_event(
AssistantEvent {
conversation_id: None,
kind: AssistantKind::Inline,
message_id,
phase: if undo {
AssistantPhase::Rejected
} else {
AssistantPhase::Accepted
},
model: model.telemetry_id(),
model_provider: model.provider_id().to_string(),
response_latency: None,
error_message: None,
language_name: language_name.map(|name| name.to_proto()),
},
Some(self.telemetry.clone()),
cx.http_client(),
model.api_key(cx),
cx.background_executor(),
);
}
if undo {
assist.codegen.update(cx, |codegen, cx| codegen.undo(cx));
} else {
let confirmed_alternative = assist.codegen.read(cx).active_alternative().clone();
self.confirmed_assists
.insert(assist_id, confirmed_alternative);
self.confirmed_assists.insert(assist_id, active_alternative);
}
}
// Remove the assist from the status updates map
self.assist_observations.remove(&assist_id);
}
pub fn undo_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) -> bool {
let Some(codegen) = self.confirmed_assists.remove(&assist_id) else {
return false;
};
codegen.update(cx, |this, cx| this.undo(cx));
true
}
fn dismiss_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) -> bool {
@@ -1039,10 +1020,6 @@ impl InlineAssistant {
codegen.start(user_prompt, assistant_panel_context, cx)
})
.log_err();
if let Some((tx, _)) = self.assist_observations.get(&assist_id) {
tx.send(AssistStatus::Started).ok();
}
}
pub fn stop_assist(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) {
@@ -1053,25 +1030,6 @@ impl InlineAssistant {
};
assist.codegen.update(cx, |codegen, cx| codegen.stop(cx));
if let Some((tx, _)) = self.assist_observations.get(&assist_id) {
tx.send(AssistStatus::Stopped).ok();
}
}
pub fn assist_status(&self, assist_id: InlineAssistId, cx: &AppContext) -> InlineAssistStatus {
if let Some(assist) = self.assists.get(&assist_id) {
match assist.codegen.read(cx).status(cx) {
CodegenStatus::Idle => InlineAssistStatus::Idle,
CodegenStatus::Pending => InlineAssistStatus::Pending,
CodegenStatus::Done => InlineAssistStatus::Done,
CodegenStatus::Error(_) => InlineAssistStatus::Error,
}
} else if self.confirmed_assists.contains_key(&assist_id) {
InlineAssistStatus::Confirmed
} else {
InlineAssistStatus::Canceled
}
}
fn update_editor_highlights(&self, editor: &View<Editor>, cx: &mut WindowContext) {
@@ -1234,7 +1192,7 @@ impl InlineAssistant {
let height =
deleted_lines_editor.update(cx, |editor, cx| editor.max_point(cx).row().0 + 1);
new_blocks.push(BlockProperties {
position: new_row,
placement: BlockPlacement::Above(new_row),
height,
style: BlockStyle::Flex,
render: Box::new(move |cx| {
@@ -1246,7 +1204,6 @@ impl InlineAssistant {
.child(deleted_lines_editor.clone())
.into_any_element()
}),
disposition: BlockDisposition::Above,
priority: 0,
});
}
@@ -1257,42 +1214,6 @@ impl InlineAssistant {
.collect();
})
}
pub fn observe_assist(
&mut self,
assist_id: InlineAssistId,
) -> async_watch::Receiver<AssistStatus> {
if let Some((_, rx)) = self.assist_observations.get(&assist_id) {
rx.clone()
} else {
let (tx, rx) = async_watch::channel(AssistStatus::Idle);
self.assist_observations.insert(assist_id, (tx, rx.clone()));
rx
}
}
}
pub enum InlineAssistStatus {
Idle,
Pending,
Done,
Error,
Confirmed,
Canceled,
}
impl InlineAssistStatus {
pub(crate) fn is_pending(&self) -> bool {
matches!(self, Self::Pending)
}
pub(crate) fn is_confirmed(&self) -> bool {
matches!(self, Self::Confirmed)
}
pub(crate) fn is_done(&self) -> bool {
matches!(self, Self::Done)
}
}
struct EditorInlineAssists {
@@ -1690,7 +1611,7 @@ impl PromptEditor {
// always show the cursor (even when it isn't focused) because
// typing in one will make what you typed appear in all of them.
editor.set_show_cursor_when_unfocused(true, cx);
editor.set_placeholder_text("Add a prompt…", cx);
editor.set_placeholder_text(Self::placeholder_text(codegen.read(cx), cx), cx);
editor
});
@@ -1747,6 +1668,7 @@ impl PromptEditor {
self.editor = cx.new_view(|cx| {
let mut editor = Editor::auto_height(Self::MAX_LINES as usize, cx);
editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
editor.set_placeholder_text(Self::placeholder_text(self.codegen.read(cx), cx), cx);
editor.set_placeholder_text("Add a prompt…", cx);
editor.set_text(prompt, cx);
if focus {
@@ -1757,6 +1679,20 @@ impl PromptEditor {
self.subscribe_to_editor(cx);
}
fn placeholder_text(codegen: &Codegen, cx: &WindowContext) -> String {
let context_keybinding = text_for_action(&crate::ToggleFocus, cx)
.map(|keybinding| format!("{keybinding} for context"))
.unwrap_or_default();
let action = if codegen.is_insertion {
"Generate"
} else {
"Transform"
};
format!("{action}{context_keybinding} • ↓↑ for history")
}
fn prompt(&self, cx: &AppContext) -> String {
self.editor.read(cx).text(cx)
}
@@ -1819,6 +1755,20 @@ impl PromptEditor {
) {
match event {
EditorEvent::Edited { .. } => {
if let Some(workspace) = cx.window_handle().downcast::<Workspace>() {
workspace
.update(cx, |workspace, cx| {
let is_via_ssh = workspace
.project()
.update(cx, |project, _| project.is_via_ssh());
workspace
.client()
.telemetry()
.log_edit_event("inline assist", is_via_ssh);
})
.log_err();
}
let prompt = self.editor.read(cx).text(cx);
if self
.prompt_history_ix
@@ -2290,8 +2240,6 @@ impl InlineAssist {
if assist.decorations.is_none() {
this.finish_assist(assist_id, false, cx);
} else if let Some(tx) = this.assist_observations.get(&assist_id) {
tx.0.send(AssistStatus::Finished).ok();
}
}
})
@@ -2315,7 +2263,7 @@ impl InlineAssist {
.read(cx)
.active_context(cx)?
.read(cx)
.to_completion_request(cx),
.to_completion_request(RequestType::Chat, cx),
)
} else {
None
@@ -2349,12 +2297,14 @@ pub enum CodegenEvent {
pub struct Codegen {
alternatives: Vec<Model<CodegenAlternative>>,
active_alternative: usize,
seen_alternatives: HashSet<usize>,
subscriptions: Vec<Subscription>,
buffer: Model<MultiBuffer>,
range: Range<Anchor>,
initial_transaction_id: Option<TransactionId>,
telemetry: Option<Arc<Telemetry>>,
telemetry: Arc<Telemetry>,
builder: Arc<PromptBuilder>,
is_insertion: bool,
}
impl Codegen {
@@ -2362,7 +2312,7 @@ impl Codegen {
buffer: Model<MultiBuffer>,
range: Range<Anchor>,
initial_transaction_id: Option<TransactionId>,
telemetry: Option<Arc<Telemetry>>,
telemetry: Arc<Telemetry>,
builder: Arc<PromptBuilder>,
cx: &mut ModelContext<Self>,
) -> Self {
@@ -2371,14 +2321,16 @@ impl Codegen {
buffer.clone(),
range.clone(),
false,
telemetry.clone(),
Some(telemetry.clone()),
builder.clone(),
cx,
)
});
let mut this = Self {
is_insertion: range.to_offset(&buffer.read(cx).snapshot(cx)).is_empty(),
alternatives: vec![codegen],
active_alternative: 0,
seen_alternatives: HashSet::default(),
subscriptions: Vec::new(),
buffer,
range,
@@ -2431,6 +2383,7 @@ impl Codegen {
fn activate(&mut self, index: usize, cx: &mut ModelContext<Self>) {
self.active_alternative()
.update(cx, |codegen, cx| codegen.set_active(false, cx));
self.seen_alternatives.insert(index);
self.active_alternative = index;
self.active_alternative()
.update(cx, |codegen, cx| codegen.set_active(true, cx));
@@ -2459,7 +2412,7 @@ impl Codegen {
self.buffer.clone(),
self.range.clone(),
false,
self.telemetry.clone(),
Some(self.telemetry.clone()),
self.builder.clone(),
cx,
)
@@ -2560,6 +2513,9 @@ pub struct CodegenAlternative {
active: bool,
edits: Vec<(Range<Anchor>, String)>,
line_operations: Vec<LineOperation>,
request: Option<LanguageModelRequest>,
elapsed_time: Option<f64>,
message_id: Option<String>,
}
enum CodegenStatus {
@@ -2618,6 +2574,7 @@ impl CodegenAlternative {
buffer: buffer.clone(),
old_buffer,
edit_position: None,
message_id: None,
snapshot,
last_equal_ranges: Default::default(),
transformation_transaction_id: None,
@@ -2631,6 +2588,8 @@ impl CodegenAlternative {
edits: Vec::new(),
line_operations: Vec::new(),
range,
request: None,
elapsed_time: None,
}
}
@@ -2720,19 +2679,20 @@ impl CodegenAlternative {
self.edit_position = Some(self.range.start.bias_right(&self.snapshot));
let api_key = model.api_key(cx);
let telemetry_id = model.telemetry_id();
let provider_id = model.provider_id();
let chunks: LocalBoxFuture<Result<BoxStream<Result<String>>>> =
let stream: LocalBoxFuture<Result<LanguageModelTextStream>> =
if user_prompt.trim().to_lowercase() == "delete" {
async { Ok(stream::empty().boxed()) }.boxed_local()
async { Ok(LanguageModelTextStream::default()) }.boxed_local()
} else {
let request = self.build_request(user_prompt, assistant_panel_context, cx)?;
self.request = Some(request.clone());
let chunks = cx
.spawn(|_, cx| async move { model.stream_completion_text(request, &cx).await });
async move { Ok(chunks.await?.boxed()) }.boxed_local()
cx.spawn(|_, cx| async move { model.stream_completion_text(request, &cx).await })
.boxed_local()
};
self.handle_stream(telemetry_id, provider_id.to_string(), chunks, cx);
self.handle_stream(telemetry_id, provider_id.to_string(), api_key, stream, cx);
Ok(())
}
@@ -2771,7 +2731,7 @@ impl CodegenAlternative {
let prompt = self
.builder
.generate_content_prompt(user_prompt, language_name, buffer, range)
.generate_inline_transformation_prompt(user_prompt, language_name, buffer, range)
.map_err(|e| anyhow::anyhow!("Failed to generate content prompt: {}", e))?;
let mut messages = Vec::new();
@@ -2797,9 +2757,11 @@ impl CodegenAlternative {
&mut self,
model_telemetry_id: String,
model_provider_id: String,
stream: impl 'static + Future<Output = Result<BoxStream<'static, Result<String>>>>,
model_api_key: Option<String>,
stream: impl 'static + Future<Output = Result<LanguageModelTextStream>>,
cx: &mut ModelContext<Self>,
) {
let start_time = Instant::now();
let snapshot = self.snapshot.clone();
let selected_text = snapshot
.text_for_range(self.range.start..self.range.end)
@@ -2826,6 +2788,7 @@ impl CodegenAlternative {
}
}
let http_client = cx.http_client().clone();
let telemetry = self.telemetry.clone();
let language_name = {
let multibuffer = self.buffer.read(cx);
@@ -2841,15 +2804,21 @@ impl CodegenAlternative {
let mut edit_start = self.range.start.to_offset(&snapshot);
self.generation = cx.spawn(|codegen, mut cx| {
async move {
let chunks = stream.await;
let stream = stream.await;
let message_id = stream
.as_ref()
.ok()
.and_then(|stream| stream.message_id.clone());
let generate = async {
let (mut diff_tx, mut diff_rx) = mpsc::channel(1);
let executor = cx.background_executor().clone();
let message_id = message_id.clone();
let line_based_stream_diff: Task<anyhow::Result<()>> =
cx.background_executor().spawn(async move {
let mut response_latency = None;
let request_start = Instant::now();
let diff = async {
let chunks = StripInvalidSpans::new(chunks?);
let chunks = StripInvalidSpans::new(stream?.stream);
futures::pin_mut!(chunks);
let mut diff = StreamingDiff::new(selected_text.to_string());
let mut line_diff = LineDiff::default();
@@ -2945,9 +2914,10 @@ impl CodegenAlternative {
let error_message =
result.as_ref().err().map(|error| error.to_string());
if let Some(telemetry) = telemetry {
telemetry.report_assistant_event(AssistantEvent {
report_assistant_event(
AssistantEvent {
conversation_id: None,
message_id,
kind: AssistantKind::Inline,
phase: AssistantPhase::Response,
model: model_telemetry_id,
@@ -2955,8 +2925,12 @@ impl CodegenAlternative {
response_latency,
error_message,
language_name: language_name.map(|name| name.to_proto()),
});
}
},
telemetry,
http_client,
model_api_key,
&executor,
);
result?;
Ok(())
@@ -3016,14 +2990,18 @@ impl CodegenAlternative {
};
let result = generate.await;
let elapsed_time = start_time.elapsed().as_secs_f64();
codegen
.update(&mut cx, |this, cx| {
this.message_id = message_id;
this.last_equal_ranges.clear();
if let Err(error) = result {
this.status = CodegenStatus::Error(error);
} else {
this.status = CodegenStatus::Done;
}
this.elapsed_time = Some(elapsed_time);
cx.emit(CodegenEvent::Finished);
cx.notify();
})
@@ -3370,6 +3348,10 @@ impl CodeActionProvider for AssistantCodeActionProvider {
range: Range<text::Anchor>,
cx: &mut WindowContext,
) -> Task<Result<Vec<CodeAction>>> {
if !AssistantSettings::get_global(cx).enabled {
return Task::ready(Ok(Vec::new()));
}
let snapshot = buffer.read(cx).snapshot();
let mut range = range.to_point(&snapshot);
@@ -3564,15 +3546,7 @@ mod tests {
)
});
let (chunks_tx, chunks_rx) = mpsc::unbounded();
codegen.update(cx, |codegen, cx| {
codegen.handle_stream(
String::new(),
String::new(),
future::ready(Ok(chunks_rx.map(Ok).boxed())),
cx,
)
});
let chunks_tx = simulate_response_stream(codegen.clone(), cx);
let mut new_text = concat!(
" let mut x = 0;\n",
@@ -3636,15 +3610,7 @@ mod tests {
)
});
let (chunks_tx, chunks_rx) = mpsc::unbounded();
codegen.update(cx, |codegen, cx| {
codegen.handle_stream(
String::new(),
String::new(),
future::ready(Ok(chunks_rx.map(Ok).boxed())),
cx,
)
});
let chunks_tx = simulate_response_stream(codegen.clone(), cx);
cx.background_executor.run_until_parked();
@@ -3711,15 +3677,7 @@ mod tests {
)
});
let (chunks_tx, chunks_rx) = mpsc::unbounded();
codegen.update(cx, |codegen, cx| {
codegen.handle_stream(
String::new(),
String::new(),
future::ready(Ok(chunks_rx.map(Ok).boxed())),
cx,
)
});
let chunks_tx = simulate_response_stream(codegen.clone(), cx);
cx.background_executor.run_until_parked();
@@ -3785,16 +3743,7 @@ mod tests {
)
});
let (chunks_tx, chunks_rx) = mpsc::unbounded();
codegen.update(cx, |codegen, cx| {
codegen.handle_stream(
String::new(),
String::new(),
future::ready(Ok(chunks_rx.map(Ok).boxed())),
cx,
)
});
let chunks_tx = simulate_response_stream(codegen.clone(), cx);
let new_text = concat!(
"func main() {\n",
"\tx := 0\n",
@@ -3849,16 +3798,7 @@ mod tests {
)
});
let (chunks_tx, chunks_rx) = mpsc::unbounded();
codegen.update(cx, |codegen, cx| {
codegen.handle_stream(
String::new(),
String::new(),
future::ready(Ok(chunks_rx.map(Ok).boxed())),
cx,
)
});
let chunks_tx = simulate_response_stream(codegen.clone(), cx);
chunks_tx
.unbounded_send("let mut x = 0;\nx += 1;".to_string())
.unwrap();
@@ -3932,6 +3872,26 @@ mod tests {
}
}
fn simulate_response_stream(
codegen: Model<CodegenAlternative>,
cx: &mut TestAppContext,
) -> mpsc::UnboundedSender<String> {
let (chunks_tx, chunks_rx) = mpsc::unbounded();
codegen.update(cx, |codegen, cx| {
codegen.handle_stream(
String::new(),
String::new(),
None,
future::ready(Ok(LanguageModelTextStream {
message_id: None,
stream: chunks_rx.map(Ok).boxed(),
})),
cx,
);
});
chunks_tx
}
fn rust_lang() -> Language {
Language::new(
LanguageConfig {

View File

@@ -158,39 +158,34 @@ impl PickerDelegate for ModelPickerDelegate {
.spacing(ListItemSpacing::Sparse)
.selected(selected)
.start_slot(
div().pr_1().child(
div().pr_0p5().child(
Icon::new(model_info.icon)
.color(Color::Muted)
.size(IconSize::Medium),
),
)
.child(
h_flex()
.w_full()
.justify_between()
.font_buffer(cx)
.min_w(px(240.))
.child(
h_flex()
.gap_2()
.child(Label::new(model_info.model.name().0.clone()))
.child(
Label::new(provider_name)
.size(LabelSize::XSmall)
.color(Color::Muted),
)
.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)
})
}
}),
),
h_flex().w_full().justify_between().min_w(px(200.)).child(
h_flex()
.gap_1p5()
.child(Label::new(model_info.model.name().0.clone()))
.child(
Label::new(provider_name)
.size(LabelSize::XSmall)
.color(Color::Muted),
)
.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)
})
}
}),
),
)
.end_slot(div().when(model_info.is_selected, |this| {
this.child(
@@ -212,7 +207,7 @@ impl PickerDelegate for ModelPickerDelegate {
h_flex()
.w_full()
.border_t_1()
.border_color(cx.theme().colors().border)
.border_color(cx.theme().colors().border_variant)
.p_1()
.gap_4()
.justify_between()

View File

@@ -0,0 +1,970 @@
use anyhow::{anyhow, Context as _, Result};
use collections::HashMap;
use editor::ProposedChangesEditor;
use futures::{future, TryFutureExt as _};
use gpui::{AppContext, AsyncAppContext, Model, SharedString};
use language::{AutoindentMode, Buffer, BufferSnapshot};
use project::{Project, ProjectPath};
use std::{cmp, ops::Range, path::Path, sync::Arc};
use text::{AnchorRangeExt as _, Bias, OffsetRangeExt as _, Point};
#[derive(Clone, Debug)]
pub(crate) struct AssistantPatch {
pub range: Range<language::Anchor>,
pub title: SharedString,
pub edits: Arc<[Result<AssistantEdit>]>,
pub status: AssistantPatchStatus,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum AssistantPatchStatus {
Pending,
Ready,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct AssistantEdit {
pub path: String,
pub kind: AssistantEditKind,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AssistantEditKind {
Update {
old_text: String,
new_text: String,
description: Option<String>,
},
Create {
new_text: String,
description: Option<String>,
},
InsertBefore {
old_text: String,
new_text: String,
description: Option<String>,
},
InsertAfter {
old_text: String,
new_text: String,
description: Option<String>,
},
Delete {
old_text: String,
},
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct ResolvedPatch {
pub edit_groups: HashMap<Model<Buffer>, Vec<ResolvedEditGroup>>,
pub errors: Vec<AssistantPatchResolutionError>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ResolvedEditGroup {
pub context_range: Range<language::Anchor>,
pub edits: Vec<ResolvedEdit>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ResolvedEdit {
range: Range<language::Anchor>,
new_text: String,
description: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct AssistantPatchResolutionError {
pub edit_ix: usize,
pub message: String,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum SearchDirection {
Up,
Left,
Diagonal,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct SearchState {
cost: u32,
direction: SearchDirection,
}
impl SearchState {
fn new(cost: u32, direction: SearchDirection) -> Self {
Self { cost, direction }
}
}
struct SearchMatrix {
cols: usize,
data: Vec<SearchState>,
}
impl SearchMatrix {
fn new(rows: usize, cols: usize) -> Self {
SearchMatrix {
cols,
data: vec![SearchState::new(0, SearchDirection::Diagonal); rows * cols],
}
}
fn get(&self, row: usize, col: usize) -> SearchState {
self.data[row * self.cols + col]
}
fn set(&mut self, row: usize, col: usize, cost: SearchState) {
self.data[row * self.cols + col] = cost;
}
}
impl ResolvedPatch {
pub fn apply(&self, editor: &ProposedChangesEditor, cx: &mut AppContext) {
for (buffer, groups) in &self.edit_groups {
let branch = editor.branch_buffer_for_base(buffer).unwrap();
Self::apply_edit_groups(groups, &branch, cx);
}
editor.recalculate_all_buffer_diffs();
}
fn apply_edit_groups(
groups: &Vec<ResolvedEditGroup>,
buffer: &Model<Buffer>,
cx: &mut AppContext,
) {
let mut edits = Vec::new();
for group in groups {
for suggestion in &group.edits {
edits.push((suggestion.range.clone(), suggestion.new_text.clone()));
}
}
buffer.update(cx, |buffer, cx| {
buffer.edit(
edits,
Some(AutoindentMode::Block {
original_indent_columns: Vec::new(),
}),
cx,
);
});
}
}
impl ResolvedEdit {
pub fn try_merge(&mut self, other: &Self, buffer: &text::BufferSnapshot) -> bool {
let range = &self.range;
let other_range = &other.range;
// Don't merge if we don't contain the other suggestion.
if range.start.cmp(&other_range.start, buffer).is_gt()
|| range.end.cmp(&other_range.end, buffer).is_lt()
{
return false;
}
let other_offset_range = other_range.to_offset(buffer);
let offset_range = range.to_offset(buffer);
// If the other range is empty at the start of this edit's range, combine the new text
if other_offset_range.is_empty() && other_offset_range.start == offset_range.start {
self.new_text = format!("{}\n{}", other.new_text, self.new_text);
self.range.start = other_range.start;
if let Some((description, other_description)) =
self.description.as_mut().zip(other.description.as_ref())
{
*description = format!("{}\n{}", other_description, description)
}
} else {
if let Some((description, other_description)) =
self.description.as_mut().zip(other.description.as_ref())
{
description.push('\n');
description.push_str(other_description);
}
}
true
}
}
impl AssistantEdit {
pub fn new(
path: Option<String>,
operation: Option<String>,
old_text: Option<String>,
new_text: Option<String>,
description: Option<String>,
) -> Result<Self> {
let path = path.ok_or_else(|| anyhow!("missing path"))?;
let operation = operation.ok_or_else(|| anyhow!("missing operation"))?;
let kind = match operation.as_str() {
"update" => AssistantEditKind::Update {
old_text: old_text.ok_or_else(|| anyhow!("missing old_text"))?,
new_text: new_text.ok_or_else(|| anyhow!("missing new_text"))?,
description,
},
"insert_before" => AssistantEditKind::InsertBefore {
old_text: old_text.ok_or_else(|| anyhow!("missing old_text"))?,
new_text: new_text.ok_or_else(|| anyhow!("missing new_text"))?,
description,
},
"insert_after" => AssistantEditKind::InsertAfter {
old_text: old_text.ok_or_else(|| anyhow!("missing old_text"))?,
new_text: new_text.ok_or_else(|| anyhow!("missing new_text"))?,
description,
},
"delete" => AssistantEditKind::Delete {
old_text: old_text.ok_or_else(|| anyhow!("missing old_text"))?,
},
"create" => AssistantEditKind::Create {
description,
new_text: new_text.ok_or_else(|| anyhow!("missing new_text"))?,
},
_ => Err(anyhow!("unknown operation {operation:?}"))?,
};
Ok(Self { path, kind })
}
pub async fn resolve(
&self,
project: Model<Project>,
mut cx: AsyncAppContext,
) -> Result<(Model<Buffer>, ResolvedEdit)> {
let path = self.path.clone();
let kind = self.kind.clone();
let buffer = project
.update(&mut cx, |project, cx| {
let project_path = project
.find_project_path(Path::new(&path), cx)
.or_else(|| {
// If we couldn't find a project path for it, put it in the active worktree
// so that when we create the buffer, it can be saved.
let worktree = project
.active_entry()
.and_then(|entry_id| project.worktree_for_entry(entry_id, cx))
.or_else(|| project.worktrees(cx).next())?;
let worktree = worktree.read(cx);
Some(ProjectPath {
worktree_id: worktree.id(),
path: Arc::from(Path::new(&path)),
})
})
.with_context(|| format!("worktree not found for {:?}", path))?;
anyhow::Ok(project.open_buffer(project_path, cx))
})??
.await?;
let snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot())?;
let suggestion = cx
.background_executor()
.spawn(async move { kind.resolve(&snapshot) })
.await;
Ok((buffer, suggestion))
}
}
impl AssistantEditKind {
fn resolve(self, snapshot: &BufferSnapshot) -> ResolvedEdit {
match self {
Self::Update {
old_text,
new_text,
description,
} => {
let range = Self::resolve_location(&snapshot, &old_text);
ResolvedEdit {
range,
new_text,
description,
}
}
Self::Create {
new_text,
description,
} => ResolvedEdit {
range: text::Anchor::MIN..text::Anchor::MAX,
description,
new_text,
},
Self::InsertBefore {
old_text,
mut new_text,
description,
} => {
let range = Self::resolve_location(&snapshot, &old_text);
new_text.push('\n');
ResolvedEdit {
range: range.start..range.start,
new_text,
description,
}
}
Self::InsertAfter {
old_text,
mut new_text,
description,
} => {
let range = Self::resolve_location(&snapshot, &old_text);
new_text.insert(0, '\n');
ResolvedEdit {
range: range.end..range.end,
new_text,
description,
}
}
Self::Delete { old_text } => {
let range = Self::resolve_location(&snapshot, &old_text);
ResolvedEdit {
range,
new_text: String::new(),
description: None,
}
}
}
}
fn resolve_location(buffer: &text::BufferSnapshot, search_query: &str) -> Range<text::Anchor> {
const INSERTION_COST: u32 = 3;
const DELETION_COST: u32 = 10;
const WHITESPACE_INSERTION_COST: u32 = 1;
const WHITESPACE_DELETION_COST: u32 = 1;
let buffer_len = buffer.len();
let query_len = search_query.len();
let mut matrix = SearchMatrix::new(query_len + 1, buffer_len + 1);
let mut leading_deletion_cost = 0_u32;
for (row, query_byte) in search_query.bytes().enumerate() {
let deletion_cost = if query_byte.is_ascii_whitespace() {
WHITESPACE_DELETION_COST
} else {
DELETION_COST
};
leading_deletion_cost = leading_deletion_cost.saturating_add(deletion_cost);
matrix.set(
row + 1,
0,
SearchState::new(leading_deletion_cost, SearchDirection::Diagonal),
);
for (col, buffer_byte) in buffer.bytes_in_range(0..buffer.len()).flatten().enumerate() {
let insertion_cost = if buffer_byte.is_ascii_whitespace() {
WHITESPACE_INSERTION_COST
} else {
INSERTION_COST
};
let up = SearchState::new(
matrix.get(row, col + 1).cost.saturating_add(deletion_cost),
SearchDirection::Up,
);
let left = SearchState::new(
matrix.get(row + 1, col).cost.saturating_add(insertion_cost),
SearchDirection::Left,
);
let diagonal = SearchState::new(
if query_byte == *buffer_byte {
matrix.get(row, col).cost
} else {
matrix
.get(row, col)
.cost
.saturating_add(deletion_cost + insertion_cost)
},
SearchDirection::Diagonal,
);
matrix.set(row + 1, col + 1, up.min(left).min(diagonal));
}
}
// Traceback to find the best match
let mut best_buffer_end = buffer_len;
let mut best_cost = u32::MAX;
for col in 1..=buffer_len {
let cost = matrix.get(query_len, col).cost;
if cost < best_cost {
best_cost = cost;
best_buffer_end = col;
}
}
let mut query_ix = query_len;
let mut buffer_ix = best_buffer_end;
while query_ix > 0 && buffer_ix > 0 {
let current = matrix.get(query_ix, buffer_ix);
match current.direction {
SearchDirection::Diagonal => {
query_ix -= 1;
buffer_ix -= 1;
}
SearchDirection::Up => {
query_ix -= 1;
}
SearchDirection::Left => {
buffer_ix -= 1;
}
}
}
let mut start = buffer.offset_to_point(buffer.clip_offset(buffer_ix, Bias::Left));
start.column = 0;
let mut end = buffer.offset_to_point(buffer.clip_offset(best_buffer_end, Bias::Right));
if end.column > 0 {
end.column = buffer.line_len(end.row);
}
buffer.anchor_after(start)..buffer.anchor_before(end)
}
}
impl AssistantPatch {
pub(crate) async fn resolve(
&self,
project: Model<Project>,
cx: &mut AsyncAppContext,
) -> ResolvedPatch {
let mut resolve_tasks = Vec::new();
for (ix, edit) in self.edits.iter().enumerate() {
if let Ok(edit) = edit.as_ref() {
resolve_tasks.push(
edit.resolve(project.clone(), cx.clone())
.map_err(move |error| (ix, error)),
);
}
}
let edits = future::join_all(resolve_tasks).await;
let mut errors = Vec::new();
let mut edits_by_buffer = HashMap::default();
for entry in edits {
match entry {
Ok((buffer, edit)) => {
edits_by_buffer
.entry(buffer)
.or_insert_with(Vec::new)
.push(edit);
}
Err((edit_ix, error)) => errors.push(AssistantPatchResolutionError {
edit_ix,
message: error.to_string(),
}),
}
}
// Expand the context ranges of each edit and group edits with overlapping context ranges.
let mut edit_groups_by_buffer = HashMap::default();
for (buffer, edits) in edits_by_buffer {
if let Ok(snapshot) = buffer.update(cx, |buffer, _| buffer.text_snapshot()) {
edit_groups_by_buffer.insert(buffer, Self::group_edits(edits, &snapshot));
}
}
ResolvedPatch {
edit_groups: edit_groups_by_buffer,
errors,
}
}
fn group_edits(
mut edits: Vec<ResolvedEdit>,
snapshot: &text::BufferSnapshot,
) -> Vec<ResolvedEditGroup> {
let mut edit_groups = Vec::<ResolvedEditGroup>::new();
// Sort edits by their range so that earlier, larger ranges come first
edits.sort_by(|a, b| a.range.cmp(&b.range, &snapshot));
// Merge overlapping edits
edits.dedup_by(|a, b| b.try_merge(a, &snapshot));
// Create context ranges for each edit
for edit in edits {
let context_range = {
let edit_point_range = edit.range.to_point(&snapshot);
let start_row = edit_point_range.start.row.saturating_sub(5);
let end_row = cmp::min(edit_point_range.end.row + 5, snapshot.max_point().row);
let start = snapshot.anchor_before(Point::new(start_row, 0));
let end = snapshot.anchor_after(Point::new(end_row, snapshot.line_len(end_row)));
start..end
};
if let Some(last_group) = edit_groups.last_mut() {
if last_group
.context_range
.end
.cmp(&context_range.start, &snapshot)
.is_ge()
{
// Merge with the previous group if context ranges overlap
last_group.context_range.end = context_range.end;
last_group.edits.push(edit);
} else {
// Create a new group
edit_groups.push(ResolvedEditGroup {
context_range,
edits: vec![edit],
});
}
} else {
// Create the first group
edit_groups.push(ResolvedEditGroup {
context_range,
edits: vec![edit],
});
}
}
edit_groups
}
pub fn path_count(&self) -> usize {
self.paths().count()
}
pub fn paths(&self) -> impl '_ + Iterator<Item = &str> {
let mut prev_path = None;
self.edits.iter().filter_map(move |edit| {
if let Ok(edit) = edit {
let path = Some(edit.path.as_str());
if path != prev_path {
prev_path = path;
return path;
}
}
None
})
}
}
impl PartialEq for AssistantPatch {
fn eq(&self, other: &Self) -> bool {
self.range == other.range
&& self.title == other.title
&& Arc::ptr_eq(&self.edits, &other.edits)
}
}
impl Eq for AssistantPatch {}
#[cfg(test)]
mod tests {
use super::*;
use gpui::{AppContext, Context};
use language::{
language_settings::AllLanguageSettings, Language, LanguageConfig, LanguageMatcher,
};
use settings::SettingsStore;
use ui::BorrowAppContext;
use unindent::Unindent as _;
use util::test::{generate_marked_text, marked_text_ranges};
#[gpui::test]
fn test_resolve_location(cx: &mut AppContext) {
assert_location_resolution(
concat!(
" Lorem\n",
"« ipsum\n",
" dolor sit amet»\n",
" consecteur",
),
"ipsum\ndolor",
cx,
);
assert_location_resolution(
&"
«fn foo1(a: usize) -> usize {
40
fn foo2(b: usize) -> usize {
42
}
"
.unindent(),
"fn foo1(b: usize) {\n40\n}",
cx,
);
assert_location_resolution(
&"
fn main() {
« Foo
.bar()
.baz()
.qux()»
}
fn foo2(b: usize) -> usize {
42
}
"
.unindent(),
"Foo.bar.baz.qux()",
cx,
);
assert_location_resolution(
&"
class Something {
one() { return 1; }
« two() { return 2222; }
three() { return 333; }
four() { return 4444; }
five() { return 5555; }
six() { return 6666; }
» seven() { return 7; }
eight() { return 8; }
}
"
.unindent(),
&"
two() { return 2222; }
four() { return 4444; }
five() { return 5555; }
six() { return 6666; }
"
.unindent(),
cx,
);
}
#[gpui::test]
fn test_resolve_edits(cx: &mut AppContext) {
init_test(cx);
assert_edits(
"
/// A person
struct Person {
name: String,
age: usize,
}
/// A dog
struct Dog {
weight: f32,
}
impl Person {
fn name(&self) -> &str {
&self.name
}
}
"
.unindent(),
vec![
AssistantEditKind::Update {
old_text: "
name: String,
"
.unindent(),
new_text: "
first_name: String,
last_name: String,
"
.unindent(),
description: None,
},
AssistantEditKind::Update {
old_text: "
fn name(&self) -> &str {
&self.name
}
"
.unindent(),
new_text: "
fn name(&self) -> String {
format!(\"{} {}\", self.first_name, self.last_name)
}
"
.unindent(),
description: None,
},
],
"
/// A person
struct Person {
first_name: String,
last_name: String,
age: usize,
}
/// A dog
struct Dog {
weight: f32,
}
impl Person {
fn name(&self) -> String {
format!(\"{} {}\", self.first_name, self.last_name)
}
}
"
.unindent(),
cx,
);
// Ensure InsertBefore merges correctly with Update of the same text
assert_edits(
"
fn foo() {
}
"
.unindent(),
vec![
AssistantEditKind::InsertBefore {
old_text: "
fn foo() {"
.unindent(),
new_text: "
fn bar() {
qux();
}"
.unindent(),
description: Some("implement bar".into()),
},
AssistantEditKind::Update {
old_text: "
fn foo() {
}"
.unindent(),
new_text: "
fn foo() {
bar();
}"
.unindent(),
description: Some("call bar in foo".into()),
},
AssistantEditKind::InsertAfter {
old_text: "
fn foo() {
}
"
.unindent(),
new_text: "
fn qux() {
// todo
}
"
.unindent(),
description: Some("implement qux".into()),
},
],
"
fn bar() {
qux();
}
fn foo() {
bar();
}
fn qux() {
// todo
}
"
.unindent(),
cx,
);
// Correctly indent new text when replacing multiple adjacent indented blocks.
assert_edits(
"
impl Numbers {
fn one() {
1
}
fn two() {
2
}
fn three() {
3
}
}
"
.unindent(),
vec![
AssistantEditKind::Update {
old_text: "
fn one() {
1
}
"
.unindent(),
new_text: "
fn one() {
101
}
"
.unindent(),
description: None,
},
AssistantEditKind::Update {
old_text: "
fn two() {
2
}
"
.unindent(),
new_text: "
fn two() {
102
}
"
.unindent(),
description: None,
},
AssistantEditKind::Update {
old_text: "
fn three() {
3
}
"
.unindent(),
new_text: "
fn three() {
103
}
"
.unindent(),
description: None,
},
],
"
impl Numbers {
fn one() {
101
}
fn two() {
102
}
fn three() {
103
}
}
"
.unindent(),
cx,
);
assert_edits(
"
impl Person {
fn set_name(&mut self, name: String) {
self.name = name;
}
fn name(&self) -> String {
return self.name;
}
}
"
.unindent(),
vec![
AssistantEditKind::Update {
old_text: "self.name = name;".unindent(),
new_text: "self._name = name;".unindent(),
description: None,
},
AssistantEditKind::Update {
old_text: "return self.name;\n".unindent(),
new_text: "return self._name;\n".unindent(),
description: None,
},
],
"
impl Person {
fn set_name(&mut self, name: String) {
self._name = name;
}
fn name(&self) -> String {
return self._name;
}
}
"
.unindent(),
cx,
);
}
fn init_test(cx: &mut AppContext) {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
language::init(cx);
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<AllLanguageSettings>(cx, |_| {});
});
}
#[track_caller]
fn assert_location_resolution(
text_with_expected_range: &str,
query: &str,
cx: &mut AppContext,
) {
let (text, _) = marked_text_ranges(text_with_expected_range, false);
let buffer = cx.new_model(|cx| Buffer::local(text.clone(), cx));
let snapshot = buffer.read(cx).snapshot();
let range = AssistantEditKind::resolve_location(&snapshot, query).to_offset(&snapshot);
let text_with_actual_range = generate_marked_text(&text, &[range], false);
pretty_assertions::assert_eq!(text_with_actual_range, text_with_expected_range);
}
#[track_caller]
fn assert_edits(
old_text: String,
edits: Vec<AssistantEditKind>,
new_text: String,
cx: &mut AppContext,
) {
let buffer =
cx.new_model(|cx| Buffer::local(old_text, cx).with_language(Arc::new(rust_lang()), cx));
let snapshot = buffer.read(cx).snapshot();
let resolved_edits = edits
.into_iter()
.map(|kind| kind.resolve(&snapshot))
.collect();
let edit_groups = AssistantPatch::group_edits(resolved_edits, &snapshot);
ResolvedPatch::apply_edit_groups(&edit_groups, &buffer, cx);
let actual_new_text = buffer.read(cx).text();
pretty_assertions::assert_eq!(actual_new_text, new_text);
}
fn rust_lang() -> Language {
Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
..Default::default()
},
Some(language::tree_sitter_rust::LANGUAGE.into()),
)
.with_indents_query(
r#"
(call_expression) @indent
(field_expression) @indent
(_ "(" ")" @end) @indent
(_ "{" "}" @end) @indent
"#,
)
.unwrap()
}
}

View File

@@ -45,15 +45,6 @@ pub struct ProjectSlashCommandPromptContext {
pub context_buffer: String,
}
/// Context required to generate a workflow step resolution prompt.
#[derive(Debug, Serialize)]
pub struct StepResolutionContext {
/// The full context, including <step>...</step> tags
pub workflow_context: String,
/// The text of the specific step from the context to resolve
pub step_to_resolve: String,
}
pub struct PromptLoadingParams<'a> {
pub fs: Arc<dyn Fs>,
pub repo_path: Option<PathBuf>,
@@ -213,7 +204,7 @@ impl PromptBuilder {
Ok(())
}
pub fn generate_content_prompt(
pub fn generate_inline_transformation_prompt(
&self,
user_prompt: String,
language_name: Option<&LanguageName>,
@@ -319,8 +310,8 @@ impl PromptBuilder {
.render("terminal_assistant_prompt", &context)
}
pub fn generate_workflow_prompt(&self) -> Result<String, RenderError> {
self.handlebars.lock().render("edit_workflow", &())
pub fn generate_suggest_edits_prompt(&self) -> Result<String, RenderError> {
self.handlebars.lock().render("suggest_edits", &())
}
pub fn generate_project_slash_command_prompt(

View File

@@ -31,10 +31,11 @@ pub mod now_command;
pub mod project_command;
pub mod prompt_command;
pub mod search_command;
pub mod selection_command;
pub mod streaming_example_command;
pub mod symbols_command;
pub mod tab_command;
pub mod terminal_command;
pub mod workflow_command;
pub(crate) struct SlashCommandCompletionProvider {
cancel_flag: Mutex<Arc<AtomicBool>>,

View File

@@ -1,7 +1,8 @@
use super::create_label_for_command;
use super::{SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use feature_flags::FeatureFlag;
use futures::StreamExt;
use gpui::{AppContext, AsyncAppContext, Task, WeakView};
@@ -13,10 +14,12 @@ use language_model::{
use semantic_index::{FileSummary, SemanticDb};
use smol::channel;
use std::sync::{atomic::AtomicBool, Arc};
use ui::{BorrowAppContext, WindowContext};
use ui::{prelude::*, BorrowAppContext, WindowContext};
use util::ResultExt;
use workspace::Workspace;
use crate::slash_command::create_label_for_command;
pub struct AutoSlashCommandFeatureFlag;
impl FeatureFlag for AutoSlashCommandFeatureFlag {
@@ -34,6 +37,10 @@ impl SlashCommand for AutoCommand {
"Automatically infer what context to add".into()
}
fn icon(&self) -> IconName {
IconName::Wand
}
fn menu_text(&self) -> String {
self.description()
}
@@ -92,7 +99,7 @@ impl SlashCommand for AutoCommand {
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let Some(workspace) = workspace.upgrade() else {
return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
};
@@ -144,7 +151,8 @@ impl SlashCommand for AutoCommand {
text: prompt,
sections: Vec::new(),
run_commands_in_text: true,
})
}
.to_event_stream())
})
}
}

View File

@@ -1,6 +1,8 @@
use super::{SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Context, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use fs::Fs;
use gpui::{AppContext, Model, Task, WeakView};
use language::{BufferSnapshot, LspAdapterDelegate};
@@ -123,7 +125,7 @@ impl SlashCommand for CargoWorkspaceSlashCommand {
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let output = workspace.update(cx, |workspace, cx| {
let project = workspace.project().clone();
let fs = workspace.project().read(cx).fs().clone();
@@ -145,7 +147,8 @@ impl SlashCommand for CargoWorkspaceSlashCommand {
metadata: None,
}],
run_commands_in_text: false,
})
}
.to_event_stream())
})
});
output.unwrap_or_else(|error| Task::ready(Err(error)))

View File

@@ -1,14 +1,14 @@
use anyhow::{anyhow, Result};
use assistant_slash_command::{
AfterCompletion, ArgumentCompletion, SlashCommand, SlashCommandOutput,
SlashCommandOutputSection,
SlashCommandOutputSection, SlashCommandResult,
};
use collections::HashMap;
use context_servers::{
manager::{ContextServer, ContextServerManager},
protocol::PromptInfo,
types::Prompt,
};
use gpui::{Task, WeakView, WindowContext};
use gpui::{AppContext, Task, WeakView, WindowContext};
use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
@@ -16,13 +16,15 @@ use text::LineEnding;
use ui::{IconName, SharedString};
use workspace::Workspace;
use crate::slash_command::create_label_for_command;
pub struct ContextServerSlashCommand {
server_id: String,
prompt: PromptInfo,
prompt: Prompt,
}
impl ContextServerSlashCommand {
pub fn new(server: &Arc<ContextServer>, prompt: PromptInfo) -> Self {
pub fn new(server: &Arc<ContextServer>, prompt: Prompt) -> Self {
Self {
server_id: server.id.clone(),
prompt,
@@ -35,12 +37,28 @@ impl SlashCommand for ContextServerSlashCommand {
self.prompt.name.clone()
}
fn label(&self, cx: &AppContext) -> language::CodeLabel {
let mut parts = vec![self.prompt.name.as_str()];
if let Some(args) = &self.prompt.arguments {
if let Some(arg) = args.first() {
parts.push(arg.name.as_str());
}
}
create_label_for_command(&parts[0], &parts[1..], cx)
}
fn description(&self) -> String {
format!("Run context server command: {}", self.prompt.name)
match &self.prompt.description {
Some(desc) => desc.clone(),
None => format!("Run '{}' from {}", self.prompt.name, self.server_id),
}
}
fn menu_text(&self) -> String {
format!("Run '{}' from {}", self.prompt.name, self.server_id)
match &self.prompt.description {
Some(desc) => desc.clone(),
None => format!("Run '{}' from {}", self.prompt.name, self.server_id),
}
}
fn requires_argument(&self) -> bool {
@@ -111,7 +129,7 @@ impl SlashCommand for ContextServerSlashCommand {
_workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let server_id = self.server_id.clone();
let prompt_name = self.prompt.name.clone();
@@ -128,7 +146,28 @@ impl SlashCommand for ContextServerSlashCommand {
return Err(anyhow!("Context server not initialized"));
};
let result = protocol.run_prompt(&prompt_name, prompt_args).await?;
let mut prompt = result.prompt;
// Check that there are only user roles
if result
.messages
.iter()
.any(|msg| !matches!(msg.role, context_servers::types::SamplingRole::User))
{
return Err(anyhow!(
"Prompt contains non-user roles, which is not supported"
));
}
// Extract text from user messages into a single prompt string
let mut prompt = result
.messages
.into_iter()
.filter_map(|msg| match msg.content {
context_servers::types::SamplingContent::Text { text } => Some(text),
_ => None,
})
.collect::<Vec<String>>()
.join("\n\n");
// We must normalize the line endings here, since servers might return CR characters.
LineEnding::normalize(&mut prompt);
@@ -146,7 +185,8 @@ impl SlashCommand for ContextServerSlashCommand {
}],
text: prompt,
run_commands_in_text: false,
})
}
.to_event_stream())
})
} else {
Task::ready(Err(anyhow!("Context server not found")))
@@ -154,7 +194,7 @@ impl SlashCommand for ContextServerSlashCommand {
}
}
fn completion_argument(prompt: &PromptInfo, arguments: &[String]) -> Result<(String, String)> {
fn completion_argument(prompt: &Prompt, arguments: &[String]) -> Result<(String, String)> {
if arguments.is_empty() {
return Err(anyhow!("No arguments given"));
}
@@ -170,7 +210,7 @@ fn completion_argument(prompt: &PromptInfo, arguments: &[String]) -> Result<(Str
}
}
fn prompt_arguments(prompt: &PromptInfo, arguments: &[String]) -> Result<HashMap<String, String>> {
fn prompt_arguments(prompt: &Prompt, arguments: &[String]) -> Result<HashMap<String, String>> {
match &prompt.arguments {
Some(args) if args.len() > 1 => Err(anyhow!(
"Prompt has more than one argument, which is not supported"
@@ -199,7 +239,7 @@ fn prompt_arguments(prompt: &PromptInfo, arguments: &[String]) -> Result<HashMap
/// MCP servers can return prompts with multiple arguments. Since we only
/// support one argument, we ignore all others. This is the necessary predicate
/// for this.
pub fn acceptable_prompt(prompt: &PromptInfo) -> bool {
pub fn acceptable_prompt(prompt: &Prompt) -> bool {
match &prompt.arguments {
None => true,
Some(args) if args.len() <= 1 => true,

View File

@@ -1,7 +1,9 @@
use super::{SlashCommand, SlashCommandOutput};
use crate::prompt_library::PromptStore;
use anyhow::{anyhow, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use gpui::{Task, WeakView};
use language::{BufferSnapshot, LspAdapterDelegate};
use std::{
@@ -48,7 +50,7 @@ impl SlashCommand for DefaultSlashCommand {
_workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let store = PromptStore::global(cx);
cx.background_executor().spawn(async move {
let store = store.await?;
@@ -76,7 +78,8 @@ impl SlashCommand for DefaultSlashCommand {
}],
text,
run_commands_in_text: true,
})
}
.to_event_stream())
})
}
}

View File

@@ -2,6 +2,7 @@ use crate::slash_command::file_command::{FileCommandMetadata, FileSlashCommand};
use anyhow::{anyhow, Result};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use collections::HashSet;
use futures::future;
@@ -9,6 +10,7 @@ use gpui::{Task, WeakView, WindowContext};
use language::{BufferSnapshot, LspAdapterDelegate};
use std::sync::{atomic::AtomicBool, Arc};
use text::OffsetRangeExt;
use ui::prelude::*;
use workspace::Workspace;
pub(crate) struct DeltaSlashCommand;
@@ -26,6 +28,10 @@ impl SlashCommand for DeltaSlashCommand {
self.description()
}
fn icon(&self) -> IconName {
IconName::Diff
}
fn requires_argument(&self) -> bool {
false
}
@@ -48,7 +54,7 @@ impl SlashCommand for DeltaSlashCommand {
workspace: WeakView<Workspace>,
delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let mut paths = HashSet::default();
let mut file_command_old_outputs = Vec::new();
let mut file_command_new_outputs = Vec::new();
@@ -85,25 +91,28 @@ impl SlashCommand for DeltaSlashCommand {
.zip(file_command_new_outputs)
{
if let Ok(new_output) = new_output {
if let Some(file_command_range) = new_output.sections.first() {
let new_text = &new_output.text[file_command_range.range.clone()];
if old_text.chars().ne(new_text.chars()) {
output.sections.extend(new_output.sections.into_iter().map(
|section| SlashCommandOutputSection {
range: output.text.len() + section.range.start
..output.text.len() + section.range.end,
icon: section.icon,
label: section.label,
metadata: section.metadata,
},
));
output.text.push_str(&new_output.text);
if let Ok(new_output) = SlashCommandOutput::from_event_stream(new_output).await
{
if let Some(file_command_range) = new_output.sections.first() {
let new_text = &new_output.text[file_command_range.range.clone()];
if old_text.chars().ne(new_text.chars()) {
output.sections.extend(new_output.sections.into_iter().map(
|section| SlashCommandOutputSection {
range: output.text.len() + section.range.start
..output.text.len() + section.range.end,
icon: section.icon,
label: section.label,
metadata: section.metadata,
},
));
output.text.push_str(&new_output.text);
}
}
}
}
}
Ok(output)
Ok(output.to_event_stream())
})
}
}

View File

@@ -1,6 +1,8 @@
use super::{create_label_for_command, SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use fuzzy::{PathMatch, StringMatchCandidate};
use gpui::{AppContext, Model, Task, View, WeakView};
use language::{
@@ -19,6 +21,8 @@ use util::paths::PathMatcher;
use util::ResultExt;
use workspace::Workspace;
use crate::slash_command::create_label_for_command;
pub(crate) struct DiagnosticsSlashCommand;
impl DiagnosticsSlashCommand {
@@ -94,6 +98,10 @@ impl SlashCommand for DiagnosticsSlashCommand {
"Insert diagnostics".into()
}
fn icon(&self) -> IconName {
IconName::XCircle
}
fn menu_text(&self) -> String {
self.description()
}
@@ -167,7 +175,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let Some(workspace) = workspace.upgrade() else {
return Task::ready(Err(anyhow!("workspace was dropped")));
};
@@ -176,7 +184,11 @@ impl SlashCommand for DiagnosticsSlashCommand {
let task = collect_diagnostics(workspace.read(cx).project().clone(), options, cx);
cx.spawn(move |_| async move { task.await?.ok_or_else(|| anyhow!("No diagnostics found")) })
cx.spawn(move |_| async move {
task.await?
.map(|output| output.to_event_stream())
.ok_or_else(|| anyhow!("No diagnostics found"))
})
}
}

View File

@@ -6,6 +6,7 @@ use std::time::Duration;
use anyhow::{anyhow, bail, Result};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use gpui::{AppContext, BackgroundExecutor, Model, Task, WeakView};
use indexed_docs::{
@@ -274,7 +275,7 @@ impl SlashCommand for DocsSlashCommand {
_workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
if arguments.is_empty() {
return Task::ready(Err(anyhow!("missing an argument")));
};
@@ -355,7 +356,8 @@ impl SlashCommand for DocsSlashCommand {
})
.collect(),
run_commands_in_text: false,
})
}
.to_event_stream())
})
}
}

View File

@@ -6,6 +6,7 @@ use std::sync::Arc;
use anyhow::{anyhow, bail, Context, Result};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use futures::AsyncReadExt;
use gpui::{Task, WeakView};
@@ -133,7 +134,7 @@ impl SlashCommand for FetchSlashCommand {
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let Some(argument) = arguments.first() else {
return Task::ready(Err(anyhow!("missing URL")));
};
@@ -166,7 +167,8 @@ impl SlashCommand for FetchSlashCommand {
metadata: None,
}],
run_commands_in_text: false,
})
}
.to_event_stream())
})
}
}

View File

@@ -1,11 +1,16 @@
use super::{diagnostics_command::collect_buffer_diagnostics, SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Context as _, Result};
use assistant_slash_command::{AfterCompletion, ArgumentCompletion, SlashCommandOutputSection};
use assistant_slash_command::{
AfterCompletion, ArgumentCompletion, SlashCommand, SlashCommandContent, SlashCommandEvent,
SlashCommandOutput, SlashCommandOutputSection, SlashCommandResult,
};
use futures::channel::mpsc;
use futures::Stream;
use fuzzy::PathMatch;
use gpui::{AppContext, Model, Task, View, WeakView};
use language::{BufferSnapshot, CodeLabel, HighlightId, LineEnding, LspAdapterDelegate};
use project::{PathMatchCandidateSet, Project};
use serde::{Deserialize, Serialize};
use smol::stream::StreamExt;
use std::{
fmt::Write,
ops::{Range, RangeInclusive},
@@ -110,7 +115,7 @@ impl SlashCommand for FileSlashCommand {
}
fn description(&self) -> String {
"Insert file".into()
"Insert file and/or directory".into()
}
fn menu_text(&self) -> String {
@@ -121,6 +126,10 @@ impl SlashCommand for FileSlashCommand {
true
}
fn icon(&self) -> IconName {
IconName::File
}
fn complete_argument(
self: Arc<Self>,
arguments: &[String],
@@ -181,7 +190,7 @@ impl SlashCommand for FileSlashCommand {
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let Some(workspace) = workspace.upgrade() else {
return Task::ready(Err(anyhow!("workspace was dropped")));
};
@@ -190,7 +199,12 @@ impl SlashCommand for FileSlashCommand {
return Task::ready(Err(anyhow!("missing path")));
};
collect_files(workspace.read(cx).project().clone(), arguments, cx)
Task::ready(Ok(collect_files(
workspace.read(cx).project().clone(),
arguments,
cx,
)
.boxed()))
}
}
@@ -198,7 +212,7 @@ fn collect_files(
project: Model<Project>,
glob_inputs: &[String],
cx: &mut AppContext,
) -> Task<Result<SlashCommandOutput>> {
) -> impl Stream<Item = Result<SlashCommandEvent>> {
let Ok(matchers) = glob_inputs
.into_iter()
.map(|glob_input| {
@@ -207,7 +221,7 @@ fn collect_files(
})
.collect::<anyhow::Result<Vec<custom_path_matcher::PathMatcher>>>()
else {
return Task::ready(Err(anyhow!("invalid path")));
return futures::stream::once(async { Err(anyhow!("invalid path")) }).boxed();
};
let project_handle = project.downgrade();
@@ -217,11 +231,11 @@ fn collect_files(
.map(|worktree| worktree.read(cx).snapshot())
.collect::<Vec<_>>();
let (events_tx, events_rx) = mpsc::unbounded();
cx.spawn(|mut cx| async move {
let mut output = SlashCommandOutput::default();
for snapshot in snapshots {
let worktree_id = snapshot.id();
let mut directory_stack: Vec<(Arc<Path>, String, usize)> = Vec::new();
let mut directory_stack: Vec<Arc<Path>> = Vec::new();
let mut folded_directory_names_stack = Vec::new();
let mut is_top_level_directory = true;
@@ -237,17 +251,19 @@ fn collect_files(
continue;
}
while let Some((dir, _, _)) = directory_stack.last() {
while let Some(dir) = directory_stack.last() {
if entry.path.starts_with(dir) {
break;
}
let (_, entry_name, start) = directory_stack.pop().unwrap();
output.sections.push(build_entry_output_section(
start..output.text.len().saturating_sub(1),
Some(&PathBuf::from(entry_name)),
true,
None,
));
directory_stack.pop().unwrap();
events_tx
.unbounded_send(Ok(SlashCommandEvent::EndSection { metadata: None }))?;
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
SlashCommandContent::Text {
text: "\n".into(),
run_commands_in_text: false,
},
)))?;
}
let filename = entry
@@ -279,23 +295,46 @@ fn collect_files(
continue;
}
let prefix_paths = folded_directory_names_stack.drain(..).as_slice().join("/");
let entry_start = output.text.len();
if prefix_paths.is_empty() {
if is_top_level_directory {
output
.text
.push_str(&path_including_worktree_name.to_string_lossy());
let label = if is_top_level_directory {
is_top_level_directory = false;
path_including_worktree_name.to_string_lossy().to_string()
} else {
output.text.push_str(&filename);
}
directory_stack.push((entry.path.clone(), filename, entry_start));
filename
};
events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
icon: IconName::Folder,
label: label.clone().into(),
metadata: None,
}))?;
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
SlashCommandContent::Text {
text: label,
run_commands_in_text: false,
},
)))?;
directory_stack.push(entry.path.clone());
} else {
let entry_name = format!("{}/{}", prefix_paths, &filename);
output.text.push_str(&entry_name);
directory_stack.push((entry.path.clone(), entry_name, entry_start));
events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
icon: IconName::Folder,
label: entry_name.clone().into(),
metadata: None,
}))?;
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
SlashCommandContent::Text {
text: entry_name,
run_commands_in_text: false,
},
)))?;
directory_stack.push(entry.path.clone());
}
output.text.push('\n');
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
SlashCommandContent::Text {
text: "\n".into(),
run_commands_in_text: false,
},
)))?;
} else if entry.is_file() {
let Some(open_buffer_task) = project_handle
.update(&mut cx, |project, cx| {
@@ -306,6 +345,7 @@ fn collect_files(
continue;
};
if let Some(buffer) = open_buffer_task.await.log_err() {
let mut output = SlashCommandOutput::default();
let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot())?;
append_buffer_to_output(
&snapshot,
@@ -313,33 +353,24 @@ fn collect_files(
&mut output,
)
.log_err();
let mut buffer_events = output.to_event_stream();
while let Some(event) = buffer_events.next().await {
events_tx.unbounded_send(event)?;
}
}
}
}
while let Some((dir, entry, start)) = directory_stack.pop() {
if directory_stack.is_empty() {
let mut root_path = PathBuf::new();
root_path.push(snapshot.root_name());
root_path.push(&dir);
output.sections.push(build_entry_output_section(
start..output.text.len(),
Some(&root_path),
true,
None,
));
} else {
output.sections.push(build_entry_output_section(
start..output.text.len(),
Some(&PathBuf::from(entry.as_str())),
true,
None,
));
}
while let Some(_) = directory_stack.pop() {
events_tx.unbounded_send(Ok(SlashCommandEvent::EndSection { metadata: None }))?;
}
}
Ok(output)
anyhow::Ok(())
})
.detach_and_log_err(cx);
events_rx.boxed()
}
pub fn codeblock_fence_for_path(
@@ -510,8 +541,6 @@ pub fn append_buffer_to_output(
output.text.push('\n');
let section_ix = output.sections.len();
collect_buffer_diagnostics(output, buffer, false);
output.sections.insert(
section_ix,
build_entry_output_section(prev_len..output.text.len(), path, false, None),
@@ -524,11 +553,14 @@ pub fn append_buffer_to_output(
#[cfg(test)]
mod test {
use assistant_slash_command::SlashCommandOutput;
use fs::FakeFs;
use gpui::TestAppContext;
use pretty_assertions::assert_eq;
use project::Project;
use serde_json::json;
use settings::SettingsStore;
use smol::stream::StreamExt;
use crate::slash_command::file_command::collect_files;
@@ -569,8 +601,9 @@ mod test {
let project = Project::test(fs, ["/root".as_ref()], cx).await;
let result_1 = cx
.update(|cx| collect_files(project.clone(), &["root/dir".to_string()], cx))
let result_1 =
cx.update(|cx| collect_files(project.clone(), &["root/dir".to_string()], cx));
let result_1 = SlashCommandOutput::from_event_stream(result_1.boxed())
.await
.unwrap();
@@ -578,17 +611,17 @@ mod test {
// 4 files + 2 directories
assert_eq!(result_1.sections.len(), 6);
let result_2 = cx
.update(|cx| collect_files(project.clone(), &["root/dir/".to_string()], cx))
let result_2 =
cx.update(|cx| collect_files(project.clone(), &["root/dir/".to_string()], cx));
let result_2 = SlashCommandOutput::from_event_stream(result_2.boxed())
.await
.unwrap();
assert_eq!(result_1, result_2);
let result = cx
.update(|cx| collect_files(project.clone(), &["root/dir*".to_string()], cx))
.await
.unwrap();
let result =
cx.update(|cx| collect_files(project.clone(), &["root/dir*".to_string()], cx).boxed());
let result = SlashCommandOutput::from_event_stream(result).await.unwrap();
assert!(result.text.starts_with("root/dir"));
// 5 files + 2 directories
@@ -631,8 +664,9 @@ mod test {
let project = Project::test(fs, ["/zed".as_ref()], cx).await;
let result = cx
.update(|cx| collect_files(project.clone(), &["zed/assets/themes".to_string()], cx))
let result =
cx.update(|cx| collect_files(project.clone(), &["zed/assets/themes".to_string()], cx));
let result = SlashCommandOutput::from_event_stream(result.boxed())
.await
.unwrap();
@@ -692,8 +726,9 @@ mod test {
let project = Project::test(fs, ["/zed".as_ref()], cx).await;
let result = cx
.update(|cx| collect_files(project.clone(), &["zed/assets/themes".to_string()], cx))
let result =
cx.update(|cx| collect_files(project.clone(), &["zed/assets/themes".to_string()], cx));
let result = SlashCommandOutput::from_event_stream(result.boxed())
.await
.unwrap();
@@ -716,6 +751,8 @@ mod test {
assert_eq!(result.sections[6].label, "summercamp");
assert_eq!(result.sections[7].label, "zed/assets/themes");
assert_eq!(result.text, "zed/assets/themes\n```zed/assets/themes/LICENSE\n1\n```\n\nsummercamp\n```zed/assets/themes/summercamp/LICENSE\n1\n```\n\nsubdir\n```zed/assets/themes/summercamp/subdir/LICENSE\n1\n```\n\nsubsubdir\n```zed/assets/themes/summercamp/subdir/subsubdir/LICENSE\n3\n```\n\n");
// Ensure that the project lasts until after the last await
drop(project);
}

View File

@@ -4,6 +4,7 @@ use std::sync::Arc;
use anyhow::Result;
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use chrono::Local;
use gpui::{Task, WeakView};
@@ -48,7 +49,7 @@ impl SlashCommand for NowSlashCommand {
_workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
_cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let now = Local::now();
let text = format!("Today is {now}.", now = now.to_rfc2822());
let range = 0..text.len();
@@ -62,6 +63,7 @@ impl SlashCommand for NowSlashCommand {
metadata: None,
}],
run_commands_in_text: false,
}))
}
.to_event_stream()))
}
}

View File

@@ -4,7 +4,7 @@ use super::{
};
use crate::PromptBuilder;
use anyhow::{anyhow, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection, SlashCommandResult};
use feature_flags::FeatureFlag;
use gpui::{AppContext, Task, WeakView, WindowContext};
use language::{Anchor, CodeLabel, LspAdapterDelegate};
@@ -24,7 +24,8 @@ use std::{
ops::DerefMut,
sync::{atomic::AtomicBool, Arc},
};
use ui::{BorrowAppContext as _, IconName};
use ui::prelude::*;
use workspace::Workspace;
pub struct ProjectSlashCommand {
@@ -50,6 +51,10 @@ impl SlashCommand for ProjectSlashCommand {
"Generate a semantic search based on context".into()
}
fn icon(&self) -> IconName {
IconName::Folder
}
fn menu_text(&self) -> String {
self.description()
}
@@ -76,7 +81,7 @@ impl SlashCommand for ProjectSlashCommand {
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let model_registry = LanguageModelRegistry::read_global(cx);
let current_model = model_registry.active_model();
let prompt_builder = self.prompt_builder.clone();
@@ -162,7 +167,8 @@ impl SlashCommand for ProjectSlashCommand {
text: output,
sections,
run_commands_in_text: true,
})
}
.to_event_stream())
})
.await
})

View File

@@ -1,7 +1,9 @@
use super::{SlashCommand, SlashCommandOutput};
use crate::prompt_library::PromptStore;
use anyhow::{anyhow, Context, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use gpui::{Task, WeakView};
use language::{BufferSnapshot, LspAdapterDelegate};
use std::sync::{atomic::AtomicBool, Arc};
@@ -19,6 +21,10 @@ impl SlashCommand for PromptSlashCommand {
"Insert prompt from library".into()
}
fn icon(&self) -> IconName {
IconName::Library
}
fn menu_text(&self) -> String {
self.description()
}
@@ -61,7 +67,7 @@ impl SlashCommand for PromptSlashCommand {
_workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let title = arguments.to_owned().join(" ");
if title.trim().is_empty() {
return Task::ready(Err(anyhow!("missing prompt name")));
@@ -100,7 +106,8 @@ impl SlashCommand for PromptSlashCommand {
metadata: None,
}],
run_commands_in_text: true,
})
}
.to_event_stream())
})
}
}

View File

@@ -1,10 +1,8 @@
use super::{
create_label_for_command,
file_command::{build_entry_output_section, codeblock_fence_for_path},
SlashCommand, SlashCommandOutput,
};
use anyhow::Result;
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use feature_flags::FeatureFlag;
use gpui::{AppContext, Task, WeakView};
use language::{CodeLabel, LspAdapterDelegate};
@@ -16,10 +14,17 @@ use std::{
use ui::{prelude::*, IconName};
use workspace::Workspace;
use crate::slash_command::create_label_for_command;
use crate::slash_command::file_command::{build_entry_output_section, codeblock_fence_for_path};
pub(crate) struct SearchSlashCommandFeatureFlag;
impl FeatureFlag for SearchSlashCommandFeatureFlag {
const NAME: &'static str = "search-slash-command";
fn enabled_for_staff() -> bool {
false
}
}
pub(crate) struct SearchSlashCommand;
@@ -37,6 +42,10 @@ impl SlashCommand for SearchSlashCommand {
"Search your project semantically".into()
}
fn icon(&self) -> IconName {
IconName::SearchCode
}
fn menu_text(&self) -> String {
self.description()
}
@@ -63,7 +72,7 @@ impl SlashCommand for SearchSlashCommand {
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let Some(workspace) = workspace.upgrade() else {
return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
};
@@ -129,6 +138,7 @@ impl SlashCommand for SearchSlashCommand {
sections,
run_commands_in_text: false,
}
.to_event_stream()
})
.await;

View File

@@ -0,0 +1,98 @@
use crate::assistant_panel::selections_creases;
use anyhow::{anyhow, Result};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandContent, SlashCommandEvent,
SlashCommandOutputSection, SlashCommandResult,
};
use futures::StreamExt;
use gpui::{AppContext, Task, WeakView};
use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use ui::{IconName, SharedString, WindowContext};
use workspace::Workspace;
pub(crate) struct SelectionCommand;
impl SlashCommand for SelectionCommand {
fn name(&self) -> String {
"selection".into()
}
fn label(&self, _cx: &AppContext) -> CodeLabel {
CodeLabel::plain(self.name(), None)
}
fn description(&self) -> String {
"Insert editor selection".into()
}
fn icon(&self) -> IconName {
IconName::Quote
}
fn menu_text(&self) -> String {
self.description()
}
fn requires_argument(&self) -> bool {
false
}
fn accepts_arguments(&self) -> bool {
true
}
fn complete_argument(
self: Arc<Self>,
_arguments: &[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")))
}
fn run(
self: Arc<Self>,
_arguments: &[String],
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
_context_buffer: BufferSnapshot,
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<SlashCommandResult> {
let mut events = vec![];
let Some(creases) = workspace
.update(cx, selections_creases)
.unwrap_or_else(|e| {
events.push(Err(e));
None
})
else {
return Task::ready(Err(anyhow!("no active selection")));
};
for (text, title) in creases {
events.push(Ok(SlashCommandEvent::StartSection {
icon: IconName::TextSnippet,
label: SharedString::from(title),
metadata: None,
}));
events.push(Ok(SlashCommandEvent::Content(SlashCommandContent::Text {
text,
run_commands_in_text: false,
})));
events.push(Ok(SlashCommandEvent::EndSection { metadata: None }));
events.push(Ok(SlashCommandEvent::Content(SlashCommandContent::Text {
text: "\n".to_string(),
run_commands_in_text: false,
})));
}
let result = futures::stream::iter(events).boxed();
Task::ready(Ok(result))
}
}

View File

@@ -0,0 +1,136 @@
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::time::Duration;
use anyhow::Result;
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandContent, SlashCommandEvent,
SlashCommandOutputSection, SlashCommandResult,
};
use feature_flags::FeatureFlag;
use futures::channel::mpsc;
use gpui::{Task, WeakView};
use language::{BufferSnapshot, LspAdapterDelegate};
use smol::stream::StreamExt;
use smol::Timer;
use ui::prelude::*;
use workspace::Workspace;
pub struct StreamingExampleSlashCommandFeatureFlag;
impl FeatureFlag for StreamingExampleSlashCommandFeatureFlag {
const NAME: &'static str = "streaming-example-slash-command";
}
pub(crate) struct StreamingExampleSlashCommand;
impl SlashCommand for StreamingExampleSlashCommand {
fn name(&self) -> String {
"streaming-example".into()
}
fn description(&self) -> String {
"An example slash command that showcases streaming.".into()
}
fn menu_text(&self) -> String {
self.description()
}
fn requires_argument(&self) -> bool {
false
}
fn complete_argument(
self: Arc<Self>,
_arguments: &[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>,
_arguments: &[String],
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
_context_buffer: BufferSnapshot,
_workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<SlashCommandResult> {
let (events_tx, events_rx) = mpsc::unbounded();
cx.background_executor()
.spawn(async move {
events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
icon: IconName::FileRust,
label: "Section 1".into(),
metadata: None,
}))?;
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
SlashCommandContent::Text {
text: "Hello".into(),
run_commands_in_text: false,
},
)))?;
events_tx.unbounded_send(Ok(SlashCommandEvent::EndSection { metadata: None }))?;
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
SlashCommandContent::Text {
text: "\n".into(),
run_commands_in_text: false,
},
)))?;
Timer::after(Duration::from_secs(1)).await;
events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
icon: IconName::FileRust,
label: "Section 2".into(),
metadata: None,
}))?;
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
SlashCommandContent::Text {
text: "World".into(),
run_commands_in_text: false,
},
)))?;
events_tx.unbounded_send(Ok(SlashCommandEvent::EndSection { metadata: None }))?;
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
SlashCommandContent::Text {
text: "\n".into(),
run_commands_in_text: false,
},
)))?;
for n in 1..=10 {
Timer::after(Duration::from_secs(1)).await;
events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection {
icon: IconName::StarFilled,
label: format!("Section {n}").into(),
metadata: None,
}))?;
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
SlashCommandContent::Text {
text: "lorem ipsum ".repeat(n).trim().into(),
run_commands_in_text: false,
},
)))?;
events_tx
.unbounded_send(Ok(SlashCommandEvent::EndSection { metadata: None }))?;
events_tx.unbounded_send(Ok(SlashCommandEvent::Content(
SlashCommandContent::Text {
text: "\n".into(),
run_commands_in_text: false,
},
)))?;
}
anyhow::Ok(())
})
.detach_and_log_err(cx);
Task::ready(Ok(events_rx.boxed()))
}
}

View File

@@ -1,6 +1,8 @@
use super::{SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Context as _, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use editor::Editor;
use gpui::{Task, WeakView};
use language::{BufferSnapshot, LspAdapterDelegate};
@@ -20,6 +22,10 @@ impl SlashCommand for OutlineSlashCommand {
"Insert symbols for active tab".into()
}
fn icon(&self) -> IconName {
IconName::ListTree
}
fn menu_text(&self) -> String {
self.description()
}
@@ -46,7 +52,7 @@ impl SlashCommand for OutlineSlashCommand {
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let output = workspace.update(cx, |workspace, cx| {
let Some(active_item) = workspace.active_item(cx) else {
return Task::ready(Err(anyhow!("no active tab")));
@@ -83,7 +89,8 @@ impl SlashCommand for OutlineSlashCommand {
}],
text: outline_text,
run_commands_in_text: false,
})
}
.to_event_stream())
})
});

View File

@@ -1,6 +1,8 @@
use super::{file_command::append_buffer_to_output, SlashCommand, SlashCommandOutput};
use anyhow::{Context, Result};
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use collections::{HashMap, HashSet};
use editor::Editor;
use futures::future::join_all;
@@ -10,10 +12,12 @@ use std::{
path::PathBuf,
sync::{atomic::AtomicBool, Arc},
};
use ui::{ActiveTheme, WindowContext};
use ui::{prelude::*, ActiveTheme, WindowContext};
use util::ResultExt;
use workspace::Workspace;
use crate::slash_command::file_command::append_buffer_to_output;
pub(crate) struct TabSlashCommand;
const ALL_TABS_COMPLETION_ITEM: &str = "all";
@@ -27,6 +31,10 @@ impl SlashCommand for TabSlashCommand {
"Insert open tabs (active tab by default)".to_owned()
}
fn icon(&self) -> IconName {
IconName::FileTree
}
fn menu_text(&self) -> String {
self.description()
}
@@ -132,7 +140,7 @@ impl SlashCommand for TabSlashCommand {
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let tab_items_search = tab_items_for_queries(
Some(workspace),
arguments,
@@ -146,7 +154,7 @@ impl SlashCommand for TabSlashCommand {
for (full_path, buffer, _) in tab_items_search.await? {
append_buffer_to_output(&buffer, full_path.as_deref(), &mut output).log_err();
}
Ok(output)
Ok(output.to_event_stream())
})
}
}

View File

@@ -4,6 +4,7 @@ use std::sync::Arc;
use anyhow::Result;
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
SlashCommandResult,
};
use gpui::{AppContext, Task, View, WeakView};
use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate};
@@ -32,6 +33,10 @@ impl SlashCommand for TerminalSlashCommand {
"Insert terminal output".into()
}
fn icon(&self) -> IconName {
IconName::Terminal
}
fn menu_text(&self) -> String {
self.description()
}
@@ -62,7 +67,7 @@ impl SlashCommand for TerminalSlashCommand {
workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
) -> Task<SlashCommandResult> {
let Some(workspace) = workspace.upgrade() else {
return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
};
@@ -96,7 +101,8 @@ impl SlashCommand for TerminalSlashCommand {
metadata: None,
}],
run_commands_in_text: false,
}))
}
.to_event_stream()))
}
}

View File

@@ -1,79 +0,0 @@
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::{BufferSnapshot, 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 prompt to opt into the edit workflow".into()
}
fn menu_text(&self) -> String {
self.description()
}
fn requires_argument(&self) -> bool {
false
}
fn complete_argument(
self: Arc<Self>,
_arguments: &[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>,
_arguments: &[String],
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
_context_buffer: BufferSnapshot,
_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(),
metadata: None,
}],
run_commands_in_text: false,
})
})
}
}

View File

@@ -1,17 +1,10 @@
use std::sync::Arc;
use assistant_slash_command::SlashCommandRegistry;
use gpui::AnyElement;
use gpui::DismissEvent;
use gpui::WeakView;
use picker::PickerEditorPosition;
use ui::ListItemSpacing;
use gpui::SharedString;
use gpui::Task;
use picker::{Picker, PickerDelegate};
use ui::{prelude::*, ListItem, PopoverMenu, PopoverTrigger};
use gpui::{AnyElement, DismissEvent, SharedString, Task, WeakView};
use picker::{Picker, PickerDelegate, PickerEditorPosition};
use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverTrigger};
use crate::assistant_panel::ContextEditor;
@@ -27,6 +20,7 @@ struct SlashCommandInfo {
name: SharedString,
description: SharedString,
args: Option<SharedString>,
icon: IconName,
}
#[derive(Clone)]
@@ -145,16 +139,20 @@ impl PickerDelegate for SlashCommandDelegate {
}
ret
}
fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
if let Some(command) = self.filtered_commands.get(self.selected_index) {
if let SlashCommandEntry::Info(info) = command {
self.active_context_editor
.update(cx, |context_editor, cx| {
context_editor.insert_command(&info.name, cx)
})
.ok();
} else if let SlashCommandEntry::Advert { on_confirm, .. } = command {
on_confirm(cx);
match command {
SlashCommandEntry::Info(info) => {
self.active_context_editor
.update(cx, |context_editor, cx| {
context_editor.insert_command(&info.name, cx)
})
.ok();
}
SlashCommandEntry::Advert { on_confirm, .. } => {
on_confirm(cx);
}
}
cx.emit(DismissEvent);
}
@@ -178,53 +176,51 @@ impl PickerDelegate for SlashCommandDelegate {
SlashCommandEntry::Info(info) => Some(
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.spacing(ListItemSpacing::Dense)
.selected(selected)
.child(
h_flex()
v_flex()
.group(format!("command-entry-label-{ix}"))
.w_full()
.min_w(px(250.))
.child(
v_flex()
.child(
h_flex()
.child(div().font_buffer(cx).child({
let mut label = format!("/{}", info.name);
if let Some(args) =
info.args.as_ref().filter(|_| selected)
{
label.push_str(&args);
}
Label::new(label).size(LabelSize::Small)
}))
.children(info.args.clone().filter(|_| !selected).map(
|args| {
div()
.font_buffer(cx)
.child(
Label::new(args)
.size(LabelSize::Small)
.color(Color::Muted),
)
.visible_on_hover(format!(
"command-entry-label-{ix}"
))
},
)),
)
.child(
Label::new(info.description.clone())
.size(LabelSize::Small)
.color(Color::Muted),
),
h_flex()
.gap_1p5()
.child(Icon::new(info.icon).size(IconSize::XSmall))
.child(div().font_buffer(cx).child({
let mut label = format!("{}", info.name);
if let Some(args) = info.args.as_ref().filter(|_| selected)
{
label.push_str(&args);
}
Label::new(label).size(LabelSize::Small)
}))
.children(info.args.clone().filter(|_| !selected).map(
|args| {
div()
.font_buffer(cx)
.child(
Label::new(args)
.size(LabelSize::Small)
.color(Color::Muted),
)
.visible_on_hover(format!(
"command-entry-label-{ix}"
))
},
)),
)
.child(
Label::new(info.description.clone())
.size(LabelSize::Small)
.color(Color::Muted),
),
),
),
SlashCommandEntry::Advert { renderer, .. } => Some(
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.spacing(ListItemSpacing::Dense)
.selected(selected)
.child(renderer(cx)),
),
@@ -251,24 +247,40 @@ impl<T: PopoverTrigger> RenderOnce for SlashCommandSelector<T> {
name: command_name.into(),
description: menu_text,
args,
icon: command.icon(),
}))
})
.chain([SlashCommandEntry::Advert {
name: "create-your-command".into(),
renderer: |cx| {
v_flex()
.w_full()
.child(
h_flex()
.w_full()
.font_buffer(cx)
.items_center()
.gap_1()
.child(div().font_buffer(cx).child(
Label::new("create-your-command").size(LabelSize::Small),
))
.child(Icon::new(IconName::ArrowUpRight).size(IconSize::XSmall)),
.justify_between()
.child(
h_flex()
.items_center()
.gap_1p5()
.child(Icon::new(IconName::Plus).size(IconSize::XSmall))
.child(
div().font_buffer(cx).child(
Label::new("create-your-command")
.size(LabelSize::Small),
),
),
)
.child(
Icon::new(IconName::ArrowUpRight)
.size(IconSize::XSmall)
.color(Color::Muted),
),
)
.child(
Label::new("Learn how to create a custom command")
Label::new("Create your custom command")
.size(LabelSize::Small)
.color(Color::Muted),
)

View File

@@ -28,13 +28,36 @@ impl Matrix {
self.cols = cols;
}
fn swap_columns(&mut self, col1: usize, col2: usize) {
if col1 == col2 {
return;
}
if col1 >= self.cols {
panic!("column out of bounds");
}
if col2 >= self.cols {
panic!("column out of bounds");
}
unsafe {
let ptr = self.cells.as_mut_ptr();
std::ptr::swap_nonoverlapping(
ptr.add(col1 * self.rows),
ptr.add(col2 * self.rows),
self.rows,
);
}
}
fn get(&self, row: usize, col: usize) -> f64 {
if row >= self.rows {
panic!("row out of bounds")
}
if col >= self.cols {
panic!("col out of bounds")
panic!("column out of bounds")
}
self.cells[col * self.rows + row]
}
@@ -45,7 +68,7 @@ impl Matrix {
}
if col >= self.cols {
panic!("col out of bounds")
panic!("column out of bounds")
}
self.cells[col * self.rows + row] = value;
@@ -106,26 +129,32 @@ impl StreamingDiff {
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);
self.scores.swap_columns(0, self.scores.cols - 1);
self.scores
.resize(self.old.len() + 1, self.new.len() - self.new_text_ix + 1);
self.equal_runs.retain(|(_i, j), _| *j == self.new_text_ix);
for j in self.new_text_ix + 1..=self.new.len() {
self.scores.set(0, j, j as f64 * Self::INSERTION_SCORE);
let relative_j = j - self.new_text_ix;
self.scores
.set(0, relative_j, j as f64 * Self::INSERTION_SCORE);
for i in 1..=self.old.len() {
let insertion_score = self.scores.get(i, j - 1) + Self::INSERTION_SCORE;
let deletion_score = self.scores.get(i - 1, j) + Self::DELETION_SCORE;
let insertion_score = self.scores.get(i, relative_j - 1) + Self::INSERTION_SCORE;
let deletion_score = self.scores.get(i - 1, relative_j) + Self::DELETION_SCORE;
let equality_score = if self.old[i - 1] == self.new[j - 1] {
let mut equal_run = self.equal_runs.get(&(i - 1, j - 1)).copied().unwrap_or(0);
equal_run += 1;
self.equal_runs.insert((i, j), equal_run);
let exponent = cmp::min(equal_run as i32 / 4, Self::MAX_EQUALITY_EXPONENT);
self.scores.get(i - 1, j - 1) + Self::EQUALITY_BASE.powi(exponent)
self.scores.get(i - 1, relative_j - 1) + Self::EQUALITY_BASE.powi(exponent)
} else {
f64::NEG_INFINITY
};
let score = insertion_score.max(deletion_score).max(equality_score);
self.scores.set(i, j, score);
self.scores.set(i, relative_j, score);
}
}
@@ -133,7 +162,7 @@ impl StreamingDiff {
let mut next_old_text_ix = self.old_text_ix;
let next_new_text_ix = self.new.len();
for i in self.old_text_ix..=self.old.len() {
let score = self.scores.get(i, next_new_text_ix);
let score = self.scores.get(i, next_new_text_ix - self.new_text_ix);
if score > max_score {
max_score = score;
next_old_text_ix = i;
@@ -174,7 +203,9 @@ impl StreamingDiff {
let (prev_i, prev_j) = [insertion_score, deletion_score, equality_score]
.iter()
.max_by_key(|cell| cell.map(|(i, j)| OrderedFloat(self.scores.get(i, j))))
.max_by_key(|cell| {
cell.map(|(i, j)| OrderedFloat(self.scores.get(i, j - self.new_text_ix)))
})
.unwrap()
.unwrap();

View File

@@ -1,6 +1,6 @@
use crate::{
humanize_token_count, prompts::PromptBuilder, AssistantPanel, AssistantPanelEvent,
ModelSelector, DEFAULT_CONTEXT_LINES,
ModelSelector, RequestType, DEFAULT_CONTEXT_LINES,
};
use anyhow::{Context as _, Result};
use client::telemetry::Telemetry;
@@ -17,7 +17,8 @@ use gpui::{
};
use language::Buffer;
use language_model::{
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
logging::report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
LanguageModelRequestMessage, Role,
};
use settings::Settings;
use std::{
@@ -251,7 +252,7 @@ impl TerminalInlineAssistant {
.read(cx)
.active_context(cx)?
.read(cx)
.to_completion_request(cx),
.to_completion_request(RequestType::Chat, cx),
)
})
} else {
@@ -306,6 +307,33 @@ impl TerminalInlineAssistant {
this.focus_handle(cx).focus(cx);
})
.log_err();
if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
let codegen = assist.codegen.read(cx);
let executor = cx.background_executor().clone();
report_assistant_event(
AssistantEvent {
conversation_id: None,
kind: AssistantKind::InlineTerminal,
message_id: codegen.message_id.clone(),
phase: if undo {
AssistantPhase::Rejected
} else {
AssistantPhase::Accepted
},
model: model.telemetry_id(),
model_provider: model.provider_id().to_string(),
response_latency: None,
error_message: None,
language_name: None,
},
codegen.telemetry.clone(),
cx.http_client(),
model.api_key(cx),
&executor,
);
}
assist.codegen.update(cx, |codegen, cx| {
if undo {
codegen.undo(cx);
@@ -1016,6 +1044,7 @@ pub struct Codegen {
telemetry: Option<Arc<Telemetry>>,
terminal: Model<Terminal>,
generation: Task<()>,
message_id: Option<String>,
transaction: Option<TerminalTransaction>,
}
@@ -1026,6 +1055,7 @@ impl Codegen {
telemetry,
status: CodegenStatus::Idle,
generation: Task::ready(()),
message_id: None,
transaction: None,
}
}
@@ -1035,6 +1065,8 @@ impl Codegen {
return;
};
let model_api_key = model.api_key(cx);
let http_client = cx.http_client();
let telemetry = self.telemetry.clone();
self.status = CodegenStatus::Pending;
self.transaction = Some(TerminalTransaction::start(self.terminal.clone()));
@@ -1043,44 +1075,62 @@ impl Codegen {
let model_provider_id = model.provider_id();
let response = model.stream_completion_text(prompt, &cx).await;
let generate = async {
let message_id = response
.as_ref()
.ok()
.and_then(|response| response.message_id.clone());
let (mut hunks_tx, mut hunks_rx) = mpsc::channel(1);
let task = cx.background_executor().spawn(async move {
let mut response_latency = None;
let request_start = Instant::now();
let task = async {
let mut chunks = response?;
while let Some(chunk) = chunks.next().await {
if response_latency.is_none() {
response_latency = Some(request_start.elapsed());
let task = cx.background_executor().spawn({
let message_id = message_id.clone();
let executor = cx.background_executor().clone();
async move {
let mut response_latency = None;
let request_start = Instant::now();
let task = async {
let mut chunks = response?.stream;
while let Some(chunk) = chunks.next().await {
if response_latency.is_none() {
response_latency = Some(request_start.elapsed());
}
let chunk = chunk?;
hunks_tx.send(chunk).await?;
}
let chunk = chunk?;
hunks_tx.send(chunk).await?;
}
anyhow::Ok(())
};
let result = task.await;
let error_message = result.as_ref().err().map(|error| error.to_string());
report_assistant_event(
AssistantEvent {
conversation_id: None,
kind: AssistantKind::InlineTerminal,
message_id,
phase: AssistantPhase::Response,
model: model_telemetry_id,
model_provider: model_provider_id.to_string(),
response_latency,
error_message,
language_name: None,
},
telemetry,
http_client,
model_api_key,
&executor,
);
result?;
anyhow::Ok(())
};
let result = task.await;
let error_message = result.as_ref().err().map(|error| error.to_string());
if let Some(telemetry) = telemetry {
telemetry.report_assistant_event(AssistantEvent {
conversation_id: None,
kind: AssistantKind::Inline,
phase: AssistantPhase::Response,
model: model_telemetry_id,
model_provider: model_provider_id.to_string(),
response_latency,
error_message,
language_name: None,
});
}
result?;
anyhow::Ok(())
});
this.update(&mut cx, |this, _| {
this.message_id = message_id;
})?;
while let Some(hunk) = hunks_rx.next().await {
this.update(&mut cx, |this, cx| {
if let Some(transaction) = &mut this.transaction {

View File

@@ -1 +1,2 @@
pub mod context_server_tool;
pub mod now_tool;

View File

@@ -0,0 +1,82 @@
use anyhow::{anyhow, bail};
use assistant_tool::Tool;
use context_servers::manager::ContextServerManager;
use context_servers::types;
use gpui::Task;
pub struct ContextServerTool {
server_id: String,
tool: types::Tool,
}
impl ContextServerTool {
pub fn new(server_id: impl Into<String>, tool: types::Tool) -> Self {
Self {
server_id: server_id.into(),
tool,
}
}
}
impl Tool for ContextServerTool {
fn name(&self) -> String {
self.tool.name.clone()
}
fn description(&self) -> String {
self.tool.description.clone().unwrap_or_default()
}
fn input_schema(&self) -> serde_json::Value {
match &self.tool.input_schema {
serde_json::Value::Null => {
serde_json::json!({ "type": "object", "properties": [] })
}
serde_json::Value::Object(map) if map.is_empty() => {
serde_json::json!({ "type": "object", "properties": [] })
}
_ => self.tool.input_schema.clone(),
}
}
fn run(
self: std::sync::Arc<Self>,
input: serde_json::Value,
_workspace: gpui::WeakView<workspace::Workspace>,
cx: &mut ui::WindowContext,
) -> gpui::Task<gpui::Result<String>> {
let manager = ContextServerManager::global(cx);
let manager = manager.read(cx);
if let Some(server) = manager.get_server(&self.server_id) {
cx.foreground_executor().spawn({
let tool_name = self.tool.name.clone();
async move {
let Some(protocol) = server.client.read().clone() else {
bail!("Context server not initialized");
};
let arguments = if let serde_json::Value::Object(map) = input {
Some(map.into_iter().collect())
} else {
None
};
log::trace!(
"Running tool: {} with arguments: {:?}",
tool_name,
arguments
);
let response = protocol.run_tool(tool_name, arguments).await?;
let tool_result = match response.tool_result {
serde_json::Value::String(s) => s,
_ => serde_json::to_string(&response.tool_result)?,
};
Ok(tool_result)
}
})
} else {
Task::ready(Err(anyhow!("Context server not found")))
}
}
}

View File

@@ -1,507 +0,0 @@
use crate::{AssistantPanel, InlineAssistId, InlineAssistant};
use anyhow::{anyhow, Context as _, Result};
use collections::HashMap;
use editor::Editor;
use gpui::AsyncAppContext;
use gpui::{Model, Task, UpdateGlobal as _, View, WeakView, WindowContext};
use language::{Buffer, BufferSnapshot};
use project::{Project, ProjectPath};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::{ops::Range, path::Path, sync::Arc};
use text::Bias;
use workspace::Workspace;
#[derive(Debug)]
pub(crate) struct WorkflowStep {
pub range: Range<language::Anchor>,
pub leading_tags_end: text::Anchor,
pub trailing_tag_start: Option<text::Anchor>,
pub edits: Arc<[Result<WorkflowStepEdit>]>,
pub resolution_task: Option<Task<()>>,
pub resolution: Option<Arc<Result<WorkflowStepResolution>>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct WorkflowStepEdit {
pub path: String,
pub kind: WorkflowStepEditKind,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct WorkflowStepResolution {
pub title: String,
pub suggestion_groups: HashMap<Model<Buffer>, Vec<WorkflowSuggestionGroup>>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WorkflowSuggestionGroup {
pub context_range: Range<language::Anchor>,
pub suggestions: Vec<WorkflowSuggestion>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum WorkflowSuggestion {
Update {
range: Range<language::Anchor>,
description: String,
},
CreateFile {
description: String,
},
InsertBefore {
position: language::Anchor,
description: String,
},
InsertAfter {
position: language::Anchor,
description: String,
},
Delete {
range: Range<language::Anchor>,
},
}
impl WorkflowSuggestion {
pub fn range(&self) -> Range<language::Anchor> {
match self {
Self::Update { range, .. } => range.clone(),
Self::CreateFile { .. } => language::Anchor::MIN..language::Anchor::MAX,
Self::InsertBefore { position, .. } | Self::InsertAfter { position, .. } => {
*position..*position
}
Self::Delete { range, .. } => range.clone(),
}
}
pub fn description(&self) -> Option<&str> {
match self {
Self::Update { description, .. }
| Self::CreateFile { description }
| Self::InsertBefore { description, .. }
| Self::InsertAfter { description, .. } => Some(description),
Self::Delete { .. } => None,
}
}
fn description_mut(&mut self) -> Option<&mut String> {
match self {
Self::Update { description, .. }
| Self::CreateFile { description }
| Self::InsertBefore { description, .. }
| Self::InsertAfter { description, .. } => Some(description),
Self::Delete { .. } => None,
}
}
pub fn try_merge(&mut self, other: &Self, buffer: &BufferSnapshot) -> bool {
let range = self.range();
let other_range = other.range();
// Don't merge if we don't contain the other suggestion.
if range.start.cmp(&other_range.start, buffer).is_gt()
|| range.end.cmp(&other_range.end, buffer).is_lt()
{
return false;
}
if let Some(description) = self.description_mut() {
if let Some(other_description) = other.description() {
description.push('\n');
description.push_str(other_description);
}
}
true
}
pub fn show(
&self,
editor: &View<Editor>,
excerpt_id: editor::ExcerptId,
workspace: &WeakView<Workspace>,
assistant_panel: &View<AssistantPanel>,
cx: &mut WindowContext,
) -> Option<InlineAssistId> {
let mut initial_transaction_id = None;
let initial_prompt;
let suggestion_range;
let buffer = editor.read(cx).buffer().clone();
let snapshot = buffer.read(cx).snapshot(cx);
match self {
Self::Update {
range, description, ..
} => {
initial_prompt = description.clone();
suggestion_range = snapshot.anchor_in_excerpt(excerpt_id, range.start)?
..snapshot.anchor_in_excerpt(excerpt_id, range.end)?;
}
Self::CreateFile { description } => {
initial_prompt = description.clone();
suggestion_range = editor::Anchor::min()..editor::Anchor::min();
}
Self::InsertBefore {
position,
description,
..
} => {
let position = snapshot.anchor_in_excerpt(excerpt_id, *position)?;
initial_prompt = description.clone();
suggestion_range = buffer.update(cx, |buffer, cx| {
buffer.start_transaction(cx);
let line_start = buffer.insert_empty_line(position, true, true, cx);
initial_transaction_id = buffer.end_transaction(cx);
buffer.refresh_preview(cx);
let line_start = buffer.read(cx).anchor_before(line_start);
line_start..line_start
});
}
Self::InsertAfter {
position,
description,
..
} => {
let position = snapshot.anchor_in_excerpt(excerpt_id, *position)?;
initial_prompt = description.clone();
suggestion_range = buffer.update(cx, |buffer, cx| {
buffer.start_transaction(cx);
let line_start = buffer.insert_empty_line(position, true, true, cx);
initial_transaction_id = buffer.end_transaction(cx);
buffer.refresh_preview(cx);
let line_start = buffer.read(cx).anchor_before(line_start);
line_start..line_start
});
}
Self::Delete { range, .. } => {
initial_prompt = "Delete".to_string();
suggestion_range = snapshot.anchor_in_excerpt(excerpt_id, range.start)?
..snapshot.anchor_in_excerpt(excerpt_id, range.end)?;
}
}
InlineAssistant::update_global(cx, |inline_assistant, cx| {
Some(inline_assistant.suggest_assist(
editor,
suggestion_range,
initial_prompt,
initial_transaction_id,
false,
Some(workspace.clone()),
Some(assistant_panel),
cx,
))
})
}
}
impl WorkflowStepEdit {
pub fn new(
path: Option<String>,
operation: Option<String>,
search: Option<String>,
description: Option<String>,
) -> Result<Self> {
let path = path.ok_or_else(|| anyhow!("missing path"))?;
let operation = operation.ok_or_else(|| anyhow!("missing operation"))?;
let kind = match operation.as_str() {
"update" => WorkflowStepEditKind::Update {
search: search.ok_or_else(|| anyhow!("missing search"))?,
description: description.ok_or_else(|| anyhow!("missing description"))?,
},
"insert_before" => WorkflowStepEditKind::InsertBefore {
search: search.ok_or_else(|| anyhow!("missing search"))?,
description: description.ok_or_else(|| anyhow!("missing description"))?,
},
"insert_after" => WorkflowStepEditKind::InsertAfter {
search: search.ok_or_else(|| anyhow!("missing search"))?,
description: description.ok_or_else(|| anyhow!("missing description"))?,
},
"delete" => WorkflowStepEditKind::Delete {
search: search.ok_or_else(|| anyhow!("missing search"))?,
},
"create" => WorkflowStepEditKind::Create {
description: description.ok_or_else(|| anyhow!("missing description"))?,
},
_ => Err(anyhow!("unknown operation {operation:?}"))?,
};
Ok(Self { path, kind })
}
pub async fn resolve(
&self,
project: Model<Project>,
mut cx: AsyncAppContext,
) -> Result<(Model<Buffer>, super::WorkflowSuggestion)> {
let path = self.path.clone();
let kind = self.kind.clone();
let buffer = project
.update(&mut cx, |project, cx| {
let project_path = project
.find_project_path(Path::new(&path), cx)
.or_else(|| {
// If we couldn't find a project path for it, put it in the active worktree
// so that when we create the buffer, it can be saved.
let worktree = project
.active_entry()
.and_then(|entry_id| project.worktree_for_entry(entry_id, cx))
.or_else(|| project.worktrees(cx).next())?;
let worktree = worktree.read(cx);
Some(ProjectPath {
worktree_id: worktree.id(),
path: Arc::from(Path::new(&path)),
})
})
.with_context(|| format!("worktree not found for {:?}", path))?;
anyhow::Ok(project.open_buffer(project_path, cx))
})??
.await?;
let snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot())?;
let suggestion = cx
.background_executor()
.spawn(async move {
match kind {
WorkflowStepEditKind::Update {
search,
description,
} => {
let range = Self::resolve_location(&snapshot, &search);
WorkflowSuggestion::Update { range, description }
}
WorkflowStepEditKind::Create { description } => {
WorkflowSuggestion::CreateFile { description }
}
WorkflowStepEditKind::InsertBefore {
search,
description,
} => {
let range = Self::resolve_location(&snapshot, &search);
WorkflowSuggestion::InsertBefore {
position: range.start,
description,
}
}
WorkflowStepEditKind::InsertAfter {
search,
description,
} => {
let range = Self::resolve_location(&snapshot, &search);
WorkflowSuggestion::InsertAfter {
position: range.end,
description,
}
}
WorkflowStepEditKind::Delete { search } => {
let range = Self::resolve_location(&snapshot, &search);
WorkflowSuggestion::Delete { range }
}
}
})
.await;
Ok((buffer, suggestion))
}
fn resolve_location(buffer: &text::BufferSnapshot, search_query: &str) -> Range<text::Anchor> {
const INSERTION_SCORE: f64 = -1.0;
const DELETION_SCORE: f64 = -1.0;
const REPLACEMENT_SCORE: f64 = -1.0;
const EQUALITY_SCORE: f64 = 5.0;
struct Matrix {
cols: usize,
data: Vec<f64>,
}
impl Matrix {
fn new(rows: usize, cols: usize) -> Self {
Matrix {
cols,
data: vec![0.0; rows * cols],
}
}
fn get(&self, row: usize, col: usize) -> f64 {
self.data[row * self.cols + col]
}
fn set(&mut self, row: usize, col: usize, value: f64) {
self.data[row * self.cols + col] = value;
}
}
let buffer_len = buffer.len();
let query_len = search_query.len();
let mut matrix = Matrix::new(query_len + 1, buffer_len + 1);
for (i, query_byte) in search_query.bytes().enumerate() {
for (j, buffer_byte) in buffer.bytes_in_range(0..buffer.len()).flatten().enumerate() {
let match_score = if query_byte == *buffer_byte {
EQUALITY_SCORE
} else {
REPLACEMENT_SCORE
};
let up = matrix.get(i + 1, j) + DELETION_SCORE;
let left = matrix.get(i, j + 1) + INSERTION_SCORE;
let diagonal = matrix.get(i, j) + match_score;
let score = up.max(left.max(diagonal)).max(0.);
matrix.set(i + 1, j + 1, score);
}
}
// Traceback to find the best match
let mut best_buffer_end = buffer_len;
let mut best_score = 0.0;
for col in 1..=buffer_len {
let score = matrix.get(query_len, col);
if score > best_score {
best_score = score;
best_buffer_end = col;
}
}
let mut query_ix = query_len;
let mut buffer_ix = best_buffer_end;
while query_ix > 0 && buffer_ix > 0 {
let current = matrix.get(query_ix, buffer_ix);
let up = matrix.get(query_ix - 1, buffer_ix);
let left = matrix.get(query_ix, buffer_ix - 1);
if current == left + INSERTION_SCORE {
buffer_ix -= 1;
} else if current == up + DELETION_SCORE {
query_ix -= 1;
} else {
query_ix -= 1;
buffer_ix -= 1;
}
}
let mut start = buffer.offset_to_point(buffer.clip_offset(buffer_ix, Bias::Left));
start.column = 0;
let mut end = buffer.offset_to_point(buffer.clip_offset(best_buffer_end, Bias::Right));
end.column = buffer.line_len(end.row);
buffer.anchor_after(start)..buffer.anchor_before(end)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "operation")]
pub enum WorkflowStepEditKind {
/// Rewrites the specified text entirely based on the given description.
/// This operation completely replaces the given text.
Update {
/// A string in the source text to apply the update to.
search: String,
/// A brief description of the transformation to apply to the symbol.
description: String,
},
/// Creates a new file with the given path based on the provided description.
/// This operation adds a new file to the codebase.
Create {
/// A brief description of the file to be created.
description: String,
},
/// Inserts text before the specified text in the source file.
InsertBefore {
/// A string in the source text to insert text before.
search: String,
/// A brief description of how the new text should be generated.
description: String,
},
/// Inserts text after the specified text in the source file.
InsertAfter {
/// A string in the source text to insert text after.
search: String,
/// A brief description of how the new text should be generated.
description: String,
},
/// Deletes the specified symbol from the containing file.
Delete {
/// A string in the source text to delete.
search: String,
},
}
#[cfg(test)]
mod tests {
use super::*;
use gpui::{AppContext, Context};
use text::{OffsetRangeExt, Point};
#[gpui::test]
fn test_resolve_location(cx: &mut AppContext) {
{
let buffer = cx.new_model(|cx| {
Buffer::local(
concat!(
" Lorem\n",
" ipsum\n",
" dolor sit amet\n",
" consecteur",
),
cx,
)
});
let snapshot = buffer.read(cx).snapshot();
assert_eq!(
WorkflowStepEdit::resolve_location(&snapshot, "ipsum\ndolor").to_point(&snapshot),
Point::new(1, 0)..Point::new(2, 18)
);
}
{
let buffer = cx.new_model(|cx| {
Buffer::local(
concat!(
"fn foo1(a: usize) -> usize {\n",
" 42\n",
"}\n",
"\n",
"fn foo2(b: usize) -> usize {\n",
" 42\n",
"}\n",
),
cx,
)
});
let snapshot = buffer.read(cx).snapshot();
assert_eq!(
WorkflowStepEdit::resolve_location(&snapshot, "fn foo1(b: usize) {\n42\n}")
.to_point(&snapshot),
Point::new(0, 0)..Point::new(2, 1)
);
}
{
let buffer = cx.new_model(|cx| {
Buffer::local(
concat!(
"fn main() {\n",
" Foo\n",
" .bar()\n",
" .baz()\n",
" .qux()\n",
"}\n",
"\n",
"fn foo2(b: usize) -> usize {\n",
" 42\n",
"}\n",
),
cx,
)
});
let snapshot = buffer.read(cx).snapshot();
assert_eq!(
WorkflowStepEdit::resolve_location(&snapshot, "Foo.bar.baz.qux()")
.to_point(&snapshot),
Point::new(1, 0)..Point::new(4, 14)
);
}
}
}

View File

@@ -15,9 +15,15 @@ path = "src/assistant_slash_command.rs"
anyhow.workspace = true
collections.workspace = true
derive_more.workspace = true
futures.workspace = true
gpui.workspace = true
language.workspace = true
parking_lot.workspace = true
serde.workspace = true
serde_json.workspace = true
workspace.workspace = true
[dev-dependencies]
gpui = { workspace = true, features = ["test-support"] }
pretty_assertions.workspace = true
workspace = { workspace = true, features = ["test-support"] }

View File

@@ -1,6 +1,8 @@
mod slash_command_registry;
use anyhow::Result;
use futures::stream::{self, BoxStream};
use futures::StreamExt;
use gpui::{AnyElement, AppContext, ElementId, SharedString, Task, WeakView, WindowContext};
use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate, OffsetRangeExt};
use serde::{Deserialize, Serialize};
@@ -56,8 +58,13 @@ pub struct ArgumentCompletion {
pub replace_previous_arguments: bool,
}
pub type SlashCommandResult = Result<BoxStream<'static, Result<SlashCommandEvent>>>;
pub trait SlashCommand: 'static + Send + Sync {
fn name(&self) -> String;
fn icon(&self) -> IconName {
IconName::Slash
}
fn label(&self, _cx: &AppContext) -> CodeLabel {
CodeLabel::plain(self.name(), None)
}
@@ -87,7 +94,7 @@ pub trait SlashCommand: 'static + Send + Sync {
// perhaps another kind of delegate is needed here.
delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>>;
) -> Task<SlashCommandResult>;
}
pub type RenderFoldPlaceholder = Arc<
@@ -96,13 +103,146 @@ pub type RenderFoldPlaceholder = Arc<
+ Fn(ElementId, Arc<dyn Fn(&mut WindowContext)>, &mut WindowContext) -> AnyElement,
>;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum SlashCommandContent {
Text {
text: String,
run_commands_in_text: bool,
},
}
#[derive(Debug, PartialEq, Eq)]
pub enum SlashCommandEvent {
StartSection {
icon: IconName,
label: SharedString,
metadata: Option<serde_json::Value>,
},
Content(SlashCommandContent),
EndSection {
metadata: Option<serde_json::Value>,
},
}
#[derive(Debug, Default, PartialEq, Clone)]
pub struct SlashCommandOutput {
pub text: String,
pub sections: Vec<SlashCommandOutputSection<usize>>,
pub run_commands_in_text: bool,
}
impl SlashCommandOutput {
pub fn ensure_valid_section_ranges(&mut self) {
for section in &mut self.sections {
section.range.start = section.range.start.min(self.text.len());
section.range.end = section.range.end.min(self.text.len());
while !self.text.is_char_boundary(section.range.start) {
section.range.start -= 1;
}
while !self.text.is_char_boundary(section.range.end) {
section.range.end += 1;
}
}
}
/// Returns this [`SlashCommandOutput`] as a stream of [`SlashCommandEvent`]s.
pub fn to_event_stream(mut self) -> BoxStream<'static, Result<SlashCommandEvent>> {
self.ensure_valid_section_ranges();
let mut events = Vec::new();
let mut last_section_end = 0;
for section in self.sections {
if last_section_end < section.range.start {
events.push(Ok(SlashCommandEvent::Content(SlashCommandContent::Text {
text: self
.text
.get(last_section_end..section.range.start)
.unwrap_or_default()
.to_string(),
run_commands_in_text: self.run_commands_in_text,
})));
}
events.push(Ok(SlashCommandEvent::StartSection {
icon: section.icon,
label: section.label,
metadata: section.metadata.clone(),
}));
events.push(Ok(SlashCommandEvent::Content(SlashCommandContent::Text {
text: self
.text
.get(section.range.start..section.range.end)
.unwrap_or_default()
.to_string(),
run_commands_in_text: self.run_commands_in_text,
})));
events.push(Ok(SlashCommandEvent::EndSection {
metadata: section.metadata,
}));
last_section_end = section.range.end;
}
if last_section_end < self.text.len() {
events.push(Ok(SlashCommandEvent::Content(SlashCommandContent::Text {
text: self.text[last_section_end..].to_string(),
run_commands_in_text: self.run_commands_in_text,
})));
}
stream::iter(events).boxed()
}
pub async fn from_event_stream(
mut events: BoxStream<'static, Result<SlashCommandEvent>>,
) -> Result<SlashCommandOutput> {
let mut output = SlashCommandOutput::default();
let mut section_stack = Vec::new();
while let Some(event) = events.next().await {
match event? {
SlashCommandEvent::StartSection {
icon,
label,
metadata,
} => {
let start = output.text.len();
section_stack.push(SlashCommandOutputSection {
range: start..start,
icon,
label,
metadata,
});
}
SlashCommandEvent::Content(SlashCommandContent::Text {
text,
run_commands_in_text,
}) => {
output.text.push_str(&text);
output.run_commands_in_text = run_commands_in_text;
if let Some(section) = section_stack.last_mut() {
section.range.end = output.text.len();
}
}
SlashCommandEvent::EndSection { metadata } => {
if let Some(mut section) = section_stack.pop() {
section.metadata = metadata;
output.sections.push(section);
}
}
}
}
while let Some(section) = section_stack.pop() {
output.sections.push(section);
}
Ok(output)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SlashCommandOutputSection<T> {
pub range: Range<T>,
@@ -116,3 +256,243 @@ impl SlashCommandOutputSection<language::Anchor> {
self.range.start.is_valid(buffer) && !self.range.to_offset(buffer).is_empty()
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use serde_json::json;
use super::*;
#[gpui::test]
async fn test_slash_command_output_to_events_round_trip() {
// Test basic output consisting of a single section.
{
let text = "Hello, world!".to_string();
let range = 0..text.len();
let output = SlashCommandOutput {
text,
sections: vec![SlashCommandOutputSection {
range,
icon: IconName::Code,
label: "Section 1".into(),
metadata: None,
}],
run_commands_in_text: false,
};
let events = output.clone().to_event_stream().collect::<Vec<_>>().await;
let events = events
.into_iter()
.filter_map(|event| event.ok())
.collect::<Vec<_>>();
assert_eq!(
events,
vec![
SlashCommandEvent::StartSection {
icon: IconName::Code,
label: "Section 1".into(),
metadata: None
},
SlashCommandEvent::Content(SlashCommandContent::Text {
text: "Hello, world!".into(),
run_commands_in_text: false
}),
SlashCommandEvent::EndSection { metadata: None }
]
);
let new_output =
SlashCommandOutput::from_event_stream(output.clone().to_event_stream())
.await
.unwrap();
assert_eq!(new_output, output);
}
// Test output where the sections do not comprise all of the text.
{
let text = "Apple\nCucumber\nBanana\n".to_string();
let output = SlashCommandOutput {
text,
sections: vec![
SlashCommandOutputSection {
range: 0..6,
icon: IconName::Check,
label: "Fruit".into(),
metadata: None,
},
SlashCommandOutputSection {
range: 15..22,
icon: IconName::Check,
label: "Fruit".into(),
metadata: None,
},
],
run_commands_in_text: false,
};
let events = output.clone().to_event_stream().collect::<Vec<_>>().await;
let events = events
.into_iter()
.filter_map(|event| event.ok())
.collect::<Vec<_>>();
assert_eq!(
events,
vec![
SlashCommandEvent::StartSection {
icon: IconName::Check,
label: "Fruit".into(),
metadata: None
},
SlashCommandEvent::Content(SlashCommandContent::Text {
text: "Apple\n".into(),
run_commands_in_text: false
}),
SlashCommandEvent::EndSection { metadata: None },
SlashCommandEvent::Content(SlashCommandContent::Text {
text: "Cucumber\n".into(),
run_commands_in_text: false
}),
SlashCommandEvent::StartSection {
icon: IconName::Check,
label: "Fruit".into(),
metadata: None
},
SlashCommandEvent::Content(SlashCommandContent::Text {
text: "Banana\n".into(),
run_commands_in_text: false
}),
SlashCommandEvent::EndSection { metadata: None }
]
);
let new_output =
SlashCommandOutput::from_event_stream(output.clone().to_event_stream())
.await
.unwrap();
assert_eq!(new_output, output);
}
// Test output consisting of multiple sections.
{
let text = "Line 1\nLine 2\nLine 3\nLine 4\n".to_string();
let output = SlashCommandOutput {
text,
sections: vec![
SlashCommandOutputSection {
range: 0..6,
icon: IconName::FileCode,
label: "Section 1".into(),
metadata: Some(json!({ "a": true })),
},
SlashCommandOutputSection {
range: 7..13,
icon: IconName::FileDoc,
label: "Section 2".into(),
metadata: Some(json!({ "b": true })),
},
SlashCommandOutputSection {
range: 14..20,
icon: IconName::FileGit,
label: "Section 3".into(),
metadata: Some(json!({ "c": true })),
},
SlashCommandOutputSection {
range: 21..27,
icon: IconName::FileToml,
label: "Section 4".into(),
metadata: Some(json!({ "d": true })),
},
],
run_commands_in_text: false,
};
let events = output.clone().to_event_stream().collect::<Vec<_>>().await;
let events = events
.into_iter()
.filter_map(|event| event.ok())
.collect::<Vec<_>>();
assert_eq!(
events,
vec![
SlashCommandEvent::StartSection {
icon: IconName::FileCode,
label: "Section 1".into(),
metadata: Some(json!({ "a": true }))
},
SlashCommandEvent::Content(SlashCommandContent::Text {
text: "Line 1".into(),
run_commands_in_text: false
}),
SlashCommandEvent::EndSection {
metadata: Some(json!({ "a": true }))
},
SlashCommandEvent::Content(SlashCommandContent::Text {
text: "\n".into(),
run_commands_in_text: false
}),
SlashCommandEvent::StartSection {
icon: IconName::FileDoc,
label: "Section 2".into(),
metadata: Some(json!({ "b": true }))
},
SlashCommandEvent::Content(SlashCommandContent::Text {
text: "Line 2".into(),
run_commands_in_text: false
}),
SlashCommandEvent::EndSection {
metadata: Some(json!({ "b": true }))
},
SlashCommandEvent::Content(SlashCommandContent::Text {
text: "\n".into(),
run_commands_in_text: false
}),
SlashCommandEvent::StartSection {
icon: IconName::FileGit,
label: "Section 3".into(),
metadata: Some(json!({ "c": true }))
},
SlashCommandEvent::Content(SlashCommandContent::Text {
text: "Line 3".into(),
run_commands_in_text: false
}),
SlashCommandEvent::EndSection {
metadata: Some(json!({ "c": true }))
},
SlashCommandEvent::Content(SlashCommandContent::Text {
text: "\n".into(),
run_commands_in_text: false
}),
SlashCommandEvent::StartSection {
icon: IconName::FileToml,
label: "Section 4".into(),
metadata: Some(json!({ "d": true }))
},
SlashCommandEvent::Content(SlashCommandContent::Text {
text: "Line 4".into(),
run_commands_in_text: false
}),
SlashCommandEvent::EndSection {
metadata: Some(json!({ "d": true }))
},
SlashCommandEvent::Content(SlashCommandContent::Text {
text: "\n".into(),
run_commands_in_text: false
}),
]
);
let new_output =
SlashCommandOutput::from_event_stream(output.clone().to_event_stream())
.await
.unwrap();
assert_eq!(new_output, output);
}
}
}

View File

@@ -32,4 +32,5 @@ settings.workspace = true
smol.workspace = true
tempfile.workspace = true
util.workspace = true
which.workspace = true
workspace.workspace = true

View File

@@ -11,6 +11,7 @@ use gpui::{
};
use markdown_preview::markdown_preview_view::{MarkdownPreviewMode, MarkdownPreviewView};
use paths::remote_servers_dir;
use schemars::JsonSchema;
use serde::Deserialize;
use serde_derive::Serialize;
@@ -33,6 +34,7 @@ use std::{
};
use update_notification::UpdateNotification;
use util::ResultExt;
use which::which;
use workspace::notifications::NotificationId;
use workspace::Workspace;
@@ -82,9 +84,9 @@ pub struct AutoUpdater {
}
#[derive(Deserialize)]
struct JsonRelease {
version: String,
url: String,
pub struct JsonRelease {
pub version: String,
pub url: String,
}
struct MacOsUnmounter {
@@ -430,10 +432,14 @@ impl AutoUpdater {
cx.notify();
}
pub async fn get_latest_remote_server_release(
// If you are packaging Zed and need to override the place it downloads SSH remotes from,
// you can override this function. You should also update get_remote_server_release_url to return
// Ok(None).
pub async fn download_remote_server_release(
os: &str,
arch: &str,
mut release_channel: ReleaseChannel,
release_channel: ReleaseChannel,
version: Option<SemanticVersion>,
cx: &mut AsyncAppContext,
) -> Result<PathBuf> {
let this = cx.update(|cx| {
@@ -443,15 +449,12 @@ impl AutoUpdater {
.ok_or_else(|| anyhow!("auto-update not initialized"))
})??;
if release_channel == ReleaseChannel::Dev {
release_channel = ReleaseChannel::Nightly;
}
let release = Self::get_latest_release(
let release = Self::get_release(
&this,
"zed-remote-server",
os,
arch,
version,
Some(release_channel),
cx,
)
@@ -466,13 +469,97 @@ impl AutoUpdater {
let client = this.read_with(cx, |this, _| this.http_client.clone())?;
if smol::fs::metadata(&version_path).await.is_err() {
log::info!("downloading zed-remote-server {os} {arch}");
log::info!(
"downloading zed-remote-server {os} {arch} version {}",
release.version
);
download_remote_server_binary(&version_path, release, client, cx).await?;
}
Ok(version_path)
}
pub async fn get_remote_server_release_url(
os: &str,
arch: &str,
release_channel: ReleaseChannel,
version: Option<SemanticVersion>,
cx: &mut AsyncAppContext,
) -> Result<Option<(String, String)>> {
let this = cx.update(|cx| {
cx.default_global::<GlobalAutoUpdate>()
.0
.clone()
.ok_or_else(|| anyhow!("auto-update not initialized"))
})??;
let release = Self::get_release(
&this,
"zed-remote-server",
os,
arch,
version,
Some(release_channel),
cx,
)
.await?;
let update_request_body = build_remote_server_update_request_body(cx)?;
let body = serde_json::to_string(&update_request_body)?;
Ok(Some((release.url, body)))
}
async fn get_release(
this: &Model<Self>,
asset: &str,
os: &str,
arch: &str,
version: Option<SemanticVersion>,
release_channel: Option<ReleaseChannel>,
cx: &mut AsyncAppContext,
) -> Result<JsonRelease> {
let client = this.read_with(cx, |this, _| this.http_client.clone())?;
if let Some(version) = version {
let channel = release_channel.map(|c| c.dev_name()).unwrap_or("stable");
let url = format!("/api/releases/{channel}/{version}/{asset}-{os}-{arch}.gz?update=1",);
Ok(JsonRelease {
version: version.to_string(),
url: client.build_url(&url),
})
} else {
let mut url_string = client.build_url(&format!(
"/api/releases/latest?asset={}&os={}&arch={}",
asset, os, arch
));
if let Some(param) = release_channel.and_then(|c| c.release_query_param()) {
url_string += "&";
url_string += param;
}
let mut response = client.get(&url_string, Default::default(), true).await?;
let mut body = Vec::new();
response.body_mut().read_to_end(&mut body).await?;
if !response.status().is_success() {
return Err(anyhow!(
"failed to fetch release: {:?}",
String::from_utf8_lossy(&body),
));
}
serde_json::from_slice(body.as_slice()).with_context(|| {
format!(
"error deserializing release {:?}",
String::from_utf8_lossy(&body),
)
})
}
}
async fn get_latest_release(
this: &Model<Self>,
asset: &str,
@@ -481,38 +568,7 @@ impl AutoUpdater {
release_channel: Option<ReleaseChannel>,
cx: &mut AsyncAppContext,
) -> Result<JsonRelease> {
let client = this.read_with(cx, |this, _| this.http_client.clone())?;
let mut url_string = client.build_url(&format!(
"/api/releases/latest?asset={}&os={}&arch={}",
asset, os, arch
));
if let Some(param) = release_channel.and_then(|c| c.release_query_param()) {
url_string += "&";
url_string += param;
}
let mut response = client.get(&url_string, Default::default(), true).await?;
let mut body = Vec::new();
response
.body_mut()
.read_to_end(&mut body)
.await
.context("error reading release")?;
if !response.status().is_success() {
Err(anyhow!(
"failed to fetch release: {:?}",
String::from_utf8_lossy(&body),
))?;
}
serde_json::from_slice(body.as_slice()).with_context(|| {
format!(
"error deserializing release {:?}",
String::from_utf8_lossy(&body),
)
})
Self::get_release(this, asset, os, arch, None, release_channel, cx).await
}
async fn update(this: Model<Self>, mut cx: AsyncAppContext) -> Result<()> {
@@ -560,6 +616,12 @@ impl AutoUpdater {
"linux" => Ok("zed.tar.gz"),
_ => Err(anyhow!("not supported: {:?}", OS)),
}?;
anyhow::ensure!(
which("rsync").is_ok(),
"Aborting. Could not find rsync which is required for auto-updates."
);
let downloaded_asset = temp_dir.path().join(filename);
download_release(&downloaded_asset, release, client, &cx).await?;
@@ -621,7 +683,25 @@ async fn download_remote_server_binary(
client: Arc<HttpClientWithUrl>,
cx: &AsyncAppContext,
) -> Result<()> {
let mut target_file = File::create(&target_path).await?;
let temp = tempfile::Builder::new().tempfile_in(remote_servers_dir())?;
let mut temp_file = File::create(&temp).await?;
let update_request_body = build_remote_server_update_request_body(cx)?;
let request_body = AsyncBody::from(serde_json::to_string(&update_request_body)?);
let mut response = client.get(&release.url, request_body, true).await?;
if !response.status().is_success() {
return Err(anyhow!(
"failed to download remote server release: {:?}",
response.status()
));
}
smol::io::copy(response.body_mut(), &mut temp_file).await?;
smol::fs::rename(&temp, &target_path).await?;
Ok(())
}
fn build_remote_server_update_request_body(cx: &AsyncAppContext) -> Result<UpdateRequestBody> {
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();
@@ -637,17 +717,14 @@ async fn download_remote_server_binary(
is_staff,
)
})?;
let request_body = AsyncBody::from(serde_json::to_string(&UpdateRequestBody {
Ok(UpdateRequestBody {
installation_id,
release_channel,
telemetry: telemetry_enabled,
is_staff,
destination: "remote",
})?);
let mut response = client.get(&release.url, request_body, true).await?;
smol::io::copy(response.body_mut(), &mut target_file).await?;
Ok(())
})
}
async fn download_release(

View File

@@ -13,7 +13,6 @@ path = "src/call.rs"
doctest = false
[features]
no-webrtc = ["live_kit_client/no-webrtc"]
test-support = [
"client/test-support",
"collections/test-support",

View File

@@ -1194,26 +1194,15 @@ impl Room {
project: Model<Project>,
cx: &mut ModelContext<Self>,
) -> Task<Result<u64>> {
let request = if let Some(dev_server_project_id) = project.read(cx).dev_server_project_id()
{
self.client.request(proto::ShareProject {
room_id: self.id(),
worktrees: vec![],
dev_server_project_id: Some(dev_server_project_id.0),
is_ssh_project: false,
})
} else {
if let Some(project_id) = project.read(cx).remote_id() {
return Task::ready(Ok(project_id));
}
if let Some(project_id) = project.read(cx).remote_id() {
return Task::ready(Ok(project_id));
}
self.client.request(proto::ShareProject {
room_id: self.id(),
worktrees: project.read(cx).worktree_metadata_protos(cx),
dev_server_project_id: None,
is_ssh_project: project.read(cx).is_via_ssh(),
})
};
let request = self.client.request(proto::ShareProject {
room_id: self.id(),
worktrees: project.read(cx).worktree_metadata_protos(cx),
is_ssh_project: project.read(cx).is_via_ssh(),
});
cx.spawn(|this, mut cx| async move {
let response = request.await?;

View File

@@ -3,7 +3,7 @@ mod channel_index;
use crate::{channel_buffer::ChannelBuffer, channel_chat::ChannelChat, ChannelMessage};
use anyhow::{anyhow, Result};
use channel_index::ChannelIndex;
use client::{ChannelId, Client, ClientSettings, ProjectId, Subscription, User, UserId, UserStore};
use client::{ChannelId, Client, ClientSettings, Subscription, User, UserId, UserStore};
use collections::{hash_map, HashMap, HashSet};
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
use gpui::{
@@ -33,30 +33,11 @@ struct NotesVersion {
version: clock::Global,
}
#[derive(Debug, Clone)]
pub struct HostedProject {
project_id: ProjectId,
channel_id: ChannelId,
name: SharedString,
_visibility: proto::ChannelVisibility,
}
impl From<proto::HostedProject> for HostedProject {
fn from(project: proto::HostedProject) -> Self {
Self {
project_id: ProjectId(project.project_id),
channel_id: ChannelId(project.channel_id),
_visibility: project.visibility(),
name: project.name.into(),
}
}
}
pub struct ChannelStore {
pub channel_index: ChannelIndex,
channel_invitations: Vec<Arc<Channel>>,
channel_participants: HashMap<ChannelId, Vec<Arc<User>>>,
channel_states: HashMap<ChannelId, ChannelState>,
hosted_projects: HashMap<ProjectId, HostedProject>,
outgoing_invites: HashSet<(ChannelId, UserId)>,
update_channels_tx: mpsc::UnboundedSender<proto::UpdateChannels>,
opened_buffers: HashMap<ChannelId, OpenedModelHandle<ChannelBuffer>>,
@@ -85,7 +66,6 @@ pub struct ChannelState {
observed_notes_version: NotesVersion,
observed_chat_message: Option<u64>,
role: Option<ChannelRole>,
projects: HashSet<ProjectId>,
}
impl Channel {
@@ -216,7 +196,6 @@ impl ChannelStore {
channel_invitations: Vec::default(),
channel_index: ChannelIndex::default(),
channel_participants: Default::default(),
hosted_projects: Default::default(),
outgoing_invites: Default::default(),
opened_buffers: Default::default(),
opened_chats: Default::default(),
@@ -316,19 +295,6 @@ impl ChannelStore {
self.channel_index.by_id().get(&channel_id)
}
pub fn projects_for_id(&self, channel_id: ChannelId) -> Vec<(SharedString, ProjectId)> {
let mut projects: Vec<(SharedString, ProjectId)> = self
.channel_states
.get(&channel_id)
.map(|state| state.projects.clone())
.unwrap_or_default()
.into_iter()
.flat_map(|id| Some((self.hosted_projects.get(&id)?.name.clone(), id)))
.collect();
projects.sort();
projects
}
pub fn has_open_channel_buffer(&self, channel_id: ChannelId, _cx: &AppContext) -> bool {
if let Some(buffer) = self.opened_buffers.get(&channel_id) {
if let OpenedModelHandle::Open(buffer) = buffer {
@@ -1102,9 +1068,7 @@ impl ChannelStore {
let channels_changed = !payload.channels.is_empty()
|| !payload.delete_channels.is_empty()
|| !payload.latest_channel_message_ids.is_empty()
|| !payload.latest_channel_buffer_versions.is_empty()
|| !payload.hosted_projects.is_empty()
|| !payload.deleted_hosted_projects.is_empty();
|| !payload.latest_channel_buffer_versions.is_empty();
if channels_changed {
if !payload.delete_channels.is_empty() {
@@ -1161,34 +1125,6 @@ impl ChannelStore {
.or_default()
.update_latest_message_id(latest_channel_message.message_id);
}
for hosted_project in payload.hosted_projects {
let hosted_project: HostedProject = hosted_project.into();
if let Some(old_project) = self
.hosted_projects
.insert(hosted_project.project_id, hosted_project.clone())
{
self.channel_states
.entry(old_project.channel_id)
.or_default()
.remove_hosted_project(old_project.project_id);
}
self.channel_states
.entry(hosted_project.channel_id)
.or_default()
.add_hosted_project(hosted_project.project_id);
}
for hosted_project_id in payload.deleted_hosted_projects {
let hosted_project_id = ProjectId(hosted_project_id);
if let Some(old_project) = self.hosted_projects.remove(&hosted_project_id) {
self.channel_states
.entry(old_project.channel_id)
.or_default()
.remove_hosted_project(old_project.project_id);
}
}
}
cx.notify();
@@ -1295,12 +1231,4 @@ impl ChannelState {
};
}
}
fn add_hosted_project(&mut self, project_id: ProjectId) {
self.projects.insert(project_id);
}
fn remove_hosted_project(&mut self, project_id: ProjectId) {
self.projects.remove(&project_id);
}
}

View File

@@ -15,7 +15,6 @@ pub enum CliRequest {
urls: Vec<String>,
wait: bool,
open_new_workspace: Option<bool>,
dev_server_token: Option<String>,
env: Option<HashMap<String, String>>,
},
}

View File

@@ -151,6 +151,12 @@ fn main() -> Result<()> {
}
}
if let Some(_) = args.dev_server_token {
return Err(anyhow::anyhow!(
"Dev servers were removed in v0.157.x please upgrade to SSH remoting: https://zed.dev/docs/remote-development"
))?;
}
let sender: JoinHandle<anyhow::Result<()>> = thread::spawn({
let exit_status = exit_status.clone();
move || {
@@ -162,7 +168,6 @@ fn main() -> Result<()> {
urls,
wait: args.wait,
open_new_workspace,
dev_server_token: args.dev_server_token,
env,
})?;

View File

@@ -44,7 +44,6 @@ sha2.workspace = true
smol.workspace = true
sysinfo.workspace = true
telemetry_events.workspace = true
tempfile.workspace = true
text.workspace = true
thiserror.workspace = true
time.workspace = true

View File

@@ -4,6 +4,7 @@ pub mod test;
mod socks;
pub mod telemetry;
pub mod user;
pub mod zed_urls;
use anyhow::{anyhow, bail, Context as _, Result};
use async_recursion::async_recursion;
@@ -29,7 +30,6 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
use socks::connect_socks_proxy_stream;
use std::fmt;
use std::pin::Pin;
use std::{
any::TypeId,
@@ -53,15 +53,6 @@ pub use rpc::*;
pub use telemetry_events::Event;
pub use user::*;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DevServerToken(pub String);
impl fmt::Display for DevServerToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
static ZED_SERVER_URL: LazyLock<Option<String>> =
LazyLock::new(|| std::env::var("ZED_SERVER_URL").ok());
static ZED_RPC_URL: LazyLock<Option<String>> = LazyLock::new(|| std::env::var("ZED_RPC_URL").ok());
@@ -303,20 +294,14 @@ struct ClientState {
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Credentials {
DevServer { token: DevServerToken },
User { user_id: u64, access_token: String },
pub struct Credentials {
pub user_id: u64,
pub access_token: String,
}
impl Credentials {
pub fn authorization_header(&self) -> String {
match self {
Credentials::DevServer { token } => format!("dev-server-token {}", token),
Credentials::User {
user_id,
access_token,
} => format!("{} {}", user_id, access_token),
}
format!("{} {}", self.user_id, self.access_token)
}
}
@@ -599,11 +584,11 @@ impl Client {
}
pub fn user_id(&self) -> Option<u64> {
if let Some(Credentials::User { user_id, .. }) = self.state.read().credentials.as_ref() {
Some(*user_id)
} else {
None
}
self.state
.read()
.credentials
.as_ref()
.map(|credentials| credentials.user_id)
}
pub fn peer_id(&self) -> Option<PeerId> {
@@ -792,11 +777,6 @@ impl Client {
.is_some()
}
pub fn set_dev_server_token(&self, token: DevServerToken) -> &Self {
self.state.write().credentials = Some(Credentials::DevServer { token });
self
}
#[async_recursion(?Send)]
pub async fn authenticate_and_connect(
self: &Arc<Self>,
@@ -847,9 +827,7 @@ impl Client {
}
}
let credentials = credentials.unwrap();
if let Credentials::User { user_id, .. } = &credentials {
self.set_id(*user_id);
}
self.set_id(credentials.user_id);
if was_disconnected {
self.set_status(Status::Connecting, cx);
@@ -865,9 +843,8 @@ impl Client {
Ok(conn) => {
self.state.write().credentials = Some(credentials.clone());
if !read_from_provider && IMPERSONATE_LOGIN.is_none() {
if let Credentials::User{user_id, access_token} = credentials {
self.credentials_provider.write_credentials(user_id, access_token, cx).await.log_err();
}
self.credentials_provider.write_credentials(credentials.user_id, credentials.access_token, cx).await.log_err();
}
futures::select_biased! {
@@ -1300,7 +1277,7 @@ impl Client {
.decrypt_string(&access_token)
.context("failed to decrypt access token")?;
Ok(Credentials::User {
Ok(Credentials {
user_id: user_id.parse()?,
access_token,
})
@@ -1421,7 +1398,7 @@ impl Client {
// Use the admin API token to authenticate as the impersonated user.
api_token.insert_str(0, "ADMIN_TOKEN:");
Ok(Credentials::User {
Ok(Credentials {
user_id: response.user.id,
access_token: api_token,
})
@@ -1666,7 +1643,7 @@ impl CredentialsProvider for DevelopmentCredentialsProvider {
let credentials: DevelopmentCredentials = serde_json::from_slice(&json).log_err()?;
Some(Credentials::User {
Some(Credentials {
user_id: credentials.user_id,
access_token: credentials.access_token,
})
@@ -1720,7 +1697,7 @@ impl CredentialsProvider for KeychainCredentialsProvider {
.await
.log_err()??;
Some(Credentials::User {
Some(Credentials {
user_id: user_id.parse().ok()?,
access_token: String::from_utf8(access_token).ok()?,
})
@@ -1854,7 +1831,7 @@ mod tests {
// Time out when client tries to connect.
client.override_authenticate(move |cx| {
cx.background_executor().spawn(async move {
Ok(Credentials::User {
Ok(Credentials {
user_id,
access_token: "token".into(),
})

View File

@@ -13,6 +13,7 @@ use parking_lot::Mutex;
use release_channel::ReleaseChannel;
use settings::{Settings, SettingsStore};
use sha2::{Digest, Sha256};
use std::fs::File;
use std::io::Write;
use std::{env, mem, path::PathBuf, sync::Arc, time::Duration};
use sysinfo::{CpuRefreshKind, Pid, ProcessRefreshKind, RefreshKind, System};
@@ -21,10 +22,7 @@ use telemetry_events::{
EventRequestBody, EventWrapper, ExtensionEvent, InlineCompletionEvent, MemoryEvent, ReplEvent,
SettingEvent,
};
use tempfile::NamedTempFile;
#[cfg(not(debug_assertions))]
use util::ResultExt;
use util::TryFutureExt;
use util::{ResultExt, TryFutureExt};
use worktree::{UpdatedEntriesSet, WorktreeId};
use self::event_coalescer::EventCoalescer;
@@ -46,7 +44,7 @@ struct TelemetryState {
architecture: &'static str,
events_queue: Vec<EventWrapper>,
flush_events_task: Option<Task<()>>,
log_file: Option<NamedTempFile>,
log_file: Option<File>,
is_staff: Option<bool>,
first_event_date_time: Option<DateTime<Utc>>,
event_coalescer: EventCoalescer,
@@ -223,15 +221,13 @@ impl Telemetry {
os_name: os_name(),
app_version: release_channel::AppVersion::global(cx).to_string(),
}));
Self::log_file_path();
#[cfg(not(debug_assertions))]
cx.background_executor()
.spawn({
let state = state.clone();
async move {
if let Some(tempfile) =
NamedTempFile::new_in(paths::logs_dir().as_path()).log_err()
{
if let Some(tempfile) = File::create(Self::log_file_path()).log_err() {
state.lock().log_file = Some(tempfile);
}
}
@@ -280,8 +276,8 @@ impl Telemetry {
Task::ready(())
}
pub fn log_file_path(&self) -> Option<PathBuf> {
Some(self.state.lock().log_file.as_ref()?.path().to_path_buf())
pub fn log_file_path() -> PathBuf {
paths::logs_dir().join("telemetry.log")
}
pub fn start(
@@ -341,6 +337,13 @@ impl Telemetry {
.detach();
}
pub fn metrics_enabled(self: &Arc<Self>) -> bool {
let state = self.state.lock();
let enabled = state.settings.metrics;
drop(state);
return enabled;
}
pub fn set_authenticated_user_info(
self: &Arc<Self>,
metrics_id: Option<String>,
@@ -638,7 +641,6 @@ impl Telemetry {
let mut json_bytes = Vec::new();
if let Some(file) = &mut this.state.lock().log_file {
let file = file.as_file_mut();
for event in &mut events {
json_bytes.clear();
serde_json::to_writer(&mut json_bytes, event)?;

View File

@@ -49,7 +49,7 @@ impl FakeServer {
let mut state = state.lock();
state.auth_count += 1;
let access_token = state.access_token.to_string();
Ok(Credentials::User {
Ok(Credentials {
user_id: client_user_id,
access_token,
})
@@ -73,7 +73,7 @@ impl FakeServer {
}
if credentials
!= (Credentials::User {
!= (Credentials {
user_id: client_user_id,
access_token: state.lock().access_token.to_string(),
})

View File

@@ -28,9 +28,6 @@ impl std::fmt::Display for ChannelId {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct ProjectId(pub u64);
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct DevServerId(pub u64);
#[derive(
Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, serde::Serialize, serde::Deserialize,
)]
@@ -51,6 +48,7 @@ pub struct Collaborator {
pub peer_id: proto::PeerId,
pub replica_id: ReplicaId,
pub user_id: UserId,
pub is_host: bool,
}
impl PartialOrd for User {
@@ -827,6 +825,7 @@ impl Collaborator {
peer_id: message.peer_id.ok_or_else(|| anyhow!("invalid peer id"))?,
replica_id: message.replica_id as ReplicaId,
user_id: message.user_id as UserId,
is_host: message.is_host,
})
}
}

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