{
div()
.id(id)
@@ -52,12 +60,13 @@ impl Render for Example {
.border_color(gpui::black())
.bg(gpui::black())
.text_color(gpui::white())
- .focus(|this| this.border_color(gpui::blue()))
+ .focus(tab_stop_style)
.shadow_sm()
}
div()
.id("app")
+ .track_focus(&self.focus_handle)
.on_action(cx.listener(Self::on_tab))
.on_action(cx.listener(Self::on_tab_prev))
.size_full()
@@ -86,7 +95,7 @@ impl Render for Example {
.border_color(gpui::black())
.when(
item_handle.tab_stop && item_handle.is_focused(window),
- |this| this.border_color(gpui::blue()),
+ tab_stop_style,
)
.map(|this| match item_handle.tab_stop {
true => this
diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs
index 759d33563e..ded7bae316 100644
--- a/crates/gpui/src/app.rs
+++ b/crates/gpui/src/app.rs
@@ -2023,6 +2023,10 @@ impl HttpClient for NullHttpClient {
.boxed()
}
+ fn user_agent(&self) -> Option<&http_client::http::HeaderValue> {
+ None
+ }
+
fn proxy(&self) -> Option<&Url> {
None
}
diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs
index 1e72d23868..febf294e48 100644
--- a/crates/gpui/src/platform.rs
+++ b/crates/gpui/src/platform.rs
@@ -1216,6 +1216,10 @@ pub enum WindowKind {
/// A window that appears above all other windows, usually used for alerts or popups
/// use sparingly!
PopUp,
+ /// An overlay such as a notification window, a launcher, ...
+ ///
+ /// Only supported on wayland
+ Overlay,
}
/// The appearance of the window, as defined by the operating system.
diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs
index 72e4477ecf..33b22e7ce5 100644
--- a/crates/gpui/src/platform/linux/wayland/client.rs
+++ b/crates/gpui/src/platform/linux/wayland/client.rs
@@ -61,6 +61,7 @@ use wayland_protocols::xdg::decoration::zv1::client::{
};
use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
use wayland_protocols_plasma::blur::client::{org_kde_kwin_blur, org_kde_kwin_blur_manager};
+use wayland_protocols_wlr::layer_shell::v1::client::{zwlr_layer_shell_v1, zwlr_layer_surface_v1};
use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
use xkbcommon::xkb::{self, KEYMAP_COMPILE_NO_FLAGS, Keycode};
@@ -114,6 +115,7 @@ pub struct Globals {
pub fractional_scale_manager:
Option
,
pub decoration_manager: Option,
+ pub layer_shell: Option,
pub blur_manager: Option,
pub text_input_manager: Option,
pub executor: ForegroundExecutor,
@@ -151,6 +153,7 @@ impl Globals {
viewporter: globals.bind(&qh, 1..=1, ()).ok(),
fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
+ layer_shell: globals.bind(&qh, 1..=1, ()).ok(),
blur_manager: globals.bind(&qh, 1..=1, ()).ok(),
text_input_manager: globals.bind(&qh, 1..=1, ()).ok(),
executor,
@@ -929,6 +932,7 @@ delegate_noop!(WaylandClientStatePtr: ignore wl_buffer::WlBuffer);
delegate_noop!(WaylandClientStatePtr: ignore wl_region::WlRegion);
delegate_noop!(WaylandClientStatePtr: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1);
delegate_noop!(WaylandClientStatePtr: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
+delegate_noop!(WaylandClientStatePtr: ignore zwlr_layer_shell_v1::ZwlrLayerShellV1);
delegate_noop!(WaylandClientStatePtr: ignore org_kde_kwin_blur_manager::OrgKdeKwinBlurManager);
delegate_noop!(WaylandClientStatePtr: ignore zwp_text_input_manager_v3::ZwpTextInputManagerV3);
delegate_noop!(WaylandClientStatePtr: ignore org_kde_kwin_blur::OrgKdeKwinBlur);
@@ -1074,6 +1078,31 @@ impl Dispatch for WaylandClientStatePtr {
}
}
+impl Dispatch for WaylandClientStatePtr {
+ fn event(
+ this: &mut Self,
+ _: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
+ event: ::Event,
+ surface_id: &ObjectId,
+ _: &Connection,
+ _: &QueueHandle,
+ ) {
+ let client = this.get_client();
+ let mut state = client.borrow_mut();
+ let Some(window) = get_window(&mut state, surface_id) else {
+ return;
+ };
+ drop(state);
+
+ let should_close = window.handle_layersurface_event(event);
+
+ if should_close {
+ // The close logic will be handled in drop_window()
+ window.close();
+ }
+ }
+}
+
impl Dispatch for WaylandClientStatePtr {
fn event(
_: &mut Self,
diff --git a/crates/gpui/src/platform/linux/wayland/window.rs b/crates/gpui/src/platform/linux/wayland/window.rs
index 2b2207e22c..33c908d1b2 100644
--- a/crates/gpui/src/platform/linux/wayland/window.rs
+++ b/crates/gpui/src/platform/linux/wayland/window.rs
@@ -1,3 +1,6 @@
+use blade_graphics as gpu;
+use collections::HashMap;
+use futures::channel::oneshot::Receiver;
use std::{
cell::{Ref, RefCell, RefMut},
ffi::c_void,
@@ -6,9 +9,14 @@ use std::{
sync::Arc,
};
-use blade_graphics as gpu;
-use collections::HashMap;
-use futures::channel::oneshot::Receiver;
+use crate::{
+ Capslock,
+ platform::{
+ PlatformAtlas, PlatformInputHandler, PlatformWindow,
+ blade::{BladeContext, BladeRenderer, BladeSurfaceConfig},
+ linux::wayland::{display::WaylandDisplay, serial::SerialKind},
+ },
+};
use raw_window_handle as rwh;
use wayland_backend::client::ObjectId;
@@ -20,6 +28,8 @@ use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1
use wayland_protocols::xdg::shell::client::xdg_surface;
use wayland_protocols::xdg::shell::client::xdg_toplevel::{self};
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur;
+use wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_shell_v1::Layer;
+use wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1;
use crate::scene::Scene;
use crate::{
@@ -27,15 +37,7 @@ use crate::{
PlatformDisplay, PlatformInput, Point, PromptButton, PromptLevel, RequestFrameOptions,
ResizeEdge, ScaledPixels, Size, Tiling, WaylandClientStatePtr, WindowAppearance,
WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls, WindowDecorations,
- WindowParams, px, size,
-};
-use crate::{
- Capslock,
- platform::{
- PlatformAtlas, PlatformInputHandler, PlatformWindow,
- blade::{BladeContext, BladeRenderer, BladeSurfaceConfig},
- linux::wayland::{display::WaylandDisplay, serial::SerialKind},
- },
+ WindowKind, WindowParams, px, size,
};
#[derive(Default)]
@@ -81,14 +83,12 @@ struct InProgressConfigure {
}
pub struct WaylandWindowState {
- xdg_surface: xdg_surface::XdgSurface,
+ surface_state: WaylandSurfaceState,
acknowledged_first_configure: bool,
pub surface: wl_surface::WlSurface,
- decoration: Option,
app_id: Option,
appearance: WindowAppearance,
blur: Option,
- toplevel: xdg_toplevel::XdgToplevel,
viewport: Option,
outputs: HashMap,
display: Option<(ObjectId, Output)>,
@@ -114,6 +114,78 @@ pub struct WaylandWindowState {
client_inset: Option,
}
+pub enum WaylandSurfaceState {
+ Xdg(WaylandXdgSurfaceState),
+ LayerShell(WaylandLayerSurfaceState),
+}
+
+pub struct WaylandXdgSurfaceState {
+ xdg_surface: xdg_surface::XdgSurface,
+ toplevel: xdg_toplevel::XdgToplevel,
+ decoration: Option,
+}
+
+pub struct WaylandLayerSurfaceState {
+ layer_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
+}
+
+impl WaylandSurfaceState {
+ fn ack_configure(&self, serial: u32) {
+ match self {
+ WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, .. }) => {
+ xdg_surface.ack_configure(serial);
+ }
+ WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) => {
+ layer_surface.ack_configure(serial);
+ }
+ }
+ }
+
+ fn decoration(&self) -> Option<&zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1> {
+ if let WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { decoration, .. }) = self {
+ decoration.as_ref()
+ } else {
+ None
+ }
+ }
+
+ fn toplevel(&self) -> Option<&xdg_toplevel::XdgToplevel> {
+ if let WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { toplevel, .. }) = self {
+ Some(toplevel)
+ } else {
+ None
+ }
+ }
+
+ fn set_geometry(&self, x: i32, y: i32, width: i32, height: i32) {
+ match self {
+ WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, .. }) => {
+ xdg_surface.set_window_geometry(x, y, width, height);
+ }
+ WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) => {
+ // cannot set window position of a layer surface
+ layer_surface.set_size(width as u32, height as u32);
+ }
+ }
+ }
+
+ fn destroy(&mut self) {
+ match self {
+ WaylandSurfaceState::Xdg(WaylandXdgSurfaceState {
+ xdg_surface,
+ toplevel,
+ decoration: _decoration,
+ }) => {
+ toplevel.destroy();
+ xdg_surface.destroy();
+ }
+ WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface }) => {
+ layer_surface.destroy();
+ }
+ }
+ }
+}
+
#[derive(Clone)]
pub struct WaylandWindowStatePtr {
state: Rc>,
@@ -124,9 +196,7 @@ impl WaylandWindowState {
pub(crate) fn new(
handle: AnyWindowHandle,
surface: wl_surface::WlSurface,
- xdg_surface: xdg_surface::XdgSurface,
- toplevel: xdg_toplevel::XdgToplevel,
- decoration: Option,
+ surface_state: WaylandSurfaceState,
appearance: WindowAppearance,
viewport: Option,
client: WaylandClientStatePtr,
@@ -156,13 +226,11 @@ impl WaylandWindowState {
};
Ok(Self {
- xdg_surface,
+ surface_state,
acknowledged_first_configure: false,
surface,
- decoration,
app_id: None,
blur: None,
- toplevel,
viewport,
globals,
outputs: HashMap::default(),
@@ -235,17 +303,16 @@ impl Drop for WaylandWindow {
let client = state.client.clone();
state.renderer.destroy();
- if let Some(decoration) = &state.decoration {
+ if let Some(decoration) = &state.surface_state.decoration() {
decoration.destroy();
}
if let Some(blur) = &state.blur {
blur.release();
}
- state.toplevel.destroy();
+ state.surface_state.destroy();
if let Some(viewport) = &state.viewport {
viewport.destroy();
}
- state.xdg_surface.destroy();
state.surface.destroy();
let state_ptr = self.0.clone();
@@ -279,27 +346,65 @@ impl WaylandWindow {
appearance: WindowAppearance,
) -> anyhow::Result<(Self, ObjectId)> {
let surface = globals.compositor.create_surface(&globals.qh, ());
- let xdg_surface = globals
- .wm_base
- .get_xdg_surface(&surface, &globals.qh, surface.id());
- let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id());
- if let Some(size) = params.window_min_size {
- toplevel.set_min_size(size.width.0 as i32, size.height.0 as i32);
- }
+ let surface_state = match (params.kind, globals.layer_shell.as_ref()) {
+ // Matching on layer_shell here means that if kind is Overlay, but the compositor doesn't support layer_shell,
+ // we end up defaulting to xdg_surface anyway
+ (WindowKind::Overlay, Some(layer_shell)) => {
+ let layer_surface = layer_shell.get_layer_surface(
+ &surface,
+ None,
+ Layer::Overlay,
+ "".to_string(),
+ &globals.qh,
+ surface.id(),
+ );
+
+ let width = params.bounds.size.width.0;
+ let height = params.bounds.size.height.0;
+ layer_surface.set_size(width as u32, height as u32);
+ layer_surface.set_keyboard_interactivity(
+ zwlr_layer_surface_v1::KeyboardInteractivity::OnDemand,
+ );
+
+ WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface })
+ }
+ _ => {
+ let xdg_surface =
+ globals
+ .wm_base
+ .get_xdg_surface(&surface, &globals.qh, surface.id());
+
+ let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id());
+
+ if let Some(size) = params.window_min_size {
+ toplevel.set_min_size(size.width.0 as i32, size.height.0 as i32);
+ }
+
+ // Attempt to set up window decorations based on the requested configuration
+ let decoration = globals
+ .decoration_manager
+ .as_ref()
+ .map(|decoration_manager| {
+ decoration_manager.get_toplevel_decoration(
+ &toplevel,
+ &globals.qh,
+ surface.id(),
+ )
+ });
+
+ WaylandSurfaceState::Xdg(WaylandXdgSurfaceState {
+ xdg_surface,
+ toplevel,
+ decoration,
+ })
+ }
+ };
if let Some(fractional_scale_manager) = globals.fractional_scale_manager.as_ref() {
fractional_scale_manager.get_fractional_scale(&surface, &globals.qh, surface.id());
}
- // Attempt to set up window decorations based on the requested configuration
- let decoration = globals
- .decoration_manager
- .as_ref()
- .map(|decoration_manager| {
- decoration_manager.get_toplevel_decoration(&toplevel, &globals.qh, surface.id())
- });
-
let viewport = globals
.viewporter
.as_ref()
@@ -309,9 +414,7 @@ impl WaylandWindow {
state: Rc::new(RefCell::new(WaylandWindowState::new(
handle,
surface.clone(),
- xdg_surface,
- toplevel,
- decoration,
+ surface_state,
appearance,
viewport,
client,
@@ -403,7 +506,7 @@ impl WaylandWindowStatePtr {
}
}
let mut state = self.state.borrow_mut();
- state.xdg_surface.ack_configure(serial);
+ state.surface_state.ack_configure(serial);
let window_geometry = inset_by_tiling(
state.bounds.map_origin(|_| px(0.0)),
@@ -413,7 +516,7 @@ impl WaylandWindowStatePtr {
.map(|v| v.0 as i32)
.map_size(|v| if v <= 0 { 1 } else { v });
- state.xdg_surface.set_window_geometry(
+ state.surface_state.set_geometry(
window_geometry.origin.x,
window_geometry.origin.y,
window_geometry.size.width,
@@ -578,6 +681,42 @@ impl WaylandWindowStatePtr {
}
}
+ pub fn handle_layersurface_event(&self, event: zwlr_layer_surface_v1::Event) -> bool {
+ match event {
+ zwlr_layer_surface_v1::Event::Configure {
+ width,
+ height,
+ serial,
+ } => {
+ let mut size = if width == 0 || height == 0 {
+ None
+ } else {
+ Some(size(px(width as f32), px(height as f32)))
+ };
+
+ let mut state = self.state.borrow_mut();
+ state.in_progress_configure = Some(InProgressConfigure {
+ size,
+ fullscreen: false,
+ maximized: false,
+ resizing: false,
+ tiling: Tiling::default(),
+ });
+ drop(state);
+
+ // just do the same thing we'd do as an xdg_surface
+ self.handle_xdg_surface_event(xdg_surface::Event::Configure { serial });
+
+ false
+ }
+ zwlr_layer_surface_v1::Event::Closed => {
+ // unlike xdg, we don't have a choice here: the surface is closing.
+ true
+ }
+ _ => false,
+ }
+ }
+
#[allow(clippy::mutable_key_type)]
pub fn handle_surface_event(
&self,
@@ -840,7 +979,7 @@ impl PlatformWindow for WaylandWindow {
let state_ptr = self.0.clone();
let dp_size = size.to_device_pixels(self.scale_factor());
- state.xdg_surface.set_window_geometry(
+ state.surface_state.set_geometry(
state.bounds.origin.x.0 as i32,
state.bounds.origin.y.0 as i32,
dp_size.width.0,
@@ -934,12 +1073,16 @@ impl PlatformWindow for WaylandWindow {
}
fn set_title(&mut self, title: &str) {
- self.borrow().toplevel.set_title(title.to_string());
+ if let Some(toplevel) = self.borrow().surface_state.toplevel() {
+ toplevel.set_title(title.to_string());
+ }
}
fn set_app_id(&mut self, app_id: &str) {
let mut state = self.borrow_mut();
- state.toplevel.set_app_id(app_id.to_owned());
+ if let Some(toplevel) = self.borrow().surface_state.toplevel() {
+ toplevel.set_app_id(app_id.to_owned());
+ }
state.app_id = Some(app_id.to_owned());
}
@@ -950,24 +1093,30 @@ impl PlatformWindow for WaylandWindow {
}
fn minimize(&self) {
- self.borrow().toplevel.set_minimized();
+ if let Some(toplevel) = self.borrow().surface_state.toplevel() {
+ toplevel.set_minimized();
+ }
}
fn zoom(&self) {
let state = self.borrow();
- if !state.maximized {
- state.toplevel.set_maximized();
- } else {
- state.toplevel.unset_maximized();
+ if let Some(toplevel) = state.surface_state.toplevel() {
+ if !state.maximized {
+ toplevel.set_maximized();
+ } else {
+ toplevel.unset_maximized();
+ }
}
}
fn toggle_fullscreen(&self) {
- let mut state = self.borrow_mut();
- if !state.fullscreen {
- state.toplevel.set_fullscreen(None);
- } else {
- state.toplevel.unset_fullscreen();
+ let mut state = self.borrow();
+ if let Some(toplevel) = state.surface_state.toplevel() {
+ if !state.fullscreen {
+ toplevel.set_fullscreen(None);
+ } else {
+ toplevel.unset_fullscreen();
+ }
}
}
@@ -1032,27 +1181,33 @@ impl PlatformWindow for WaylandWindow {
fn show_window_menu(&self, position: Point) {
let state = self.borrow();
let serial = state.client.get_serial(SerialKind::MousePress);
- state.toplevel.show_window_menu(
- &state.globals.seat,
- serial,
- position.x.0 as i32,
- position.y.0 as i32,
- );
+ if let Some(toplevel) = state.surface_state.toplevel() {
+ toplevel.show_window_menu(
+ &state.globals.seat,
+ serial,
+ position.x.0 as i32,
+ position.y.0 as i32,
+ );
+ }
}
fn start_window_move(&self) {
let state = self.borrow();
let serial = state.client.get_serial(SerialKind::MousePress);
- state.toplevel._move(&state.globals.seat, serial);
+ if let Some(toplevel) = state.surface_state.toplevel() {
+ toplevel._move(&state.globals.seat, serial);
+ }
}
fn start_window_resize(&self, edge: crate::ResizeEdge) {
let state = self.borrow();
- state.toplevel.resize(
- &state.globals.seat,
- state.client.get_serial(SerialKind::MousePress),
- edge.to_xdg(),
- )
+ if let Some(toplevel) = state.surface_state.toplevel() {
+ toplevel.resize(
+ &state.globals.seat,
+ state.client.get_serial(SerialKind::MousePress),
+ edge.to_xdg(),
+ )
+ }
}
fn window_decorations(&self) -> Decorations {
@@ -1068,7 +1223,7 @@ impl PlatformWindow for WaylandWindow {
fn request_decorations(&self, decorations: WindowDecorations) {
let mut state = self.borrow_mut();
state.decorations = decorations;
- if let Some(decoration) = state.decoration.as_ref() {
+ if let Some(decoration) = state.surface_state.decoration() {
decoration.set_mode(decorations.to_xdg());
update_window(state);
}
diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs
index aedf131909..f01d33147b 100644
--- a/crates/gpui/src/platform/mac/window.rs
+++ b/crates/gpui/src/platform/mac/window.rs
@@ -559,7 +559,7 @@ impl MacWindow {
}
let native_window: id = match kind {
- WindowKind::Normal => msg_send![WINDOW_CLASS, alloc],
+ WindowKind::Normal | WindowKind::Overlay => msg_send![WINDOW_CLASS, alloc],
WindowKind::PopUp => {
style_mask |= NSWindowStyleMaskNonactivatingPanel;
msg_send![PANEL_CLASS, alloc]
@@ -711,7 +711,7 @@ impl MacWindow {
native_window.makeFirstResponder_(native_view);
match kind {
- WindowKind::Normal => {
+ WindowKind::Normal | WindowKind::Overlay => {
native_window.setLevel_(NSNormalWindowLevel);
native_window.setAcceptsMouseMovedEvents_(YES);
}
diff --git a/crates/gpui/src/tab_stop.rs b/crates/gpui/src/tab_stop.rs
index 1aa4cd6d9f..7dde42efed 100644
--- a/crates/gpui/src/tab_stop.rs
+++ b/crates/gpui/src/tab_stop.rs
@@ -32,20 +32,18 @@ impl TabHandles {
self.handles.clear();
}
- fn current_index(&self, focused_id: Option<&FocusId>) -> usize {
- self.handles
- .iter()
- .position(|h| Some(&h.id) == focused_id)
- .unwrap_or_default()
+ fn current_index(&self, focused_id: Option<&FocusId>) -> Option {
+ self.handles.iter().position(|h| Some(&h.id) == focused_id)
}
pub(crate) fn next(&self, focused_id: Option<&FocusId>) -> Option {
- let ix = self.current_index(focused_id);
-
- let mut next_ix = ix + 1;
- if next_ix + 1 > self.handles.len() {
- next_ix = 0;
- }
+ let next_ix = self
+ .current_index(focused_id)
+ .and_then(|ix| {
+ let next_ix = ix + 1;
+ (next_ix < self.handles.len()).then_some(next_ix)
+ })
+ .unwrap_or_default();
if let Some(next_handle) = self.handles.get(next_ix) {
Some(next_handle.clone())
@@ -55,7 +53,7 @@ impl TabHandles {
}
pub(crate) fn prev(&self, focused_id: Option<&FocusId>) -> Option {
- let ix = self.current_index(focused_id);
+ let ix = self.current_index(focused_id).unwrap_or_default();
let prev_ix;
if ix == 0 {
prev_ix = self.handles.len().saturating_sub(1);
@@ -108,8 +106,14 @@ mod tests {
]
);
- // next
- assert_eq!(tab.next(None), Some(tab.handles[1].clone()));
+ // Select first tab index if no handle is currently focused.
+ assert_eq!(tab.next(None), Some(tab.handles[0].clone()));
+ // Select last tab index if no handle is currently focused.
+ assert_eq!(
+ tab.prev(None),
+ Some(tab.handles[tab.handles.len() - 1].clone())
+ );
+
assert_eq!(
tab.next(Some(&tab.handles[0].id)),
Some(tab.handles[1].clone())
diff --git a/crates/http_client/src/http_client.rs b/crates/http_client/src/http_client.rs
index eebab86e21..434bd74fc8 100644
--- a/crates/http_client/src/http_client.rs
+++ b/crates/http_client/src/http_client.rs
@@ -4,6 +4,7 @@ pub mod github;
pub use anyhow::{Result, anyhow};
pub use async_body::{AsyncBody, Inner};
use derive_more::Deref;
+use http::HeaderValue;
pub use http::{self, Method, Request, Response, StatusCode, Uri};
use futures::future::BoxFuture;
@@ -39,6 +40,8 @@ impl HttpRequestExt for http::request::Builder {
pub trait HttpClient: 'static + Send + Sync {
fn type_name(&self) -> &'static str;
+ fn user_agent(&self) -> Option<&HeaderValue>;
+
fn send(
&self,
req: http::Request,
@@ -118,6 +121,10 @@ impl HttpClient for HttpClientWithProxy {
self.client.send(req)
}
+ fn user_agent(&self) -> Option<&HeaderValue> {
+ self.client.user_agent()
+ }
+
fn proxy(&self) -> Option<&Url> {
self.proxy.as_ref()
}
@@ -135,6 +142,10 @@ impl HttpClient for Arc {
self.client.send(req)
}
+ fn user_agent(&self) -> Option<&HeaderValue> {
+ self.client.user_agent()
+ }
+
fn proxy(&self) -> Option<&Url> {
self.proxy.as_ref()
}
@@ -250,6 +261,10 @@ impl HttpClient for Arc {
self.client.send(req)
}
+ fn user_agent(&self) -> Option<&HeaderValue> {
+ self.client.user_agent()
+ }
+
fn proxy(&self) -> Option<&Url> {
self.client.proxy.as_ref()
}
@@ -267,6 +282,10 @@ impl HttpClient for HttpClientWithUrl {
self.client.send(req)
}
+ fn user_agent(&self) -> Option<&HeaderValue> {
+ self.client.user_agent()
+ }
+
fn proxy(&self) -> Option<&Url> {
self.client.proxy.as_ref()
}
@@ -314,6 +333,10 @@ impl HttpClient for BlockedHttpClient {
})
}
+ fn user_agent(&self) -> Option<&HeaderValue> {
+ None
+ }
+
fn proxy(&self) -> Option<&Url> {
None
}
@@ -334,6 +357,7 @@ type FakeHttpHandler = Box<
#[cfg(feature = "test-support")]
pub struct FakeHttpClient {
handler: FakeHttpHandler,
+ user_agent: HeaderValue,
}
#[cfg(feature = "test-support")]
@@ -348,6 +372,7 @@ impl FakeHttpClient {
client: HttpClientWithProxy {
client: Arc::new(Self {
handler: Box::new(move |req| Box::pin(handler(req))),
+ user_agent: HeaderValue::from_static(type_name::()),
}),
proxy: None,
},
@@ -390,6 +415,10 @@ impl HttpClient for FakeHttpClient {
future
}
+ fn user_agent(&self) -> Option<&HeaderValue> {
+ Some(&self.user_agent)
+ }
+
fn proxy(&self) -> Option<&Url> {
None
}
diff --git a/crates/inline_completion_button/Cargo.toml b/crates/inline_completion_button/Cargo.toml
index c2a619d500..b34e59336b 100644
--- a/crates/inline_completion_button/Cargo.toml
+++ b/crates/inline_completion_button/Cargo.toml
@@ -15,6 +15,7 @@ doctest = false
[dependencies]
anyhow.workspace = true
client.workspace = true
+cloud_llm_client.workspace = true
copilot.workspace = true
editor.workspace = true
feature_flags.workspace = true
@@ -32,7 +33,6 @@ ui.workspace = true
workspace-hack.workspace = true
workspace.workspace = true
zed_actions.workspace = true
-zed_llm_client.workspace = true
zeta.workspace = true
[dev-dependencies]
diff --git a/crates/inline_completion_button/src/inline_completion_button.rs b/crates/inline_completion_button/src/inline_completion_button.rs
index 2615a8beef..81d9181cfc 100644
--- a/crates/inline_completion_button/src/inline_completion_button.rs
+++ b/crates/inline_completion_button/src/inline_completion_button.rs
@@ -1,5 +1,6 @@
use anyhow::Result;
use client::{DisableAiSettings, UserStore, zed_urls};
+use cloud_llm_client::UsageLimit;
use copilot::{Copilot, Status};
use editor::{
Editor, SelectionEffects,
@@ -34,7 +35,6 @@ use workspace::{
notifications::NotificationId,
};
use zed_actions::OpenBrowser;
-use zed_llm_client::UsageLimit;
use zeta::RateCompletions;
actions!(
diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs
index 7cda2b4b5a..549afc931c 100644
--- a/crates/language/src/language.rs
+++ b/crates/language/src/language.rs
@@ -166,7 +166,6 @@ pub struct CachedLspAdapter {
pub reinstall_attempt_count: AtomicU64,
cached_binary: futures::lock::Mutex