Detailed changes
@@ -5714,6 +5714,17 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "quick_action_bar"
+version = "0.1.0"
+dependencies = [
+ "editor",
+ "gpui",
+ "search",
+ "theme",
+ "workspace",
+]
+
[[package]]
name = "quote"
version = "1.0.32"
@@ -9922,6 +9933,7 @@ dependencies = [
"project",
"project_panel",
"project_symbols",
+ "quick_action_bar",
"rand 0.8.5",
"recent_projects",
"regex",
@@ -0,0 +1,5 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="3" cy="9" r="1" fill="black"/>
+<circle cx="3" cy="5" r="1" fill="black"/>
+<path d="M7 3H10M13 3H10M10 3C10 3 10 11 10 11.5" stroke="black" stroke-width="1.25"/>
+</svg>
@@ -7867,7 +7867,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
.insert_tree(
"/a",
json!({
- "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
+ "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
"other.rs": "// Test file",
}),
)
@@ -8177,7 +8177,7 @@ async fn test_inlay_hint_refresh_is_forwarded(
.insert_tree(
"/a",
json!({
- "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
+ "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
"other.rs": "// Test file",
}),
)
@@ -302,10 +302,11 @@ actions!(
Hover,
Format,
ToggleSoftWrap,
+ ToggleInlayHints,
RevealInFinder,
CopyPath,
CopyRelativePath,
- CopyHighlightJson
+ CopyHighlightJson,
]
);
@@ -446,6 +447,7 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(Editor::toggle_code_actions);
cx.add_action(Editor::open_excerpts);
cx.add_action(Editor::toggle_soft_wrap);
+ cx.add_action(Editor::toggle_inlay_hints);
cx.add_action(Editor::reveal_in_finder);
cx.add_action(Editor::copy_path);
cx.add_action(Editor::copy_relative_path);
@@ -1237,7 +1239,8 @@ enum GotoDefinitionKind {
}
#[derive(Debug, Clone)]
-enum InlayRefreshReason {
+enum InlayHintRefreshReason {
+ Toggle(bool),
SettingsChange(InlayHintSettings),
NewLinesShown,
BufferEdited(HashSet<Arc<Language>>),
@@ -1354,8 +1357,8 @@ impl Editor {
}));
}
project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
- if let project::Event::RefreshInlays = event {
- editor.refresh_inlays(InlayRefreshReason::RefreshRequested, cx);
+ if let project::Event::RefreshInlayHints = event {
+ editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
};
}));
}
@@ -2669,13 +2672,41 @@ impl Editor {
}
}
- fn refresh_inlays(&mut self, reason: InlayRefreshReason, cx: &mut ViewContext<Self>) {
+ pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
+ self.refresh_inlay_hints(
+ InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
+ cx,
+ );
+ }
+
+ pub fn inlay_hints_enabled(&self) -> bool {
+ self.inlay_hint_cache.enabled
+ }
+
+ fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
if self.project.is_none() || self.mode != EditorMode::Full {
return;
}
let (invalidate_cache, required_languages) = match reason {
- InlayRefreshReason::SettingsChange(new_settings) => {
+ InlayHintRefreshReason::Toggle(enabled) => {
+ self.inlay_hint_cache.enabled = enabled;
+ if enabled {
+ (InvalidationStrategy::RefreshRequested, None)
+ } else {
+ self.inlay_hint_cache.clear();
+ self.splice_inlay_hints(
+ self.visible_inlay_hints(cx)
+ .iter()
+ .map(|inlay| inlay.id)
+ .collect(),
+ Vec::new(),
+ cx,
+ );
+ return;
+ }
+ }
+ InlayHintRefreshReason::SettingsChange(new_settings) => {
match self.inlay_hint_cache.update_settings(
&self.buffer,
new_settings,
@@ -2693,11 +2724,13 @@ impl Editor {
ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
}
}
- InlayRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
- InlayRefreshReason::BufferEdited(buffer_languages) => {
+ InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
+ InlayHintRefreshReason::BufferEdited(buffer_languages) => {
(InvalidationStrategy::BufferEdited, Some(buffer_languages))
}
- InlayRefreshReason::RefreshRequested => (InvalidationStrategy::RefreshRequested, None),
+ InlayHintRefreshReason::RefreshRequested => {
+ (InvalidationStrategy::RefreshRequested, None)
+ }
};
if let Some(InlaySplice {
@@ -2774,6 +2807,7 @@ impl Editor {
self.display_map.update(cx, |display_map, cx| {
display_map.splice_inlays(to_remove, to_insert, cx);
});
+ cx.notify();
}
fn trigger_on_type_formatting(
@@ -7696,8 +7730,8 @@ impl Editor {
.cloned()
.collect::<HashSet<_>>();
if !languages_affected.is_empty() {
- self.refresh_inlays(
- InlayRefreshReason::BufferEdited(languages_affected),
+ self.refresh_inlay_hints(
+ InlayHintRefreshReason::BufferEdited(languages_affected),
cx,
);
}
@@ -7735,8 +7769,8 @@ impl Editor {
fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
self.refresh_copilot_suggestions(true, cx);
- self.refresh_inlays(
- InlayRefreshReason::SettingsChange(inlay_hint_settings(
+ self.refresh_inlay_hints(
+ InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
self.selections.newest_anchor().head(),
&self.buffer.read(cx).snapshot(cx),
cx,
@@ -24,7 +24,7 @@ pub struct InlayHintCache {
hints: HashMap<ExcerptId, Arc<RwLock<CachedExcerptHints>>>,
allowed_hint_kinds: HashSet<Option<InlayHintKind>>,
version: usize,
- enabled: bool,
+ pub(super) enabled: bool,
update_tasks: HashMap<ExcerptId, TasksForRanges>,
}
@@ -380,7 +380,7 @@ impl InlayHintCache {
}
}
- fn clear(&mut self) {
+ pub fn clear(&mut self) {
self.version += 1;
self.update_tasks.clear();
self.hints.clear();
@@ -2001,7 +2001,7 @@ mod tests {
});
}
- #[gpui::test]
+ #[gpui::test(iterations = 10)]
async fn test_multiple_excerpts_large_multibuffer(
deterministic: Arc<Deterministic>,
cx: &mut gpui::TestAppContext,
@@ -2335,10 +2335,12 @@ mod tests {
all hints should be invalidated and requeried for all of its visible excerpts"
);
assert_eq!(expected_layers, visible_hint_labels(editor, cx));
- assert_eq!(
- editor.inlay_hint_cache().version,
- last_scroll_update_version + expected_layers.len(),
- "Due to every excerpt having one hint, cache should update per new excerpt received"
+
+ let current_cache_version = editor.inlay_hint_cache().version;
+ let minimum_expected_version = last_scroll_update_version + expected_layers.len();
+ assert!(
+ current_cache_version == minimum_expected_version || current_cache_version == minimum_expected_version + 1,
+ "Due to every excerpt having one hint, cache should update per new excerpt received + 1 potential sporadic update"
);
});
}
@@ -2683,6 +2685,127 @@ all hints should be invalidated and requeried for all of its visible excerpts"
});
}
+ #[gpui::test]
+ async fn test_toggle_inlay_hints(cx: &mut gpui::TestAppContext) {
+ init_test(cx, |settings| {
+ settings.defaults.inlay_hints = Some(InlayHintSettings {
+ enabled: false,
+ show_type_hints: true,
+ show_parameter_hints: true,
+ show_other_hints: true,
+ })
+ });
+
+ let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
+
+ editor.update(cx, |editor, cx| {
+ editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+ });
+ cx.foreground().start_waiting();
+ let lsp_request_count = Arc::new(AtomicU32::new(0));
+ let closure_lsp_request_count = Arc::clone(&lsp_request_count);
+ fake_server
+ .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+ let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
+ async move {
+ assert_eq!(
+ params.text_document.uri,
+ lsp::Url::from_file_path(file_with_hints).unwrap(),
+ );
+
+ let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
+ Ok(Some(vec![lsp::InlayHint {
+ position: lsp::Position::new(0, i),
+ label: lsp::InlayHintLabel::String(i.to_string()),
+ kind: None,
+ text_edits: None,
+ tooltip: None,
+ padding_left: None,
+ padding_right: None,
+ data: None,
+ }]))
+ }
+ })
+ .next()
+ .await;
+ cx.foreground().run_until_parked();
+ editor.update(cx, |editor, cx| {
+ let expected_hints = vec!["1".to_string()];
+ assert_eq!(
+ expected_hints,
+ cached_hint_labels(editor),
+ "Should display inlays after toggle despite them disabled in settings"
+ );
+ assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+ assert_eq!(
+ editor.inlay_hint_cache().version,
+ 1,
+ "First toggle should be cache's first update"
+ );
+ });
+
+ editor.update(cx, |editor, cx| {
+ editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+ });
+ cx.foreground().run_until_parked();
+ editor.update(cx, |editor, cx| {
+ assert!(
+ cached_hint_labels(editor).is_empty(),
+ "Should clear hints after 2nd toggle"
+ );
+ assert!(visible_hint_labels(editor, cx).is_empty());
+ assert_eq!(editor.inlay_hint_cache().version, 2);
+ });
+
+ update_test_language_settings(cx, |settings| {
+ settings.defaults.inlay_hints = Some(InlayHintSettings {
+ enabled: true,
+ show_type_hints: true,
+ show_parameter_hints: true,
+ show_other_hints: true,
+ })
+ });
+ cx.foreground().run_until_parked();
+ editor.update(cx, |editor, cx| {
+ let expected_hints = vec!["2".to_string()];
+ assert_eq!(
+ expected_hints,
+ cached_hint_labels(editor),
+ "Should query LSP hints for the 2nd time after enabling hints in settings"
+ );
+ assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+ assert_eq!(editor.inlay_hint_cache().version, 3);
+ });
+
+ editor.update(cx, |editor, cx| {
+ editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+ });
+ cx.foreground().run_until_parked();
+ editor.update(cx, |editor, cx| {
+ assert!(
+ cached_hint_labels(editor).is_empty(),
+ "Should clear hints after enabling in settings and a 3rd toggle"
+ );
+ assert!(visible_hint_labels(editor, cx).is_empty());
+ assert_eq!(editor.inlay_hint_cache().version, 4);
+ });
+
+ editor.update(cx, |editor, cx| {
+ editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+ });
+ cx.foreground().run_until_parked();
+ editor.update(cx, |editor, cx| {
+ let expected_hints = vec!["3".to_string()];
+ assert_eq!(
+ expected_hints,
+ cached_hint_labels(editor),
+ "Should query LSP hints for the 3rd time after enabling hints in settings and toggling them back on"
+ );
+ assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+ assert_eq!(editor.inlay_hint_cache().version, 5);
+ });
+ }
+
pub(crate) fn init_test(cx: &mut TestAppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
cx.foreground().forbid_parking();
@@ -2759,6 +2882,12 @@ all hints should be invalidated and requeried for all of its visible excerpts"
.downcast::<Editor>()
.unwrap();
+ editor.update(cx, |editor, cx| {
+ assert!(cached_hint_labels(editor).is_empty());
+ assert!(visible_hint_labels(editor, cx).is_empty());
+ assert_eq!(editor.inlay_hint_cache().version, 0);
+ });
+
("/a/main.rs", editor, fake_server)
}
@@ -19,7 +19,7 @@ use crate::{
display_map::{DisplaySnapshot, ToDisplayPoint},
hover_popover::hide_hover,
persistence::DB,
- Anchor, DisplayPoint, Editor, EditorMode, Event, InlayRefreshReason, MultiBufferSnapshot,
+ Anchor, DisplayPoint, Editor, EditorMode, Event, InlayHintRefreshReason, MultiBufferSnapshot,
ToPoint,
};
@@ -301,7 +301,7 @@ impl Editor {
cx.spawn(|editor, mut cx| async move {
editor
.update(&mut cx, |editor, cx| {
- editor.refresh_inlays(InlayRefreshReason::NewLinesShown, cx)
+ editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx)
})
.ok()
})
@@ -333,7 +333,7 @@ impl Editor {
cx,
);
- self.refresh_inlays(InlayRefreshReason::NewLinesShown, cx);
+ self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
}
pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
@@ -282,7 +282,7 @@ pub enum Event {
new_peer_id: proto::PeerId,
},
CollaboratorLeft(proto::PeerId),
- RefreshInlays,
+ RefreshInlayHints,
}
pub enum LanguageServerState {
@@ -2872,7 +2872,7 @@ impl Project {
.upgrade(&cx)
.ok_or_else(|| anyhow!("project dropped"))?;
this.update(&mut cx, |project, cx| {
- cx.emit(Event::RefreshInlays);
+ cx.emit(Event::RefreshInlayHints);
project.remote_id().map(|project_id| {
project.client.send(proto::RefreshInlayHints { project_id })
})
@@ -3436,7 +3436,7 @@ impl Project {
cx: &mut ModelContext<Self>,
) {
if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
- cx.emit(Event::RefreshInlays);
+ cx.emit(Event::RefreshInlayHints);
status.pending_work.remove(&token);
cx.notify();
}
@@ -6810,7 +6810,7 @@ impl Project {
mut cx: AsyncAppContext,
) -> Result<proto::Ack> {
this.update(&mut cx, |_, cx| {
- cx.emit(Event::RefreshInlays);
+ cx.emit(Event::RefreshInlayHints);
});
Ok(proto::Ack {})
}
@@ -0,0 +1,22 @@
+[package]
+name = "quick_action_bar"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/quick_action_bar.rs"
+doctest = false
+
+[dependencies]
+editor = { path = "../editor" }
+gpui = { path = "../gpui" }
+search = { path = "../search" }
+theme = { path = "../theme" }
+workspace = { path = "../workspace" }
+
+[dev-dependencies]
+editor = { path = "../editor", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
+theme = { path = "../theme", features = ["test-support"] }
+workspace = { path = "../workspace", features = ["test-support"] }
@@ -0,0 +1,163 @@
+use editor::Editor;
+use gpui::{
+ elements::{Empty, Flex, MouseEventHandler, ParentElement, Svg},
+ platform::{CursorStyle, MouseButton},
+ Action, AnyElement, Element, Entity, EventContext, Subscription, View, ViewContext, ViewHandle,
+};
+
+use search::{buffer_search, BufferSearchBar};
+use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView};
+
+pub struct QuickActionBar {
+ buffer_search_bar: ViewHandle<BufferSearchBar>,
+ active_item: Option<Box<dyn ItemHandle>>,
+ _inlay_hints_enabled_subscription: Option<Subscription>,
+}
+
+impl QuickActionBar {
+ pub fn new(buffer_search_bar: ViewHandle<BufferSearchBar>) -> Self {
+ Self {
+ buffer_search_bar,
+ active_item: None,
+ _inlay_hints_enabled_subscription: None,
+ }
+ }
+
+ fn active_editor(&self) -> Option<ViewHandle<Editor>> {
+ self.active_item
+ .as_ref()
+ .and_then(|item| item.downcast::<Editor>())
+ }
+}
+
+impl Entity for QuickActionBar {
+ type Event = ();
+}
+
+impl View for QuickActionBar {
+ fn ui_name() -> &'static str {
+ "QuickActionsBar"
+ }
+
+ fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
+ let Some(editor) = self.active_editor() else { return Empty::new().into_any(); };
+
+ let inlay_hints_enabled = editor.read(cx).inlay_hints_enabled();
+ let mut bar = Flex::row().with_child(render_quick_action_bar_button(
+ 0,
+ "icons/inlay_hint.svg",
+ inlay_hints_enabled,
+ (
+ "Toggle Inlay Hints".to_string(),
+ Some(Box::new(editor::ToggleInlayHints)),
+ ),
+ cx,
+ |this, cx| {
+ if let Some(editor) = this.active_editor() {
+ editor.update(cx, |editor, cx| {
+ editor.toggle_inlay_hints(&editor::ToggleInlayHints, cx);
+ });
+ }
+ },
+ ));
+
+ if editor.read(cx).buffer().read(cx).is_singleton() {
+ let search_bar_shown = !self.buffer_search_bar.read(cx).is_dismissed();
+ let search_action = buffer_search::Deploy { focus: true };
+
+ bar = bar.with_child(render_quick_action_bar_button(
+ 1,
+ "icons/magnifying_glass.svg",
+ search_bar_shown,
+ (
+ "Buffer Search".to_string(),
+ Some(Box::new(search_action.clone())),
+ ),
+ cx,
+ move |this, cx| {
+ this.buffer_search_bar.update(cx, |buffer_search_bar, cx| {
+ if search_bar_shown {
+ buffer_search_bar.dismiss(&buffer_search::Dismiss, cx);
+ } else {
+ buffer_search_bar.deploy(&search_action, cx);
+ }
+ });
+ },
+ ));
+ }
+
+ bar.into_any()
+ }
+}
+
+fn render_quick_action_bar_button<
+ F: 'static + Fn(&mut QuickActionBar, &mut EventContext<QuickActionBar>),
+>(
+ index: usize,
+ icon: &'static str,
+ toggled: bool,
+ tooltip: (String, Option<Box<dyn Action>>),
+ cx: &mut ViewContext<QuickActionBar>,
+ on_click: F,
+) -> AnyElement<QuickActionBar> {
+ enum QuickActionBarButton {}
+
+ let theme = theme::current(cx);
+ let (tooltip_text, action) = tooltip;
+
+ MouseEventHandler::new::<QuickActionBarButton, _>(index, cx, |mouse_state, _| {
+ let style = theme
+ .workspace
+ .toolbar
+ .toggleable_tool
+ .in_state(toggled)
+ .style_for(mouse_state);
+ Svg::new(icon)
+ .with_color(style.color)
+ .constrained()
+ .with_width(style.icon_width)
+ .aligned()
+ .constrained()
+ .with_width(style.button_width)
+ .with_height(style.button_width)
+ .contained()
+ .with_style(style.container)
+ })
+ .with_cursor_style(CursorStyle::PointingHand)
+ .on_click(MouseButton::Left, move |_, pane, cx| on_click(pane, cx))
+ .with_tooltip::<QuickActionBarButton>(index, tooltip_text, action, theme.tooltip.clone(), cx)
+ .into_any_named("quick action bar button")
+}
+
+impl ToolbarItemView for QuickActionBar {
+ fn set_active_pane_item(
+ &mut self,
+ active_pane_item: Option<&dyn ItemHandle>,
+ cx: &mut ViewContext<Self>,
+ ) -> ToolbarItemLocation {
+ match active_pane_item {
+ Some(active_item) => {
+ self.active_item = Some(active_item.boxed_clone());
+ self._inlay_hints_enabled_subscription.take();
+
+ if let Some(editor) = active_item.downcast::<Editor>() {
+ let mut inlay_hints_enabled = editor.read(cx).inlay_hints_enabled();
+ self._inlay_hints_enabled_subscription =
+ Some(cx.observe(&editor, move |_, editor, cx| {
+ let new_inlay_hints_enabled = editor.read(cx).inlay_hints_enabled();
+ if inlay_hints_enabled != new_inlay_hints_enabled {
+ inlay_hints_enabled = new_inlay_hints_enabled;
+ cx.notify();
+ }
+ }));
+ }
+
+ ToolbarItemLocation::PrimaryRight { flex: None }
+ }
+ None => {
+ self.active_item = None;
+ ToolbarItemLocation::Hidden
+ }
+ }
+ }
+}
@@ -36,7 +36,7 @@ pub enum Event {
}
pub fn init(cx: &mut AppContext) {
- cx.add_action(BufferSearchBar::deploy);
+ cx.add_action(BufferSearchBar::deploy_bar);
cx.add_action(BufferSearchBar::dismiss);
cx.add_action(BufferSearchBar::focus_editor);
cx.add_action(BufferSearchBar::select_next_match);
@@ -327,6 +327,19 @@ impl BufferSearchBar {
cx.notify();
}
+ pub fn deploy(&mut self, deploy: &Deploy, cx: &mut ViewContext<Self>) -> bool {
+ if self.show(cx) {
+ self.search_suggested(cx);
+ if deploy.focus {
+ self.select_query(cx);
+ cx.focus_self();
+ }
+ return true;
+ }
+
+ false
+ }
+
pub fn show(&mut self, cx: &mut ViewContext<Self>) -> bool {
if self.active_searchable_item.is_none() {
return false;
@@ -553,21 +566,15 @@ impl BufferSearchBar {
.into_any()
}
- fn deploy(pane: &mut Pane, action: &Deploy, cx: &mut ViewContext<Pane>) {
+ fn deploy_bar(pane: &mut Pane, action: &Deploy, cx: &mut ViewContext<Pane>) {
let mut propagate_action = true;
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
search_bar.update(cx, |search_bar, cx| {
- if search_bar.show(cx) {
- search_bar.search_suggested(cx);
- if action.focus {
- search_bar.select_query(cx);
- cx.focus_self();
- }
+ if search_bar.deploy(action, cx) {
propagate_action = false;
}
});
}
-
if propagate_action {
cx.propagate_action();
}
@@ -399,6 +399,7 @@ pub struct Toolbar {
pub height: f32,
pub item_spacing: f32,
pub nav_button: Interactive<IconButton>,
+ pub toggleable_tool: Toggleable<Interactive<IconButton>>,
}
#[derive(Clone, Deserialize, Default, JsonSchema)]
@@ -54,6 +54,7 @@ plugin_runtime = { path = "../plugin_runtime",optional = true }
project = { path = "../project" }
project_panel = { path = "../project_panel" }
project_symbols = { path = "../project_symbols" }
+quick_action_bar = { path = "../quick_action_bar" }
recent_projects = { path = "../recent_projects" }
rpc = { path = "../rpc" }
settings = { path = "../settings" }
@@ -30,6 +30,7 @@ use gpui::{
pub use lsp;
pub use project;
use project_panel::ProjectPanel;
+use quick_action_bar::QuickActionBar;
use search::{BufferSearchBar, ProjectSearchBar};
use serde::Deserialize;
use serde_json::to_string_pretty;
@@ -262,7 +263,10 @@ pub fn initialize_workspace(
let breadcrumbs = cx.add_view(|_| Breadcrumbs::new(workspace));
toolbar.add_item(breadcrumbs, cx);
let buffer_search_bar = cx.add_view(BufferSearchBar::new);
- toolbar.add_item(buffer_search_bar, cx);
+ toolbar.add_item(buffer_search_bar.clone(), cx);
+ let quick_action_bar =
+ cx.add_view(|_| QuickActionBar::new(buffer_search_bar));
+ toolbar.add_item(quick_action_bar, cx);
let project_search_bar = cx.add_view(|_| ProjectSearchBar::new());
toolbar.add_item(project_search_bar, cx);
let submit_feedback_button =
@@ -12,6 +12,7 @@ import tabBar from "./tab_bar"
import { interactive } from "../element"
import { titlebar } from "./titlebar"
import { useTheme } from "../theme"
+import { toggleable_icon_button } from "../component/icon_button"
export default function workspace(): any {
const theme = useTheme()
@@ -149,6 +150,11 @@ export default function workspace(): any {
},
},
}),
+ toggleable_tool: toggleable_icon_button(theme, {
+ margin: { left: 8 },
+ variant: "ghost",
+ active_color: "accent",
+ }),
padding: { left: 8, right: 8, top: 4, bottom: 4 },
},
breadcrumb_height: 24,