Merge branch 'main' into v0.119.x

Joseph T. Lyons created

Change summary

Cargo.lock                                    |   2 
crates/assistant/src/assistant_panel.rs       |  14 +
crates/collab_ui/src/collab_panel.rs          |  60 +++++++--
crates/diagnostics/src/diagnostics.rs         |   9 +
crates/feedback/Cargo.toml                    |   1 
crates/language_tools/src/lsp_log.rs          |  12 +
crates/language_tools/src/syntax_tree_view.rs |  10 +
crates/project_panel/src/project_panel.rs     |   2 
crates/search/src/buffer_search.rs            | 127 ++++++++++++++------
crates/terminal/src/mappings/mouse.rs         |  54 ++++----
crates/terminal/src/terminal.rs               |  15 -
crates/terminal_view/Cargo.toml               |   2 
crates/terminal_view/src/terminal_element.rs  |  53 ++++----
crates/terminal_view/src/terminal_panel.rs    |  30 +++-
crates/terminal_view/src/terminal_view.rs     |   9 
crates/zed/src/app_menus.rs                   |   8 -
crates/zed/src/zed.rs                         |   3 
17 files changed, 258 insertions(+), 153 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -2486,7 +2486,6 @@ dependencies = [
  "postage",
  "project",
  "regex",
- "search",
  "serde",
  "serde_derive",
  "settings",
@@ -7762,6 +7761,7 @@ dependencies = [
  "procinfo",
  "project",
  "rand 0.8.5",
+ "search",
  "serde",
  "serde_derive",
  "settings",

crates/assistant/src/assistant_panel.rs 🔗

@@ -38,7 +38,7 @@ use gpui::{
 };
 use language::{language_settings::SoftWrap, Buffer, LanguageRegistry, ToOffset as _};
 use project::Project;
-use search::BufferSearchBar;
+use search::{buffer_search::DivRegistrar, BufferSearchBar};
 use semantic_index::{SemanticIndex, SemanticIndexStatus};
 use settings::{Settings, SettingsStore};
 use std::{
@@ -1156,6 +1156,16 @@ impl Render for AssistantPanel {
                     div()
                 });
 
+            let contents = if self.active_editor().is_some() {
+                let mut registrar = DivRegistrar::new(
+                    |panel, cx| panel.toolbar.read(cx).item_of_type::<BufferSearchBar>(),
+                    cx,
+                );
+                BufferSearchBar::register_inner(&mut registrar);
+                registrar.into_div()
+            } else {
+                div()
+            };
             v_stack()
                 .key_context("AssistantPanel")
                 .size_full()
@@ -1176,7 +1186,7 @@ impl Render for AssistantPanel {
                     Some(self.toolbar.clone())
                 })
                 .child(
-                    div()
+                    contents
                         .flex_1()
                         .child(if let Some(editor) = self.active_editor() {
                             editor.clone().into_any_element()

crates/collab_ui/src/collab_panel.rs 🔗

@@ -11,15 +11,16 @@ use channel::{Channel, ChannelEvent, ChannelId, ChannelStore};
 use client::{Client, Contact, User, UserStore};
 use contact_finder::ContactFinder;
 use db::kvp::KEY_VALUE_STORE;
-use editor::Editor;
+use editor::{Editor, EditorElement, EditorStyle};
 use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
     actions, canvas, div, fill, list, overlay, point, prelude::*, px, serde_json, AnyElement,
     AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, Div, EventEmitter,
-    FocusHandle, FocusableView, InteractiveElement, IntoElement, ListOffset, ListState, Model,
-    MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, RenderOnce, SharedString,
-    Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView,
+    FocusHandle, FocusableView, FontStyle, FontWeight, InteractiveElement, IntoElement, ListOffset,
+    ListState, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render,
+    RenderOnce, SharedString, Styled, Subscription, Task, TextStyle, View, ViewContext,
+    VisualContext, WeakView, WhiteSpace,
 };
 use menu::{Cancel, Confirm, SelectNext, SelectPrev};
 use project::{Fs, Project};
@@ -29,10 +30,9 @@ use settings::{Settings, SettingsStore};
 use smallvec::SmallVec;
 use std::{mem, sync::Arc};
 use theme::{ActiveTheme, ThemeSettings};
-use ui::prelude::*;
 use ui::{
-    h_stack, v_stack, Avatar, Button, Color, ContextMenu, Icon, IconButton, IconElement, IconSize,
-    Label, ListHeader, ListItem, Tooltip,
+    prelude::*, Avatar, Button, Color, ContextMenu, Icon, IconButton, IconElement, IconSize, Label,
+    ListHeader, ListItem, Tooltip,
 };
 use util::{maybe, ResultExt, TryFutureExt};
 use workspace::{
@@ -1749,15 +1749,49 @@ impl CollabPanel {
             .size_full()
             .child(list(self.list_state.clone()).full())
             .child(
-                v_stack().p_2().child(
-                    v_stack()
-                        .border_primary(cx)
-                        .border_t()
-                        .child(self.filter_editor.clone()),
-                ),
+                v_stack()
+                    .child(div().mx_2().border_primary(cx).border_t())
+                    .child(
+                        v_stack()
+                            .p_2()
+                            .child(self.render_filter_input(&self.filter_editor, cx)),
+                    ),
             )
     }
 
+    fn render_filter_input(
+        &self,
+        editor: &View<Editor>,
+        cx: &mut ViewContext<Self>,
+    ) -> impl IntoElement {
+        let settings = ThemeSettings::get_global(cx);
+        let text_style = TextStyle {
+            color: if editor.read(cx).read_only() {
+                cx.theme().colors().text_disabled
+            } else {
+                cx.theme().colors().text
+            },
+            font_family: settings.ui_font.family.clone(),
+            font_features: settings.ui_font.features,
+            font_size: rems(0.875).into(),
+            font_weight: FontWeight::NORMAL,
+            font_style: FontStyle::Normal,
+            line_height: relative(1.3).into(),
+            background_color: None,
+            underline: None,
+            white_space: WhiteSpace::Normal,
+        };
+
+        EditorElement::new(
+            editor,
+            EditorStyle {
+                local_player: cx.theme().players().local(),
+                text: text_style,
+                ..Default::default()
+            },
+        )
+    }
+
     fn render_header(
         &self,
         section: Section,

crates/diagnostics/src/diagnostics.rs 🔗

@@ -641,8 +641,13 @@ impl Item for ProjectDiagnosticsEditor {
 
     fn tab_content(&self, _detail: Option<usize>, selected: bool, _: &WindowContext) -> AnyElement {
         if self.summary.error_count == 0 && self.summary.warning_count == 0 {
-            let label = Label::new("No problems");
-            label.into_any_element()
+            Label::new("No problems")
+                .color(if selected {
+                    Color::Default
+                } else {
+                    Color::Muted
+                })
+                .into_any_element()
         } else {
             h_stack()
                 .gap_1()

crates/feedback/Cargo.toml 🔗

@@ -18,7 +18,6 @@ gpui = { path = "../gpui" }
 language = { path = "../language" }
 menu = { path = "../menu" }
 project = { path = "../project" }
-search = { path = "../search" }
 settings = { path = "../settings" }
 theme = { path = "../theme" }
 ui = { path = "../ui" }

crates/language_tools/src/lsp_log.rs 🔗

@@ -10,7 +10,7 @@ use language::{LanguageServerId, LanguageServerName};
 use lsp::IoKind;
 use project::{search::SearchQuery, Project};
 use std::{borrow::Cow, sync::Arc};
-use ui::{h_stack, popover_menu, Button, Checkbox, Clickable, ContextMenu, Label, Selection};
+use ui::{popover_menu, prelude::*, Button, Checkbox, ContextMenu, Label, Selection};
 use workspace::{
     item::{Item, ItemHandle},
     searchable::{SearchEvent, SearchableItem, SearchableItemHandle},
@@ -614,8 +614,14 @@ impl Item for LspLogView {
         Editor::to_item_events(event, f)
     }
 
-    fn tab_content(&self, _: Option<usize>, _: bool, _: &WindowContext<'_>) -> AnyElement {
-        Label::new("LSP Logs").into_any_element()
+    fn tab_content(&self, _: Option<usize>, selected: bool, _: &WindowContext<'_>) -> AnyElement {
+        Label::new("LSP Logs")
+            .color(if selected {
+                Color::Default
+            } else {
+                Color::Muted
+            })
+            .into_any_element()
     }
 
     fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {

crates/language_tools/src/syntax_tree_view.rs 🔗

@@ -405,8 +405,14 @@ impl Item for SyntaxTreeView {
 
     fn to_item_events(_: &Self::Event, _: impl FnMut(workspace::item::ItemEvent)) {}
 
-    fn tab_content(&self, _: Option<usize>, _: bool, _: &WindowContext<'_>) -> AnyElement {
-        Label::new("Syntax Tree").into_any_element()
+    fn tab_content(&self, _: Option<usize>, selected: bool, _: &WindowContext<'_>) -> AnyElement {
+        Label::new("Syntax Tree")
+            .color(if selected {
+                Color::Default
+            } else {
+                Color::Muted
+            })
+            .into_any_element()
     }
 
     fn clone_on_split(

crates/project_panel/src/project_panel.rs 🔗

@@ -1395,7 +1395,7 @@ impl ProjectPanel {
                     .child(if let Some(icon) = &icon {
                         div().child(IconElement::from_path(icon.to_string()).color(Color::Muted))
                     } else {
-                        div()
+                        div().size(IconSize::default().rems()).invisible()
                     })
                     .child(
                         if let (Some(editor), true) = (Some(&self.filename_editor), show_editor) {

crates/search/src/buffer_search.rs 🔗

@@ -422,89 +422,134 @@ impl ToolbarItemView for BufferSearchBar {
     }
 }
 
-impl BufferSearchBar {
-    fn register(workspace: &mut Workspace) {
-        workspace.register_action(move |workspace, deploy: &Deploy, cx| {
-            let pane = workspace.active_pane();
+/// Registrar inverts the dependency between search and it's downstream user, allowing said downstream user to register search action without knowing exactly what those actions are.
+pub trait SearchActionsRegistrar {
+    fn register_handler<A: Action>(
+        &mut self,
+        callback: fn(&mut BufferSearchBar, &A, &mut ViewContext<BufferSearchBar>),
+    );
+}
+
+type GetSearchBar<T> =
+    for<'a, 'b> fn(&'a T, &'a mut ViewContext<'b, T>) -> Option<View<BufferSearchBar>>;
+
+/// Registers search actions on a div that can be taken out.
+pub struct DivRegistrar<'a, 'b, T: 'static> {
+    div: Option<Div>,
+    cx: &'a mut ViewContext<'b, T>,
+    search_getter: GetSearchBar<T>,
+}
+
+impl<'a, 'b, T: 'static> DivRegistrar<'a, 'b, T> {
+    pub fn new(search_getter: GetSearchBar<T>, cx: &'a mut ViewContext<'b, T>) -> Self {
+        Self {
+            div: Some(div()),
+            cx,
+            search_getter,
+        }
+    }
+    pub fn into_div(self) -> Div {
+        // This option is always Some; it's an option in the first place because we want to call methods
+        // on div that require ownership.
+        self.div.unwrap()
+    }
+}
+
+impl<T: 'static> SearchActionsRegistrar for DivRegistrar<'_, '_, T> {
+    fn register_handler<A: gpui::Action>(
+        &mut self,
+        callback: fn(&mut BufferSearchBar, &A, &mut ViewContext<BufferSearchBar>),
+    ) {
+        let getter = self.search_getter;
+        self.div = self.div.take().map(|div| {
+            div.on_action(self.cx.listener(move |this, action, cx| {
+                (getter)(this, cx)
+                    .clone()
+                    .map(|search_bar| search_bar.update(cx, |this, cx| callback(this, action, cx)));
+            }))
+        });
+    }
+}
 
-            pane.update(cx, |this, cx| {
-                this.toolbar().update(cx, |this, cx| {
+/// Register actions for an active pane.
+impl SearchActionsRegistrar for Workspace {
+    fn register_handler<A: Action>(
+        &mut self,
+        callback: fn(&mut BufferSearchBar, &A, &mut ViewContext<BufferSearchBar>),
+    ) {
+        self.register_action(move |workspace, action: &A, cx| {
+            let pane = workspace.active_pane();
+            pane.update(cx, move |this, cx| {
+                this.toolbar().update(cx, move |this, cx| {
                     if let Some(search_bar) = this.item_of_type::<BufferSearchBar>() {
-                        search_bar.update(cx, |this, cx| {
-                            this.deploy(deploy, cx);
-                        });
-                        return;
+                        search_bar.update(cx, move |this, cx| callback(this, action, cx));
+                        cx.notify();
                     }
-                    let view = cx.new_view(|cx| BufferSearchBar::new(cx));
-                    this.add_item(view.clone(), cx);
-                    view.update(cx, |this, cx| this.deploy(deploy, cx));
-                    cx.notify();
                 })
             });
         });
-        fn register_action<A: Action>(
-            workspace: &mut Workspace,
-            update: fn(&mut BufferSearchBar, &A, &mut ViewContext<BufferSearchBar>),
-        ) {
-            workspace.register_action(move |workspace, action: &A, cx| {
-                let pane = workspace.active_pane();
-                pane.update(cx, move |this, cx| {
-                    this.toolbar().update(cx, move |this, cx| {
-                        if let Some(search_bar) = this.item_of_type::<BufferSearchBar>() {
-                            search_bar.update(cx, move |this, cx| update(this, action, cx));
-                            cx.notify();
-                        }
-                    })
-                });
-            });
-        }
-
-        register_action(workspace, |this, action: &ToggleCaseSensitive, cx| {
+    }
+}
+impl BufferSearchBar {
+    pub fn register_inner(registrar: &mut impl SearchActionsRegistrar) {
+        registrar.register_handler(|this, action: &ToggleCaseSensitive, cx| {
             if this.supported_options().case {
                 this.toggle_case_sensitive(action, cx);
             }
         });
-        register_action(workspace, |this, action: &ToggleWholeWord, cx| {
+
+        registrar.register_handler(|this, action: &ToggleWholeWord, cx| {
             if this.supported_options().word {
                 this.toggle_whole_word(action, cx);
             }
         });
-        register_action(workspace, |this, action: &ToggleReplace, cx| {
+
+        registrar.register_handler(|this, action: &ToggleReplace, cx| {
             if this.supported_options().replacement {
                 this.toggle_replace(action, cx);
             }
         });
-        register_action(workspace, |this, _: &ActivateRegexMode, cx| {
+
+        registrar.register_handler(|this, _: &ActivateRegexMode, cx| {
             if this.supported_options().regex {
                 this.activate_search_mode(SearchMode::Regex, cx);
             }
         });
-        register_action(workspace, |this, _: &ActivateTextMode, cx| {
+
+        registrar.register_handler(|this, _: &ActivateTextMode, cx| {
             this.activate_search_mode(SearchMode::Text, cx);
         });
-        register_action(workspace, |this, action: &CycleMode, cx| {
+
+        registrar.register_handler(|this, action: &CycleMode, cx| {
             if this.supported_options().regex {
                 // If regex is not supported then search has just one mode (text) - in that case there's no point in supporting
                 // cycling.
                 this.cycle_mode(action, cx)
             }
         });
-        register_action(workspace, |this, action: &SelectNextMatch, cx| {
+
+        registrar.register_handler(|this, action: &SelectNextMatch, cx| {
             this.select_next_match(action, cx);
         });
-        register_action(workspace, |this, action: &SelectPrevMatch, cx| {
+        registrar.register_handler(|this, action: &SelectPrevMatch, cx| {
             this.select_prev_match(action, cx);
         });
-        register_action(workspace, |this, action: &SelectAllMatches, cx| {
+        registrar.register_handler(|this, action: &SelectAllMatches, cx| {
             this.select_all_matches(action, cx);
         });
-        register_action(workspace, |this, _: &editor::Cancel, cx| {
+        registrar.register_handler(|this, _: &editor::Cancel, cx| {
             if !this.dismissed {
                 this.dismiss(&Dismiss, cx);
                 return;
             }
             cx.propagate();
         });
+        registrar.register_handler(|this, deploy, cx| {
+            this.deploy(deploy, cx);
+        })
+    }
+    fn register(workspace: &mut Workspace) {
+        Self::register_inner(workspace);
     }
     pub fn new(cx: &mut ViewContext<Self>) -> Self {
         let query_editor = cx.new_view(|cx| Editor::single_line(cx));

crates/terminal/src/mappings/mouse.rs 🔗

@@ -158,39 +158,41 @@ pub fn mouse_moved_report(point: AlacPoint, e: &MouseMoveEvent, mode: TermMode)
     }
 }
 
-pub fn mouse_side(
+pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint {
+    grid_point_and_side(pos, cur_size, display_offset).0
+}
+
+pub fn grid_point_and_side(
     pos: Point<Pixels>,
     cur_size: TerminalSize,
-) -> alacritty_terminal::index::Direction {
-    let cell_width = cur_size.cell_width.floor();
-    if cell_width == px(0.) {
-        return Side::Right;
-    }
-
-    let x = pos.x.floor();
-
-    let cell_x = cmp::max(px(0.), x - cell_width) % cell_width;
-    let half_cell_width = (cur_size.cell_width / 2.0).floor();
-    let additional_padding = (cur_size.width() - cur_size.cell_width * 2.) % cur_size.cell_width;
-    let end_of_grid = cur_size.width() - cur_size.cell_width - additional_padding;
-
-    //Width: Pixels or columns?
-    if cell_x > half_cell_width
-    // Edge case when mouse leaves the window.
-    || x >= end_of_grid
-    {
+    display_offset: usize,
+) -> (AlacPoint, Side) {
+    let mut col = GridCol((pos.x / cur_size.cell_width) as usize);
+    let cell_x = cmp::max(px(0.), pos.x) % cur_size.cell_width;
+    let half_cell_width = cur_size.cell_width / 2.0;
+    let mut side = if cell_x > half_cell_width {
         Side::Right
     } else {
         Side::Left
-    }
-}
+    };
 
-pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint {
-    let col = GridCol((pos.x / cur_size.cell_width) as usize);
+    if col > cur_size.last_column() {
+        col = cur_size.last_column();
+        side = Side::Right;
+    }
     let col = min(col, cur_size.last_column());
-    let line = (pos.y / cur_size.line_height) as i32;
-    let line = min(line, cur_size.bottommost_line().0);
-    AlacPoint::new(GridLine(line - display_offset as i32), col)
+    let mut line = (pos.y / cur_size.line_height) as i32;
+    if line > cur_size.bottommost_line() {
+        line = cur_size.bottommost_line().0 as i32;
+        side = Side::Right;
+    } else if line < 0 {
+        side = Side::Left;
+    }
+
+    (
+        AlacPoint::new(GridLine(line - display_offset as i32), col),
+        side,
+    )
 }
 
 ///Generate the bytes to send to the terminal, from the cell location, a mouse event, and the terminal mode

crates/terminal/src/terminal.rs 🔗

@@ -28,7 +28,8 @@ use futures::{
 };
 
 use mappings::mouse::{
-    alt_scroll, grid_point, mouse_button_report, mouse_moved_report, mouse_side, scroll_report,
+    alt_scroll, grid_point, grid_point_and_side, mouse_button_report, mouse_moved_report,
+    scroll_report,
 };
 
 use procinfo::LocalProcessInfo;
@@ -704,14 +705,12 @@ impl Terminal {
             }
             InternalEvent::UpdateSelection(position) => {
                 if let Some(mut selection) = term.selection.take() {
-                    let point = grid_point(
+                    let (point, side) = grid_point_and_side(
                         *position,
                         self.last_content.size,
                         term.grid().display_offset(),
                     );
 
-                    let side = mouse_side(*position, self.last_content.size);
-
                     selection.update(point, side);
                     term.selection = Some(selection);
 
@@ -1088,12 +1087,11 @@ impl Terminal {
         let position = e.position - origin;
         self.last_mouse_position = Some(position);
         if self.mouse_mode(e.modifiers.shift) {
-            let point = grid_point(
+            let (point, side) = grid_point_and_side(
                 position,
                 self.last_content.size,
                 self.last_content.display_offset,
             );
-            let side = mouse_side(position, self.last_content.size);
 
             if self.mouse_changed(point, side) {
                 if let Some(bytes) = mouse_moved_report(point, e, self.last_content.mode) {
@@ -1175,15 +1173,12 @@ impl Terminal {
             }
         } else if e.button == MouseButton::Left {
             let position = e.position - origin;
-            let point = grid_point(
+            let (point, side) = grid_point_and_side(
                 position,
                 self.last_content.size,
                 self.last_content.display_offset,
             );
 
-            // Use .opposite so that selection is inclusive of the cell clicked.
-            let side = mouse_side(position, self.last_content.size);
-
             let selection_type = match e.click_count {
                 0 => return, //This is a release
                 1 => Some(SelectionType::Simple),

crates/terminal_view/Cargo.toml 🔗

@@ -13,7 +13,7 @@ editor = { path = "../editor" }
 language = { path = "../language" }
 gpui = { path = "../gpui" }
 project = { path = "../project" }
-# search = { path = "../search" }
+search = { path = "../search" }
 settings = { path = "../settings" }
 theme = { path = "../theme" }
 util = { path = "../util" }

crates/terminal_view/src/terminal_element.rs 🔗

@@ -2,10 +2,11 @@ use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
 use gpui::{
     div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace,
     BorrowWindow, Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font,
-    FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState,
-    Interactivity, IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton,
-    Pixels, PlatformInputHandler, Point, ShapedLine, StatefulInteractiveElement, StyleRefinement,
-    Styled, TextRun, TextStyle, TextSystem, UnderlineStyle, WhiteSpace, WindowContext,
+    FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveBounds, InteractiveElement,
+    InteractiveElementState, Interactivity, IntoElement, LayoutId, Model, ModelContext,
+    ModifiersChangedEvent, MouseButton, MouseMoveEvent, Pixels, PlatformInputHandler, Point,
+    ShapedLine, StatefulInteractiveElement, StyleRefinement, Styled, TextRun, TextStyle,
+    TextSystem, UnderlineStyle, WhiteSpace, WindowContext,
 };
 use itertools::Itertools;
 use language::CursorShape;
@@ -598,33 +599,48 @@ impl TerminalElement {
     ) {
         let focus = self.focus.clone();
         let terminal = self.terminal.clone();
+        let interactive_bounds = InteractiveBounds {
+            bounds: bounds.intersect(&cx.content_mask().bounds),
+            stacking_order: cx.stacking_order().clone(),
+        };
 
         self.interactivity.on_mouse_down(MouseButton::Left, {
             let terminal = terminal.clone();
             let focus = focus.clone();
             move |e, cx| {
                 cx.focus(&focus);
-                //todo!(context menu)
-                // v.context_menu.update(cx, |menu, _cx| menu.delay_cancel());
                 terminal.update(cx, |terminal, cx| {
                     terminal.mouse_down(&e, origin);
-
                     cx.notify();
                 })
             }
         });
-        self.interactivity.on_mouse_move({
-            let terminal = terminal.clone();
-            let focus = focus.clone();
-            move |e, cx| {
-                if e.pressed_button.is_some() && focus.is_focused(cx) && !cx.has_active_drag() {
+
+        cx.on_mouse_event({
+            let bounds = bounds.clone();
+            let focus = self.focus.clone();
+            let terminal = self.terminal.clone();
+            move |e: &MouseMoveEvent, phase, cx| {
+                if phase != DispatchPhase::Bubble || !focus.is_focused(cx) {
+                    return;
+                }
+
+                if e.pressed_button.is_some() && !cx.has_active_drag() {
                     terminal.update(cx, |terminal, cx| {
                         terminal.mouse_drag(e, origin, bounds);
                         cx.notify();
                     })
                 }
+
+                if interactive_bounds.visibly_contains(&e.position, cx) {
+                    terminal.update(cx, |terminal, cx| {
+                        terminal.mouse_move(&e, origin);
+                        cx.notify();
+                    })
+                }
             }
         });
+
         self.interactivity.on_mouse_up(
             MouseButton::Left,
             TerminalElement::generic_button_handler(
@@ -651,19 +667,6 @@ impl TerminalElement {
                 }
             }
         });
-
-        self.interactivity.on_mouse_move({
-            let terminal = terminal.clone();
-            let focus = focus.clone();
-            move |e, cx| {
-                if focus.is_focused(cx) {
-                    terminal.update(cx, |terminal, cx| {
-                        terminal.mouse_move(&e, origin);
-                        cx.notify();
-                    })
-                }
-            }
-        });
         self.interactivity.on_scroll_wheel({
             let terminal = terminal.clone();
             move |e, cx| {

crates/terminal_view/src/terminal_panel.rs 🔗

@@ -3,11 +3,12 @@ use std::{path::PathBuf, sync::Arc};
 use crate::TerminalView;
 use db::kvp::KEY_VALUE_STORE;
 use gpui::{
-    actions, div, serde_json, AppContext, AsyncWindowContext, Entity, EventEmitter, ExternalPaths,
+    actions, serde_json, AppContext, AsyncWindowContext, Entity, EventEmitter, ExternalPaths,
     FocusHandle, FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, Subscription,
     Task, View, ViewContext, VisualContext, WeakView, WindowContext,
 };
 use project::Fs;
+use search::{buffer_search::DivRegistrar, BufferSearchBar};
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsStore};
 use terminal::terminal_settings::{TerminalDockPosition, TerminalSettings};
@@ -101,9 +102,9 @@ impl TerminalPanel {
                     })
                     .into_any_element()
             });
-            // let buffer_search_bar = cx.build_view(search::BufferSearchBar::new);
-            // pane.toolbar()
-            //     .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx));
+            let buffer_search_bar = cx.new_view(search::BufferSearchBar::new);
+            pane.toolbar()
+                .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx));
             pane
         });
         let subscriptions = vec![
@@ -329,8 +330,20 @@ impl TerminalPanel {
 impl EventEmitter<PanelEvent> for TerminalPanel {}
 
 impl Render for TerminalPanel {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
-        div().size_full().child(self.pane.clone())
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        let mut registrar = DivRegistrar::new(
+            |panel, cx| {
+                panel
+                    .pane
+                    .read(cx)
+                    .toolbar()
+                    .read(cx)
+                    .item_of_type::<BufferSearchBar>()
+            },
+            cx,
+        );
+        BufferSearchBar::register_inner(&mut registrar);
+        registrar.into_div().size_full().child(self.pane.clone())
     }
 }
 
@@ -410,11 +423,6 @@ impl Panel for TerminalPanel {
         "TerminalPanel"
     }
 
-    // todo!()
-    // fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {
-    //     ("Terminal Panel".into(), Some(Box::new(ToggleFocus)))
-    // }
-
     fn icon(&self, _cx: &WindowContext) -> Option<Icon> {
         Some(Icon::Terminal)
     }

crates/terminal_view/src/terminal_view.rs 🔗

@@ -28,7 +28,7 @@ use workspace::{
     item::{BreadcrumbText, Item, ItemEvent},
     notifications::NotifyResultExt,
     register_deserializable_item,
-    searchable::{SearchEvent, SearchOptions, SearchableItem},
+    searchable::{SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle},
     CloseActiveItem, NewCenterTerminal, Pane, ToolbarItemLocation, Workspace, WorkspaceId,
 };
 
@@ -714,10 +714,9 @@ impl Item for TerminalView {
         false
     }
 
-    // todo!(search)
-    // fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
-    //     Some(Box::new(handle.clone()))
-    // }
+    fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+        Some(Box::new(handle.clone()))
+    }
 
     fn breadcrumb_location(&self) -> ToolbarItemLocation {
         ToolbarItemLocation::PrimaryLeft

crates/zed/src/app_menus.rs 🔗

@@ -150,14 +150,6 @@ pub fn app_menus() -> Vec<Menu<'static>> {
                 MenuItem::action("View Dependency Licenses", crate::OpenLicenses),
                 MenuItem::action("Show Welcome", workspace::Welcome),
                 MenuItem::separator(),
-                // todo!(): Needs `feedback` crate.
-                // MenuItem::action("Give us feedback", feedback::feedback_editor::GiveFeedback),
-                // MenuItem::action(
-                //     "Copy System Specs Into Clipboard",
-                //     feedback::CopySystemSpecsIntoClipboard,
-                // ),
-                // MenuItem::action("File Bug Report", feedback::FileBugReport),
-                // MenuItem::action("Request Feature", feedback::RequestFeature),
                 MenuItem::separator(),
                 MenuItem::action(
                     "Documentation",

crates/zed/src/zed.rs 🔗

@@ -400,7 +400,6 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
             });
 
         workspace.focus_handle(cx).focus(cx);
-        load_default_keymap(cx);
     })
     .detach();
 }
@@ -571,6 +570,8 @@ pub fn handle_keymap_file_changes(
     })
     .detach();
 
+    load_default_keymap(cx);
+
     cx.spawn(move |cx| async move {
         let mut user_keymap = KeymapFile::default();
         loop {