mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Add editor::ToggleInlayHints
command and a bar to toggle editor-related items (#2839)
Closes https://linear.app/zed-industries/issue/Z-2735/make-inlay-hints-discoverable Deals with https://github.com/zed-industries/community/issues/1764 Adds `editor::ToggleInlayHints` command and a new panel with two buttons for * toggling hints on/off for every editor separately (overrides settings) Would benefit from a new icon. * toggling buffer search on/off Does not have a keybinding shown in its tooltip, that is a separate issue with the way `KeystrokeLabel` gets these for panels not in the view directly. Release Notes: - Adds `editor::ToggleInlayHints` command and a bar to toggle editor-related items
This commit is contained in:
commit
988ea3c16b
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -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",
|
||||
|
5
assets/icons/inlay_hint.svg
Normal file
5
assets/icons/inlay_hint.svg
Normal file
@ -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>
|
After Width: | Height: | Size: 276 B |
@ -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 {})
|
||||
}
|
||||
|
22
crates/quick_action_bar/Cargo.toml
Normal file
22
crates/quick_action_bar/Cargo.toml
Normal file
@ -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"] }
|
163
crates/quick_action_bar/src/quick_action_bar.rs
Normal file
163
crates/quick_action_bar/src/quick_action_bar.rs
Normal file
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user