From 8b0a0dfb11c9d56fac1f6356809a39f09be47607 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 26 Dec 2024 14:23:10 -0500 Subject: [PATCH 1/6] 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 --- crates/gpui/examples/shadow.rs | 565 ++++++++++++++++++++++++++++++++- 1 file changed, 558 insertions(+), 7 deletions(-) diff --git a/crates/gpui/examples/shadow.rs b/crates/gpui/examples/shadow.rs index c4f379325c..5519b4b839 100644 --- a/crates/gpui/examples/shadow.rs +++ b/crates/gpui/examples/shadow.rs @@ -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, 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) -> 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); }); } From 52614482bf59e5541251fbf22bdfa22016ef9bcd Mon Sep 17 00:00:00 2001 From: sofaspawn Date: Fri, 27 Dec 2024 01:10:27 +0530 Subject: [PATCH 2/6] 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 --- docs/src/configuring-zed.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/configuring-zed.md b/docs/src/configuring-zed.md index 08ebe4dc3c..fb729e96b0 100644 --- a/docs/src/configuring-zed.md +++ b/docs/src/configuring-zed.md @@ -1637,6 +1637,7 @@ Or to set a `socks5` proxy: 2. `prefer_line` (deprecated, same as `none`) 3. `editor_width` to wrap lines that overflow the editor width 4. `preferred_line_length` to wrap lines that overflow `preferred_line_length` config value +5. `bounded` to wrap lines at the minimum of `editor_width` and `preferred_line_length` ## Wrap Guides (Vertical Rulers) From c742cda8e8361116a849f2695fda38d5e77d27a6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2024 01:55:11 +0200 Subject: [PATCH 3/6] Update Rust crate libc to v0.2.169 (#22427) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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
rust-lang/libc (libc) ### [`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 [#​4193](https://redirect.github.com/rust-lang/libc/pull/4193) - Fuchsia: add a `sockaddr_vm` definition [#​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 [#​4195](https://redirect.github.com/rust-lang/libc/pull/4195) - Fix `c_char` on various targets [#​4199](https://redirect.github.com/rust-lang/libc/pull/4199) - Mirror `c_char` configuration from `rust-lang/rust` [#​4198](https://redirect.github.com/rust-lang/libc/pull/4198) ##### Cleanup - Do not re-export `c_void` in target-specific code [#​4200](https://redirect.github.com/rust-lang/libc/pull/4200)
--- ### 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. --- - [ ] If you want to rebase/retry this PR, check this box --- Release Notes: - N/A Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b02376fef2..0aa07c96d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6944,9 +6944,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" From af2ac1a4a0190860577a50cb0585f54b5ee11da5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2024 01:55:28 +0200 Subject: [PATCH 4/6] Update Rust crate env_logger to v0.11.6 (#22425) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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
rust-cli/env_logger (env_logger) ### [`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
--- ### 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. --- - [ ] If you want to rebase/retry this PR, check this box --- Release Notes: - N/A Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 90 +++++++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0aa07c96d8..ee4507d949 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", @@ -2141,7 +2141,7 @@ dependencies = [ "cap-primitives", "cap-std", "io-lifetimes 2.0.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2169,7 +2169,7 @@ dependencies = [ "ipnet", "maybe-owned", "rustix 0.38.42", - "windows-sys 0.59.0", + "windows-sys 0.52.0", "winx", ] @@ -2652,7 +2652,7 @@ dependencies = [ "dashmap 6.1.0", "derive_more", "editor", - "env_logger 0.11.5", + "env_logger 0.11.6", "envy", "extension", "file_finder", @@ -2808,7 +2808,7 @@ dependencies = [ "command_palette_hooks", "ctor", "editor", - "env_logger 0.11.5", + "env_logger 0.11.6", "fuzzy", "go_to_line", "gpui", @@ -3679,7 +3679,7 @@ dependencies = [ "collections", "ctor", "editor", - "env_logger 0.11.5", + "env_logger 0.11.6", "feature_flags", "gpui", "language", @@ -3884,7 +3884,7 @@ dependencies = [ "ctor", "db", "emojis", - "env_logger 0.11.5", + "env_logger 0.11.6", "feature_flags", "file_icons", "fs", @@ -4081,9 +4081,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", @@ -4135,7 +4135,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]] @@ -4187,7 +4187,7 @@ dependencies = [ "client", "clock", "collections", - "env_logger 0.11.5", + "env_logger 0.11.6", "feature_flags", "fs", "git", @@ -4303,7 +4303,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "env_logger 0.11.5", + "env_logger 0.11.6", "extension", "fs", "language", @@ -4331,7 +4331,7 @@ dependencies = [ "collections", "context_server_settings", "ctor", - "env_logger 0.11.5", + "env_logger 0.11.6", "extension", "fs", "futures 0.3.31", @@ -4524,7 +4524,7 @@ dependencies = [ "collections", "ctor", "editor", - "env_logger 0.11.5", + "env_logger 0.11.6", "file_icons", "futures 0.3.31", "fuzzy", @@ -4802,7 +4802,7 @@ checksum = "5e2e6123af26f0f2c51cc66869137080199406754903cc926a7690401ce09cb4" dependencies = [ "io-lifetimes 2.0.4", "rustix 0.38.42", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -5325,7 +5325,7 @@ dependencies = [ "ctor", "derive_more", "embed-resource", - "env_logger 0.11.5", + "env_logger 0.11.6", "etagere", "filedescriptor", "flume", @@ -6376,7 +6376,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]] @@ -6679,7 +6679,7 @@ dependencies = [ "collections", "ctor", "ec4rs", - "env_logger 0.11.5", + "env_logger 0.11.6", "fs", "futures 0.3.31", "fuzzy", @@ -6846,7 +6846,7 @@ dependencies = [ "collections", "copilot", "editor", - "env_logger 0.11.5", + "env_logger 0.11.6", "futures 0.3.31", "gpui", "itertools 0.13.0", @@ -6987,7 +6987,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]] @@ -7319,7 +7319,7 @@ dependencies = [ "async-pipe", "collections", "ctor", - "env_logger 0.11.5", + "env_logger 0.11.6", "futures 0.3.31", "gpui", "log", @@ -7382,7 +7382,7 @@ version = "0.1.0" dependencies = [ "anyhow", "assets", - "env_logger 0.11.5", + "env_logger 0.11.6", "futures 0.3.31", "gpui", "language", @@ -7497,7 +7497,7 @@ dependencies = [ "clap", "clap_complete", "elasticlunr-rs", - "env_logger 0.11.5", + "env_logger 0.11.6", "futures-util", "handlebars 6.2.0", "ignore", @@ -7686,7 +7686,7 @@ dependencies = [ "clock", "collections", "ctor", - "env_logger 0.11.5", + "env_logger 0.11.6", "futures 0.3.31", "gpui", "itertools 0.13.0", @@ -9192,7 +9192,7 @@ dependencies = [ "anyhow", "ctor", "editor", - "env_logger 0.11.5", + "env_logger 0.11.6", "gpui", "menu", "serde", @@ -9549,7 +9549,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", @@ -9931,7 +9931,7 @@ dependencies = [ "once_cell", "socket2 0.5.8", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -10305,7 +10305,7 @@ dependencies = [ "clap", "client", "clock", - "env_logger 0.11.5", + "env_logger 0.11.6", "extension", "extension_host", "fork", @@ -10364,7 +10364,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", @@ -10639,7 +10639,7 @@ dependencies = [ "arrayvec", "criterion", "ctor", - "env_logger 0.11.5", + "env_logger 0.11.6", "gpui", "log", "rand 0.8.5", @@ -10665,7 +10665,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", @@ -10831,7 +10831,7 @@ dependencies = [ "libc", "linux-raw-sys 0.4.14", "once_cell", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -11249,7 +11249,7 @@ dependencies = [ "client", "clock", "collections", - "env_logger 0.11.5", + "env_logger 0.11.6", "feature_flags", "fs", "futures 0.3.31", @@ -12251,7 +12251,7 @@ version = "0.1.0" dependencies = [ "arrayvec", "ctor", - "env_logger 0.11.5", + "env_logger 0.11.6", "log", "rand 0.8.5", "rayon", @@ -12265,7 +12265,7 @@ dependencies = [ "client", "collections", "editor", - "env_logger 0.11.5", + "env_logger 0.11.6", "futures 0.3.31", "gpui", "http_client", @@ -12551,7 +12551,7 @@ dependencies = [ "fd-lock", "io-lifetimes 2.0.4", "rustix 0.38.42", - "windows-sys 0.59.0", + "windows-sys 0.52.0", "winx", ] @@ -12563,7 +12563,7 @@ dependencies = [ "collections", "ctor", "editor", - "env_logger 0.11.5", + "env_logger 0.11.6", "gpui", "language", "menu", @@ -12683,7 +12683,7 @@ dependencies = [ "fastrand 2.3.0", "once_cell", "rustix 0.38.42", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -12784,7 +12784,7 @@ dependencies = [ "clock", "collections", "ctor", - "env_logger 0.11.5", + "env_logger 0.11.6", "gpui", "http_client", "log", @@ -14986,7 +14986,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]] @@ -15435,7 +15435,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]] @@ -15583,7 +15583,7 @@ dependencies = [ "collections", "db", "derive_more", - "env_logger 0.11.5", + "env_logger 0.11.6", "fs", "futures 0.3.31", "gpui", @@ -15619,7 +15619,7 @@ dependencies = [ "anyhow", "clock", "collections", - "env_logger 0.11.5", + "env_logger 0.11.6", "fs", "futures 0.3.31", "fuzzy", @@ -16016,7 +16016,7 @@ dependencies = [ "db", "diagnostics", "editor", - "env_logger 0.11.5", + "env_logger 0.11.6", "extension", "extension_host", "extensions_ui", @@ -16442,7 +16442,7 @@ dependencies = [ "collections", "ctor", "editor", - "env_logger 0.11.5", + "env_logger 0.11.6", "futures 0.3.31", "gpui", "http_client", From d71180abc2251254b37f2d45601210810d535836 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2024 00:11:37 +0000 Subject: [PATCH 5/6] Update Rust crate async-broadcast to v0.7.2 (#22424) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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
smol-rs/async-broadcast (async-broadcast) ### [`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`. [#​41](https://redirect.github.com/smol-rs/async-broadcast/issues/41) - Use `Mutex` instead of `RwLock` for securing the inner data. [#​42](https://redirect.github.com/smol-rs/async-broadcast/issues/42) - Many non-user-facing internal improvements and fixes.
--- ### 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. --- - [ ] If you want to rebase/retry this PR, check this box --- Release Notes: - N/A Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee4507d949..7fec983b26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", From ed61abb8b88b4b6bb96273b129e7d2b7174fe3a1 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 27 Dec 2024 18:43:01 +0200 Subject: [PATCH 6/6] Resolve completion items once exactly (#22448) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- crates/assistant/src/slash_command.rs | 12 +- .../src/chat_panel/message_editor.rs | 11 +- crates/editor/src/code_context_menus.rs | 1 + crates/editor/src/editor.rs | 44 +++- crates/editor/src/editor_tests.rs | 246 +++++++++++++++--- crates/project/src/lsp_command.rs | 1 + crates/project/src/lsp_store.rs | 222 +++++++++------- crates/project/src/project.rs | 34 +-- crates/proto/proto/zed.proto | 1 + 9 files changed, 371 insertions(+), 201 deletions(-) diff --git a/crates/assistant/src/slash_command.rs b/crates/assistant/src/slash_command.rs index 73f5cfc2e6..f9fb4fa803 100644 --- a/crates/assistant/src/slash_command.rs +++ b/crates/assistant/src/slash_command.rs @@ -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, - _: project::Completion, - _: bool, - _: &mut ViewContext, - ) -> Task>> { - Task::ready(Ok(None)) - } - fn is_completion_trigger( &self, buffer: &Model, diff --git a/crates/collab_ui/src/chat_panel/message_editor.rs b/crates/collab_ui/src/chat_panel/message_editor.rs index 1dd1b4e0bc..7a05e6bce4 100644 --- a/crates/collab_ui/src/chat_panel/message_editor.rs +++ b/crates/collab_ui/src/chat_panel/message_editor.rs @@ -79,16 +79,6 @@ impl CompletionProvider for MessageEditorCompletionProvider { Task::ready(Ok(false)) } - fn apply_additional_edits_for_completion( - &self, - _buffer: Model, - _completion: Completion, - _push_to_history: bool, - _cx: &mut ViewContext, - ) -> Task>> { - Task::ready(Ok(None)) - } - fn is_completion_trigger( &self, _buffer: &Model, @@ -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() diff --git a/crates/editor/src/code_context_menus.rs b/crates/editor/src/code_context_menus.rs index fbd9a4d94f..9f591368f0 100644 --- a/crates/editor/src/code_context_menus.rs +++ b/crates/editor/src/code_context_menus.rs @@ -224,6 +224,7 @@ impl CompletionsMenu { documentation: None, lsp_completion: Default::default(), confirm: None, + resolved: true, }) .collect(); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 02f01dbaec..bddcc631f1 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3830,8 +3830,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 +3978,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 +5092,7 @@ impl Editor { })) } - #[cfg(feature = "test-support")] + #[cfg(any(feature = "test-support", test))] pub fn context_menu_visible(&self) -> bool { self.context_menu .borrow() @@ -13447,11 +13452,14 @@ pub trait CompletionProvider { fn apply_additional_edits_for_completion( &self, - buffer: Model, - completion: Completion, - push_to_history: bool, - cx: &mut ViewContext, - ) -> Task>>; + _buffer: Model, + _completions: Rc>>, + _completion_index: usize, + _push_to_history: bool, + _cx: &mut ViewContext, + ) -> Task>> { + Task::ready(Ok(None)) + } fn is_completion_trigger( &self, @@ -13610,6 +13618,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 +13684,30 @@ impl CompletionProvider for Model { cx: &mut ViewContext, ) -> Task> { 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, - completion: Completion, + completions: Rc>>, + completion_index: usize, push_to_history: bool, cx: &mut ViewContext, ) -> Task>> { 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, + ) + }) }) } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index cc7d612bbf..7377b6809b 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -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::(move |_, _, _| { + let item1 = item1.clone(); + cx.handle_request::({ 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::(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::({ + 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::(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::({ + 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::(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!["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::>() + items_out[items_out.len() - 16..items_out.len() - 4] + .iter() + .cloned() + .collect::>() ); resolved_items.lock().clear(); } diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index b68e22051f..5b8f7759c0 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1918,6 +1918,7 @@ impl LspCommand for GetCompletions { new_text, server_id, lsp_completion, + resolved: false, } }) .collect()) diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 4ec6f905b7..940ddce2d0 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -4152,38 +4152,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 +4187,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 +4217,10 @@ impl LspStore { async fn resolve_completion_local( server: Arc, - adapter: Arc, snapshot: &BufferSnapshot, completions: Rc>>, completion_index: usize, - completion: lsp::CompletionItem, - language_registry: Arc, - ) { + ) -> Result<()> { let can_resolve = server .capabilities() .completion_provider @@ -4232,30 +4228,17 @@ impl LspStore { .and_then(|options| options.resolve_provider) .unwrap_or(false); if !can_resolve { - return; + return Ok(()); } - let request = server.request::(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::(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 +4266,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, + snapshot: &BufferSnapshot, + completions: Rc>>, + completion_index: usize, + language_registry: Arc, + ) -> 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 +4333,8 @@ impl LspStore { new_label.filter_text() ); } + + Ok(()) } #[allow(clippy::too_many_arguments)] @@ -4326,29 +4344,30 @@ impl LspStore { buffer_id: BufferId, completions: Rc>>, completion_index: usize, - completion: lsp::CompletionItem, client: AnyProtoClient, language_registry: Arc, - ) { + ) -> 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 +4385,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 +4397,15 @@ impl LspStore { completion.old_range = old_start..old_end; } } + + Ok(()) } pub fn apply_additional_edits_for_completion( &self, buffer_handle: Model, - completion: Completion, + completions: Rc>>, + completion_index: usize, push_to_history: bool, cx: &mut ModelContext, ) -> Task>> { @@ -4391,8 +4414,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 +4424,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 +4450,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::(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 +6828,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 +6840,9 @@ impl LspStore { filter_range: Default::default(), }, confirm: None, - }, + resolved: completion.resolved, + }]))), + 0, false, cx, ) @@ -7780,6 +7807,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 +7827,7 @@ impl LspStore { new_text: completion.new_text, server_id: LanguageServerId(completion.server_id as usize), lsp_completion, + resolved: completion.resolved, }) } @@ -7900,6 +7929,7 @@ async fn populate_labels_for_completions( documentation, lsp_completion, confirm: None, + resolved: false, }) } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 855be5cec2..177e05bd62 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -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, /// 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, - completion_indices: Vec, - completions: Rc>>, - cx: &mut ModelContext, - ) -> Task> { - 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, - completion: Completion, - push_to_history: bool, - cx: &mut ModelContext, - ) -> Task>> { - 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( &mut self, buffer_handle: &Model, diff --git a/crates/proto/proto/zed.proto b/crates/proto/proto/zed.proto index 14dc999031..29e90cc71e 100644 --- a/crates/proto/proto/zed.proto +++ b/crates/proto/proto/zed.proto @@ -927,6 +927,7 @@ message Completion { string new_text = 3; uint64 server_id = 4; bytes lsp_completion = 5; + bool resolved = 6; } message GetCodeActions {