Compare commits

..

40 Commits

Author SHA1 Message Date
Michael Sloan
aac524858e Remove util::extend_sorted and its remaining use in fs watching 2024-12-31 15:32:23 -07:00
Michael Sloan
6ef5d8f748 Improve fuzzy match performance and fix corner case that omits results (#22524)
* Removes `max_results` from the matcher interface as this is better
dealt with in consumers once all results are known. The current
implementation was quite inefficient as it was using binary search to
find insertion points and then doing an insert which copies the entire
suffix each time.

* There was a corner case where if the binary search found a match
candidate with the same score, it was dropped. Now fixed.

* Uses of `util::extend_sorted` when merging results from worker threads
also repeatedly uses binary search and insertion which copies the entire
suffix. A followup will remove that and its usage.

* Adds `util::truncate_to_bottom_n_sorted_by` which uses quickselect +
sort to efficiently get a sorted count limited result.

* Improves interface of Matcher::match_candidates by providing the match
positions to the build function. This allows for removal of the `Match`
trait. It also fixes a bug where the Match's own Ord wasn't being used,
which seems relevant to PathMatch for cases where scores are the same.

Release Notes:

- N/A
2024-12-31 20:56:23 +00:00
Kirill Bulatov
f912c545e7 Deduplicate edits from WorkspaceEdit LSP responses (#22512)
Closes https://github.com/zed-industries/zed/issues/21515

Release Notes:

- Fixed zls renames applying duplicate edits
2024-12-31 00:50:57 +00:00
Kirill Bulatov
ddc469ca3e Ensure zls is used for Zig as a primary language server (#22511)
Part of https://github.com/zed-industries/zed/issues/22415

I've noticed that I cannot work with any Zig projects, as there were no
"go to definition", formatting and inlay hints.

After debugging, I've discovered that `typos` was registered as a first
language server, becoming the "primary" one for Zig.
That one does not have any proper capabilities, hence all corresponding
LSP requests were no-op.

While this solution is not ideal (I wonder, how many other set-ups are
broken due to the same thing?), we'd better fix things for now this way
at least.

Release Notes:

- Fixed `zls` not working properly when `typos` extension is installed
2024-12-30 23:34:25 +00:00
Michael Sloan
e9bd4b2890 Have Zed cli output logs path to stderr (#22509)
When switching from just running the editor directly to using the CLI I
missed the logs in the terminal and started using `--foreground`. I
realized this was because there was a tiny amount of effort involved in
finding out where logs were being written to. Having the cli output it
to stderr helps make this more visible.

Seems like such a minor thing not listing in release notes.

Release Notes:

- N/A
2024-12-30 21:14:08 +00:00
Vitaly Slobodin
a119688a50 ruby: Document use_bundler configuration option (#22498)
Hello,

This PR documents the `use_bundler` configuration option that was added
in https://github.com/zed-extensions/ruby/releases/tag/v0.3.3

**What it does?**

This option allows users to run `solargraph` and `rubocop` within the
context of Bundler, which is the most commonly used scenario in the Ruby
ecosystem. Ruby LSP is not configured to run in the context of Bundler
because it is recommended not to add it to the project dependencies
(https://shopify.github.io/ruby-lsp/composed-bundle.html). However, if
this is the case, the Ruby LSP can still be configured to run in the
context of Bundler via the same configuration option.

This configuration option has the following default values:

- `rubocop` - `use_bundler` is `true`.
- `solargraph` - `use_bundler` is `true`.
- `ruby-lsp` - `use_bundler` is `false`.

Release Notes:

- N/A
2024-12-30 16:56:25 +00:00
Cole Miller
dcbff982ad Decide which panel should be active for a dock based on ordering panels (#22346)
This means that `workspace::ToggleRightDock` will open the assistant if
no right-dock panel has been manually activated, instead of the chat as
before. Also cleans up the `active_panel_index` logic a bit.

cc @nathansobo 

Release Notes:

- Make `workspace::ToggleRightDock` open the assistant panel if no
right-dock panel has yet been activated
2024-12-30 14:43:17 +00:00
Danilo Leal
ad51df7644 Improve multibuffer excerpt affordances (#22167)
Changes:
- [x] Increase expand affordance surface area
- [x] Ensure expand buttons have tooltips with keybindings
- [x] Make line numbers clickable to jump you to location (only in
multibuffers)
- [x] Hide the "Jump To File" element in not-focused excerpts

Before merging it:

- [x] Fix off-by-one header focus styles glitch

Improvements to consider for follow-up PRs:

1. Experiment with increasing the width of the clickable surface area
for line numbers
2. Don't show (or disable) the "expand excerpt" button when at the top
or bottom edge of the file
3. Once you jump to location, centralize the cursor scroll position

Release Notes:

- Improved multibuffer's "expand excerpt" affordance
- Fixed "jump to file/location" and "expand excerpt" keybinding display
- Made clicking on line numbers in multibuffers jump you to cursor
location in file

---------

Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Co-authored-by: Agus Zubiaga <agus@zed.dev>
2024-12-30 12:23:11 +00:00
Piotr Osiewicz
3f33ca01a8 chore: Remove outline dependency from breadcrumbs (#22504)
This slashes our incremental dev times (touch editor) by 0.6s
(8.1->7.6s) due to unblocking terminal_view build sooner.

Closes #ISSUE

Release Notes:

- N/A
2024-12-30 12:08:26 +00:00
Jason Lee
5f4f3a9c87 chore: Remove duplicate lines in crates/zed/build.rs (#22500)
Release Notes:

- N/A

This may a mistake by merge in PR #21550


344284e013/crates/zed/build.rs (L8-L14)
2024-12-30 11:52:40 +00:00
Kirill Bulatov
344284e013 Invalidate tooltips when mouse leaves element's hitbox (#22488)
Closes https://github.com/zed-industries/zed/issues/21657

In case of the task rerun button tooltip from


f6dabadaf7/crates/terminal_view/src/terminal_view.rs (L1051-L1070)

, the actual button element is not styled as invisible, only its parent.
Zed won't render such element since it's parent is hidden, but will
consider it "visible" all the time its `paint` is called, spawning a
task with the delay, that will create the tooltip:


f6dabadaf7/crates/gpui/src/elements/div.rs (L1949-L1959)

When the parent is hidden, the child won't be painted anymore, and no
mouse listeners will be able to detect this fact and hide the tooltip.

Hence, check such cases separately, during `prepaint`, and invalidate
the tooltips that are not valid anymore.
We cannot use `hitbox.is_hovered(cx)` as it's not really hovered during
prepaint, so a mouse position check is used instead.

Release Notes:

- Fixed tooltips getting stuck
2024-12-29 19:37:04 +00:00
Jayden Seric
f6dabadaf7 Syntax highlight the JavaScript keyword using (#22479)
The ECMAScript Explicit Resource Management proposal (stage 3) that
specifies the keyword `using`:

https://github.com/tc39/proposal-explicit-resource-management

This has already been done for the TypeScript and TSX languages:

- https://github.com/zed-industries/zed/issues/14762
- https://github.com/zed-industries/zed/pull/14772

Release Notes:

- Syntax highlight the JavaScript keyword `using`
([#22479](https://github.com/zed-industries/zed/pull/22479); thanks
[jaydenseric](https://github.com/jaydenseric)).
2024-12-29 08:17:51 +00:00
Michael Sloan
ca6825066f Fix SHA for zed-patches branch of livekit-rust-sdks (#22478)
Also renamed the repo to `livekit-rustk-sdks` on GitHub for clarity. The
old name will still work for old references.

See [discussion
here](https://github.com/zed-industries/zed/pull/21292#issuecomment-2529573128).
The only changes in the target repo are immaterial:

```
$ git diff 799f10133d93ba2a88642cd480d01ec4da53408c 060964da10574cd9bf06463a53bf6e0769c5c45e
diff --git a/livekit-protocol/src/livekit.rs b/livekit-protocol/src/livekit.rs
index 43f5496..19181c2 100644
--- a/livekit-protocol/src/livekit.rs
+++ b/livekit-protocol/src/livekit.rs
@@ -3837,7 +3837,7 @@ pub struct SendDataRequest {
     #[prost(string, optional, tag="5")]
     pub topic: ::core::option::Option<::prost::alloc::string::String>,
 }
-
+///
 #[allow(clippy::derive_partial_eq_without_eq)]
 #[derive(Clone, PartialEq, ::prost::Message)]
 pub struct SendDataResponse {
diff --git a/livekit-runtime/Cargo.toml b/livekit-runtime/Cargo.toml
index 4d83cdf..7051f30 100644
--- a/livekit-runtime/Cargo.toml
+++ b/livekit-runtime/Cargo.toml
@@ -7,6 +7,7 @@ edition = "2021"
 repository = "https://github.com/livekit/rust-sdks"

 [features]
+tokio = ["dep:tokio", "dep:tokio-stream"]
 async = ["dep:async-std", "dep:futures", "dep:async-io"]
 dispatcher = ["dep:futures", "dep:async-io", "dep:async-std", "dep:async-task"]
```

Release Notes:

- N/A
2024-12-29 00:18:58 +00:00
Michael Sloan
a15360bcc8 Dequalify WindowContext and ViewContext references (#22477)
Release Notes:

- N/A
2024-12-28 23:09:55 +00:00
Michael Sloan
016b5d60e1 Cleanups preparing for WindowContext refactor (#22475)
* Remove unnecessary WindowContext and ViewContext '_ lifetimes

* Removed some cases where WindowContext has a different name than `cx`.

Release Notes:

- N/A
2024-12-28 21:36:14 +00:00
Michael Sloan
9815358bdd Dequalify WindowContext and AsyncWindowContext identifiers (#22444)
This makes a WIP automated refactoring a bit easier to write

Release Notes:

- N/A
2024-12-28 21:21:32 +00:00
Kirill Bulatov
ac60dcd67a Fix Project strong reference leaks (#22470)
Closes https://github.com/zed-industries/zed/issues/21906

* After https://github.com/zed-industries/zed/pull/21238,
`TerminalPanel` and `Project` strong references were moved into
`Pane`-related closures, creating a cycle, that did not allow
registering project release and shutting down corresponding language
servers

* After https://github.com/zed-industries/zed/pull/22329, a special
`Editor` was created with a strong reference to the `Project` which
seemed to do nothing bad in general, but when a working Zed was running
a Zed Dev build, had the same issue with preventing language servers
from shutting down.

The latter is very odd, and seems quite dangerous, as any arbitrary
`Editor` with `Project` in it may do the same, yet it seems that we did
not store them before the way git panel does.

I have tried creating a test, yet seems that we need to initialize a lot
of Zed for it which I failed — all my attempts resulted in a single
language server being present in the `Project`'s statuses.

Release Notes:

- Fixed language servers not being released between project reopens
2024-12-28 17:29:15 +00:00
Kirill Bulatov
3e3a5f04a2 Small fixes after terminal split (#22468)
* removes cache access workarounds as
https://github.com/zed-industries/zed/pull/21165 is supposed to fix this
* use `WeakModel<Project>` inside `Pane` just in case 

Release Notes:

- N/A
2024-12-28 17:29:13 +00:00
Cole Miller
7903f4ea58 Fix the install-linux fix (#22466)
Follow-up fix to #22465, forgot my shell fundamentals!

Release Notes:

- N/A
2024-12-28 16:50:29 +00:00
Cole Miller
a3dec643a1 Fix script/install-linux in non-CI settings (#22465)
Closes #22456 

Release Notes:

- N/A
2024-12-28 15:59:54 +00:00
Dzmitry Malyshau
02cc0b9afa Update Blade with acquire fixes, multisampling (#22409)
Fixes #22406

Picks up:
- https://github.com/kvark/blade/pull/234 and
https://github.com/kvark/blade/pull/237 (`OutOfDate` workaround)
- https://github.com/kvark/blade/pull/229 ("metal-rs" to "objc2"
migration)

Release Notes:

- Improved handling of resizing and multiple monitors in Linux/Windows
2024-12-27 21:29:03 +00:00
Marcel Kersten
787466383e copilot: Update Copilot Chat to o1 GA model version (#22376)
Closes #22375

Release Notes:

- Fixed model version of o1 in GitHub Copilot Chat

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
2024-12-27 18:09:50 +00:00
Danilo Leal
34f8bb246a assistant2: Scroll to the bottom when you submit a new message (#22451)
Up until now, in the assistant 2, if you scrolled up either while a
message was being generated or after it's been generated, then submitted
a new message, you'd keep your scroll position. Now, with this PR, if
your scroll position is somewhere else that's not the bottom, as you
submit a new message, you'll be back at the bottom.


https://github.com/user-attachments/assets/8b111c10-27ff-4d7b-9b10-4c31093c6457

Release Notes:

- N/A

Co-authored-by: Agus Zubiaga <hi@aguz.me>
2024-12-27 16:54:35 +00:00
Danilo Leal
56b425fced assistant2: Show the popover keybinding when context is empty (#22452)
This PR makes it so we always show the "Add Context {keybinding}" text
when there's no context pills attached. Also, while we haven't fully
implemented the mention system (triggered by typing `@`), we removed the
instruction on the message editor placeholder. Once that's fully in
place, we should return with it!

<img width="800" alt="Screenshot 2024-12-27 at 1 35 56 PM"
src="https://github.com/user-attachments/assets/201cf784-e7ac-420a-adf2-51b6e075c2b6"
/>

Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
2024-12-27 16:54:31 +00:00
Kirill Bulatov
ed61abb8b8 Resolve completion items once exactly (#22448)
Closes https://github.com/zed-industries/zed/issues/19214
Closes https://github.com/zed-industries/zed/pull/22443

Adds `resolved` property into Zed completion item data, to ensure we
resolve every completion item exactly once.

There are 2 paths for singplayer Zed, and corresponding 2 analogues for
multi player code, where resolve may happen:
* completions menu display & selection, that ends up using
`resolve_completions` in `lsp_store.rs`
* applying a completion menu entry, that ends up using
`apply_additional_edits_for_completion` in `lsp_store.rs`

Now, all local counterparts check `enabled` field before resolving and
set it to true afterwards, and reuse the same `resolve_completion_local`
method for resolving the items.

A logic for re-generating docs and item labels was moved out from the
`resolve_completion_local` method into a separate method, as
`apply_additional_edits_for_completion` does not need that, but needs
the rest of the logic for resolving.
During the extraction, I've noted that multiplayer clients are not
getting the item labels, regenerated after the resolve — as the Zed
protocol-based flow is not the exact copy of the local resolving.
To improve that, `resolve_completion_remote` needs to be adjusted, but
this change is omitted to avoid bloating the PR.

Release Notes:

- Fixed autocomplete inserting multiple imports
2024-12-27 16:43:01 +00:00
renovate[bot]
d71180abc2 Update Rust crate async-broadcast to v0.7.2 (#22424)
This PR contains the following updates:

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

---

### Release Notes

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

###
[`v0.7.2`](https://redirect.github.com/smol-rs/async-broadcast/blob/HEAD/CHANGELOG.md#Version-072)

[Compare
Source](https://redirect.github.com/smol-rs/async-broadcast/compare/0.7.1...0.7.2)

- Add `Sender::broadcast_blocking` and `Receiver::recv_blocking`.
[#&#8203;41](https://redirect.github.com/smol-rs/async-broadcast/issues/41)
- Use `Mutex` instead of `RwLock` for securing the inner data.
[#&#8203;42](https://redirect.github.com/smol-rs/async-broadcast/issues/42)
-   Many non-user-facing internal improvements and fixes.

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-27 00:11:37 +00:00
renovate[bot]
af2ac1a4a0 Update Rust crate env_logger to v0.11.6 (#22425)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [env_logger](https://redirect.github.com/rust-cli/env_logger) |
workspace.dependencies | patch | `0.11.5` -> `0.11.6` |

---

### Release Notes

<details>
<summary>rust-cli/env_logger (env_logger)</summary>

###
[`v0.11.6`](https://redirect.github.com/rust-cli/env_logger/blob/HEAD/CHANGELOG.md#0116---2024-12-20)

[Compare
Source](https://redirect.github.com/rust-cli/env_logger/compare/v0.11.5...v0.11.6)

##### Features

-   Opt-in file and line rendering

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-26 23:55:28 +00:00
renovate[bot]
c742cda8e8 Update Rust crate libc to v0.2.169 (#22427)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

##### Added

- FreeBSD: add more socket TCP stack constants
[#&#8203;4193](https://redirect.github.com/rust-lang/libc/pull/4193)
- Fuchsia: add a `sockaddr_vm` definition
[#&#8203;4194](https://redirect.github.com/rust-lang/libc/pull/4194)

##### Fixed

**Breaking**:
[rust-lang/rust#132975](https://redirect.github.com/rust-lang/rust/pull/132975)
corrected the signedness of `core::ffi::c_char` on various Tier 2 and
Tier 3 platforms (mostly Arm and RISC-V) to match Clang. This release
contains the corresponding changes to `libc`, including the following
specific pull requests:

- ESP-IDF: Replace arch-conditional `c_char` with a reexport
[#&#8203;4195](https://redirect.github.com/rust-lang/libc/pull/4195)
- Fix `c_char` on various targets
[#&#8203;4199](https://redirect.github.com/rust-lang/libc/pull/4199)
- Mirror `c_char` configuration from `rust-lang/rust`
[#&#8203;4198](https://redirect.github.com/rust-lang/libc/pull/4198)

##### Cleanup

- Do not re-export `c_void` in target-specific code
[#&#8203;4200](https://redirect.github.com/rust-lang/libc/pull/4200)

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-26 23:55:11 +00:00
sofaspawn
52614482bf docs: Add "bounded" mode to soft_wrap documentation (#22430)
Closes #22272

Release Notes:

- Added documentation for the new "bounded" mode in the soft_wrap
configuration

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2024-12-26 19:40:27 +00:00
Nate Butler
8b0a0dfb11 gpui: Update Shadow Example (#22434)
This PR adds a more comprehensive shadow example to gpui:

![CleanShot 2024-12-26 at 13 54
05@2x](https://github.com/user-attachments/assets/452fb8a1-d294-4b56-b0e0-f4e4ca6c29d4)

This is prep to work on the following issues:

- Shadows with a blur of 0 do not render. Expected: A shadow with sharp
edges (#22433)
- Spread does not correctly conform to the shape the shadow is cast from

Release Notes:

- N/A
2024-12-26 19:23:10 +00:00
renovate[bot]
f5935d0cec Pin actions/checkout action to 11bd719 (#22422)
This PR contains the following updates:

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

---

### Configuration

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

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

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

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

---

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

---

Release Notes:

- N/A

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-26 13:30:18 +00:00
renovate[bot]
d32fe0216f Update Rust crate anyhow to v1.0.95 (#22423)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

- Add
[`Error::from_boxed`](https://docs.rs/anyhow/1/anyhow/struct.Error.html#method.from_boxed)
([#&#8203;401](https://redirect.github.com/dtolnay/anyhow/issues/401),
[#&#8203;402](https://redirect.github.com/dtolnay/anyhow/issues/402))

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

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-26 13:29:27 +00:00
Danilo Leal
2957263b23 pane: Add keybinding to "Close Left" and "Close Right" actions (#22402)
Was super missing these as I use these actions all the time 😄 

<img width="680" alt="Screenshot 2024-12-24 at 3 18 04 PM"
src="https://github.com/user-attachments/assets/5dbf9ebd-afc5-438c-aad7-c17ca59d9f9b"
/>

Release Notes:

- Add keybinding to "Close Left" and "Close Right" actions
2024-12-26 13:22:08 +00:00
张小白
95911aaa14 windows: Fix crashing when minimizing a window on Windows 11 (#22414)
Closes #22366

This PR fixes the issue of crashing when minimizing a window on Windows
11. And this PR supersedes #22366.

The main change in this PR is to stop rendering the window when it is
minimized. Additionally, I’ve made some modifications to the code in
#21756 to improve clarity (I think...).

cc @mgsloan 

Release Notes:

- N/A
2024-12-25 09:52:19 +00:00
Piotr Osiewicz
1a9f0a647a chore: Remove explicit usages of once_cell in favor of std (#22407)
Closes #ISSUE

Release Notes:

- N/A
2024-12-25 00:33:26 +00:00
Agus Zubiaga
45c714110e Sticky multibuffer headers (#22391)
https://github.com/user-attachments/assets/92cc5ff7-d8be-4e4b-ac6e-68eb310fffce

Release Notes:

- Multibuffer headers will now stick to the top of the viewport as you
scroll
- Added support for expanding diagnostic excerpts

---------

Co-authored-by: Michael <michael@zed.dev>
2024-12-24 17:55:25 +00:00
Marshall Bowers
4c84600630 assistant2: Derive the Context icon at render time instead of storing (#22397)
This PR is a follow up to #22385 that makes it so we treat the icon as
derived state.

Release Notes:

- N/A
2024-12-24 16:13:35 +00:00
Danilo Leal
564936e1fe assistant2: Add stray visual adjusments (#22386)
This PR adds just some tiny visual clean ups to the assistant2 panel.
Nothing major, honestly.

<img width="800" alt="Screenshot 2024-12-24 at 12 19 46 AM"
src="https://github.com/user-attachments/assets/da22aa7f-8a42-4ff0-9e4c-5e8b60b28802"
/>

Release Notes:

- N/A
2024-12-24 04:26:13 +00:00
Danilo Leal
3d4e0780c4 assistant2: Add icons to the context pill (#22385)
This PR adds an icon field to the `ContextKind` enum, which means that
icons will now display on context pills, both on the message editor and
on the active thread.

<img width="800" alt="Screenshot 2024-12-24 at 12 23 17 AM"
src="https://github.com/user-attachments/assets/f00e540b-30fe-49ac-b3df-7c7a5dc86d65"
/>

Release Notes:

- N/A
2024-12-24 04:26:09 +00:00
Danilo Leal
44a46e3713 assistant2: Don't render the context space if there's none (#22383)
Note the extra bottom space on the before version. That was because,
previously, the container that holds the context pills in an active
thread was being rendered even if there was no attached context.

| Before | After |
|--------|--------|
| <img width="1577" alt="Screenshot 2024-12-23 at 8 42 00 PM"
src="https://github.com/user-attachments/assets/b74bdc67-0a08-4d59-b1ec-43a00a59a373"
/> | <img width="1577" alt="Screenshot 2024-12-23 at 8 39 00 PM"
src="https://github.com/user-attachments/assets/1cbc340d-19df-4cce-8c0b-b671703a0ff5"
/> |

Release Notes:

- N/A
2024-12-24 04:26:04 +00:00
137 changed files with 2944 additions and 1547 deletions

View File

@@ -29,7 +29,7 @@ jobs:
outputs:
docs_only: ${{ steps.check_changes.outputs.docs_only }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Check for non-docs changes

364
Cargo.lock generated
View File

@@ -258,9 +258,9 @@ checksum = "34cd60c5e3152cef0a592f1b296f1cc93715d89d2551d85315828c3a09575ff4"
[[package]]
name = "anyhow"
version = "1.0.94"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
[[package]]
name = "approx"
@@ -388,7 +388,7 @@ dependencies = [
"ctor",
"db",
"editor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"feature_flags",
"fs",
"futures 0.3.31",
@@ -579,9 +579,9 @@ dependencies = [
[[package]]
name = "async-broadcast"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e"
checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
dependencies = [
"event-listener 5.3.1",
"event-listener-strategy",
@@ -1805,16 +1805,14 @@ dependencies = [
[[package]]
name = "blade-graphics"
version = "0.5.0"
source = "git+https://github.com/kvark/blade?rev=099555282605c7c4cca9e66a8f40148298347f80#099555282605c7c4cca9e66a8f40148298347f80"
version = "0.6.0"
source = "git+https://github.com/kvark/blade?rev=091a8401033847bb9b6ace3fcf70448d069621c5#091a8401033847bb9b6ace3fcf70448d069621c5"
dependencies = [
"ash",
"ash-window",
"bitflags 2.6.0",
"block",
"bytemuck",
"codespan-reporting",
"core-graphics-types 0.1.3",
"glow",
"gpu-alloc",
"gpu-alloc-ash",
@@ -1823,10 +1821,14 @@ dependencies = [
"khronos-egl",
"libloading",
"log",
"metal",
"mint",
"naga",
"objc",
"objc2",
"objc2-app-kit",
"objc2-foundation",
"objc2-metal",
"objc2-quartz-core",
"objc2-ui-kit",
"raw-window-handle",
"slab",
"wasm-bindgen",
@@ -1836,7 +1838,7 @@ dependencies = [
[[package]]
name = "blade-macros"
version = "0.3.0"
source = "git+https://github.com/kvark/blade?rev=099555282605c7c4cca9e66a8f40148298347f80#099555282605c7c4cca9e66a8f40148298347f80"
source = "git+https://github.com/kvark/blade?rev=091a8401033847bb9b6ace3fcf70448d069621c5#091a8401033847bb9b6ace3fcf70448d069621c5"
dependencies = [
"proc-macro2",
"quote",
@@ -1845,8 +1847,8 @@ dependencies = [
[[package]]
name = "blade-util"
version = "0.1.0"
source = "git+https://github.com/kvark/blade?rev=099555282605c7c4cca9e66a8f40148298347f80#099555282605c7c4cca9e66a8f40148298347f80"
version = "0.2.0"
source = "git+https://github.com/kvark/blade?rev=091a8401033847bb9b6ace3fcf70448d069621c5#091a8401033847bb9b6ace3fcf70448d069621c5"
dependencies = [
"blade-graphics",
"bytemuck",
@@ -1891,6 +1893,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block2"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
dependencies = [
"objc2",
]
[[package]]
name = "blocking"
version = "1.6.1"
@@ -1934,10 +1945,10 @@ dependencies = [
"editor",
"gpui",
"itertools 0.13.0",
"outline",
"theme",
"ui",
"workspace",
"zed_actions",
]
[[package]]
@@ -2141,7 +2152,7 @@ dependencies = [
"cap-primitives",
"cap-std",
"io-lifetimes 2.0.4",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -2169,7 +2180,7 @@ dependencies = [
"ipnet",
"maybe-owned",
"rustix 0.38.42",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
"winx",
]
@@ -2484,7 +2495,6 @@ dependencies = [
"exec",
"fork",
"ipc-channel",
"once_cell",
"parking_lot",
"paths",
"plist",
@@ -2511,7 +2521,6 @@ dependencies = [
"gpui",
"http_client",
"log",
"once_cell",
"parking_lot",
"paths",
"postage",
@@ -2654,7 +2663,7 @@ dependencies = [
"dashmap 6.1.0",
"derive_more",
"editor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"envy",
"extension",
"file_finder",
@@ -2810,7 +2819,7 @@ dependencies = [
"command_palette_hooks",
"ctor",
"editor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"fuzzy",
"go_to_line",
"gpui",
@@ -2919,7 +2928,6 @@ dependencies = [
"serde_json",
"settings",
"smol",
"ui",
"url",
"util",
"workspace",
@@ -3681,7 +3689,7 @@ dependencies = [
"collections",
"ctor",
"editor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"feature_flags",
"gpui",
"language",
@@ -3886,7 +3894,7 @@ dependencies = [
"ctor",
"db",
"emojis",
"env_logger 0.11.5",
"env_logger 0.11.6",
"feature_flags",
"file_icons",
"fs",
@@ -4083,9 +4091,9 @@ dependencies = [
[[package]]
name = "env_logger"
version = "0.11.5"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"
dependencies = [
"anstream",
"anstyle",
@@ -4137,7 +4145,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -4189,7 +4197,7 @@ dependencies = [
"client",
"clock",
"collections",
"env_logger 0.11.5",
"env_logger 0.11.6",
"feature_flags",
"fs",
"git",
@@ -4305,7 +4313,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"env_logger 0.11.5",
"env_logger 0.11.6",
"extension",
"fs",
"language",
@@ -4333,7 +4341,7 @@ dependencies = [
"collections",
"context_server_settings",
"ctor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"extension",
"fs",
"futures 0.3.31",
@@ -4526,7 +4534,7 @@ dependencies = [
"collections",
"ctor",
"editor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"file_icons",
"futures 0.3.31",
"fuzzy",
@@ -4804,7 +4812,7 @@ checksum = "5e2e6123af26f0f2c51cc66869137080199406754903cc926a7690401ce09cb4"
dependencies = [
"io-lifetimes 2.0.4",
"rustix 0.38.42",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -5327,7 +5335,7 @@ dependencies = [
"ctor",
"derive_more",
"embed-resource",
"env_logger 0.11.5",
"env_logger 0.11.6",
"etagere",
"filedescriptor",
"flume",
@@ -5344,6 +5352,8 @@ dependencies = [
"metal",
"num_cpus",
"objc",
"objc2",
"objc2-metal",
"oo7",
"open",
"parking",
@@ -6378,7 +6388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65"
dependencies = [
"io-lifetimes 2.0.4",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -6681,7 +6691,7 @@ dependencies = [
"collections",
"ctor",
"ec4rs",
"env_logger 0.11.5",
"env_logger 0.11.6",
"fs",
"futures 0.3.31",
"fuzzy",
@@ -6848,7 +6858,7 @@ dependencies = [
"collections",
"copilot",
"editor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"futures 0.3.31",
"gpui",
"itertools 0.13.0",
@@ -6946,9 +6956,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "libc"
version = "0.2.168"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libdbus-sys"
@@ -6989,7 +6999,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
"windows-targets 0.52.6",
"windows-targets 0.48.5",
]
[[package]]
@@ -7033,7 +7043,7 @@ dependencies = [
[[package]]
name = "libwebrtc"
version = "0.3.7"
source = "git+https://github.com/zed-industries/rust-sdks?rev=799f10133d93ba2a88642cd480d01ec4da53408c#799f10133d93ba2a88642cd480d01ec4da53408c"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=060964da10574cd9bf06463a53bf6e0769c5c45e#060964da10574cd9bf06463a53bf6e0769c5c45e"
dependencies = [
"cxx",
"jni",
@@ -7124,7 +7134,7 @@ checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
[[package]]
name = "livekit"
version = "0.7.0"
source = "git+https://github.com/zed-industries/rust-sdks?rev=799f10133d93ba2a88642cd480d01ec4da53408c#799f10133d93ba2a88642cd480d01ec4da53408c"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=060964da10574cd9bf06463a53bf6e0769c5c45e#060964da10574cd9bf06463a53bf6e0769c5c45e"
dependencies = [
"chrono",
"futures-util",
@@ -7146,7 +7156,7 @@ dependencies = [
[[package]]
name = "livekit-api"
version = "0.4.1"
source = "git+https://github.com/zed-industries/rust-sdks?rev=799f10133d93ba2a88642cd480d01ec4da53408c#799f10133d93ba2a88642cd480d01ec4da53408c"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=060964da10574cd9bf06463a53bf6e0769c5c45e#060964da10574cd9bf06463a53bf6e0769c5c45e"
dependencies = [
"async-tungstenite 0.25.1",
"futures-util",
@@ -7171,7 +7181,7 @@ dependencies = [
[[package]]
name = "livekit-protocol"
version = "0.3.6"
source = "git+https://github.com/zed-industries/rust-sdks?rev=799f10133d93ba2a88642cd480d01ec4da53408c#799f10133d93ba2a88642cd480d01ec4da53408c"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=060964da10574cd9bf06463a53bf6e0769c5c45e#060964da10574cd9bf06463a53bf6e0769c5c45e"
dependencies = [
"futures-util",
"livekit-runtime",
@@ -7188,7 +7198,7 @@ dependencies = [
[[package]]
name = "livekit-runtime"
version = "0.3.1"
source = "git+https://github.com/zed-industries/rust-sdks?rev=799f10133d93ba2a88642cd480d01ec4da53408c#799f10133d93ba2a88642cd480d01ec4da53408c"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=060964da10574cd9bf06463a53bf6e0769c5c45e#060964da10574cd9bf06463a53bf6e0769c5c45e"
dependencies = [
"async-io 2.4.0",
"async-std",
@@ -7321,7 +7331,7 @@ dependencies = [
"async-pipe",
"collections",
"ctor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"futures 0.3.31",
"gpui",
"log",
@@ -7384,7 +7394,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"assets",
"env_logger 0.11.5",
"env_logger 0.11.6",
"futures 0.3.31",
"gpui",
"language",
@@ -7499,7 +7509,7 @@ dependencies = [
"clap",
"clap_complete",
"elasticlunr-rs",
"env_logger 0.11.5",
"env_logger 0.11.6",
"futures-util",
"handlebars 6.2.0",
"ignore",
@@ -7580,7 +7590,8 @@ dependencies = [
[[package]]
name = "metal"
version = "0.30.0"
source = "git+https://github.com/gfx-rs/metal-rs?rev=ef768ff9d742ae6a0f4e83ddc8031264e7d460c4#ef768ff9d742ae6a0f4e83ddc8031264e7d460c4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3572083504c43e14aec05447f8a3d57cce0f66d7a3c1b9058572eca4d70ab9"
dependencies = [
"bitflags 2.6.0",
"block",
@@ -7688,7 +7699,7 @@ dependencies = [
"clock",
"collections",
"ctor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"futures 0.3.31",
"gpui",
"itertools 0.13.0",
@@ -7720,8 +7731,9 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
[[package]]
name = "naga"
version = "23.0.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=1a643291c2e8854ba7e4f5445a4388202731bfa1#1a643291c2e8854ba7e4f5445a4388202731bfa1"
version = "23.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f"
dependencies = [
"arrayvec",
"bit-set 0.8.0",
@@ -8151,6 +8163,208 @@ dependencies = [
"malloc_buf",
]
[[package]]
name = "objc-sys"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
[[package]]
name = "objc2"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
dependencies = [
"objc-sys",
"objc2-encode",
]
[[package]]
name = "objc2-app-kit"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
dependencies = [
"bitflags 2.6.0",
"block2",
"libc",
"objc2",
"objc2-core-data",
"objc2-core-image",
"objc2-foundation",
"objc2-quartz-core",
]
[[package]]
name = "objc2-cloud-kit"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
dependencies = [
"bitflags 2.6.0",
"block2",
"objc2",
"objc2-core-location",
"objc2-foundation",
]
[[package]]
name = "objc2-contacts"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
dependencies = [
"block2",
"objc2",
"objc2-foundation",
]
[[package]]
name = "objc2-core-data"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
dependencies = [
"bitflags 2.6.0",
"block2",
"objc2",
"objc2-foundation",
]
[[package]]
name = "objc2-core-image"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
dependencies = [
"block2",
"objc2",
"objc2-foundation",
"objc2-metal",
]
[[package]]
name = "objc2-core-location"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
dependencies = [
"block2",
"objc2",
"objc2-contacts",
"objc2-foundation",
]
[[package]]
name = "objc2-encode"
version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8"
[[package]]
name = "objc2-foundation"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
"bitflags 2.6.0",
"block2",
"libc",
"objc2",
]
[[package]]
name = "objc2-link-presentation"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
dependencies = [
"block2",
"objc2",
"objc2-app-kit",
"objc2-foundation",
]
[[package]]
name = "objc2-metal"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
dependencies = [
"bitflags 2.6.0",
"block2",
"objc2",
"objc2-foundation",
]
[[package]]
name = "objc2-quartz-core"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
dependencies = [
"bitflags 2.6.0",
"block2",
"objc2",
"objc2-foundation",
"objc2-metal",
]
[[package]]
name = "objc2-symbols"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc"
dependencies = [
"objc2",
"objc2-foundation",
]
[[package]]
name = "objc2-ui-kit"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
dependencies = [
"bitflags 2.6.0",
"block2",
"objc2",
"objc2-cloud-kit",
"objc2-core-data",
"objc2-core-image",
"objc2-core-location",
"objc2-foundation",
"objc2-link-presentation",
"objc2-quartz-core",
"objc2-symbols",
"objc2-uniform-type-identifiers",
"objc2-user-notifications",
]
[[package]]
name = "objc2-uniform-type-identifiers"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
dependencies = [
"block2",
"objc2",
"objc2-foundation",
]
[[package]]
name = "objc2-user-notifications"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
dependencies = [
"bitflags 2.6.0",
"block2",
"objc2",
"objc2-core-location",
"objc2-foundation",
]
[[package]]
name = "object"
version = "0.36.5"
@@ -8415,6 +8629,7 @@ dependencies = [
"ui",
"util",
"workspace",
"zed_actions",
]
[[package]]
@@ -9194,7 +9409,7 @@ dependencies = [
"anyhow",
"ctor",
"editor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"gpui",
"menu",
"serde",
@@ -9551,7 +9766,7 @@ dependencies = [
"client",
"clock",
"collections",
"env_logger 0.11.5",
"env_logger 0.11.6",
"fancy-regex 0.14.0",
"fs",
"futures 0.3.31",
@@ -9933,7 +10148,7 @@ dependencies = [
"once_cell",
"socket2 0.5.8",
"tracing",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -10266,7 +10481,6 @@ name = "release_channel"
version = "0.1.0"
dependencies = [
"gpui",
"once_cell",
]
[[package]]
@@ -10308,7 +10522,7 @@ dependencies = [
"clap",
"client",
"clock",
"env_logger 0.11.5",
"env_logger 0.11.6",
"extension",
"extension_host",
"fork",
@@ -10367,7 +10581,7 @@ dependencies = [
"collections",
"command_palette_hooks",
"editor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"feature_flags",
"file_icons",
"futures 0.3.31",
@@ -10642,7 +10856,7 @@ dependencies = [
"arrayvec",
"criterion",
"ctor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"gpui",
"log",
"rand 0.8.5",
@@ -10668,7 +10882,7 @@ dependencies = [
"base64 0.22.1",
"chrono",
"collections",
"env_logger 0.11.5",
"env_logger 0.11.6",
"futures 0.3.31",
"gpui",
"parking_lot",
@@ -10834,7 +11048,7 @@ dependencies = [
"libc",
"linux-raw-sys 0.4.14",
"once_cell",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -11252,7 +11466,7 @@ dependencies = [
"client",
"clock",
"collections",
"env_logger 0.11.5",
"env_logger 0.11.6",
"feature_flags",
"fs",
"futures 0.3.31",
@@ -12254,7 +12468,7 @@ version = "0.1.0"
dependencies = [
"arrayvec",
"ctor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"log",
"rand 0.8.5",
"rayon",
@@ -12268,7 +12482,7 @@ dependencies = [
"client",
"collections",
"editor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"futures 0.3.31",
"gpui",
"http_client",
@@ -12554,7 +12768,7 @@ dependencies = [
"fd-lock",
"io-lifetimes 2.0.4",
"rustix 0.38.42",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
"winx",
]
@@ -12566,7 +12780,7 @@ dependencies = [
"collections",
"ctor",
"editor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"gpui",
"language",
"menu",
@@ -12686,7 +12900,7 @@ dependencies = [
"fastrand 2.3.0",
"once_cell",
"rustix 0.38.42",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -12787,7 +13001,7 @@ dependencies = [
"clock",
"collections",
"ctor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"gpui",
"http_client",
"log",
@@ -14837,7 +15051,7 @@ dependencies = [
[[package]]
name = "webrtc-sys"
version = "0.3.5"
source = "git+https://github.com/zed-industries/rust-sdks?rev=799f10133d93ba2a88642cd480d01ec4da53408c#799f10133d93ba2a88642cd480d01ec4da53408c"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=060964da10574cd9bf06463a53bf6e0769c5c45e#060964da10574cd9bf06463a53bf6e0769c5c45e"
dependencies = [
"cc",
"cxx",
@@ -14850,7 +15064,7 @@ dependencies = [
[[package]]
name = "webrtc-sys-build"
version = "0.3.5"
source = "git+https://github.com/zed-industries/rust-sdks?rev=799f10133d93ba2a88642cd480d01ec4da53408c#799f10133d93ba2a88642cd480d01ec4da53408c"
source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=060964da10574cd9bf06463a53bf6e0769c5c45e#060964da10574cd9bf06463a53bf6e0769c5c45e"
dependencies = [
"fs2",
"regex",
@@ -14989,7 +15203,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.48.0",
]
[[package]]
@@ -15438,7 +15652,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d"
dependencies = [
"bitflags 2.6.0",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -15586,7 +15800,7 @@ dependencies = [
"collections",
"db",
"derive_more",
"env_logger 0.11.5",
"env_logger 0.11.6",
"fs",
"futures 0.3.31",
"gpui",
@@ -15622,7 +15836,7 @@ dependencies = [
"anyhow",
"clock",
"collections",
"env_logger 0.11.5",
"env_logger 0.11.6",
"fs",
"futures 0.3.31",
"fuzzy",
@@ -16019,7 +16233,7 @@ dependencies = [
"db",
"diagnostics",
"editor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"extension",
"extension_host",
"extensions_ui",
@@ -16445,7 +16659,7 @@ dependencies = [
"collections",
"ctor",
"editor",
"env_logger 0.11.5",
"env_logger 0.11.6",
"futures 0.3.31",
"gpui",
"http_client",

View File

@@ -355,9 +355,9 @@ async-watch = "0.3.1"
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
base64 = "0.22"
bitflags = "2.6.0"
blade-graphics = { git = "https://github.com/kvark/blade", rev = "099555282605c7c4cca9e66a8f40148298347f80" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "099555282605c7c4cca9e66a8f40148298347f80" }
blade-util = { git = "https://github.com/kvark/blade", rev = "099555282605c7c4cca9e66a8f40148298347f80" }
blade-graphics = { git = "https://github.com/kvark/blade", rev = "091a8401033847bb9b6ace3fcf70448d069621c5" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "091a8401033847bb9b6ace3fcf70448d069621c5" }
blade-util = { git = "https://github.com/kvark/blade", rev = "091a8401033847bb9b6ace3fcf70448d069621c5" }
blake3 = "1.5.3"
bytes = "1.0"
cargo_metadata = "0.19"
@@ -401,14 +401,13 @@ jupyter-websocket-client = { version = "0.8.0" }
libc = "0.2"
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
linkify = "0.10.0"
livekit = { git = "https://github.com/zed-industries/rust-sdks", rev="799f10133d93ba2a88642cd480d01ec4da53408c", features = ["dispatcher", "services-dispatcher", "rustls-tls-native-roots"], default-features = false }
livekit = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev="060964da10574cd9bf06463a53bf6e0769c5c45e", features = ["dispatcher", "services-dispatcher", "rustls-tls-native-roots"], default-features = false }
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
markup5ever_rcdom = "0.3.0"
nanoid = "0.4"
nbformat = { version = "0.9.0" }
nix = "0.29"
num-format = "0.4.4"
once_cell = "1.19.0"
ordered-float = "2.1.1"
palette = { version = "0.7.5", default-features = false, features = ["std"] }
parking_lot = "0.12.1"
@@ -525,12 +524,7 @@ wasmtime-wasi = "24"
which = "6.0.0"
wit-component = "0.201"
zstd = "0.11"
# Custom metal-rs is only needed for "macos-blade" feature of GPUI
#TODO: switch to crates once these are published:
# - https://github.com/gfx-rs/metal-rs/pull/335
# - https://github.com/gfx-rs/metal-rs/pull/336
# - https://github.com/gfx-rs/metal-rs/pull/337
metal = { git = "https://github.com/gfx-rs/metal-rs", rev = "ef768ff9d742ae6a0f4e83ddc8031264e7d460c4" }
metal = "0.30"
[workspace.dependencies.async-stripe]
git = "https://github.com/zed-industries/async-stripe"

View File

@@ -260,6 +260,8 @@
"ctrl-f4": "pane::CloseActiveItem",
"alt-ctrl-t": ["pane::CloseInactiveItems", { "close_pinned": false }],
"alt-ctrl-shift-w": "workspace::CloseInactiveTabsAndPanes",
"ctrl-k e": ["pane::CloseItemsToTheLeft", { "close_pinned": false }],
"ctrl-k t": ["pane::CloseItemsToTheRight", { "close_pinned": false }],
"ctrl-k u": ["pane::CloseCleanItems", { "close_pinned": false }],
"ctrl-k w": ["pane::CloseAllItems", { "close_pinned": false }],
"ctrl-shift-f": "project_search::ToggleFocus",

View File

@@ -327,6 +327,8 @@
"cmd-w": "pane::CloseActiveItem",
"alt-cmd-t": ["pane::CloseInactiveItems", { "close_pinned": false }],
"ctrl-alt-cmd-w": "workspace::CloseInactiveTabsAndPanes",
"cmd-k e": ["pane::CloseItemsToTheLeft", { "close_pinned": false }],
"cmd-k t": ["pane::CloseItemsToTheRight", { "close_pinned": false }],
"cmd-k u": ["pane::CloseCleanItems", { "close_pinned": false }],
"cmd-k cmd-w": ["pane::CloseAllItems", { "close_pinned": false }],
"cmd-f": "project_search::ToggleFocus",

View File

@@ -389,9 +389,6 @@
"bindings": {
"w": "vim::Word",
"shift-w": ["vim::Word", { "ignorePunctuation": true }],
// Subword TextObject
// "w": "vim::Subword",
// "shift-w": ["vim::Subword", { "ignorePunctuation": true }],
"t": "vim::Tag",
"s": "vim::Sentence",
"p": "vim::Paragraph",

View File

@@ -1103,6 +1103,9 @@
"prettier": {
"allowed": true
}
},
"Zig": {
"language_servers": ["zls", "..."]
}
},
// Different settings for specific language models.

View File

@@ -1458,6 +1458,10 @@ impl Panel for AssistantPanel {
fn toggle_action(&self) -> Box<dyn Action> {
Box::new(ToggleFocus)
}
fn activation_priority(&self) -> u32 {
4
}
}
impl EventEmitter<PanelEvent> for AssistantPanel {}
@@ -4966,8 +4970,8 @@ fn fold_toggle(
) -> impl Fn(
MultiBufferRow,
bool,
Arc<dyn Fn(bool, &mut WindowContext<'_>) + Send + Sync>,
&mut WindowContext<'_>,
Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
&mut WindowContext,
) -> AnyElement {
move |row, is_folded, fold, _cx| {
Disclosure::new((name, row.0 as u64), !is_folded)

View File

@@ -149,6 +149,7 @@ impl SlashCommandCompletionProvider {
server_id: LanguageServerId(0),
lsp_completion: Default::default(),
confirm,
resolved: true,
})
})
.collect()
@@ -242,6 +243,7 @@ impl SlashCommandCompletionProvider {
server_id: LanguageServerId(0),
lsp_completion: Default::default(),
confirm,
resolved: true,
}
})
.collect())
@@ -330,16 +332,6 @@ impl CompletionProvider for SlashCommandCompletionProvider {
Task::ready(Ok(true))
}
fn apply_additional_edits_for_completion(
&self,
_: Model<Buffer>,
_: project::Completion,
_: bool,
_: &mut ViewContext<Editor>,
) -> Task<Result<Option<language::Transaction>>> {
Task::ready(Ok(None))
}
fn is_completion_trigger(
&self,
buffer: &Model<Buffer>,

View File

@@ -5,7 +5,7 @@ use assistant_slash_command::{
};
use feature_flags::FeatureFlag;
use futures::StreamExt;
use gpui::{AppContext, AsyncAppContext, Task, WeakView};
use gpui::{AppContext, AsyncAppContext, AsyncWindowContext, Task, WeakView, WindowContext};
use language::{CodeLabel, LspAdapterDelegate};
use language_model::{
LanguageModelCompletionEvent, LanguageModelRegistry, LanguageModelRequest,
@@ -14,7 +14,7 @@ use language_model::{
use semantic_index::{FileSummary, SemanticDb};
use smol::channel;
use std::sync::{atomic::AtomicBool, Arc};
use ui::{prelude::*, BorrowAppContext, WindowContext};
use ui::{prelude::*, BorrowAppContext};
use util::ResultExt;
use workspace::Workspace;
@@ -115,7 +115,7 @@ impl SlashCommand for AutoCommand {
return Task::ready(Err(anyhow!("no project indexer")));
};
let task = cx.spawn(|cx: gpui::AsyncWindowContext| async move {
let task = cx.spawn(|cx: AsyncWindowContext| async move {
let summaries = project_index
.read_with(&cx, |project_index, cx| project_index.all_summaries(cx))?
.await?;

View File

@@ -281,7 +281,7 @@ fn tab_items_for_queries(
fn active_item_buffer(
workspace: &mut Workspace,
cx: &mut ui::ViewContext<Workspace>,
cx: &mut ViewContext<Workspace>,
) -> anyhow::Result<BufferSnapshot> {
let active_editor = workspace
.active_item(cx)

View File

@@ -27,8 +27,8 @@ enum SlashCommandEntry {
Info(SlashCommandInfo),
Advert {
name: SharedString,
renderer: fn(&mut WindowContext<'_>) -> AnyElement,
on_confirm: fn(&mut WindowContext<'_>),
renderer: fn(&mut WindowContext) -> AnyElement,
on_confirm: fn(&mut WindowContext),
},
}

View File

@@ -3,8 +3,8 @@ use std::sync::Arc;
use assistant_tool::ToolWorkingSet;
use collections::HashMap;
use gpui::{
list, AbsoluteLength, AnyElement, AppContext, CornersRefinement, DefiniteLength,
EdgesRefinement, Empty, Length, ListAlignment, ListState, Model, StyleRefinement, Subscription,
list, AbsoluteLength, AnyElement, AppContext, DefiniteLength, EdgesRefinement, Empty, Length,
ListAlignment, ListOffset, ListState, Model, StyleRefinement, Subscription,
TextStyleRefinement, View, WeakView,
};
use language::LanguageRegistry;
@@ -108,7 +108,7 @@ impl ActiveThread {
selection_background_color: cx.theme().players().local().selection,
code_block: StyleRefinement {
margin: EdgesRefinement {
top: Some(Length::Definite(rems(0.5).into())),
top: Some(Length::Definite(rems(1.0).into())),
left: Some(Length::Definite(rems(0.).into())),
right: Some(Length::Definite(rems(0.).into())),
bottom: Some(Length::Definite(rems(1.).into())),
@@ -120,19 +120,13 @@ impl ActiveThread {
bottom: Some(DefiniteLength::Absolute(AbsoluteLength::Pixels(Pixels(8.)))),
},
background: Some(colors.editor_foreground.opacity(0.01).into()),
border_color: Some(colors.border_variant.opacity(0.25)),
border_color: Some(colors.border_variant.opacity(0.3)),
border_widths: EdgesRefinement {
top: Some(AbsoluteLength::Pixels(Pixels(1.0))),
left: Some(AbsoluteLength::Pixels(Pixels(1.))),
right: Some(AbsoluteLength::Pixels(Pixels(1.))),
bottom: Some(AbsoluteLength::Pixels(Pixels(1.))),
},
corner_radii: CornersRefinement {
top_left: Some(AbsoluteLength::Pixels(Pixels(2.))),
top_right: Some(AbsoluteLength::Pixels(Pixels(2.))),
bottom_right: Some(AbsoluteLength::Pixels(Pixels(2.))),
bottom_left: Some(AbsoluteLength::Pixels(Pixels(2.))),
},
text: Some(TextStyleRefinement {
font_family: Some(theme_settings.buffer_font.family.clone()),
font_size: Some(buffer_font_size.into()),
@@ -159,6 +153,10 @@ impl ActiveThread {
)
});
self.rendered_messages_by_id.insert(*id, markdown);
self.list_state.scroll_to(ListOffset {
item_ix: old_len,
offset_in_item: Pixels(0.0),
});
}
fn handle_thread_event(
@@ -272,15 +270,20 @@ impl ActiveThread {
),
),
)
.child(v_flex().p_2p5().text_ui(cx).child(markdown.clone()))
.child(div().p_2p5().text_ui(cx).child(markdown.clone()))
.when_some(context, |parent, context| {
parent.child(
h_flex().flex_wrap().gap_1().p_1p5().children(
context
.iter()
.map(|context| ContextPill::new(context.clone())),
),
)
if !context.is_empty() {
parent.child(
h_flex()
.flex_wrap()
.gap_1()
.px_1p5()
.pb_1p5()
.children(context.iter().map(|c| ContextPill::new(c.clone()))),
)
} else {
parent
}
}),
)
.into_any()

View File

@@ -286,6 +286,10 @@ impl Panel for AssistantPanel {
fn toggle_action(&self) -> Box<dyn Action> {
Box::new(ToggleFocus)
}
fn activation_priority(&self) -> u32 {
3
}
}
impl AssistantPanel {

View File

@@ -21,7 +21,7 @@ pub struct Context {
pub text: SharedString,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ContextKind {
File,
Directory,

View File

@@ -176,7 +176,7 @@ impl PickerDelegate for FetchContextPickerDelegate {
fn set_selected_index(&mut self, _ix: usize, _cx: &mut ViewContext<Picker<Self>>) {}
fn placeholder_text(&self, _cx: &mut ui::WindowContext) -> Arc<str> {
fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
"Enter a URL…".into()
}

View File

@@ -9,6 +9,7 @@ use crate::context_store::ContextStore;
use crate::thread_store::ThreadStore;
use crate::ui::ContextPill;
use crate::ToggleContextPicker;
use settings::Settings;
pub struct ContextStrip {
context_store: Model<ContextStore>,
@@ -45,7 +46,7 @@ impl ContextStrip {
impl Render for ContextStrip {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let context = self.context_store.read(cx).context();
let context = self.context_store.read(cx).context().clone();
let context_picker = self.context_picker.clone();
let focus_handle = self.focus_handle.clone();
@@ -76,6 +77,29 @@ impl Render for ContextStrip {
})
.with_handle(self.context_picker_menu_handle.clone()),
)
.when(context.is_empty(), {
|parent| {
parent.child(
h_flex()
.id("no-content-info")
.ml_1p5()
.gap_2()
.font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
.text_size(TextSize::Small.rems(cx))
.text_color(cx.theme().colors().text_muted)
.child("Add Context")
.children(
ui::KeyBinding::for_action_in(
&ToggleContextPicker,
&self.focus_handle,
cx,
)
.map(|binding| binding.into_any_element()),
)
.opacity(0.5),
)
}
})
.children(context.iter().map(|context| {
ContextPill::new(context.clone()).on_remove({
let context = context.clone();
@@ -88,19 +112,21 @@ impl Render for ContextStrip {
}))
})
}))
.when(!context.is_empty(), |parent| {
parent.child(
IconButton::new("remove-all-context", IconName::Eraser)
.icon_size(IconSize::Small)
.tooltip(move |cx| Tooltip::text("Remove All Context", cx))
.on_click({
let context_store = self.context_store.clone();
cx.listener(move |_this, _event, cx| {
context_store.update(cx, |this, _cx| this.clear());
cx.notify();
})
}),
)
.when(!context.is_empty(), {
move |parent| {
parent.child(
IconButton::new("remove-all-context", IconName::Eraser)
.icon_size(IconSize::Small)
.tooltip(move |cx| Tooltip::text("Remove All Context", cx))
.on_click({
let context_store = self.context_store.clone();
cx.listener(move |_this, _event, cx| {
context_store.update(cx, |this, _cx| this.clear());
cx.notify();
})
}),
)
}
})
}
}

View File

@@ -54,7 +54,7 @@ impl MessageEditor {
let editor = cx.new_view(|cx| {
let mut editor = Editor::auto_height(10, cx);
editor.set_placeholder_text("Ask anything, @ to add context", cx);
editor.set_placeholder_text("Ask anything", cx);
editor.set_show_indent_guides(false, cx);
editor
@@ -220,67 +220,73 @@ impl Render for MessageEditor {
.p_2()
.bg(bg_color)
.child(self.context_strip.clone())
.child({
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: cx.theme().colors().editor_foreground,
font_family: settings.ui_font.family.clone(),
font_features: settings.ui_font.features.clone(),
font_size: font_size.into(),
font_weight: settings.ui_font.weight,
line_height: line_height.into(),
..Default::default()
};
.child(
v_flex()
.gap_4()
.child({
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: cx.theme().colors().text,
font_family: settings.ui_font.family.clone(),
font_features: settings.ui_font.features.clone(),
font_size: font_size.into(),
font_weight: settings.ui_font.weight,
line_height: line_height.into(),
..Default::default()
};
EditorElement::new(
&self.editor,
EditorStyle {
background: bg_color,
local_player: cx.theme().players().local(),
text: text_style,
..Default::default()
},
)
})
.child(
PopoverMenu::new("inline-context-picker")
.menu(move |_cx| Some(inline_context_picker.clone()))
.attach(gpui::Corner::TopLeft)
.anchor(gpui::Corner::BottomLeft)
.offset(gpui::Point {
x: px(0.0),
y: px(-16.0),
EditorElement::new(
&self.editor,
EditorStyle {
background: bg_color,
local_player: cx.theme().players().local(),
text: text_style,
..Default::default()
},
)
})
.with_handle(self.inline_context_picker_menu_handle.clone()),
)
.child(
h_flex()
.justify_between()
.child(SwitchWithLabel::new(
"use-tools",
Label::new("Tools"),
self.use_tools.into(),
cx.listener(|this, selection, _cx| {
this.use_tools = match selection {
ToggleState::Selected => true,
ToggleState::Unselected | ToggleState::Indeterminate => false,
};
}),
))
.child(
h_flex().gap_1().child(self.model_selector.clone()).child(
ButtonLike::new("chat")
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.child(Label::new("Submit"))
.children(
KeyBinding::for_action_in(&Chat, &focus_handle, cx)
.map(|binding| binding.into_any_element()),
)
.on_click(move |_event, cx| {
focus_handle.dispatch_action(&Chat, cx);
PopoverMenu::new("inline-context-picker")
.menu(move |_cx| Some(inline_context_picker.clone()))
.attach(gpui::Corner::TopLeft)
.anchor(gpui::Corner::BottomLeft)
.offset(gpui::Point {
x: px(0.0),
y: px(-16.0),
})
.with_handle(self.inline_context_picker_menu_handle.clone()),
)
.child(
h_flex()
.justify_between()
.child(SwitchWithLabel::new(
"use-tools",
Label::new("Tools"),
self.use_tools.into(),
cx.listener(|this, selection, _cx| {
this.use_tools = match selection {
ToggleState::Selected => true,
ToggleState::Unselected | ToggleState::Indeterminate => {
false
}
};
}),
),
))
.child(
h_flex().gap_1().child(self.model_selector.clone()).child(
ButtonLike::new("chat")
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.child(Label::new("Submit"))
.children(
KeyBinding::for_action_in(&Chat, &focus_handle, cx)
.map(|binding| binding.into_any_element()),
)
.on_click(move |_event, cx| {
focus_handle.dispatch_action(&Chat, cx);
}),
),
),
),
)
}

View File

@@ -3,7 +3,7 @@ use std::rc::Rc;
use gpui::ClickEvent;
use ui::{prelude::*, IconButtonShape};
use crate::context::Context;
use crate::context::{Context, ContextKind};
#[derive(IntoElement)]
pub struct ContextPill {
@@ -27,15 +27,28 @@ impl ContextPill {
impl RenderOnce for ContextPill {
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let padding_right = if self.on_remove.is_some() {
px(2.)
} else {
px(4.)
};
let icon = match self.context.kind {
ContextKind::File => IconName::File,
ContextKind::Directory => IconName::Folder,
ContextKind::FetchedUrl => IconName::Globe,
ContextKind::Thread => IconName::MessageCircle,
};
h_flex()
.gap_1()
.pl_1p5()
.pr_0p5()
.pl_1()
.pr(padding_right)
.pb(px(1.))
.border_1()
.border_color(cx.theme().colors().border.opacity(0.5))
.bg(cx.theme().colors().element_background)
.rounded_md()
.child(Icon::new(icon).size(IconSize::XSmall).color(Color::Muted))
.child(Label::new(self.context.name.clone()).size(LabelSize::Small))
.when_some(self.on_remove, |parent, on_remove| {
parent.child(

View File

@@ -18,7 +18,7 @@ pub struct UpdateNotification {
impl EventEmitter<DismissEvent> for UpdateNotification {}
impl Render for UpdateNotification {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let app_name = ReleaseChannel::global(cx).display_name();
v_flex()

View File

@@ -16,10 +16,10 @@ doctest = false
editor.workspace = true
gpui.workspace = true
itertools.workspace = true
outline.workspace = true
theme.workspace = true
ui.workspace = true
workspace.workspace = true
zed_actions.workspace = true
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }

View File

@@ -102,8 +102,11 @@ impl Render for Breadcrumbs {
.on_click({
let editor = editor.clone();
move |_, cx| {
if let Some(editor) = editor.upgrade() {
outline::toggle(editor, &editor::actions::ToggleOutline, cx)
if let Some((editor, callback)) = editor
.upgrade()
.zip(zed_actions::outline::TOGGLE_OUTLINE.get())
{
callback(editor.to_any(), cx);
}
}
})
@@ -112,14 +115,14 @@ impl Render for Breadcrumbs {
let focus_handle = editor.read(cx).focus_handle(cx);
Tooltip::for_action_in(
"Show Symbol Outline",
&editor::actions::ToggleOutline,
&zed_actions::outline::ToggleOutline,
&focus_handle,
cx,
)
} else {
Tooltip::for_action(
"Show Symbol Outline",
&editor::actions::ToggleOutline,
&zed_actions::outline::ToggleOutline,
cx,
)
}

View File

@@ -25,7 +25,6 @@ anyhow.workspace = true
clap.workspace = true
collections.workspace = true
ipc-channel = "0.19"
once_cell.workspace = true
parking_lot.workspace = true
paths.workspace = true
release_channel.workspace = true

View File

@@ -257,6 +257,7 @@ fn main() -> Result<()> {
if args.foreground {
app.run_foreground(url)?;
} else {
eprintln!("Logs are written to {:?}", paths::log_file());
app.launch(url)?;
sender.join().unwrap()?;
pipe_handle.join().unwrap()?;
@@ -277,6 +278,7 @@ mod linux {
os::unix::net::{SocketAddr, UnixDatagram},
path::{Path, PathBuf},
process::{self, ExitStatus},
sync::LazyLock,
thread,
time::Duration,
};
@@ -284,12 +286,11 @@ mod linux {
use anyhow::anyhow;
use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
use fork::Fork;
use once_cell::sync::Lazy;
use crate::{Detect, InstalledApp};
static RELEASE_CHANNEL: Lazy<String> =
Lazy::new(|| include_str!("../../zed/RELEASE_CHANNEL").trim().to_string());
static RELEASE_CHANNEL: LazyLock<String> =
LazyLock::new(|| include_str!("../../zed/RELEASE_CHANNEL").trim().to_string());
struct App(PathBuf);

View File

@@ -27,7 +27,6 @@ futures.workspace = true
gpui.workspace = true
http_client.workspace = true
log.workspace = true
once_cell.workspace = true
paths.workspace = true
parking_lot.workspace = true
postage.workspace = true

View File

@@ -8,7 +8,6 @@ use futures::channel::mpsc;
use futures::{Future, StreamExt};
use gpui::{AppContext, BackgroundExecutor, Task};
use http_client::{self, AsyncBody, HttpClient, HttpClientWithUrl, Method, Request};
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use release_channel::ReleaseChannel;
use settings::{Settings, SettingsStore};
@@ -16,7 +15,12 @@ use sha2::{Digest, Sha256};
use std::fs::File;
use std::io::Write;
use std::time::Instant;
use std::{env, mem, path::PathBuf, sync::Arc, time::Duration};
use std::{
env, mem,
path::PathBuf,
sync::{Arc, LazyLock},
time::Duration,
};
use telemetry_events::{
AppEvent, AssistantEvent, CallEvent, EditEvent, Event, EventRequestBody, EventWrapper,
InlineCompletionEvent,
@@ -84,7 +88,7 @@ const FLUSH_INTERVAL: Duration = Duration::from_secs(1);
#[cfg(not(debug_assertions))]
const FLUSH_INTERVAL: Duration = Duration::from_secs(60 * 5);
static ZED_CLIENT_CHECKSUM_SEED: Lazy<Option<Vec<u8>>> = Lazy::new(|| {
static ZED_CLIENT_CHECKSUM_SEED: LazyLock<Option<Vec<u8>>> = LazyLock::new(|| {
option_env!("ZED_CLIENT_CHECKSUM_SEED")
.map(|s| s.as_bytes().into())
.or_else(|| {

View File

@@ -1096,7 +1096,7 @@ impl FocusableView for ChatPanel {
}
impl Panel for ChatPanel {
fn position(&self, cx: &gpui::WindowContext) -> DockPosition {
fn position(&self, cx: &WindowContext) -> DockPosition {
ChatPanelSettings::get_global(cx).dock
}
@@ -1112,7 +1112,7 @@ impl Panel for ChatPanel {
);
}
fn size(&self, cx: &gpui::WindowContext) -> Pixels {
fn size(&self, cx: &WindowContext) -> Pixels {
self.width
.unwrap_or_else(|| ChatPanelSettings::get_global(cx).default_width)
}
@@ -1141,6 +1141,7 @@ impl Panel for ChatPanel {
ChatPanelButton::WhenInCall => ActiveCall::global(cx)
.read(cx)
.room()
.filter(|room| room.read(cx).contains_guests())
.map(|_| ui::IconName::MessageBubbles),
}
}
@@ -1159,6 +1160,10 @@ impl Panel for ChatPanel {
.room()
.is_some_and(|room| room.read(cx).contains_guests())
}
fn activation_priority(&self) -> u32 {
7
}
}
impl EventEmitter<PanelEvent> for ChatPanel {}

View File

@@ -79,16 +79,6 @@ impl CompletionProvider for MessageEditorCompletionProvider {
Task::ready(Ok(false))
}
fn apply_additional_edits_for_completion(
&self,
_buffer: Model<Buffer>,
_completion: Completion,
_push_to_history: bool,
_cx: &mut ViewContext<Editor>,
) -> Task<Result<Option<language::Transaction>>> {
Task::ready(Ok(None))
}
fn is_completion_trigger(
&self,
_buffer: &Model<Buffer>,
@@ -319,6 +309,7 @@ impl MessageEditor {
server_id: LanguageServerId(0), // TODO: Make this optional or something?
lsp_completion: Default::default(), // TODO: Make this optional or something?
confirm: None,
resolved: true,
}
})
.collect()

View File

@@ -2719,7 +2719,7 @@ impl Render for CollabPanel {
impl EventEmitter<PanelEvent> for CollabPanel {}
impl Panel for CollabPanel {
fn position(&self, cx: &gpui::WindowContext) -> DockPosition {
fn position(&self, cx: &WindowContext) -> DockPosition {
CollaborationPanelSettings::get_global(cx).dock
}
@@ -2735,7 +2735,7 @@ impl Panel for CollabPanel {
);
}
fn size(&self, cx: &gpui::WindowContext) -> Pixels {
fn size(&self, cx: &WindowContext) -> Pixels {
self.width
.unwrap_or_else(|| CollaborationPanelSettings::get_global(cx).default_width)
}
@@ -2746,7 +2746,7 @@ impl Panel for CollabPanel {
cx.notify();
}
fn icon(&self, cx: &gpui::WindowContext) -> Option<ui::IconName> {
fn icon(&self, cx: &WindowContext) -> Option<ui::IconName> {
CollaborationPanelSettings::get_global(cx)
.button
.then_some(ui::IconName::UserGroup)
@@ -2763,6 +2763,10 @@ impl Panel for CollabPanel {
fn persistent_name() -> &'static str {
"CollabPanel"
}
fn activation_priority(&self) -> u32 {
6
}
}
impl FocusableView for CollabPanel {

View File

@@ -662,7 +662,7 @@ impl Panel for NotificationPanel {
"NotificationPanel"
}
fn position(&self, cx: &gpui::WindowContext) -> DockPosition {
fn position(&self, cx: &WindowContext) -> DockPosition {
NotificationPanelSettings::get_global(cx).dock
}
@@ -678,7 +678,7 @@ impl Panel for NotificationPanel {
);
}
fn size(&self, cx: &gpui::WindowContext) -> Pixels {
fn size(&self, cx: &WindowContext) -> Pixels {
self.width
.unwrap_or_else(|| NotificationPanelSettings::get_global(cx).default_width)
}
@@ -702,7 +702,7 @@ impl Panel for NotificationPanel {
}
}
fn icon(&self, cx: &gpui::WindowContext) -> Option<IconName> {
fn icon(&self, cx: &WindowContext) -> Option<IconName> {
let show_button = NotificationPanelSettings::get_global(cx).button;
if !show_button {
return None;
@@ -731,6 +731,10 @@ impl Panel for NotificationPanel {
fn toggle_action(&self) -> Box<dyn gpui::Action> {
Box::new(ToggleFocus)
}
fn activation_priority(&self) -> u32 {
8
}
}
pub struct NotificationToast {

View File

@@ -28,7 +28,6 @@ serde.workspace = true
serde_json.workspace = true
settings.workspace = true
smol.workspace = true
ui.workspace = true
url = { workspace = true, features = ["serde"] }
util.workspace = true
workspace.workspace = true

View File

@@ -2,7 +2,7 @@ use std::sync::Arc;
use anyhow::{anyhow, bail};
use assistant_tool::Tool;
use gpui::{Model, Task};
use gpui::{Model, Task, WindowContext};
use crate::manager::ContextServerManager;
use crate::types;
@@ -52,7 +52,7 @@ impl Tool for ContextServerTool {
self: std::sync::Arc<Self>,
input: serde_json::Value,
_workspace: gpui::WeakView<workspace::Workspace>,
cx: &mut ui::WindowContext,
cx: &mut WindowContext,
) -> gpui::Task<gpui::Result<String>> {
if let Some(server) = self.server_manager.read(cx).get_server(&self.server_id) {
cx.foreground_executor().spawn({

View File

@@ -34,9 +34,9 @@ pub enum Model {
Gpt4,
#[serde(alias = "gpt-3.5-turbo", rename = "gpt-3.5-turbo")]
Gpt3_5Turbo,
#[serde(alias = "o1-preview", rename = "o1-preview-2024-09-12")]
#[serde(alias = "o1-preview", rename = "o1")]
O1Preview,
#[serde(alias = "o1-mini", rename = "o1-mini-2024-09-12")]
#[serde(alias = "o1-mini", rename = "o1-mini")]
O1Mini,
#[serde(alias = "claude-3-5-sonnet", rename = "claude-3.5-sonnet")]
Claude3_5Sonnet,

View File

@@ -166,7 +166,7 @@ impl ProjectDiagnosticsEditor {
let excerpts = cx.new_model(|cx| MultiBuffer::new(project_handle.read(cx).capability()));
let editor = cx.new_view(|cx| {
let mut editor =
Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), false, cx);
Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), true, cx);
editor.set_vertical_scroll_margin(5, cx);
editor
});
@@ -836,65 +836,76 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
let (message, code_ranges) = highlight_diagnostic_message(&diagnostic, None);
let message: SharedString = message;
Arc::new(move |cx| {
let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();
let color = cx.theme().colors();
let highlight_style: HighlightStyle = color.text_accent.into();
h_flex()
.id(DIAGNOSTIC_HEADER)
.block_mouse_down()
.h(2. * cx.line_height())
.pl_10()
.pr_5()
.w_full()
.justify_between()
.gap_2()
.relative()
.child(
h_flex()
.gap_3()
.map(|stack| {
stack.child(
svg()
.size(cx.text_style().font_size)
.flex_none()
.map(|icon| {
if diagnostic.severity == DiagnosticSeverity::ERROR {
icon.path(IconName::XCircle.path())
.text_color(Color::Error.color(cx))
} else {
icon.path(IconName::Warning.path())
.text_color(Color::Warning.color(cx))
}
}),
)
})
.child(
h_flex()
.gap_1()
.child(
StyledText::new(message.clone()).with_highlights(
&cx.text_style(),
code_ranges
.iter()
.map(|range| (range.clone(), highlight_style)),
),
)
.when_some(diagnostic.code.as_ref(), |stack, code| {
stack.child(
div()
.child(SharedString::from(format!("({code})")))
.text_color(cx.theme().colors().text_muted),
)
}),
),
div()
.top(px(0.))
.absolute()
.w_full()
.h_px()
.bg(color.border_variant),
)
.child(
h_flex()
.gap_1()
.when_some(diagnostic.source.as_ref(), |stack, source| {
stack.child(
div()
.child(SharedString::from(source.clone()))
.text_color(cx.theme().colors().text_muted),
)
}),
.block_mouse_down()
.h(2. * cx.line_height())
.pl_10()
.pr_5()
.w_full()
.justify_between()
.gap_2()
.child(
h_flex()
.gap_3()
.map(|stack| {
stack.child(svg().size(cx.text_style().font_size).flex_none().map(
|icon| {
if diagnostic.severity == DiagnosticSeverity::ERROR {
icon.path(IconName::XCircle.path())
.text_color(Color::Error.color(cx))
} else {
icon.path(IconName::Warning.path())
.text_color(Color::Warning.color(cx))
}
},
))
})
.child(
h_flex()
.gap_1()
.child(
StyledText::new(message.clone()).with_highlights(
&cx.text_style(),
code_ranges
.iter()
.map(|range| (range.clone(), highlight_style)),
),
)
.when_some(diagnostic.code.as_ref(), |stack, code| {
stack.child(
div()
.child(SharedString::from(format!("({code})")))
.text_color(cx.theme().colors().text_muted),
)
}),
),
)
.child(h_flex().gap_1().when_some(
diagnostic.source.as_ref(),
|stack, source| {
stack.child(
div()
.child(SharedString::from(source.clone()))
.text_color(cx.theme().colors().text_muted),
)
},
)),
)
.into_any_element()
})

View File

@@ -167,10 +167,10 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
(DisplayRow(15), EXCERPT_HEADER.into()),
(DisplayRow(16), DIAGNOSTIC_HEADER.into()),
(DisplayRow(25), EXCERPT_HEADER.into()),
(DisplayRow(3), DIAGNOSTIC_HEADER.into()),
(DisplayRow(16), EXCERPT_HEADER.into()),
(DisplayRow(18), DIAGNOSTIC_HEADER.into()),
(DisplayRow(27), EXCERPT_HEADER.into()),
]
);
assert_eq!(
@@ -184,6 +184,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
"\n", // expand
" let x = vec![];\n",
" let y = vec![];\n",
"\n", // supporting diagnostic
@@ -195,6 +196,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
" c(y);\n",
"\n", // supporting diagnostic
" d(x);\n",
"\n", // expand
"\n", // context ellipsis
// diagnostic group 2
"\n", // primary message
@@ -206,11 +208,13 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
" a(x);\n",
"\n", // supporting diagnostic
" b(y);\n",
"\n", // expand
"\n", // context ellipsis
" c(y);\n",
" d(x);\n",
"\n", // supporting diagnostic
"}"
"}",
"\n", // expand
)
);
@@ -218,7 +222,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
editor.update(cx, |editor, cx| {
assert_eq!(
editor.selections.display_ranges(cx),
[DisplayPoint::new(DisplayRow(12), 6)..DisplayPoint::new(DisplayRow(12), 6)]
[DisplayPoint::new(DisplayRow(13), 6)..DisplayPoint::new(DisplayRow(13), 6)]
);
});
@@ -253,12 +257,12 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
(DisplayRow(7), FILE_HEADER.into()),
(DisplayRow(9), DIAGNOSTIC_HEADER.into()),
(DisplayRow(22), EXCERPT_HEADER.into()),
(DisplayRow(23), DIAGNOSTIC_HEADER.into()),
(DisplayRow(32), EXCERPT_HEADER.into()),
(DisplayRow(3), DIAGNOSTIC_HEADER.into()),
(DisplayRow(8), FILE_HEADER.into()),
(DisplayRow(12), DIAGNOSTIC_HEADER.into()),
(DisplayRow(25), EXCERPT_HEADER.into()),
(DisplayRow(27), DIAGNOSTIC_HEADER.into()),
(DisplayRow(36), EXCERPT_HEADER.into()),
]
);
@@ -273,6 +277,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
"\n", // expand
"const a: i32 = 'a';\n",
"\n", // supporting diagnostic
"const b: i32 = c;\n",
@@ -284,6 +289,8 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
"\n", // expand
"\n", // expand
" let x = vec![];\n",
" let y = vec![];\n",
"\n", // supporting diagnostic
@@ -299,6 +306,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 2
"\n", // primary message
"\n", // filename
"\n", // expand
"fn main() {\n",
" let x = vec![];\n",
"\n", // supporting diagnostic
@@ -306,11 +314,13 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
" a(x);\n",
"\n", // supporting diagnostic
" b(y);\n",
"\n", // expand
"\n", // context ellipsis
" c(y);\n",
" d(x);\n",
"\n", // supporting diagnostic
"}"
"}",
"\n", // expand
)
);
@@ -318,7 +328,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
editor.update(cx, |editor, cx| {
assert_eq!(
editor.selections.display_ranges(cx),
[DisplayPoint::new(DisplayRow(19), 6)..DisplayPoint::new(DisplayRow(19), 6)]
[DisplayPoint::new(DisplayRow(22), 6)..DisplayPoint::new(DisplayRow(22), 6)]
);
});
@@ -366,14 +376,14 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
(DisplayRow(7), EXCERPT_HEADER.into()),
(DisplayRow(8), DIAGNOSTIC_HEADER.into()),
(DisplayRow(13), FILE_HEADER.into()),
(DisplayRow(15), DIAGNOSTIC_HEADER.into()),
(DisplayRow(28), EXCERPT_HEADER.into()),
(DisplayRow(29), DIAGNOSTIC_HEADER.into()),
(DisplayRow(38), EXCERPT_HEADER.into()),
(DisplayRow(3), DIAGNOSTIC_HEADER.into()),
(DisplayRow(8), EXCERPT_HEADER.into()),
(DisplayRow(10), DIAGNOSTIC_HEADER.into()),
(DisplayRow(15), FILE_HEADER.into()),
(DisplayRow(19), DIAGNOSTIC_HEADER.into()),
(DisplayRow(32), EXCERPT_HEADER.into()),
(DisplayRow(34), DIAGNOSTIC_HEADER.into()),
(DisplayRow(43), EXCERPT_HEADER.into()),
]
);
@@ -388,6 +398,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
"\n", // expand
"const a: i32 = 'a';\n",
"\n", // supporting diagnostic
"const b: i32 = c;\n",
@@ -395,6 +406,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 2
"\n", // primary message
"\n", // padding
"\n", // expand
"const a: i32 = 'a';\n",
"const b: i32 = c;\n",
"\n", // supporting diagnostic
@@ -406,6 +418,8 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
"\n", // expand
"\n", // expand
" let x = vec![];\n",
" let y = vec![];\n",
"\n", // supporting diagnostic
@@ -421,6 +435,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 2
"\n", // primary message
"\n", // filename
"\n", // expand
"fn main() {\n",
" let x = vec![];\n",
"\n", // supporting diagnostic
@@ -428,11 +443,13 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
" a(x);\n",
"\n", // supporting diagnostic
" b(y);\n",
"\n", // expand
"\n", // context ellipsis
" c(y);\n",
" d(x);\n",
"\n", // supporting diagnostic
"}"
"}",
"\n", // expand
)
);
}
@@ -513,7 +530,7 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
(DisplayRow(3), DIAGNOSTIC_HEADER.into()),
]
);
assert_eq!(
@@ -524,8 +541,9 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
"\n", // expand
"a();\n", //
"b();",
"b();", "\n", // expand
)
);
@@ -561,9 +579,9 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
(DisplayRow(6), EXCERPT_HEADER.into()),
(DisplayRow(7), DIAGNOSTIC_HEADER.into()),
(DisplayRow(3), DIAGNOSTIC_HEADER.into()),
(DisplayRow(7), EXCERPT_HEADER.into()),
(DisplayRow(9), DIAGNOSTIC_HEADER.into()),
]
);
assert_eq!(
@@ -574,8 +592,10 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
"\n", // expand
"a();\n", // location
"b();\n", //
"\n", // expand
"\n", // collapsed context
// diagnostic group 2
"\n", // primary message
@@ -583,6 +603,7 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
"a();\n", // context
"b();\n", //
"c();", // context
"\n", // expand
)
);
@@ -629,9 +650,9 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
(DisplayRow(7), EXCERPT_HEADER.into()),
(DisplayRow(8), DIAGNOSTIC_HEADER.into()),
(DisplayRow(3), DIAGNOSTIC_HEADER.into()),
(DisplayRow(8), EXCERPT_HEADER.into()),
(DisplayRow(10), DIAGNOSTIC_HEADER.into()),
]
);
assert_eq!(
@@ -642,9 +663,11 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
"\n", // expand
"a();\n", // location
"b();\n", //
"c();\n", // context
"\n", // expand
"\n", // collapsed context
// diagnostic group 2
"\n", // primary message
@@ -652,6 +675,7 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
"b();\n", // context
"c();\n", //
"d();", // context
"\n", // expand
)
);
@@ -687,9 +711,9 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
(DisplayRow(2), DIAGNOSTIC_HEADER.into()),
(DisplayRow(7), EXCERPT_HEADER.into()),
(DisplayRow(8), DIAGNOSTIC_HEADER.into()),
(DisplayRow(3), DIAGNOSTIC_HEADER.into()),
(DisplayRow(8), EXCERPT_HEADER.into()),
(DisplayRow(10), DIAGNOSTIC_HEADER.into()),
]
);
assert_eq!(
@@ -700,9 +724,11 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
"\n", // expand
"b();\n", // location
"c();\n", //
"d();\n", // context
"\n", // expand
"\n", // collapsed context
// diagnostic group 2
"\n", // primary message
@@ -710,6 +736,7 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
"c();\n", // context
"d();\n", //
"e();", // context
"\n", // expand
)
);
}

View File

@@ -388,6 +388,4 @@ gpui::actions!(
]
);
action_as!(outline, ToggleOutline as Toggle);
action_as!(go_to_line, ToggleGoToLine as Toggle);

View File

@@ -16,7 +16,7 @@ fn is_c_language(language: &Language) -> bool {
pub fn switch_source_header(
editor: &mut Editor,
_: &SwitchSourceHeader,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) {
let Some(project) = &editor.project else {
return;

View File

@@ -224,6 +224,7 @@ impl CompletionsMenu {
documentation: None,
lsp_completion: Default::default(),
confirm: None,
resolved: true,
})
.collect();

View File

@@ -32,6 +32,7 @@ use crate::{
pub use block_map::{
Block, BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockId, BlockMap,
BlockPlacement, BlockPoint, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
StickyHeaderExcerpt,
};
use block_map::{BlockRow, BlockSnapshot};
use collections::{HashMap, HashSet};
@@ -1105,6 +1106,10 @@ impl DisplaySnapshot {
.map(|(row, block)| (DisplayRow(row), block))
}
pub fn sticky_header_excerpt(&self, row: DisplayRow) -> Option<StickyHeaderExcerpt<'_>> {
self.block_snapshot.sticky_header_excerpt(row.0)
}
pub fn block_for_id(&self, id: BlockId) -> Option<Block> {
self.block_snapshot.block_for_id(id)
}

View File

@@ -1411,6 +1411,66 @@ impl BlockSnapshot {
})
}
pub fn sticky_header_excerpt(&self, top_row: u32) -> Option<StickyHeaderExcerpt<'_>> {
let mut cursor = self.transforms.cursor::<BlockRow>(&());
cursor.seek(&BlockRow(top_row), Bias::Left, &());
while let Some(transform) = cursor.item() {
let start = cursor.start().0;
let end = cursor.end(&()).0;
match &transform.block {
Some(Block::ExcerptBoundary {
prev_excerpt,
next_excerpt,
starts_new_buffer,
show_excerpt_controls,
..
}) => {
let matches_start = if *show_excerpt_controls && prev_excerpt.is_some() {
start < top_row
} else {
start <= top_row
};
if matches_start && top_row <= end {
return next_excerpt.as_ref().map(|excerpt| StickyHeaderExcerpt {
next_buffer_row: None,
next_excerpt_controls_present: *show_excerpt_controls,
excerpt,
});
}
let next_buffer_row = if *starts_new_buffer { Some(end) } else { None };
return prev_excerpt.as_ref().map(|excerpt| StickyHeaderExcerpt {
excerpt,
next_buffer_row,
next_excerpt_controls_present: *show_excerpt_controls,
});
}
Some(Block::FoldedBuffer {
prev_excerpt: Some(excerpt),
..
}) if top_row <= start => {
return Some(StickyHeaderExcerpt {
next_buffer_row: Some(end),
next_excerpt_controls_present: false,
excerpt,
});
}
Some(Block::FoldedBuffer { .. }) | Some(Block::Custom(_)) | None => {}
}
// This is needed to iterate past None / FoldedBuffer / Custom blocks. For FoldedBuffer,
// if scrolled slightly past the header of a folded block, the next block is needed for
// the sticky header.
cursor.next(&());
}
None
}
pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
let buffer = self.wrap_snapshot.buffer_snapshot();
let wrap_point = match block_id {
@@ -1694,6 +1754,13 @@ impl<'a> BlockChunks<'a> {
}
}
pub struct StickyHeaderExcerpt<'a> {
pub excerpt: &'a ExcerptInfo,
pub next_excerpt_controls_present: bool,
// TODO az remove option
pub next_buffer_row: Option<u32>,
}
impl<'a> Iterator for BlockChunks<'a> {
type Item = Chunk<'a>;

View File

@@ -992,12 +992,14 @@ pub(crate) struct FocusedBlock {
}
#[derive(Clone)]
struct JumpData {
excerpt_id: ExcerptId,
position: Point,
anchor: text::Anchor,
path: Option<project::ProjectPath>,
line_offset_from_top: u32,
enum JumpData {
MultiBufferRow(MultiBufferRow),
MultiBufferPoint {
excerpt_id: ExcerptId,
position: Point,
anchor: text::Anchor,
line_offset_from_top: u32,
},
}
impl Editor {
@@ -3513,7 +3515,7 @@ impl Editor {
}
}
fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
fn visible_inlay_hints(&self, cx: &ViewContext<Editor>) -> Vec<Inlay> {
self.display_map
.read(cx)
.current_inlays()
@@ -3830,8 +3832,11 @@ impl Editor {
};
let buffer_handle = completions_menu.buffer;
let completions = completions_menu.completions.borrow_mut();
let completion = completions.get(mat.candidate_id)?;
let completion = completions_menu
.completions
.borrow()
.get(mat.candidate_id)?
.clone();
cx.stop_propagation();
let snippet;
@@ -3975,9 +3980,11 @@ impl Editor {
}
let provider = self.completion_provider.as_ref()?;
drop(completion);
let apply_edits = provider.apply_additional_edits_for_completion(
buffer_handle,
completion.clone(),
completions_menu.completions.clone(),
mat.candidate_id,
true,
cx,
);
@@ -5087,7 +5094,7 @@ impl Editor {
}))
}
#[cfg(feature = "test-support")]
#[cfg(any(feature = "test-support", test))]
pub fn context_menu_visible(&self) -> bool {
self.context_menu
.borrow()
@@ -6004,7 +6011,7 @@ impl Editor {
fn gather_revert_changes(
&mut self,
selections: &[Selection<Point>],
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
let mut revert_changes = HashMap::default();
let snapshot = self.snapshot(cx);
@@ -8931,7 +8938,7 @@ impl Editor {
fn templates_with_tags(
project: &Model<Project>,
runnable: &mut Runnable,
cx: &WindowContext<'_>,
cx: &WindowContext,
) -> Vec<(TaskSourceKind, TaskTemplate)> {
let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
let (worktree_id, file) = project
@@ -9243,7 +9250,7 @@ impl Editor {
&mut self,
snapshot: &EditorSnapshot,
position: Point,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) -> Option<MultiBufferDiffHunk> {
for (ix, position) in [position, Point::zero()].into_iter().enumerate() {
if let Some(hunk) = self.go_to_next_hunk_in_direction(
@@ -9272,7 +9279,7 @@ impl Editor {
&mut self,
snapshot: &EditorSnapshot,
position: Point,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) -> Option<MultiBufferDiffHunk> {
for (ix, position) in [position, snapshot.buffer_snapshot.max_point()]
.into_iter()
@@ -12453,28 +12460,46 @@ impl Editor {
let mut new_selections_by_buffer = HashMap::default();
match &jump_data {
Some(jump_data) => {
Some(JumpData::MultiBufferPoint {
excerpt_id,
position,
anchor,
line_offset_from_top,
}) => {
let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
if let Some(buffer) = multi_buffer_snapshot
.buffer_id_for_excerpt(jump_data.excerpt_id)
.buffer_id_for_excerpt(*excerpt_id)
.and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
{
let buffer_snapshot = buffer.read(cx).snapshot();
let jump_to_point = if buffer_snapshot.can_resolve(&jump_data.anchor) {
language::ToPoint::to_point(&jump_data.anchor, &buffer_snapshot)
let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
language::ToPoint::to_point(anchor, &buffer_snapshot)
} else {
buffer_snapshot.clip_point(jump_data.position, Bias::Left)
buffer_snapshot.clip_point(*position, Bias::Left)
};
let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
new_selections_by_buffer.insert(
buffer,
(
vec![jump_to_offset..jump_to_offset],
Some(jump_data.line_offset_from_top),
Some(*line_offset_from_top),
),
);
}
}
Some(JumpData::MultiBufferRow(row)) => {
let point = MultiBufferPoint::new(row.0, 0);
if let Some((buffer, buffer_point, _)) =
self.buffer.read(cx).point_to_buffer_point(point, cx)
{
let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
new_selections_by_buffer
.entry(buffer)
.or_insert((Vec::new(), None))
.0
.push(buffer_offset..buffer_offset)
}
}
None => {
let selections = self.selections.all::<usize>(cx);
let buffer = self.buffer.read(cx);
@@ -13447,11 +13472,14 @@ pub trait CompletionProvider {
fn apply_additional_edits_for_completion(
&self,
buffer: Model<Buffer>,
completion: Completion,
push_to_history: bool,
cx: &mut ViewContext<Editor>,
) -> Task<Result<Option<language::Transaction>>>;
_buffer: Model<Buffer>,
_completions: Rc<RefCell<Box<[Completion]>>>,
_completion_index: usize,
_push_to_history: bool,
_cx: &mut ViewContext<Editor>,
) -> Task<Result<Option<language::Transaction>>> {
Task::ready(Ok(None))
}
fn is_completion_trigger(
&self,
@@ -13610,6 +13638,7 @@ fn snippet_completions(
Some(Completion {
old_range: range,
new_text: snippet.body.clone(),
resolved: false,
label: CodeLabel {
text: matching_prefix.clone(),
runs: vec![],
@@ -13675,19 +13704,30 @@ impl CompletionProvider for Model<Project> {
cx: &mut ViewContext<Editor>,
) -> Task<Result<bool>> {
self.update(cx, |project, cx| {
project.resolve_completions(buffer, completion_indices, completions, cx)
project.lsp_store().update(cx, |lsp_store, cx| {
lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
})
})
}
fn apply_additional_edits_for_completion(
&self,
buffer: Model<Buffer>,
completion: Completion,
completions: Rc<RefCell<Box<[Completion]>>>,
completion_index: usize,
push_to_history: bool,
cx: &mut ViewContext<Editor>,
) -> Task<Result<Option<language::Transaction>>> {
self.update(cx, |project, cx| {
project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
project.lsp_store().update(cx, |lsp_store, cx| {
lsp_store.apply_additional_edits_for_completion(
buffer,
completions,
completion_index,
push_to_history,
cx,
)
})
})
}
@@ -13822,7 +13862,7 @@ impl SemanticsProvider for Model<Project> {
fn inlay_hint_settings(
location: Anchor,
snapshot: &MultiBufferSnapshot,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) -> InlayHintSettings {
let file = snapshot.file_at(location);
let language = snapshot.language_at(location).map(|l| l.name());

View File

@@ -8402,7 +8402,6 @@ async fn test_completion(cx: &mut gpui::TestAppContext) {
additional edit
"});
handle_resolve_completion_request(&mut cx, None).await;
apply_additional_edits.await.unwrap();
update_test_language_settings(&mut cx, |settings| {
@@ -10698,10 +10697,14 @@ async fn test_completions_resolve_updates_labels_if_filter_text_matches(
..lsp::CompletionItem::default()
};
cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
let item1 = item1.clone();
cx.handle_request::<lsp::request::Completion, _, _>({
let item1 = item1.clone();
let item2 = item2.clone();
async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
move |_, _, _| {
let item1 = item1.clone();
let item2 = item2.clone();
async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
}
})
.next()
.await;
@@ -10728,43 +10731,41 @@ async fn test_completions_resolve_updates_labels_if_filter_text_matches(
}
});
cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| async move {
Ok(lsp::CompletionItem {
label: "method id()".to_string(),
filter_text: Some("id".to_string()),
detail: Some("Now resolved!".to_string()),
documentation: Some(lsp::Documentation::String("Docs".to_string())),
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
new_text: ".id".to_string(),
})),
..lsp::CompletionItem::default()
})
cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
let item1 = item1.clone();
move |_, item_to_resolve, _| {
let item1 = item1.clone();
async move {
if item1 == item_to_resolve {
Ok(lsp::CompletionItem {
label: "method id()".to_string(),
filter_text: Some("id".to_string()),
detail: Some("Now resolved!".to_string()),
documentation: Some(lsp::Documentation::String("Docs".to_string())),
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: lsp::Range::new(
lsp::Position::new(0, 22),
lsp::Position::new(0, 22),
),
new_text: ".id".to_string(),
})),
..lsp::CompletionItem::default()
})
} else {
Ok(item_to_resolve)
}
}
}
})
.next()
.await;
.await
.unwrap();
cx.run_until_parked();
cx.update_editor(|editor, cx| {
editor.context_menu_next(&Default::default(), cx);
});
cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| async move {
Ok(lsp::CompletionItem {
label: "invalid changed label".to_string(),
detail: Some("Now resolved!".to_string()),
documentation: Some(lsp::Documentation::String("Docs".to_string())),
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
new_text: ".id".to_string(),
})),
..lsp::CompletionItem::default()
})
})
.next()
.await;
cx.run_until_parked();
cx.update_editor(|editor, _| {
let context_menu = editor.context_menu.borrow_mut();
let context_menu = context_menu
@@ -10787,6 +10788,172 @@ async fn test_completions_resolve_updates_labels_if_filter_text_matches(
});
}
#[gpui::test]
async fn test_completions_resolve_happens_once(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions {
trigger_characters: Some(vec![".".to_string()]),
resolve_provider: Some(true),
..Default::default()
}),
..Default::default()
},
cx,
)
.await;
cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
cx.simulate_keystroke(".");
let unresolved_item_1 = lsp::CompletionItem {
label: "id".to_string(),
filter_text: Some("id".to_string()),
detail: None,
documentation: None,
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
new_text: ".id".to_string(),
})),
..lsp::CompletionItem::default()
};
let resolved_item_1 = lsp::CompletionItem {
additional_text_edits: Some(vec![lsp::TextEdit {
range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
new_text: "!!".to_string(),
}]),
..unresolved_item_1.clone()
};
let unresolved_item_2 = lsp::CompletionItem {
label: "other".to_string(),
filter_text: Some("other".to_string()),
detail: None,
documentation: None,
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
new_text: ".other".to_string(),
})),
..lsp::CompletionItem::default()
};
let resolved_item_2 = lsp::CompletionItem {
additional_text_edits: Some(vec![lsp::TextEdit {
range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
new_text: "??".to_string(),
}]),
..unresolved_item_2.clone()
};
let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
cx.lsp
.server
.on_request::<lsp::request::ResolveCompletionItem, _, _>({
let unresolved_item_1 = unresolved_item_1.clone();
let resolved_item_1 = resolved_item_1.clone();
let unresolved_item_2 = unresolved_item_2.clone();
let resolved_item_2 = resolved_item_2.clone();
let resolve_requests_1 = resolve_requests_1.clone();
let resolve_requests_2 = resolve_requests_2.clone();
move |unresolved_request, _| {
let unresolved_item_1 = unresolved_item_1.clone();
let resolved_item_1 = resolved_item_1.clone();
let unresolved_item_2 = unresolved_item_2.clone();
let resolved_item_2 = resolved_item_2.clone();
let resolve_requests_1 = resolve_requests_1.clone();
let resolve_requests_2 = resolve_requests_2.clone();
async move {
if unresolved_request == unresolved_item_1 {
resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
Ok(resolved_item_1.clone())
} else if unresolved_request == unresolved_item_2 {
resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
Ok(resolved_item_2.clone())
} else {
panic!("Unexpected completion item {unresolved_request:?}")
}
}
}
})
.detach();
cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
let unresolved_item_1 = unresolved_item_1.clone();
let unresolved_item_2 = unresolved_item_2.clone();
async move {
Ok(Some(lsp::CompletionResponse::Array(vec![
unresolved_item_1,
unresolved_item_2,
])))
}
})
.next()
.await;
cx.condition(|editor, _| editor.context_menu_visible())
.await;
cx.update_editor(|editor, _| {
let context_menu = editor.context_menu.borrow_mut();
let context_menu = context_menu
.as_ref()
.expect("Should have the context menu deployed");
match context_menu {
CodeContextMenu::Completions(completions_menu) => {
let completions = completions_menu.completions.borrow_mut();
assert_eq!(
completions
.iter()
.map(|completion| &completion.label.text)
.collect::<Vec<_>>(),
vec!["id", "other"]
)
}
CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
}
});
cx.run_until_parked();
cx.update_editor(|editor, cx| {
editor.context_menu_next(&ContextMenuNext, cx);
});
cx.run_until_parked();
cx.update_editor(|editor, cx| {
editor.context_menu_prev(&ContextMenuPrev, cx);
});
cx.run_until_parked();
cx.update_editor(|editor, cx| {
editor.context_menu_next(&ContextMenuNext, cx);
});
cx.run_until_parked();
cx.update_editor(|editor, cx| {
editor
.compose_completion(&ComposeCompletion::default(), cx)
.expect("No task returned")
})
.await
.expect("Completion failed");
cx.run_until_parked();
cx.update_editor(|editor, cx| {
assert_eq!(
resolve_requests_1.load(atomic::Ordering::Acquire),
1,
"Should always resolve once despite multiple selections"
);
assert_eq!(
resolve_requests_2.load(atomic::Ordering::Acquire),
1,
"Should always resolve once after multiple selections and applying the completion"
);
assert_eq!(
editor.text(cx),
"fn main() { let a = ??.other; }",
"Should use resolved data when applying the completion"
);
});
}
#[gpui::test]
async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
@@ -10950,15 +11117,10 @@ async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppCo
// Completions that have already been resolved are skipped.
assert_eq!(
*resolved_items.lock(),
[
// Selected item is always resolved even if it was resolved before.
&items_out[items_out.len() - 1..items_out.len()],
&items_out[items_out.len() - 16..items_out.len() - 4]
]
.concat()
.iter()
.cloned()
.collect::<Vec<lsp::CompletionItem>>()
items_out[items_out.len() - 16..items_out.len() - 4]
.iter()
.cloned()
.collect::<Vec<lsp::CompletionItem>>()
);
resolved_items.lock().clear();
}
@@ -11098,7 +11260,7 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
},
..Default::default()
},
Some(tree_sitter_rust::LANGUAGE.into()),
Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
)));
update_test_language_settings(cx, |settings| {
settings.defaults.prettier = Some(PrettierSettings {
@@ -14570,6 +14732,62 @@ fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
}
}
#[gpui::test]
async fn test_rename_with_duplicate_edits(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
cx.set_state(indoc! {"
struct Fˇoo {}
"});
cx.update_editor(|editor, cx| {
let highlight_range = Point::new(0, 7)..Point::new(0, 10);
let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
editor.highlight_background::<DocumentHighlightRead>(
&[highlight_range],
|c| c.editor_document_highlight_read_background,
cx,
);
});
cx.update_editor(|e, cx| e.rename(&Rename, cx))
.expect("Rename was not started")
.await
.expect("Rename failed");
let mut rename_handler =
cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
let edit = lsp::TextEdit {
range: lsp::Range {
start: lsp::Position {
line: 0,
character: 7,
},
end: lsp::Position {
line: 0,
character: 10,
},
},
new_text: "FooRenamed".to_string(),
};
Ok(Some(lsp::WorkspaceEdit::new(
// Specify the same edit twice
std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
)))
});
cx.update_editor(|e, cx| e.confirm_rename(&ConfirmRename, cx))
.expect("Confirm rename was not started")
.await
.expect("Confirm rename failed");
rename_handler.next().await.unwrap();
cx.run_until_parked();
// Despite two edits, only one is actually applied as those are identical
cx.assert_editor_state(indoc! {"
struct FooRenamedˇ {}
"});
}
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
point..point

File diff suppressed because it is too large Load Diff

View File

@@ -266,7 +266,7 @@ pub fn update_inlay_link_and_hover_points(
editor: &mut Editor,
secondary_held: bool,
shift_held: bool,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) {
let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 {
Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left))

View File

@@ -365,7 +365,7 @@ impl Editor {
&mut self,
diff_base_buffer: Option<Model<Buffer>>,
hunk: &HoveredHunk,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) -> Option<()> {
let buffer = self.buffer.clone();
let multi_buffer_snapshot = buffer.read(cx).snapshot(cx);
@@ -454,7 +454,7 @@ impl Editor {
fn apply_diff_hunks_in_range(
&mut self,
range: Range<Anchor>,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) -> Option<()> {
let (buffer, range, _) = self
.buffer
@@ -530,7 +530,7 @@ impl Editor {
fn hunk_header_block(
&self,
hunk: &HoveredHunk,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) -> BlockProperties<Anchor> {
let is_branch_buffer = self
.buffer
@@ -801,7 +801,7 @@ impl Editor {
hunk: &HoveredHunk,
diff_base_buffer: Model<Buffer>,
deleted_text_height: u32,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) -> BlockProperties<Anchor> {
let gutter_color = match hunk.status {
DiffHunkStatus::Added => unreachable!(),
@@ -864,7 +864,7 @@ impl Editor {
}
}
pub(super) fn clear_expanded_diff_hunks(&mut self, cx: &mut ViewContext<'_, Editor>) -> bool {
pub(super) fn clear_expanded_diff_hunks(&mut self, cx: &mut ViewContext<Editor>) -> bool {
if self.diff_map.expand_all {
return false;
}
@@ -887,7 +887,7 @@ impl Editor {
pub(super) fn sync_expanded_diff_hunks(
diff_map: &mut DiffMap,
buffer_id: BufferId,
cx: &mut ViewContext<'_, Self>,
cx: &mut ViewContext<Self>,
) {
let diff_base_state = diff_map.diff_bases.get_mut(&buffer_id);
let mut diff_base_buffer = None;
@@ -1134,7 +1134,7 @@ fn editor_with_deleted_text(
diff_base_buffer: Model<Buffer>,
deleted_color: Hsla,
hunk: &HoveredHunk,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) -> (u32, View<Editor>) {
let parent_editor = cx.view().downgrade();
let editor = cx.new_view(|cx| {

View File

@@ -579,7 +579,7 @@ impl InlayHintCache {
buffer_id: BufferId,
excerpt_id: ExcerptId,
id: InlayId,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) {
if let Some(excerpt_hints) = self.hints.get(&excerpt_id) {
let mut guard = excerpt_hints.write();
@@ -640,7 +640,7 @@ fn spawn_new_update_tasks(
excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
invalidate: InvalidationStrategy,
update_cache_version: usize,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) {
for (excerpt_id, (excerpt_buffer, new_task_buffer_version, excerpt_visible_range)) in
excerpts_to_query
@@ -797,7 +797,7 @@ fn new_update_task(
query: ExcerptQuery,
query_ranges: QueryRanges,
excerpt_buffer: Model<Buffer>,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) -> Task<()> {
cx.spawn(move |editor, mut cx| async move {
let visible_range_update_results = future::join_all(
@@ -1129,7 +1129,7 @@ fn apply_hint_update(
invalidate: bool,
buffer_snapshot: BufferSnapshot,
multi_buffer_snapshot: MultiBufferSnapshot,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) {
let cached_excerpt_hints = editor
.inlay_hint_cache
@@ -3434,7 +3434,7 @@ pub mod tests {
labels
}
pub fn visible_hint_labels(editor: &Editor, cx: &ViewContext<'_, Editor>) -> Vec<String> {
pub fn visible_hint_labels(editor: &Editor, cx: &ViewContext<Editor>) -> Vec<String> {
let mut hints = editor
.visible_inlay_hints(cx)
.into_iter()

View File

@@ -288,7 +288,7 @@ impl EventEmitter<EditorEvent> for ProposedChangesEditor {}
impl Item for ProposedChangesEditor {
type Event = EditorEvent;
fn tab_icon(&self, _cx: &ui::WindowContext) -> Option<Icon> {
fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
Some(Icon::new(IconName::Diff))
}

View File

@@ -33,7 +33,7 @@ pub fn apply_related_actions(editor: &View<Editor>, cx: &mut WindowContext) {
pub fn expand_macro_recursively(
editor: &mut Editor,
_: &ExpandMacroRecursively,
cx: &mut ViewContext<'_, Editor>,
cx: &mut ViewContext<Editor>,
) {
if editor.selections.count() == 0 {
return;
@@ -98,7 +98,7 @@ pub fn expand_macro_recursively(
.detach_and_log_err(cx);
}
pub fn open_docs(editor: &mut Editor, _: &OpenDocs, cx: &mut ViewContext<'_, Editor>) {
pub fn open_docs(editor: &mut Editor, _: &OpenDocs, cx: &mut ViewContext<Editor>) {
if editor.selections.count() == 0 {
return;
}

View File

@@ -4,7 +4,7 @@ use crate::{
ScrollAnchor, ScrollCursorBottom, ScrollCursorCenter, ScrollCursorCenterTopBottom,
ScrollCursorTop, SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT,
};
use gpui::{Point, ViewContext};
use gpui::{AsyncWindowContext, Point, ViewContext};
impl Editor {
pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext<Editor>) {
@@ -75,7 +75,7 @@ impl Editor {
self.next_scroll_position = self.next_scroll_position.next();
self._scroll_cursor_center_top_bottom_task =
cx.spawn(|editor, mut cx: gpui::AsyncWindowContext| async move {
cx.spawn(|editor, mut cx: AsyncWindowContext| async move {
cx.background_executor()
.timer(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT)
.await;

View File

@@ -8,7 +8,7 @@ use workspace::Workspace;
fn task_context_with_editor(
editor: &mut Editor,
cx: &mut WindowContext<'_>,
cx: &mut WindowContext,
) -> AsyncTask<Option<TaskContext>> {
let Some(project) = editor.project.clone() else {
return AsyncTask::ready(None);
@@ -74,7 +74,7 @@ fn task_context_with_editor(
})
}
pub fn task_context(workspace: &Workspace, cx: &mut WindowContext<'_>) -> AsyncTask<TaskContext> {
pub fn task_context(workspace: &Workspace, cx: &mut WindowContext) -> AsyncTask<TaskContext> {
let Some(editor) = workspace
.active_item(cx)
.and_then(|item| item.act_as::<Editor>(cx))

View File

@@ -843,7 +843,7 @@ impl ExtensionsPage {
}
}
fn fetch_extensions_debounced(&mut self, cx: &mut ViewContext<'_, ExtensionsPage>) {
fn fetch_extensions_debounced(&mut self, cx: &mut ViewContext<ExtensionsPage>) {
self.extension_fetch_task = Some(cx.spawn(|this, mut cx| async move {
let search = this
.update(&mut cx, |this, cx| this.search_query(cx))

View File

@@ -1,11 +1,10 @@
use client::telemetry;
use gpui::Task;
use gpui::{Task, WindowContext};
use human_bytes::human_bytes;
use release_channel::{AppCommitSha, AppVersion, ReleaseChannel};
use serde::Serialize;
use std::{env, fmt::Display};
use sysinfo::{MemoryRefreshKind, RefreshKind, System};
use ui::WindowContext;
#[derive(Clone, Debug, Serialize)]
pub struct SystemSpecs {

View File

@@ -884,7 +884,7 @@ impl FileFinderDelegate {
fn lookup_absolute_path(
&self,
query: FileSearchQuery,
cx: &mut ViewContext<'_, Picker<Self>>,
cx: &mut ViewContext<Picker<Self>>,
) -> Task<()> {
cx.spawn(|picker, mut cx| async move {
let Some(project) = picker

View File

@@ -10,6 +10,8 @@ use git::GitHostingProviderRegistry;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
use ashpd::desktop::trash;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
use collections::BTreeSet;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
use smol::process::Command;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
use std::fs::File;
@@ -120,6 +122,17 @@ pub trait Fs: Send + Sync {
path: &Path,
) -> Result<Pin<Box<dyn Send + Stream<Item = Result<PathBuf>>>>>;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
async fn watch(
&self,
path: &Path,
latency: Duration,
) -> (
Pin<Box<dyn Send + Stream<Item = BTreeSet<PathEvent>>>>,
Arc<dyn Watcher>,
);
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
async fn watch(
&self,
path: &Path,
@@ -701,13 +714,14 @@ impl Fs for RealFs {
path: &Path,
latency: Duration,
) -> (
Pin<Box<dyn Send + Stream<Item = Vec<PathEvent>>>>,
Pin<Box<dyn Send + Stream<Item = BTreeSet<PathEvent>>>>,
Arc<dyn Watcher>,
) {
use collections::BTreeSet;
use parking_lot::Mutex;
let (tx, rx) = smol::channel::unbounded();
let pending_paths: Arc<Mutex<Vec<PathEvent>>> = Default::default();
let pending_paths: Arc<Mutex<BTreeSet<PathEvent>>> = Default::default();
let watcher = Arc::new(linux_watcher::LinuxWatcher::new(tx, pending_paths.clone()));
if watcher.add(path).is_err() {
@@ -1938,6 +1952,7 @@ impl Fs for FakeFs {
Ok(Box::pin(futures::stream::iter(paths)))
}
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
async fn watch(
&self,
path: &Path,
@@ -1966,6 +1981,38 @@ impl Fs for FakeFs {
)
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
async fn watch(
&self,
path: &Path,
_: Duration,
) -> (
Pin<Box<dyn Send + Stream<Item = BTreeSet<PathEvent>>>>,
Arc<dyn Watcher>,
) {
self.simulate_random_delay().await;
let (tx, rx) = smol::channel::unbounded();
self.state.lock().event_txs.push(tx);
let path = path.to_path_buf();
let executor = self.executor.clone();
(
Box::pin(
futures::StreamExt::filter(rx, move |events| {
let result = events
.iter()
.any(|evt_path| evt_path.path.starts_with(&path));
let executor = executor.clone();
async move {
executor.simulate_random_delay().await;
result
}
})
.map(|events| BTreeSet::from_iter(events.into_iter())),
),
Arc::new(FakeWatcher {}),
)
}
fn open_repo(&self, abs_dot_git: &Path) -> Option<Arc<dyn GitRepository>> {
let state = self.state.lock();
let entry = state.read_path(abs_dot_git).unwrap();

View File

@@ -1,3 +1,4 @@
use collections::BTreeSet;
use notify::EventKind;
use parking_lot::Mutex;
use std::sync::{Arc, OnceLock};
@@ -7,13 +8,13 @@ use crate::{PathEvent, PathEventKind, Watcher};
pub struct LinuxWatcher {
tx: smol::channel::Sender<()>,
pending_path_events: Arc<Mutex<Vec<PathEvent>>>,
pending_path_events: Arc<Mutex<BTreeSet<PathEvent>>>,
}
impl LinuxWatcher {
pub fn new(
tx: smol::channel::Sender<()>,
pending_path_events: Arc<Mutex<Vec<PathEvent>>>,
pending_path_events: Arc<Mutex<BTreeSet<PathEvent>>>,
) -> Self {
Self {
tx,
@@ -40,7 +41,7 @@ impl Watcher for LinuxWatcher {
EventKind::Remove(_) => Some(PathEventKind::Removed),
_ => None,
};
let mut path_events = event
let path_events = event
.paths
.iter()
.filter_map(|event_path| {
@@ -52,17 +53,12 @@ impl Watcher for LinuxWatcher {
.collect::<Vec<_>>();
if !path_events.is_empty() {
path_events.sort();
let mut pending_paths = pending_paths.lock();
if pending_paths.is_empty() {
let was_empty = pending_paths.is_empty();
pending_paths.extend(path_events);
if was_empty {
tx.try_send(()).ok();
}
util::extend_sorted(
&mut *pending_paths,
path_events,
usize::MAX,
|a, b| a.path.cmp(&b.path),
);
}
})
}

View File

@@ -14,7 +14,6 @@ pub struct Matcher<'a> {
lowercase_query: &'a [char],
query_char_bag: CharBag,
smart_case: bool,
max_results: usize,
min_score: f64,
match_positions: Vec<usize>,
last_positions: Vec<usize>,
@@ -22,11 +21,6 @@ pub struct Matcher<'a> {
best_position_matrix: Vec<usize>,
}
pub trait Match: Ord {
fn score(&self) -> f64;
fn set_positions(&mut self, positions: Vec<usize>);
}
pub trait MatchCandidate {
fn has_chars(&self, bag: CharBag) -> bool;
fn to_string(&self) -> Cow<'_, str>;
@@ -38,7 +32,6 @@ impl<'a> Matcher<'a> {
lowercase_query: &'a [char],
query_char_bag: CharBag,
smart_case: bool,
max_results: usize,
) -> Self {
Self {
query,
@@ -50,10 +43,11 @@ impl<'a> Matcher<'a> {
score_matrix: Vec::new(),
best_position_matrix: Vec::new(),
smart_case,
max_results,
}
}
/// Filter and score fuzzy match candidates. Results are returned unsorted, in the same order as
/// the input candidates.
pub fn match_candidates<C: MatchCandidate, R, F>(
&mut self,
prefix: &[char],
@@ -63,8 +57,7 @@ impl<'a> Matcher<'a> {
cancel_flag: &AtomicBool,
build_match: F,
) where
R: Match,
F: Fn(&C, f64) -> R,
F: Fn(&C, f64, &Vec<usize>) -> R,
{
let mut candidate_chars = Vec::new();
let mut lowercase_candidate_chars = Vec::new();
@@ -103,20 +96,7 @@ impl<'a> Matcher<'a> {
);
if score > 0.0 {
let mut mat = build_match(&candidate, score);
if let Err(i) = results.binary_search_by(|m| mat.cmp(m)) {
if results.len() < self.max_results {
mat.set_positions(self.match_positions.clone());
results.insert(i, mat);
} else if i < results.len() {
results.pop();
mat.set_positions(self.match_positions.clone());
results.insert(i, mat);
}
if results.len() == self.max_results {
self.min_score = results.last().unwrap().score();
}
}
results.push(build_match(&candidate, score, &self.match_positions));
}
}
}
@@ -325,18 +305,18 @@ mod tests {
#[test]
fn test_get_last_positions() {
let mut query: &[char] = &['d', 'c'];
let mut matcher = Matcher::new(query, query, query.into(), false, 10);
let mut matcher = Matcher::new(query, query, query.into(), false);
let result = matcher.find_last_positions(&['a', 'b', 'c'], &['b', 'd', 'e', 'f']);
assert!(!result);
query = &['c', 'd'];
let mut matcher = Matcher::new(query, query, query.into(), false, 10);
let mut matcher = Matcher::new(query, query, query.into(), false);
let result = matcher.find_last_positions(&['a', 'b', 'c'], &['b', 'd', 'e', 'f']);
assert!(result);
assert_eq!(matcher.last_positions, vec![2, 4]);
query = &['z', '/', 'z', 'f'];
let mut matcher = Matcher::new(query, query, query.into(), false, 10);
let mut matcher = Matcher::new(query, query, query.into(), false);
let result = matcher.find_last_positions(&['z', 'e', 'd', '/'], &['z', 'e', 'd', '/', 'f']);
assert!(result);
assert_eq!(matcher.last_positions, vec![0, 3, 4, 8]);
@@ -451,7 +431,7 @@ mod tests {
});
}
let mut matcher = Matcher::new(&query, &lowercase_query, query_chars, smart_case, 100);
let mut matcher = Matcher::new(&query, &lowercase_query, query_chars, smart_case);
let cancel_flag = AtomicBool::new(false);
let mut results = Vec::new();
@@ -462,16 +442,17 @@ mod tests {
path_entries.into_iter(),
&mut results,
&cancel_flag,
|candidate, score| PathMatch {
|candidate, score, positions| PathMatch {
score,
worktree_id: 0,
positions: Vec::new(),
positions: positions.clone(),
path: Arc::from(candidate.path),
path_prefix: "".into(),
distance_to_relative_ancestor: usize::MAX,
is_dir: false,
},
);
results.sort_by(|a, b| b.cmp(a));
results
.into_iter()

View File

@@ -7,7 +7,7 @@ use std::{
};
use crate::{
matcher::{Match, MatchCandidate, Matcher},
matcher::{MatchCandidate, Matcher},
CharBag,
};
@@ -42,16 +42,6 @@ pub trait PathMatchCandidateSet<'a>: Send + Sync {
fn candidates(&'a self, start: usize) -> Self::Candidates;
}
impl Match for PathMatch {
fn score(&self) -> f64 {
self.score
}
fn set_positions(&mut self, positions: Vec<usize>) {
self.positions = positions;
}
}
impl<'a> MatchCandidate for PathMatchCandidate<'a> {
fn has_chars(&self, bag: CharBag) -> bool {
self.char_bag.is_superset(bag)
@@ -102,13 +92,7 @@ pub fn match_fixed_path_set(
let query = query.chars().collect::<Vec<_>>();
let query_char_bag = CharBag::from(&lowercase_query[..]);
let mut matcher = Matcher::new(
&query,
&lowercase_query,
query_char_bag,
smart_case,
max_results,
);
let mut matcher = Matcher::new(&query, &lowercase_query, query_char_bag, smart_case);
let mut results = Vec::new();
matcher.match_candidates(
@@ -117,16 +101,17 @@ pub fn match_fixed_path_set(
candidates.into_iter(),
&mut results,
&AtomicBool::new(false),
|candidate, score| PathMatch {
|candidate, score, positions| PathMatch {
score,
worktree_id,
positions: Vec::new(),
positions: positions.clone(),
is_dir: candidate.is_dir,
path: Arc::from(candidate.path),
path_prefix: Arc::default(),
distance_to_relative_ancestor: usize::MAX,
},
);
util::truncate_to_bottom_n_sorted_by(&mut results, max_results, &|a, b| b.cmp(a));
results
}
@@ -164,13 +149,8 @@ pub async fn match_path_sets<'a, Set: PathMatchCandidateSet<'a>>(
scope.spawn(async move {
let segment_start = segment_idx * segment_size;
let segment_end = segment_start + segment_size;
let mut matcher = Matcher::new(
query,
lowercase_query,
query_char_bag,
smart_case,
max_results,
);
let mut matcher =
Matcher::new(query, lowercase_query, query_char_bag, smart_case);
let mut tree_start = 0;
for candidate_set in candidate_sets {
@@ -193,10 +173,10 @@ pub async fn match_path_sets<'a, Set: PathMatchCandidateSet<'a>>(
candidates,
results,
cancel_flag,
|candidate, score| PathMatch {
|candidate, score, positions| PathMatch {
score,
worktree_id,
positions: Vec::new(),
positions: positions.clone(),
path: Arc::from(candidate.path),
is_dir: candidate.is_dir,
path_prefix: candidate_set.prefix(),
@@ -222,14 +202,8 @@ pub async fn match_path_sets<'a, Set: PathMatchCandidateSet<'a>>(
})
.await;
let mut results = Vec::new();
for segment_result in segment_results {
if results.is_empty() {
results = segment_result;
} else {
util::extend_sorted(&mut results, segment_result, max_results, |a, b| b.cmp(a));
}
}
let mut results = segment_results.concat();
util::truncate_to_bottom_n_sorted_by(&mut results, max_results, &|a, b| b.cmp(a));
results
}

View File

@@ -1,5 +1,5 @@
use crate::{
matcher::{Match, MatchCandidate, Matcher},
matcher::{MatchCandidate, Matcher},
CharBag,
};
use gpui::BackgroundExecutor;
@@ -46,16 +46,6 @@ pub struct StringMatch {
pub string: String,
}
impl Match for StringMatch {
fn score(&self) -> f64 {
self.score
}
fn set_positions(&mut self, positions: Vec<usize>) {
self.positions = positions;
}
}
impl StringMatch {
pub fn ranges(&self) -> impl '_ + Iterator<Item = Range<usize>> {
let mut positions = self.positions.iter().peekable();
@@ -167,13 +157,8 @@ pub async fn match_strings(
scope.spawn(async move {
let segment_start = cmp::min(segment_idx * segment_size, candidates.len());
let segment_end = cmp::min(segment_start + segment_size, candidates.len());
let mut matcher = Matcher::new(
query,
lowercase_query,
query_char_bag,
smart_case,
max_results,
);
let mut matcher =
Matcher::new(query, lowercase_query, query_char_bag, smart_case);
matcher.match_candidates(
&[],
@@ -181,10 +166,10 @@ pub async fn match_strings(
candidates[segment_start..segment_end].iter(),
results,
cancel_flag,
|candidate, score| StringMatch {
|candidate, score, positions| StringMatch {
candidate_id: candidate.id,
score,
positions: Vec::new(),
positions: positions.clone(),
string: candidate.string.to_string(),
},
);
@@ -193,13 +178,7 @@ pub async fn match_strings(
})
.await;
let mut results = Vec::new();
for segment_result in segment_results {
if results.is_empty() {
results = segment_result;
} else {
util::extend_sorted(&mut results, segment_result, max_results, |a, b| b.cmp(a));
}
}
let mut results = segment_results.concat();
util::truncate_to_bottom_n_sorted_by(&mut results, max_results, &|a, b| b.cmp(a));
results
}

View File

@@ -107,7 +107,7 @@ pub struct GitPanel {
// not hidden by folding or such
visible_entries: Vec<WorktreeEntries>,
width: Option<Pixels>,
git_diff_editor: View<Editor>,
git_diff_editor: Option<View<Editor>>,
git_diff_editor_updates: Task<()>,
reveal_in_editor: Task<()>,
}
@@ -149,11 +149,7 @@ impl GitPanel {
workspace: WeakView<Workspace>,
cx: AsyncWindowContext,
) -> Task<Result<View<Self>>> {
cx.spawn(|mut cx| async move {
// Clippy incorrectly classifies this as a redundant closure
#[allow(clippy::redundant_closure)]
workspace.update(&mut cx, |workspace, cx| Self::new(workspace, cx))
})
cx.spawn(|mut cx| async move { workspace.update(&mut cx, Self::new) })
}
pub fn new(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
@@ -168,7 +164,7 @@ impl GitPanel {
this.hide_scrollbar(cx);
})
.detach();
cx.subscribe(&project, |this, project, event, cx| match event {
cx.subscribe(&project, |this, _, event, cx| match event {
project::Event::WorktreeRemoved(id) => {
this.expanded_dir_ids.remove(id);
this.update_visible_entries(None, None, cx);
@@ -186,9 +182,10 @@ impl GitPanel {
}
project::Event::Closed => {
this.git_diff_editor_updates = Task::ready(());
this.reveal_in_editor = Task::ready(());
this.expanded_dir_ids.clear();
this.visible_entries.clear();
this.git_diff_editor = diff_display_editor(project.clone(), cx);
this.git_diff_editor = None;
}
_ => {}
})
@@ -196,7 +193,7 @@ impl GitPanel {
let scroll_handle = UniformListScrollHandle::new();
let mut this = Self {
let mut git_panel = Self {
workspace: weak_workspace,
focus_handle: cx.focus_handle(),
fs,
@@ -211,13 +208,13 @@ impl GitPanel {
selected_item: None,
show_scrollbar: !Self::should_autohide_scrollbar(cx),
hide_scrollbar_task: None,
git_diff_editor: diff_display_editor(project.clone(), cx),
git_diff_editor: Some(diff_display_editor(cx)),
git_diff_editor_updates: Task::ready(()),
reveal_in_editor: Task::ready(()),
project,
};
this.update_visible_entries(None, None, cx);
this
git_panel.update_visible_entries(None, None, cx);
git_panel
});
git_panel
@@ -602,7 +599,7 @@ impl GitPanel {
);
}
let project = self.project.clone();
let project = self.project.downgrade();
self.git_diff_editor_updates = cx.spawn(|git_panel, mut cx| async move {
cx.background_executor()
.timer(UPDATE_DEBOUNCE)
@@ -610,7 +607,7 @@ impl GitPanel {
let Some(project_buffers) = git_panel
.update(&mut cx, |git_panel, cx| {
futures::future::join_all(git_panel.visible_entries.iter_mut().flat_map(
move |worktree_entries| {
|worktree_entries| {
worktree_entries
.visible_entries
.iter()
@@ -694,7 +691,7 @@ impl GitPanel {
anyhow::Ok((buffer, unstaged_changes, hunks))
});
Some((entry_path, unstaged_changes_task))
})?;
}).ok()??;
Some((entry_path, unstaged_changes_task))
})
.map(|(entry_path, open_task)| async move {
@@ -716,7 +713,7 @@ impl GitPanel {
let mut change_sets = Vec::with_capacity(project_buffers.len());
if let Some(buffer_update_task) = git_panel
.update(&mut cx, |git_panel, cx| {
let editor = git_panel.git_diff_editor.clone();
let editor = git_panel.git_diff_editor.clone()?;
let multi_buffer = editor.read(cx).buffer().clone();
let mut buffers_with_ranges = Vec::with_capacity(project_buffers.len());
for (buffer_path, open_result) in project_buffers {
@@ -735,25 +732,27 @@ impl GitPanel {
}
}
multi_buffer.update(cx, |multi_buffer, cx| {
Some(multi_buffer.update(cx, |multi_buffer, cx| {
multi_buffer.clear(cx);
multi_buffer.push_multiple_excerpts_with_context_lines(
buffers_with_ranges,
DEFAULT_MULTIBUFFER_CONTEXT,
cx,
)
})
}))
})
.ok()
.ok().flatten()
{
buffer_update_task.await;
git_panel
.update(&mut cx, |git_panel, cx| {
git_panel.git_diff_editor.update(cx, |editor, cx| {
for change_set in change_sets {
editor.add_change_set(change_set, cx);
}
})
if let Some(diff_editor) = git_panel.git_diff_editor.as_ref() {
diff_editor.update(cx, |editor, cx| {
for change_set in change_sets {
editor.add_change_set(change_set, cx);
}
});
}
})
.ok();
}
@@ -1001,7 +1000,7 @@ impl GitPanel {
let id = id.to_proto() as usize;
let checkbox_id = ElementId::Name(format!("checkbox_{}", id).into());
let is_staged = ToggleState::Selected;
let handle = cx.view().clone();
let handle = cx.view().downgrade();
h_flex()
.id(id)
@@ -1024,16 +1023,18 @@ impl GitPanel {
.toggle_state(selected)
.child(h_flex().gap_1p5().child(details.display_name.clone()))
.on_click(move |e, cx| {
handle.update(cx, |git_panel, cx| {
git_panel.selected_item = Some(details.index);
let change_focus = e.down.click_count > 1;
git_panel.reveal_entry_in_git_editor(
details.hunks.clone(),
change_focus,
None,
cx,
);
});
handle
.update(cx, |git_panel, cx| {
git_panel.selected_item = Some(details.index);
let change_focus = e.down.click_count > 1;
git_panel.reveal_entry_in_git_editor(
details.hunks.clone(),
change_focus,
None,
cx,
);
})
.ok();
}),
)
}
@@ -1043,10 +1044,12 @@ impl GitPanel {
hunks: Rc<OnceCell<Vec<DiffHunk>>>,
change_focus: bool,
debounce: Option<Duration>,
cx: &mut ViewContext<'_, Self>,
cx: &mut ViewContext<Self>,
) {
let workspace = self.workspace.clone();
let diff_editor = self.git_diff_editor.clone();
let Some(diff_editor) = self.git_diff_editor.clone() else {
return;
};
self.reveal_in_editor = cx.spawn(|_, mut cx| async move {
if let Some(debounce) = debounce {
cx.background_executor().timer(debounce).await;
@@ -1196,7 +1199,7 @@ impl Panel for GitPanel {
"GitPanel"
}
fn position(&self, cx: &gpui::WindowContext) -> DockPosition {
fn position(&self, cx: &WindowContext) -> DockPosition {
GitPanelSettings::get_global(cx).dock
}
@@ -1212,7 +1215,7 @@ impl Panel for GitPanel {
);
}
fn size(&self, cx: &gpui::WindowContext) -> Pixels {
fn size(&self, cx: &WindowContext) -> Pixels {
self.width
.unwrap_or_else(|| GitPanelSettings::get_global(cx).default_width)
}
@@ -1234,14 +1237,18 @@ impl Panel for GitPanel {
fn toggle_action(&self) -> Box<dyn Action> {
Box::new(ToggleFocus)
}
fn activation_priority(&self) -> u32 {
2
}
}
fn diff_display_editor(project: Model<Project>, cx: &mut WindowContext) -> View<Editor> {
fn diff_display_editor(cx: &mut WindowContext) -> View<Editor> {
cx.new_view(|cx| {
let multi_buffer = cx.new_model(|cx| {
MultiBuffer::new(project.read(cx).capability()).with_title("Project diff".to_string())
let multi_buffer = cx.new_model(|_| {
MultiBuffer::new(language::Capability::ReadWrite).with_title("Project diff".to_string())
});
let mut editor = Editor::for_multibuffer(multi_buffer, Some(project), true, cx);
let mut editor = Editor::for_multibuffer(multi_buffer, None, true, cx);
editor.set_expand_all_diff_hunks();
editor
})

View File

@@ -22,7 +22,7 @@ test-support = [
"x11",
]
runtime_shaders = []
macos-blade = ["blade-graphics", "blade-macros", "blade-util", "bytemuck"]
macos-blade = ["blade-graphics", "blade-macros", "blade-util", "bytemuck", "objc2", "objc2-metal"]
wayland = [
"blade-graphics",
"blade-macros",
@@ -132,11 +132,14 @@ core-foundation.workspace = true
core-foundation-sys = "0.8"
core-graphics = "0.23"
core-text = "20.1"
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "40391b7", optional = true}
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "40391b7", optional = true }
foreign-types = "0.5"
log.workspace = true
media.workspace = true
objc = "0.2"
objc2 = { version = "0.5", optional = true }
objc2-metal = { version = "0.2", optional = true }
#TODO: replace with "objc2"
metal.workspace = true
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))'.dependencies]

View File

@@ -1,25 +1,574 @@
use gpui::{
div, prelude::*, px, rgb, size, App, AppContext, Bounds, ViewContext, WindowBounds,
WindowOptions,
div, hsla, point, prelude::*, px, relative, rgb, size, App, AppContext, Bounds, BoxShadow, Div,
SharedString, ViewContext, WindowBounds, WindowOptions,
};
use smallvec::smallvec;
struct Shadow {}
impl Shadow {
fn base() -> Div {
div()
.size_16()
.bg(rgb(0xffffff))
.rounded_full()
.border_1()
.border_color(hsla(0.0, 0.0, 0.0, 0.1))
}
fn square() -> Div {
div()
.size_16()
.bg(rgb(0xffffff))
.border_1()
.border_color(hsla(0.0, 0.0, 0.0, 0.1))
}
fn rounded_small() -> Div {
div()
.size_16()
.bg(rgb(0xffffff))
.rounded(px(4.))
.border_1()
.border_color(hsla(0.0, 0.0, 0.0, 0.1))
}
fn rounded_medium() -> Div {
div()
.size_16()
.bg(rgb(0xffffff))
.rounded(px(8.))
.border_1()
.border_color(hsla(0.0, 0.0, 0.0, 0.1))
}
fn rounded_large() -> Div {
div()
.size_16()
.bg(rgb(0xffffff))
.rounded(px(12.))
.border_1()
.border_color(hsla(0.0, 0.0, 0.0, 0.1))
}
}
fn example(label: impl Into<SharedString>, example: impl IntoElement) -> impl IntoElement {
let label = label.into();
div()
.flex()
.flex_col()
.justify_center()
.items_center()
.w(relative(1. / 6.))
.border_r_1()
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
.child(
div()
.flex()
.items_center()
.justify_center()
.flex_1()
.py_12()
.child(example),
)
.child(
div()
.w_full()
.border_t_1()
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
.p_1()
.flex()
.items_center()
.child(label),
)
}
impl Render for Shadow {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
div()
.flex()
.id("shadow-example")
.overflow_y_scroll()
.bg(rgb(0xffffff))
.size_full()
.justify_center()
.items_center()
.child(div().size_8().shadow_sm())
.text_xs()
.child(div().flex().flex_col().w_full().children(vec![
div()
.border_b_1()
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
.flex()
.flex_row()
.children(vec![
example(
"Square",
Shadow::square()
.shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Rounded 4",
Shadow::rounded_small()
.shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Rounded 8",
Shadow::rounded_medium()
.shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Rounded 16",
Shadow::rounded_large()
.shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Circle",
Shadow::base()
.shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
]),
div()
.border_b_1()
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
.flex()
.w_full()
.children(vec![
example("None", Shadow::base()),
// Small shadow
example("Small", Shadow::base().shadow_sm()),
// Medium shadow
example("Medium", Shadow::base().shadow_md()),
// Large shadow
example("Large", Shadow::base().shadow_lg()),
example("Extra Large", Shadow::base().shadow_xl()),
example("2X Large", Shadow::base().shadow_2xl()),
]),
// Horizontal list of increasing blur radii
div()
.border_b_1()
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
.flex()
.children(vec![
example(
"Blur 0",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(0.),
spread_radius: px(0.),
}]),
),
example(
"Blur 2",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(2.),
spread_radius: px(0.),
}]),
),
example(
"Blur 4",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(4.),
spread_radius: px(0.),
}]),
),
example(
"Blur 8",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Blur 16",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(16.),
spread_radius: px(0.),
}]),
),
]),
// Horizontal list of increasing spread radii
div()
.border_b_1()
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
.flex()
.children(vec![
example(
"Spread 0",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Spread 2",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(2.),
}]),
),
example(
"Spread 4",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(4.),
}]),
),
example(
"Spread 8",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(8.),
}]),
),
example(
"Spread 16",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(16.),
}]),
),
]),
// Square spread examples
div()
.border_b_1()
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
.flex()
.children(vec![
example(
"Square Spread 0",
Shadow::square().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Square Spread 8",
Shadow::square().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(8.),
}]),
),
example(
"Square Spread 16",
Shadow::square().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(16.),
}]),
),
]),
// Rounded large spread examples
div()
.border_b_1()
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
.flex()
.children(vec![
example(
"Rounded Large Spread 0",
Shadow::rounded_large().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Rounded Large Spread 8",
Shadow::rounded_large().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(8.),
}]),
),
example(
"Rounded Large Spread 16",
Shadow::rounded_large().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.0, 0.0, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(16.),
}]),
),
]),
// Directional shadows
div()
.border_b_1()
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
.flex()
.children(vec![
example(
"Left",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(-8.), px(0.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Right",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(8.), px(0.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Top",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(0.), px(-8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Bottom",
Shadow::base().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
]),
// Square directional shadows
div()
.border_b_1()
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
.flex()
.children(vec![
example(
"Square Left",
Shadow::square().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(-8.), px(0.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Square Right",
Shadow::square().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(8.), px(0.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Square Top",
Shadow::square().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(0.), px(-8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Square Bottom",
Shadow::square().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
]),
// Rounded large directional shadows
div()
.border_b_1()
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
.flex()
.children(vec![
example(
"Rounded Large Left",
Shadow::rounded_large().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(-8.), px(0.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Rounded Large Right",
Shadow::rounded_large().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(8.), px(0.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Rounded Large Top",
Shadow::rounded_large().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(0.), px(-8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
example(
"Rounded Large Bottom",
Shadow::rounded_large().shadow(smallvec![BoxShadow {
color: hsla(0.0, 0.5, 0.5, 0.3),
offset: point(px(0.), px(8.)),
blur_radius: px(8.),
spread_radius: px(0.),
}]),
),
]),
// Multiple shadows for different shapes
div()
.border_b_1()
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
.flex()
.children(vec![
example(
"Circle Multiple",
Shadow::base().shadow(smallvec![
BoxShadow {
color: hsla(0.0 / 360., 1.0, 0.5, 0.3), // Red
offset: point(px(0.), px(-12.)),
blur_radius: px(8.),
spread_radius: px(2.),
},
BoxShadow {
color: hsla(60.0 / 360., 1.0, 0.5, 0.3), // Yellow
offset: point(px(12.), px(0.)),
blur_radius: px(8.),
spread_radius: px(2.),
},
BoxShadow {
color: hsla(120.0 / 360., 1.0, 0.5, 0.3), // Green
offset: point(px(0.), px(12.)),
blur_radius: px(8.),
spread_radius: px(2.),
},
BoxShadow {
color: hsla(240.0 / 360., 1.0, 0.5, 0.3), // Blue
offset: point(px(-12.), px(0.)),
blur_radius: px(8.),
spread_radius: px(2.),
},
]),
),
example(
"Square Multiple",
Shadow::square().shadow(smallvec![
BoxShadow {
color: hsla(0.0 / 360., 1.0, 0.5, 0.3), // Red
offset: point(px(0.), px(-12.)),
blur_radius: px(8.),
spread_radius: px(2.),
},
BoxShadow {
color: hsla(60.0 / 360., 1.0, 0.5, 0.3), // Yellow
offset: point(px(12.), px(0.)),
blur_radius: px(8.),
spread_radius: px(2.),
},
BoxShadow {
color: hsla(120.0 / 360., 1.0, 0.5, 0.3), // Green
offset: point(px(0.), px(12.)),
blur_radius: px(8.),
spread_radius: px(2.),
},
BoxShadow {
color: hsla(240.0 / 360., 1.0, 0.5, 0.3), // Blue
offset: point(px(-12.), px(0.)),
blur_radius: px(8.),
spread_radius: px(2.),
},
]),
),
example(
"Rounded Large Multiple",
Shadow::rounded_large().shadow(smallvec![
BoxShadow {
color: hsla(0.0 / 360., 1.0, 0.5, 0.3), // Red
offset: point(px(0.), px(-12.)),
blur_radius: px(8.),
spread_radius: px(2.),
},
BoxShadow {
color: hsla(60.0 / 360., 1.0, 0.5, 0.3), // Yellow
offset: point(px(12.), px(0.)),
blur_radius: px(8.),
spread_radius: px(2.),
},
BoxShadow {
color: hsla(120.0 / 360., 1.0, 0.5, 0.3), // Green
offset: point(px(0.), px(12.)),
blur_radius: px(8.),
spread_radius: px(2.),
},
BoxShadow {
color: hsla(240.0 / 360., 1.0, 0.5, 0.3), // Blue
offset: point(px(-12.), px(0.)),
blur_radius: px(8.),
spread_radius: px(2.),
},
]),
),
]),
]))
}
}
fn main() {
App::new().run(|cx: &mut AppContext| {
let bounds = Bounds::centered(None, size(px(300.0), px(300.0)), cx);
let bounds = Bounds::centered(None, size(px(1000.0), px(800.0)), cx);
cx.open_window(
WindowOptions {
window_bounds: Some(WindowBounds::Windowed(bounds)),
@@ -28,5 +577,7 @@ fn main() {
|cx| cx.new_view(|_cx| Shadow {}),
)
.unwrap();
cx.activate(true);
});
}

View File

@@ -1490,7 +1490,7 @@ impl Context for AppContext {
fn update_window<T, F>(&mut self, handle: AnyWindowHandle, update: F) -> Result<T>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
F: FnOnce(AnyView, &mut WindowContext) -> T,
{
self.update(|cx| {
let mut window = cx

View File

@@ -84,7 +84,7 @@ impl Context for AsyncAppContext {
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
F: FnOnce(AnyView, &mut WindowContext) -> T,
{
let app = self.app.upgrade().context("app was released")?;
let mut lock = app.borrow_mut();
@@ -349,7 +349,7 @@ impl Context for AsyncWindowContext {
fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
F: FnOnce(AnyView, &mut WindowContext) -> T,
{
self.app.update_window(window, update)
}
@@ -369,7 +369,7 @@ impl Context for AsyncWindowContext {
impl VisualContext for AsyncWindowContext {
fn new_view<V>(
&mut self,
build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,
build_view_state: impl FnOnce(&mut ViewContext<V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render,
@@ -381,7 +381,7 @@ impl VisualContext for AsyncWindowContext {
fn update_view<V: 'static, R>(
&mut self,
view: &View<V>,
update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
update: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
) -> Self::Result<R> {
self.window
.update(self, |_, cx| cx.update_view(view, update))
@@ -389,7 +389,7 @@ impl VisualContext for AsyncWindowContext {
fn replace_root_view<V>(
&mut self,
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
build_view: impl FnOnce(&mut ViewContext<V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render,

View File

@@ -263,7 +263,7 @@ impl<'a, T> Context for ModelContext<'a, T> {
fn update_window<R, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<R>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> R,
F: FnOnce(AnyView, &mut WindowContext) -> R,
{
self.app.update_window(window, update)
}

View File

@@ -77,7 +77,7 @@ impl Context for TestAppContext {
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
F: FnOnce(AnyView, &mut WindowContext) -> T,
{
let mut lock = self.app.borrow_mut();
lock.update_window(window, f)
@@ -916,7 +916,7 @@ impl Context for VisualTestContext {
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
F: FnOnce(AnyView, &mut WindowContext) -> T,
{
self.cx.update_window(window, f)
}
@@ -936,7 +936,7 @@ impl Context for VisualTestContext {
impl VisualContext for VisualTestContext {
fn new_view<V>(
&mut self,
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
build_view: impl FnOnce(&mut ViewContext<V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render,
@@ -949,7 +949,7 @@ impl VisualContext for VisualTestContext {
fn update_view<V: 'static, R>(
&mut self,
view: &View<V>,
update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
update: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
) -> Self::Result<R> {
self.window
.update(&mut self.cx, |_, cx| cx.update_view(view, update))
@@ -958,7 +958,7 @@ impl VisualContext for VisualTestContext {
fn replace_root_view<V>(
&mut self,
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
build_view: impl FnOnce(&mut ViewContext<V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render,
@@ -993,7 +993,7 @@ impl AnyWindowHandle {
pub fn build_view<V: Render + 'static>(
&self,
cx: &mut TestAppContext,
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
build_view: impl FnOnce(&mut ViewContext<V>) -> V,
) -> View<V> {
self.update(cx, |_, cx| cx.new_view(build_view)).unwrap()
}

View File

@@ -1,6 +1,6 @@
use std::time::{Duration, Instant};
use crate::{AnyElement, Element, ElementId, GlobalElementId, IntoElement};
use crate::{AnyElement, Element, ElementId, GlobalElementId, IntoElement, WindowContext};
pub use easing::*;
@@ -104,7 +104,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
fn request_layout(
&mut self,
global_id: Option<&GlobalElementId>,
cx: &mut crate::WindowContext,
cx: &mut WindowContext,
) -> (crate::LayoutId, Self::RequestLayoutState) {
cx.with_element_state(global_id.unwrap(), |state, cx| {
let state = state.unwrap_or_else(|| AnimationState {
@@ -145,7 +145,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
_id: Option<&GlobalElementId>,
_bounds: crate::Bounds<crate::Pixels>,
element: &mut Self::RequestLayoutState,
cx: &mut crate::WindowContext,
cx: &mut WindowContext,
) -> Self::PrepaintState {
element.prepaint(cx);
}
@@ -156,7 +156,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
_bounds: crate::Bounds<crate::Pixels>,
element: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
cx: &mut crate::WindowContext,
cx: &mut WindowContext,
) {
element.paint(cx);
}

View File

@@ -1417,6 +1417,19 @@ impl Interactivity {
None
};
let invalidate_tooltip = hitbox
.as_ref()
.map_or(true, |hitbox| !hitbox.bounds.contains(&cx.mouse_position()));
if invalidate_tooltip {
if let Some(active_tooltip) = element_state
.as_ref()
.and_then(|state| state.active_tooltip.as_ref())
{
*active_tooltip.borrow_mut() = None;
self.tooltip_id = None;
}
}
let scroll_offset = self.clamp_scroll_position(bounds, &style, cx);
let result = f(&style, scroll_offset, hitbox, cx);
(result, element_state)
@@ -2499,7 +2512,7 @@ impl ScrollAnchor {
}
}
/// Request scroll to this item on the next frame.
pub fn scroll_to(&self, cx: &mut WindowContext<'_>) {
pub fn scroll_to(&self, cx: &mut WindowContext) {
let this = self.clone();
cx.on_next_frame(move |_| {

View File

@@ -716,7 +716,7 @@ impl Element for List {
fn request_layout(
&mut self,
_id: Option<&GlobalElementId>,
cx: &mut crate::WindowContext,
cx: &mut WindowContext,
) -> (crate::LayoutId, Self::RequestLayoutState) {
let layout_id = match self.sizing_behavior {
ListSizingBehavior::Infer => {
@@ -827,7 +827,7 @@ impl Element for List {
bounds: Bounds<crate::Pixels>,
_: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
cx: &mut crate::WindowContext,
cx: &mut WindowContext,
) {
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
for item in &mut prepaint.layout.item_layouts {

View File

@@ -472,9 +472,9 @@ pub struct InteractiveText {
element_id: ElementId,
text: StyledText,
click_listener:
Option<Box<dyn Fn(&[Range<usize>], InteractiveTextClickEvent, &mut WindowContext<'_>)>>,
hover_listener: Option<Box<dyn Fn(Option<usize>, MouseMoveEvent, &mut WindowContext<'_>)>>,
tooltip_builder: Option<Rc<dyn Fn(usize, &mut WindowContext<'_>) -> Option<AnyView>>>,
Option<Box<dyn Fn(&[Range<usize>], InteractiveTextClickEvent, &mut WindowContext)>>,
hover_listener: Option<Box<dyn Fn(Option<usize>, MouseMoveEvent, &mut WindowContext)>>,
tooltip_builder: Option<Rc<dyn Fn(usize, &mut WindowContext) -> Option<AnyView>>>,
clickable_ranges: Vec<Range<usize>>,
}
@@ -510,7 +510,7 @@ impl InteractiveText {
pub fn on_click(
mut self,
ranges: Vec<Range<usize>>,
listener: impl Fn(usize, &mut WindowContext<'_>) + 'static,
listener: impl Fn(usize, &mut WindowContext) + 'static,
) -> Self {
self.click_listener = Some(Box::new(move |ranges, event, cx| {
for (range_ix, range) in ranges.iter().enumerate() {
@@ -528,7 +528,7 @@ impl InteractiveText {
/// index of the hovered character, or None if the mouse leaves the text.
pub fn on_hover(
mut self,
listener: impl Fn(Option<usize>, MouseMoveEvent, &mut WindowContext<'_>) + 'static,
listener: impl Fn(Option<usize>, MouseMoveEvent, &mut WindowContext) + 'static,
) -> Self {
self.hover_listener = Some(Box::new(listener));
self
@@ -537,7 +537,7 @@ impl InteractiveText {
/// tooltip lets you specify a tooltip for a given character index in the string.
pub fn tooltip(
mut self,
builder: impl Fn(usize, &mut WindowContext<'_>) -> Option<AnyView> + 'static,
builder: impl Fn(usize, &mut WindowContext) -> Option<AnyView> + 'static,
) -> Self {
self.tooltip_builder = Some(Rc::new(builder));
self

View File

@@ -202,7 +202,7 @@ pub trait Context {
/// Update a window for the given handle.
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T;
F: FnOnce(AnyView, &mut WindowContext) -> T;
/// Read a window off of the application context.
fn read_window<T, R>(
@@ -231,7 +231,7 @@ pub trait VisualContext: Context {
/// Construct a new view in the window referenced by this context.
fn new_view<V>(
&mut self,
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
build_view: impl FnOnce(&mut ViewContext<V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render;
@@ -240,13 +240,13 @@ pub trait VisualContext: Context {
fn update_view<V: 'static, R>(
&mut self,
view: &View<V>,
update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
update: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
) -> Self::Result<R>;
/// Replace the root view of a window with a new view.
fn replace_root_view<V>(
&mut self,
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
build_view: impl FnOnce(&mut ViewContext<V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render;

View File

@@ -468,7 +468,7 @@ mod test {
use crate::{
self as gpui, div, FocusHandle, InteractiveElement, IntoElement, KeyBinding, Keystroke,
ParentElement, Render, TestAppContext, VisualContext,
ParentElement, Render, TestAppContext, ViewContext, VisualContext,
};
struct TestView {
@@ -480,7 +480,7 @@ mod test {
actions!(test, [TestAction]);
impl Render for TestView {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
div().id("testview").child(
div()
.key_context("parent")

View File

@@ -214,6 +214,7 @@ impl BladeAtlasState {
},
array_layer_count: 1,
mip_level_count: 1,
sample_count: 1,
dimension: gpu::TextureDimension::D2,
usage,
});

View File

@@ -174,8 +174,9 @@ impl BladePipelines {
..Default::default()
},
depth_stencil: None,
fragment: shader.at("fs_quad"),
fragment: Some(shader.at("fs_quad")),
color_targets,
multisample_state: gpu::MultisampleState::default(),
}),
shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
name: "shadows",
@@ -187,8 +188,9 @@ impl BladePipelines {
..Default::default()
},
depth_stencil: None,
fragment: shader.at("fs_shadow"),
fragment: Some(shader.at("fs_shadow")),
color_targets,
multisample_state: gpu::MultisampleState::default(),
}),
path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
name: "path_rasterization",
@@ -200,12 +202,13 @@ impl BladePipelines {
..Default::default()
},
depth_stencil: None,
fragment: shader.at("fs_path_rasterization"),
fragment: Some(shader.at("fs_path_rasterization")),
color_targets: &[gpu::ColorTargetState {
format: PATH_TEXTURE_FORMAT,
blend: Some(gpu::BlendState::ADDITIVE),
write_mask: gpu::ColorWrites::default(),
}],
multisample_state: gpu::MultisampleState::default(),
}),
paths: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
name: "paths",
@@ -217,8 +220,9 @@ impl BladePipelines {
..Default::default()
},
depth_stencil: None,
fragment: shader.at("fs_path"),
fragment: Some(shader.at("fs_path")),
color_targets,
multisample_state: gpu::MultisampleState::default(),
}),
underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
name: "underlines",
@@ -230,8 +234,9 @@ impl BladePipelines {
..Default::default()
},
depth_stencil: None,
fragment: shader.at("fs_underline"),
fragment: Some(shader.at("fs_underline")),
color_targets,
multisample_state: gpu::MultisampleState::default(),
}),
mono_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
name: "mono-sprites",
@@ -243,8 +248,9 @@ impl BladePipelines {
..Default::default()
},
depth_stencil: None,
fragment: shader.at("fs_mono_sprite"),
fragment: Some(shader.at("fs_mono_sprite")),
color_targets,
multisample_state: gpu::MultisampleState::default(),
}),
poly_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
name: "poly-sprites",
@@ -256,8 +262,9 @@ impl BladePipelines {
..Default::default()
},
depth_stencil: None,
fragment: shader.at("fs_poly_sprite"),
fragment: Some(shader.at("fs_poly_sprite")),
color_targets,
multisample_state: gpu::MultisampleState::default(),
}),
surfaces: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
name: "surfaces",
@@ -269,8 +276,9 @@ impl BladePipelines {
..Default::default()
},
depth_stencil: None,
fragment: shader.at("fs_surface"),
fragment: Some(shader.at("fs_surface")),
color_targets,
multisample_state: gpu::MultisampleState::default(),
}),
}
}
@@ -350,8 +358,10 @@ impl BladeRenderer {
#[cfg(target_os = "macos")]
let core_video_texture_cache = unsafe {
use foreign_types::ForeignType as _;
CVMetalTextureCache::new(context.gpu.metal_device().as_ptr()).unwrap()
CVMetalTextureCache::new(
objc2::rc::Retained::as_ptr(&context.gpu.metal_device()) as *mut _
)
.unwrap()
};
Ok(Self {
@@ -440,13 +450,12 @@ impl BladeRenderer {
#[cfg(target_os = "macos")]
pub fn layer(&self) -> metal::MetalLayer {
self.surface.metal_layer()
unsafe { foreign_types::ForeignType::from_ptr(self.layer_ptr()) }
}
#[cfg(target_os = "macos")]
pub fn layer_ptr(&self) -> *mut metal::CAMetalLayer {
use metal::foreign_types::ForeignType as _;
self.surface.metal_layer().as_ptr()
objc2::rc::Retained::as_ptr(&self.surface.metal_layer()) as *mut _
}
#[profiling::function]
@@ -678,45 +687,59 @@ impl BladeRenderer {
#[cfg(target_os = "macos")]
{
let (t_y, t_cb_cr) = {
let (t_y, t_cb_cr) = unsafe {
use core_foundation::base::TCFType as _;
use std::ptr;
assert_eq!(
surface.image_buffer.pixel_format_type(),
media::core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
);
surface.image_buffer.pixel_format_type(),
media::core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
);
let y_texture = unsafe {
self.core_video_texture_cache
.create_texture_from_image(
surface.image_buffer.as_concrete_TypeRef(),
ptr::null(),
metal::MTLPixelFormat::R8Unorm,
surface.image_buffer.plane_width(0),
surface.image_buffer.plane_height(0),
0,
)
.unwrap()
};
let cb_cr_texture = unsafe {
self.core_video_texture_cache
.create_texture_from_image(
surface.image_buffer.as_concrete_TypeRef(),
ptr::null(),
metal::MTLPixelFormat::RG8Unorm,
surface.image_buffer.plane_width(1),
surface.image_buffer.plane_height(1),
1,
)
.unwrap()
};
let y_texture = self
.core_video_texture_cache
.create_texture_from_image(
surface.image_buffer.as_concrete_TypeRef(),
ptr::null(),
metal::MTLPixelFormat::R8Unorm,
surface.image_buffer.plane_width(0),
surface.image_buffer.plane_height(0),
0,
)
.unwrap();
let cb_cr_texture = self
.core_video_texture_cache
.create_texture_from_image(
surface.image_buffer.as_concrete_TypeRef(),
ptr::null(),
metal::MTLPixelFormat::RG8Unorm,
surface.image_buffer.plane_width(1),
surface.image_buffer.plane_height(1),
1,
)
.unwrap();
(
gpu::TextureView::from_metal_texture(
y_texture.as_texture_ref(),
&objc2::rc::Retained::retain(
foreign_types::ForeignTypeRef::as_ptr(
y_texture.as_texture_ref(),
)
as *mut objc2::runtime::ProtocolObject<
dyn objc2_metal::MTLTexture,
>,
)
.unwrap(),
),
gpu::TextureView::from_metal_texture(
cb_cr_texture.as_texture_ref(),
&objc2::rc::Retained::retain(
foreign_types::ForeignTypeRef::as_ptr(
cb_cr_texture.as_texture_ref(),
)
as *mut objc2::runtime::ProtocolObject<
dyn objc2_metal::MTLTexture,
>,
)
.unwrap(),
),
)
};

View File

@@ -146,19 +146,18 @@ fn handle_size_msg(
// Don't resize the renderer when the window is minimized, but record that it was minimized so
// that on restore the swap chain can be recreated via `update_drawable_size_even_if_unchanged`.
if wparam.0 == SIZE_MINIMIZED as usize {
lock.is_minimized = Some(true);
lock.restore_from_minimized = lock.callbacks.request_frame.take();
return Some(0);
}
let may_have_been_minimized = lock.is_minimized.unwrap_or(true);
lock.is_minimized = Some(false);
let width = lparam.loword().max(1) as i32;
let height = lparam.hiword().max(1) as i32;
let new_size = size(DevicePixels(width), DevicePixels(height));
let scale_factor = lock.scale_factor;
if may_have_been_minimized {
if lock.restore_from_minimized.is_some() {
lock.renderer
.update_drawable_size_even_if_unchanged(new_size);
lock.callbacks.request_frame = lock.restore_from_minimized.take();
} else {
lock.renderer.update_drawable_size(new_size);
}

View File

@@ -38,7 +38,7 @@ pub struct WindowsWindowState {
pub fullscreen_restore_bounds: Bounds<Pixels>,
pub border_offset: WindowBorderOffset,
pub scale_factor: f32,
pub is_minimized: Option<bool>,
pub restore_from_minimized: Option<Box<dyn FnMut(RequestFrameOptions)>>,
pub callbacks: Callbacks,
pub input_handler: Option<PlatformInputHandler>,
@@ -94,7 +94,7 @@ impl WindowsWindowState {
size: logical_size,
};
let border_offset = WindowBorderOffset::default();
let is_minimized = None;
let restore_from_minimized = None;
let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?;
let callbacks = Callbacks::default();
let input_handler = None;
@@ -112,7 +112,7 @@ impl WindowsWindowState {
fullscreen_restore_bounds,
border_offset,
scale_factor,
is_minimized,
restore_from_minimized,
callbacks,
input_handler,
system_key_handled,

View File

@@ -385,28 +385,20 @@ impl LineLayoutCache {
let mut previous_frame = &mut *self.previous_frame.lock();
let mut current_frame = &mut *self.current_frame.write();
if let Some(cached_keys) = previous_frame
.used_lines
.get(range.start.lines_index..range.end.lines_index)
{
for key in cached_keys {
if let Some((key, line)) = previous_frame.lines.remove_entry(key) {
current_frame.lines.insert(key, line);
}
current_frame.used_lines.push(key.clone());
for key in &previous_frame.used_lines[range.start.lines_index..range.end.lines_index] {
if let Some((key, line)) = previous_frame.lines.remove_entry(key) {
current_frame.lines.insert(key, line);
}
current_frame.used_lines.push(key.clone());
}
if let Some(cached_keys) = previous_frame
.used_wrapped_lines
.get(range.start.wrapped_lines_index..range.end.wrapped_lines_index)
for key in &previous_frame.used_wrapped_lines
[range.start.wrapped_lines_index..range.end.wrapped_lines_index]
{
for key in cached_keys {
if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) {
current_frame.wrapped_lines.insert(key, line);
}
current_frame.used_wrapped_lines.push(key.clone());
if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) {
current_frame.wrapped_lines.insert(key, line);
}
current_frame.used_wrapped_lines.push(key.clone());
}
}

View File

@@ -69,7 +69,7 @@ impl<V: 'static> View<V> {
pub fn update<C, R>(
&self,
cx: &mut C,
f: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
f: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
) -> C::Result<R>
where
C: VisualContext,
@@ -183,7 +183,7 @@ impl<V: 'static> WeakView<V> {
pub fn update<C, R>(
&self,
cx: &mut C,
f: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
f: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
) -> Result<R>
where
C: VisualContext,

View File

@@ -1753,17 +1753,12 @@ impl<'a> WindowContext<'a> {
.iter_mut()
.map(|listener| listener.take()),
);
if let Some(element_states) = window
.rendered_frame
.accessed_element_states
.get(range.start.accessed_element_states_index..range.end.accessed_element_states_index)
{
window.next_frame.accessed_element_states.extend(
element_states
.iter()
.map(|(id, type_id)| (GlobalElementId(id.0.clone()), *type_id)),
);
}
window.next_frame.accessed_element_states.extend(
window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
..range.end.accessed_element_states_index]
.iter()
.map(|(id, type_id)| (GlobalElementId(id.0.clone()), *type_id)),
);
window
.text_system

View File

@@ -11,7 +11,7 @@ pub fn derive_render(input: TokenStream) -> TokenStream {
impl #impl_generics gpui::Render for #type_name #type_generics
#where_clause
{
fn render(&mut self, _cx: &mut gpui::ViewContext<Self>) -> impl gpui::Element {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl gpui::Element {
gpui::Empty
}
}

View File

@@ -4,12 +4,11 @@ use crate::{
LanguageModelProviderState, LanguageModelRequest,
};
use futures::{channel::mpsc, future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
use gpui::{AnyView, AppContext, AsyncAppContext, Task};
use gpui::{AnyView, AppContext, AsyncAppContext, Model, Task, WindowContext};
use http_client::Result;
use parking_lot::Mutex;
use serde::Serialize;
use std::sync::Arc;
use ui::WindowContext;
pub fn language_model_id() -> LanguageModelId {
LanguageModelId::from("fake".to_string())
@@ -33,7 +32,7 @@ pub struct FakeLanguageModelProvider;
impl LanguageModelProviderState for FakeLanguageModelProvider {
type ObservableEntity = ();
fn observable_entity(&self) -> Option<gpui::Model<Self::ObservableEntity>> {
fn observable_entity(&self) -> Option<Model<Self::ObservableEntity>> {
None
}
}

View File

@@ -703,15 +703,11 @@ impl LspLogView {
});
let editor_subscription = cx.subscribe(
&editor,
|_, _, event: &EditorEvent, cx: &mut ViewContext<'_, LspLogView>| {
cx.emit(event.clone())
},
|_, _, event: &EditorEvent, cx: &mut ViewContext<LspLogView>| cx.emit(event.clone()),
);
let search_subscription = cx.subscribe(
&editor,
|_, _, event: &SearchEvent, cx: &mut ViewContext<'_, LspLogView>| {
cx.emit(event.clone())
},
|_, _, event: &SearchEvent, cx: &mut ViewContext<LspLogView>| cx.emit(event.clone()),
);
(editor, vec![editor_subscription, search_subscription])
}
@@ -730,15 +726,11 @@ impl LspLogView {
});
let editor_subscription = cx.subscribe(
&editor,
|_, _, event: &EditorEvent, cx: &mut ViewContext<'_, LspLogView>| {
cx.emit(event.clone())
},
|_, _, event: &EditorEvent, cx: &mut ViewContext<LspLogView>| cx.emit(event.clone()),
);
let search_subscription = cx.subscribe(
&editor,
|_, _, event: &SearchEvent, cx: &mut ViewContext<'_, LspLogView>| {
cx.emit(event.clone())
},
|_, _, event: &SearchEvent, cx: &mut ViewContext<LspLogView>| cx.emit(event.clone()),
);
(editor, vec![editor_subscription, search_subscription])
}

View File

@@ -273,7 +273,7 @@ impl SyntaxTreeView {
}
impl Render for SyntaxTreeView {
fn render(&mut self, cx: &mut gpui::ViewContext<'_, Self>) -> impl IntoElement {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let mut rendered = div().flex_1();
if let Some(layer) = self
@@ -422,7 +422,7 @@ impl SyntaxTreeToolbarItemView {
}
}
fn render_menu(&mut self, cx: &mut ViewContext<'_, Self>) -> Option<PopoverMenu<ContextMenu>> {
fn render_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<PopoverMenu<ContextMenu>> {
let tree_view = self.tree_view.as_ref()?;
let tree_view = tree_view.read(cx);
@@ -492,7 +492,7 @@ fn format_node_range(node: Node) -> String {
}
impl Render for SyntaxTreeToolbarItemView {
fn render(&mut self, cx: &mut ViewContext<'_, Self>) -> impl IntoElement {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
self.render_menu(cx)
.unwrap_or_else(|| PopoverMenu::new("Empty Syntax Tree"))
}

View File

@@ -194,6 +194,7 @@
"throw"
"try"
"typeof"
"using"
"var"
"void"
"while"

View File

@@ -284,6 +284,7 @@ impl<F: Future> LspRequestFuture<F::Output> for LspRequest<F> {
}
/// Combined capabilities of the server and the adapter.
#[derive(Debug)]
pub struct AdapterServerCapabilities {
// Reported capabilities by the server
pub server_capabilities: ServerCapabilities,

View File

@@ -614,11 +614,11 @@ impl Element for MarkdownElement {
};
builder.push_div(
div()
.mb_1()
.h_flex()
.mb_2()
.line_height(rems(1.3))
.items_start()
.gap_1()
.line_height(rems(1.3))
.child(bullet),
range,
markdown_end,

View File

@@ -3449,6 +3449,24 @@ impl MultiBufferSnapshot {
}
}
pub fn buffer_ids_in_selected_rows(
&self,
selection: Selection<Point>,
) -> impl Iterator<Item = BufferId> + '_ {
let mut cursor = self.excerpts.cursor::<Point>(&());
cursor.seek(&Point::new(selection.start.row, 0), Bias::Right, &());
cursor.prev(&());
iter::from_fn(move || {
cursor.next(&());
if cursor.start().row <= selection.end.row {
cursor.item().map(|item| item.buffer_id)
} else {
None
}
})
}
pub fn excerpts(
&self,
) -> impl Iterator<Item = (ExcerptId, &BufferSnapshot, ExcerptRange<text::Anchor>)> {

View File

@@ -25,6 +25,7 @@ theme.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
zed_actions.workspace = true
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }

View File

@@ -4,9 +4,7 @@ use std::{
sync::Arc,
};
use editor::{
actions::ToggleOutline, scroll::Autoscroll, Anchor, AnchorRangeExt, Editor, EditorMode,
};
use editor::{scroll::Autoscroll, Anchor, AnchorRangeExt, Editor, EditorMode};
use fuzzy::StringMatch;
use gpui::{
div, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, HighlightStyle,
@@ -24,9 +22,22 @@ use workspace::{DismissDecision, ModalView};
pub fn init(cx: &mut AppContext) {
cx.observe_new_views(OutlineView::register).detach();
zed_actions::outline::TOGGLE_OUTLINE
.set(|view, cx| {
let Ok(view) = view.downcast::<Editor>() else {
return;
};
toggle(view, &Default::default(), cx);
})
.ok();
}
pub fn toggle(editor: View<Editor>, _: &ToggleOutline, cx: &mut WindowContext) {
pub fn toggle(
editor: View<Editor>,
_: &zed_actions::outline::ToggleOutline,
cx: &mut WindowContext,
) {
let outline = editor
.read(cx)
.buffer()
@@ -459,7 +470,7 @@ mod tests {
workspace: &View<Workspace>,
cx: &mut VisualTestContext,
) -> View<Picker<OutlineViewDelegate>> {
cx.dispatch_action(ToggleOutline);
cx.dispatch_action(zed_actions::outline::ToggleOutline);
workspace.update(cx, |workspace, cx| {
workspace
.active_modal::<OutlineView>(cx)

View File

@@ -149,7 +149,7 @@ impl SearchState {
previous_matches: HashMap<Range<editor::Anchor>, Arc<OnceLock<SearchData>>>,
new_matches: Vec<Range<editor::Anchor>>,
theme: Arc<SyntaxTheme>,
cx: &mut ViewContext<'_, OutlinePanel>,
cx: &mut ViewContext<OutlinePanel>,
) -> Self {
let (highlight_search_match_tx, highlight_search_match_rx) = channel::unbounded();
let (notify_tx, notify_rx) = channel::unbounded::<()>();
@@ -1661,7 +1661,7 @@ impl OutlinePanel {
}
}
fn reveal_entry_for_selection(&mut self, editor: View<Editor>, cx: &mut ViewContext<'_, Self>) {
fn reveal_entry_for_selection(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
if !self.active || !OutlinePanelSettings::get_global(cx).auto_reveal_entries {
return;
}
@@ -2656,7 +2656,7 @@ impl OutlinePanel {
self.clear_previous(cx);
let buffer_search_subscription = cx.subscribe(
&new_active_editor,
|outline_panel: &mut Self, _, e: &SearchEvent, cx: &mut ViewContext<'_, Self>| {
|outline_panel: &mut Self, _, e: &SearchEvent, cx: &mut ViewContext<Self>| {
if matches!(e, SearchEvent::MatchesInvalidated) {
outline_panel.update_search_matches(cx);
};
@@ -2675,7 +2675,7 @@ impl OutlinePanel {
self.update_fs_entries(new_active_editor, None, cx);
}
fn clear_previous(&mut self, cx: &mut WindowContext<'_>) {
fn clear_previous(&mut self, cx: &mut WindowContext) {
self.fs_entries_update_task = Task::ready(());
self.outline_fetch_tasks.clear();
self.cached_entries_update_task = Task::ready(());
@@ -3124,7 +3124,7 @@ impl OutlinePanel {
&self,
is_singleton: bool,
query: Option<String>,
cx: &mut ViewContext<'_, Self>,
cx: &mut ViewContext<Self>,
) -> Task<(Vec<CachedEntry>, Option<usize>)> {
let project = self.project.clone();
let Some(active_editor) = self.active_editor() else {
@@ -4078,7 +4078,7 @@ impl OutlinePanel {
query: Option<String>,
show_indent_guides: bool,
indent_size: f32,
cx: &mut ViewContext<'_, Self>,
cx: &mut ViewContext<Self>,
) -> Div {
let contents = if self.cached_entries.is_empty() {
let header = if self.updating_fs_entries {
@@ -4266,7 +4266,7 @@ impl OutlinePanel {
v_flex().w_full().flex_1().overflow_hidden().child(contents)
}
fn render_filter_footer(&mut self, pinned: bool, cx: &mut ViewContext<'_, Self>) -> Div {
fn render_filter_footer(&mut self, pinned: bool, cx: &mut ViewContext<Self>) -> Div {
v_flex().flex_none().child(horizontal_separator(cx)).child(
h_flex()
.p_2()
@@ -4468,6 +4468,10 @@ impl Panel for OutlinePanel {
})
.detach()
}
fn activation_priority(&self) -> u32 {
5
}
}
impl FocusableView for OutlinePanel {

View File

@@ -16,7 +16,7 @@ pub(crate) enum Head {
impl Head {
pub fn editor<V: 'static>(
placeholder_text: Arc<str>,
edit_handler: impl FnMut(&mut V, View<Editor>, &EditorEvent, &mut ViewContext<'_, V>) + 'static,
edit_handler: impl FnMut(&mut V, View<Editor>, &EditorEvent, &mut ViewContext<V>) + 'static,
cx: &mut ViewContext<V>,
) -> Self {
let editor = cx.new_view(|cx| {
@@ -29,7 +29,7 @@ impl Head {
}
pub fn empty<V: 'static>(
blur_handler: impl FnMut(&mut V, &mut ViewContext<'_, V>) + 'static,
blur_handler: impl FnMut(&mut V, &mut ViewContext<V>) + 'static,
cx: &mut ViewContext<V>,
) -> Self {
let head = cx.new_view(EmptyHead::new);

View File

@@ -425,7 +425,7 @@ impl<D: PickerDelegate> Picker<D> {
self.cancel(&menu::Cancel, cx);
}
pub fn refresh_placeholder(&mut self, cx: &mut WindowContext<'_>) {
pub fn refresh_placeholder(&mut self, cx: &mut WindowContext) {
match &self.head {
Head::Editor(view) => {
let placeholder = self.delegate.placeholder_text(cx);
@@ -493,7 +493,7 @@ impl<D: PickerDelegate> Picker<D> {
}
}
pub fn set_query(&self, query: impl Into<Arc<str>>, cx: &mut WindowContext<'_>) {
pub fn set_query(&self, query: impl Into<Arc<str>>, cx: &mut WindowContext) {
if let Head::Editor(ref editor) = &self.head {
editor.update(cx, |editor, cx| {
editor.set_text(query, cx);

View File

@@ -1918,6 +1918,7 @@ impl LspCommand for GetCompletions {
new_text,
server_id,
lsp_completion,
resolved: false,
}
})
.collect())

View File

@@ -2353,8 +2353,16 @@ impl LocalLspStore {
let (mut edits, mut snippet_edits) = (vec![], vec![]);
for edit in op.edits {
match edit {
Edit::Plain(edit) => edits.push(edit),
Edit::Annotated(edit) => edits.push(edit.text_edit),
Edit::Plain(edit) => {
if !edits.contains(&edit) {
edits.push(edit)
}
}
Edit::Annotated(edit) => {
if !edits.contains(&edit.text_edit) {
edits.push(edit.text_edit)
}
}
Edit::Snippet(edit) => {
let Ok(snippet) = Snippet::parse(&edit.snippet.value)
else {
@@ -2365,10 +2373,13 @@ impl LocalLspStore {
snippet_edits.push((edit.range, snippet));
} else {
// Since this buffer is not focused, apply a normal edit.
edits.push(TextEdit {
let new_edit = TextEdit {
range: edit.range,
new_text: snippet.text,
});
};
if !edits.contains(&new_edit) {
edits.push(new_edit);
}
}
}
}
@@ -4152,38 +4163,27 @@ impl LspStore {
let mut did_resolve = false;
if let Some((client, project_id)) = client {
for completion_index in completion_indices {
let (server_id, completion) = {
let completions = completions.borrow_mut();
let completion = &completions[completion_index];
did_resolve = true;
let server_id = completion.server_id;
let completion = completion.lsp_completion.clone();
let server_id = completions.borrow()[completion_index].server_id;
(server_id, completion)
};
Self::resolve_completion_remote(
if Self::resolve_completion_remote(
project_id,
server_id,
buffer_id,
completions.clone(),
completion_index,
completion,
client.clone(),
language_registry.clone(),
)
.await;
.await
.log_err()
.is_some()
{
did_resolve = true;
}
}
} else {
for completion_index in completion_indices {
let (server_id, completion) = {
let completions = completions.borrow_mut();
let completion = &completions[completion_index];
let server_id = completion.server_id;
let completion = completion.lsp_completion.clone();
(server_id, completion)
};
let server_id = completions.borrow()[completion_index].server_id;
let server_and_adapter = this
.read_with(&cx, |lsp_store, _| {
@@ -4198,17 +4198,27 @@ impl LspStore {
continue;
};
did_resolve = true;
Self::resolve_completion_local(
let resolved = Self::resolve_completion_local(
server,
adapter,
&buffer_snapshot,
completions.clone(),
completion_index,
completion,
language_registry.clone(),
)
.await;
.await
.log_err()
.is_some();
if resolved {
Self::regenerate_completion_labels(
adapter,
&buffer_snapshot,
completions.clone(),
completion_index,
language_registry.clone(),
)
.await
.log_err();
did_resolve = true;
}
}
}
@@ -4218,13 +4228,10 @@ impl LspStore {
async fn resolve_completion_local(
server: Arc<lsp::LanguageServer>,
adapter: Arc<CachedLspAdapter>,
snapshot: &BufferSnapshot,
completions: Rc<RefCell<Box<[Completion]>>>,
completion_index: usize,
completion: lsp::CompletionItem,
language_registry: Arc<LanguageRegistry>,
) {
) -> Result<()> {
let can_resolve = server
.capabilities()
.completion_provider
@@ -4232,30 +4239,17 @@ impl LspStore {
.and_then(|options| options.resolve_provider)
.unwrap_or(false);
if !can_resolve {
return;
return Ok(());
}
let request = server.request::<lsp::request::ResolveCompletionItem>(completion);
let Some(completion_item) = request.await.log_err() else {
return;
let request = {
let completion = &completions.borrow()[completion_index];
if completion.resolved {
return Ok(());
}
server.request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion.clone())
};
if let Some(lsp_documentation) = completion_item.documentation.as_ref() {
let documentation = language::prepare_completion_documentation(
lsp_documentation,
&language_registry,
snapshot.language().cloned(),
)
.await;
let mut completions = completions.borrow_mut();
let completion = &mut completions[completion_index];
completion.documentation = Some(documentation);
} else {
let mut completions = completions.borrow_mut();
let completion = &mut completions[completion_index];
completion.documentation = Some(Documentation::Undocumented);
}
let completion_item = request.await?;
if let Some(text_edit) = completion_item.text_edit.as_ref() {
// Technically we don't have to parse the whole `text_edit`, since the only
@@ -4283,28 +4277,61 @@ impl LspStore {
}
}
let mut completions = completions.borrow_mut();
let completion = &mut completions[completion_index];
completion.lsp_completion = completion_item;
completion.resolved = true;
Ok(())
}
async fn regenerate_completion_labels(
adapter: Arc<CachedLspAdapter>,
snapshot: &BufferSnapshot,
completions: Rc<RefCell<Box<[Completion]>>>,
completion_index: usize,
language_registry: Arc<LanguageRegistry>,
) -> Result<()> {
let completion_item = completions.borrow()[completion_index]
.lsp_completion
.clone();
if let Some(lsp_documentation) = completion_item.documentation.as_ref() {
let documentation = language::prepare_completion_documentation(
lsp_documentation,
&language_registry,
snapshot.language().cloned(),
)
.await;
let mut completions = completions.borrow_mut();
let completion = &mut completions[completion_index];
completion.documentation = Some(documentation);
} else {
let mut completions = completions.borrow_mut();
let completion = &mut completions[completion_index];
completion.documentation = Some(Documentation::Undocumented);
}
// NB: Zed does not have `details` inside the completion resolve capabilities, but certain language servers violate the spec and do not return `details` immediately, e.g. https://github.com/yioneko/vtsls/issues/213
// So we have to update the label here anyway...
let new_label = match snapshot.language() {
Some(language) => adapter
.labels_for_completions(&[completion_item.clone()], language)
.await
.log_err()
.unwrap_or_default(),
Some(language) => {
adapter
.labels_for_completions(&[completion_item.clone()], language)
.await?
}
None => Vec::new(),
}
.pop()
.flatten()
.unwrap_or_else(|| {
CodeLabel::plain(
completion_item.label.clone(),
completion_item.label,
completion_item.filter_text.as_deref(),
)
});
let mut completions = completions.borrow_mut();
let completion = &mut completions[completion_index];
completion.lsp_completion = completion_item;
if completion.label.filter_text() == new_label.filter_text() {
completion.label = new_label;
} else {
@@ -4317,6 +4344,8 @@ impl LspStore {
new_label.filter_text()
);
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
@@ -4326,29 +4355,30 @@ impl LspStore {
buffer_id: BufferId,
completions: Rc<RefCell<Box<[Completion]>>>,
completion_index: usize,
completion: lsp::CompletionItem,
client: AnyProtoClient,
language_registry: Arc<LanguageRegistry>,
) {
) -> Result<()> {
let lsp_completion = {
let completion = &completions.borrow()[completion_index];
if completion.resolved {
return Ok(());
}
serde_json::to_string(&completion.lsp_completion)
.unwrap()
.into_bytes()
};
let request = proto::ResolveCompletionDocumentation {
project_id,
language_server_id: server_id.0 as u64,
lsp_completion: serde_json::to_string(&completion).unwrap().into_bytes(),
lsp_completion,
buffer_id: buffer_id.into(),
};
let Some(response) = client
let response = client
.request(request)
.await
.context("completion documentation resolve proto request")
.log_err()
else {
return;
};
let Some(lsp_completion) = serde_json::from_slice(&response.lsp_completion).log_err()
else {
return;
};
.context("completion documentation resolve proto request")?;
let lsp_completion = serde_json::from_slice(&response.lsp_completion)?;
let documentation = if response.documentation.is_empty() {
Documentation::Undocumented
@@ -4366,6 +4396,7 @@ impl LspStore {
let completion = &mut completions[completion_index];
completion.documentation = Some(documentation);
completion.lsp_completion = lsp_completion;
completion.resolved = true;
let old_range = response
.old_start
@@ -4377,12 +4408,15 @@ impl LspStore {
completion.old_range = old_start..old_end;
}
}
Ok(())
}
pub fn apply_additional_edits_for_completion(
&self,
buffer_handle: Model<Buffer>,
completion: Completion,
completions: Rc<RefCell<Box<[Completion]>>>,
completion_index: usize,
push_to_history: bool,
cx: &mut ModelContext<Self>,
) -> Task<Result<Option<Transaction>>> {
@@ -4391,8 +4425,9 @@ impl LspStore {
if let Some((client, project_id)) = self.upstream_client() {
cx.spawn(move |_, mut cx| async move {
let response = client
.request(proto::ApplyCompletionAdditionalEdits {
let request = {
let completion = completions.borrow()[completion_index].clone();
proto::ApplyCompletionAdditionalEdits {
project_id,
buffer_id: buffer_id.into(),
completion: Some(Self::serialize_completion(&CoreCompletion {
@@ -4400,9 +4435,13 @@ impl LspStore {
new_text: completion.new_text,
server_id: completion.server_id,
lsp_completion: completion.lsp_completion,
resolved: completion.resolved,
})),
})
.await?;
}
};
let response = client.request(request).await?;
completions.borrow_mut()[completion_index].resolved = true;
if let Some(transaction) = response.transaction {
let transaction = language::proto::deserialize_transaction(transaction)?;
@@ -4422,34 +4461,31 @@ impl LspStore {
}
})
} else {
let server_id = completion.server_id;
let lang_server = match self.language_server_for_local_buffer(buffer, server_id, cx) {
let server_id = completions.borrow()[completion_index].server_id;
let server = match self.language_server_for_local_buffer(buffer, server_id, cx) {
Some((_, server)) => server.clone(),
_ => return Task::ready(Ok(Default::default())),
_ => return Task::ready(Ok(None)),
};
let snapshot = buffer_handle.read(&cx).snapshot();
cx.spawn(move |this, mut cx| async move {
let can_resolve = lang_server
.capabilities()
.completion_provider
.as_ref()
.and_then(|options| options.resolve_provider)
.unwrap_or(false);
let additional_text_edits = if can_resolve {
lang_server
.request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion)
.await?
.additional_text_edits
} else {
completion.lsp_completion.additional_text_edits
};
Self::resolve_completion_local(
server.clone(),
&snapshot,
completions.clone(),
completion_index,
)
.await
.context("resolving completion")?;
let completion = completions.borrow()[completion_index].clone();
let additional_text_edits = completion.lsp_completion.additional_text_edits;
if let Some(edits) = additional_text_edits {
let edits = this
.update(&mut cx, |this, cx| {
this.as_local_mut().unwrap().edits_from_lsp(
&buffer_handle,
edits,
lang_server.server_id(),
server.server_id(),
None,
cx,
)
@@ -6803,7 +6839,7 @@ impl LspStore {
let apply_additional_edits = this.update(&mut cx, |this, cx| {
this.apply_additional_edits_for_completion(
buffer,
Completion {
Rc::new(RefCell::new(Box::new([Completion {
old_range: completion.old_range,
new_text: completion.new_text,
lsp_completion: completion.lsp_completion,
@@ -6815,7 +6851,9 @@ impl LspStore {
filter_range: Default::default(),
},
confirm: None,
},
resolved: completion.resolved,
}]))),
0,
false,
cx,
)
@@ -7780,6 +7818,7 @@ impl LspStore {
new_text: completion.new_text.clone(),
server_id: completion.server_id.0 as u64,
lsp_completion: serde_json::to_vec(&completion.lsp_completion).unwrap(),
resolved: completion.resolved,
}
}
@@ -7799,6 +7838,7 @@ impl LspStore {
new_text: completion.new_text,
server_id: LanguageServerId(completion.server_id as usize),
lsp_completion,
resolved: completion.resolved,
})
}
@@ -7900,6 +7940,7 @@ async fn populate_labels_for_completions(
documentation,
lsp_completion,
confirm: None,
resolved: false,
})
}
}

View File

@@ -73,10 +73,8 @@ use snippet::Snippet;
use snippet_provider::SnippetProvider;
use std::{
borrow::Cow,
cell::RefCell,
ops::Range,
path::{Component, Path, PathBuf},
rc::Rc,
str,
sync::Arc,
time::Duration,
@@ -353,6 +351,8 @@ pub struct Completion {
pub documentation: Option<Documentation>,
/// The raw completion provided by the language server.
pub lsp_completion: lsp::CompletionItem,
/// Whether this completion has been resolved, to ensure it happens once per completion.
pub resolved: bool,
/// An optional callback to invoke when this completion is confirmed.
/// Returns, whether new completions should be retriggered after the current one.
/// If `true` is returned, the editor will show a new completion menu after this completion is confirmed.
@@ -380,6 +380,7 @@ pub(crate) struct CoreCompletion {
new_text: String,
server_id: LanguageServerId,
lsp_completion: lsp::CompletionItem,
resolved: bool,
}
/// A code action provided by a language server.
@@ -2863,35 +2864,6 @@ impl Project {
})
}
pub fn resolve_completions(
&self,
buffer: Model<Buffer>,
completion_indices: Vec<usize>,
completions: Rc<RefCell<Box<[Completion]>>>,
cx: &mut ModelContext<Self>,
) -> Task<Result<bool>> {
self.lsp_store.update(cx, |lsp_store, cx| {
lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
})
}
pub fn apply_additional_edits_for_completion(
&self,
buffer_handle: Model<Buffer>,
completion: Completion,
push_to_history: bool,
cx: &mut ModelContext<Self>,
) -> Task<Result<Option<Transaction>>> {
self.lsp_store.update(cx, |lsp_store, cx| {
lsp_store.apply_additional_edits_for_completion(
buffer_handle,
completion,
push_to_history,
cx,
)
})
}
pub fn code_actions<T: Clone + ToOffset>(
&mut self,
buffer_handle: &Model<Buffer>,

View File

@@ -1209,7 +1209,7 @@ impl ProjectPanel {
self.remove(false, action.skip_prompt, cx);
}
fn remove(&mut self, trash: bool, skip_prompt: bool, cx: &mut ViewContext<'_, ProjectPanel>) {
fn remove(&mut self, trash: bool, skip_prompt: bool, cx: &mut ViewContext<ProjectPanel>) {
maybe!({
let items_to_delete = self.disjoint_entries(cx);
if items_to_delete.is_empty() {
@@ -3705,7 +3705,7 @@ impl ProjectPanel {
project: Model<Project>,
entry_id: ProjectEntryId,
skip_ignored: bool,
cx: &mut ViewContext<'_, Self>,
cx: &mut ViewContext<Self>,
) {
if let Some(worktree) = project.read(cx).worktree_for_entry(entry_id, cx) {
let worktree = worktree.read(cx);
@@ -3810,7 +3810,7 @@ fn item_width_estimate(depth: usize, item_text_chars: usize, is_symlink: bool) -
}
impl Render for ProjectPanel {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let has_worktree = !self.visible_entries.is_empty();
let project = self.project.read(cx);
let indent_size = ProjectPanelSettings::get_global(cx).indent_size;
@@ -4252,6 +4252,10 @@ impl Panel for ProjectPanel {
.map_or(false, |entry| entry.is_dir())
})
}
fn activation_priority(&self) -> u32 {
0
}
}
impl FocusableView for ProjectPanel {

View File

@@ -927,6 +927,7 @@ message Completion {
string new_text = 3;
uint64 server_id = 4;
bytes lsp_completion = 5;
bool resolved = 6;
}
message GetCodeActions {

View File

@@ -61,7 +61,7 @@ struct CreateRemoteServer {
}
impl CreateRemoteServer {
fn new(cx: &mut WindowContext<'_>) -> Self {
fn new(cx: &mut WindowContext) -> Self {
let address_editor = cx.new_view(Editor::single_line);
address_editor.update(cx, |this, cx| {
this.focus_handle(cx).focus(cx);
@@ -88,7 +88,7 @@ struct EditNicknameState {
}
impl EditNicknameState {
fn new(index: usize, cx: &mut WindowContext<'_>) -> Self {
fn new(index: usize, cx: &mut WindowContext) -> Self {
let this = Self {
index,
editor: cx.new_view(Editor::single_line),
@@ -264,7 +264,7 @@ struct DefaultState {
servers: Vec<ProjectEntry>,
}
impl DefaultState {
fn new(cx: &WindowContext<'_>) -> Self {
fn new(cx: &WindowContext) -> Self {
let handle = ScrollHandle::new();
let scrollbar = ScrollbarState::new(handle.clone());
let add_new_server = NavigableEntry::new(&handle, cx);
@@ -309,7 +309,7 @@ enum Mode {
}
impl Mode {
fn default_mode(cx: &WindowContext<'_>) -> Self {
fn default_mode(cx: &WindowContext) -> Self {
Self::Default(DefaultState::new(cx))
}
}
@@ -1003,7 +1003,7 @@ impl RemoteServerProjects {
fn callback(
workspace: WeakView<Workspace>,
connection_string: SharedString,
cx: &mut WindowContext<'_>,
cx: &mut WindowContext,
) {
cx.write_to_clipboard(ClipboardItem::new_string(
connection_string.to_string(),
@@ -1069,7 +1069,7 @@ impl RemoteServerProjects {
remote_servers: View<RemoteServerProjects>,
index: usize,
connection_string: SharedString,
cx: &mut WindowContext<'_>,
cx: &mut WindowContext,
) {
let prompt_message =
format!("Remove server `{}`?", connection_string);

View File

@@ -357,7 +357,7 @@ impl RenderOnce for SshConnectionHeader {
}
impl Render for SshConnectionModal {
fn render(&mut self, cx: &mut ui::ViewContext<Self>) -> impl ui::IntoElement {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl ui::IntoElement {
let nickname = self.prompt.read(cx).nickname.clone();
let connection_string = self.prompt.read(cx).connection_string.clone();

View File

@@ -10,4 +10,3 @@ workspace = true
[dependencies]
gpui.workspace = true
once_cell.workspace = true

View File

@@ -2,24 +2,23 @@
#![deny(missing_docs)]
use std::{env, str::FromStr};
use std::{env, str::FromStr, sync::LazyLock};
use gpui::{AppContext, Global, SemanticVersion};
use once_cell::sync::Lazy;
/// stable | dev | nightly | preview
pub static RELEASE_CHANNEL_NAME: Lazy<String> = if cfg!(debug_assertions) {
Lazy::new(|| {
pub static RELEASE_CHANNEL_NAME: LazyLock<String> = LazyLock::new(|| {
if cfg!(debug_assertions) {
env::var("ZED_RELEASE_CHANNEL")
.unwrap_or_else(|_| include_str!("../../zed/RELEASE_CHANNEL").trim().to_string())
})
} else {
Lazy::new(|| include_str!("../../zed/RELEASE_CHANNEL").trim().to_string())
};
} else {
include_str!("../../zed/RELEASE_CHANNEL").trim().to_string()
}
});
#[doc(hidden)]
pub static RELEASE_CHANNEL: Lazy<ReleaseChannel> =
Lazy::new(|| match ReleaseChannel::from_str(&RELEASE_CHANNEL_NAME) {
pub static RELEASE_CHANNEL: LazyLock<ReleaseChannel> =
LazyLock::new(|| match ReleaseChannel::from_str(&RELEASE_CHANNEL_NAME) {
Ok(channel) => channel,
_ => panic!("invalid release channel {}", *RELEASE_CHANNEL_NAME),
});

Some files were not shown because too many files have changed in this diff Show More