mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Finish converting all the pickers to the new API
This commit is contained in:
parent
d70644618a
commit
e282c7ad45
@ -12,7 +12,7 @@ use workspace::Workspace;
|
|||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(toggle_command_palette);
|
cx.add_action(toggle_command_palette);
|
||||||
Picker::<CommandPaletteDelegate>::init(cx);
|
CommandPalette::init(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
actions!(command_palette, [Toggle]);
|
actions!(command_palette, [Toggle]);
|
||||||
@ -155,9 +155,7 @@ impl PickerDelegate for CommandPaletteDelegate {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
|
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
|
||||||
cx.emit(PickerEvent::Dismiss);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
|
fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
|
||||||
if !self.matches.is_empty() {
|
if !self.matches.is_empty() {
|
||||||
@ -330,8 +328,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
palette.update(cx, |palette, cx| {
|
palette.update(cx, |palette, cx| {
|
||||||
assert_eq!(palette.matches[0].string, "editor: backspace");
|
assert_eq!(palette.delegate().matches[0].string, "editor: backspace");
|
||||||
palette.confirm(cx);
|
palette.confirm(&Default::default(), cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
editor.read_with(cx, |editor, cx| {
|
editor.read_with(cx, |editor, cx| {
|
||||||
@ -346,20 +344,24 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
CommandPaletteDelegate::toggle(workspace, &Toggle, cx);
|
toggle_command_palette(workspace, &Toggle, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Assert editor command not present
|
// Assert editor command not present
|
||||||
let palette = workspace.read_with(cx, |workspace, _| {
|
let palette = workspace.read_with(cx, |workspace, _| {
|
||||||
workspace.modal::<CommandPaletteDelegate>().unwrap()
|
workspace.modal::<CommandPalette>().unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
palette
|
palette
|
||||||
.update(cx, |palette, cx| {
|
.update(cx, |palette, cx| {
|
||||||
palette.update_matches("bcksp".to_string(), cx)
|
palette
|
||||||
|
.delegate_mut()
|
||||||
|
.update_matches("bcksp".to_string(), cx)
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
palette.update(cx, |palette, _| assert!(palette.matches.is_empty()));
|
palette.update(cx, |palette, _| {
|
||||||
|
assert!(palette.delegate().matches.is_empty())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,8 +94,7 @@ impl FileFinderDelegate {
|
|||||||
cx: &mut ViewContext<FileFinder>,
|
cx: &mut ViewContext<FileFinder>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
cx.observe(&project, |picker, _, cx| {
|
cx.observe(&project, |picker, _, cx| {
|
||||||
let query = picker.query(cx);
|
picker.update_matches(picker.query(cx), cx);
|
||||||
picker.delegate_mut().spawn_search(query, cx).detach();
|
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
Self {
|
Self {
|
||||||
@ -366,16 +365,21 @@ mod tests {
|
|||||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||||
let (_, finder) = cx.add_window(|cx| {
|
let (_, finder) = cx.add_window(|cx| {
|
||||||
Picker::new(
|
Picker::new(
|
||||||
FileFinderDelegate::new(workspace.read(cx).project().clone(), None, cx),
|
FileFinderDelegate::new(
|
||||||
|
workspace.downgrade(),
|
||||||
|
workspace.read(cx).project().clone(),
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let query = "hi".to_string();
|
let query = "hi".to_string();
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| f.spawn_search(query.clone(), cx))
|
.update(cx, |f, cx| f.delegate_mut().spawn_search(query.clone(), cx))
|
||||||
.await;
|
.await;
|
||||||
finder.read_with(cx, |f, _| assert_eq!(f.matches.len(), 5));
|
finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 5));
|
||||||
|
|
||||||
finder.update(cx, |finder, cx| {
|
finder.update(cx, |finder, cx| {
|
||||||
let delegate = finder.delegate_mut();
|
let delegate = finder.delegate_mut();
|
||||||
@ -385,7 +389,7 @@ mod tests {
|
|||||||
// returning only a subset of the matches that would have been found.
|
// returning only a subset of the matches that would have been found.
|
||||||
drop(delegate.spawn_search(query.clone(), cx));
|
drop(delegate.spawn_search(query.clone(), cx));
|
||||||
delegate.set_matches(
|
delegate.set_matches(
|
||||||
finder.delegate().latest_search_id,
|
delegate.latest_search_id,
|
||||||
true, // did-cancel
|
true, // did-cancel
|
||||||
query.clone(),
|
query.clone(),
|
||||||
vec![matches[1].clone(), matches[3].clone()],
|
vec![matches[1].clone(), matches[3].clone()],
|
||||||
@ -445,14 +449,19 @@ mod tests {
|
|||||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||||
let (_, finder) = cx.add_window(|cx| {
|
let (_, finder) = cx.add_window(|cx| {
|
||||||
Picker::new(
|
Picker::new(
|
||||||
FileFinderDelegate::new(workspace.read(cx).project().clone(), None, cx),
|
FileFinderDelegate::new(
|
||||||
|
workspace.downgrade(),
|
||||||
|
workspace.read(cx).project().clone(),
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| f.spawn_search("hi".into(), cx))
|
.update(cx, |f, cx| f.delegate_mut().spawn_search("hi".into(), cx))
|
||||||
.await;
|
.await;
|
||||||
finder.read_with(cx, |f, _| assert_eq!(f.matches.len(), 7));
|
finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 7));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
@ -472,20 +481,29 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||||
let (_, finder) = cx.add_window(|cx| {
|
let (_, finder) = cx.add_window(|cx| {
|
||||||
FileFinderDelegate::new(workspace.read(cx).project().clone(), None, cx)
|
Picker::new(
|
||||||
|
FileFinderDelegate::new(
|
||||||
|
workspace.downgrade(),
|
||||||
|
workspace.read(cx).project().clone(),
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Even though there is only one worktree, that worktree's filename
|
// Even though there is only one worktree, that worktree's filename
|
||||||
// is included in the matching, because the worktree is a single file.
|
// is included in the matching, because the worktree is a single file.
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| f.spawn_search("thf".into(), cx))
|
.update(cx, |f, cx| f.delegate_mut().spawn_search("thf".into(), cx))
|
||||||
.await;
|
.await;
|
||||||
cx.read(|cx| {
|
cx.read(|cx| {
|
||||||
let finder = finder.read(cx);
|
let finder = finder.read(cx);
|
||||||
assert_eq!(finder.matches.len(), 1);
|
let delegate = finder.delegate();
|
||||||
|
assert_eq!(delegate.matches.len(), 1);
|
||||||
|
|
||||||
let (file_name, file_name_positions, full_path, full_path_positions) =
|
let (file_name, file_name_positions, full_path, full_path_positions) =
|
||||||
finder.labels_for_match(&finder.matches[0]);
|
delegate.labels_for_match(&delegate.matches[0]);
|
||||||
assert_eq!(file_name, "the-file");
|
assert_eq!(file_name, "the-file");
|
||||||
assert_eq!(file_name_positions, &[0, 1, 4]);
|
assert_eq!(file_name_positions, &[0, 1, 4]);
|
||||||
assert_eq!(full_path, "the-file");
|
assert_eq!(full_path, "the-file");
|
||||||
@ -495,9 +513,9 @@ mod tests {
|
|||||||
// Since the worktree root is a file, searching for its name followed by a slash does
|
// Since the worktree root is a file, searching for its name followed by a slash does
|
||||||
// not match anything.
|
// not match anything.
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| f.spawn_search("thf/".into(), cx))
|
.update(cx, |f, cx| f.delegate_mut().spawn_search("thf/".into(), cx))
|
||||||
.await;
|
.await;
|
||||||
finder.read_with(cx, |f, _| assert_eq!(f.matches.len(), 0));
|
finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
@ -526,22 +544,31 @@ mod tests {
|
|||||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||||
|
|
||||||
let (_, finder) = cx.add_window(|cx| {
|
let (_, finder) = cx.add_window(|cx| {
|
||||||
FileFinderDelegate::new(workspace.read(cx).project().clone(), None, cx)
|
Picker::new(
|
||||||
|
FileFinderDelegate::new(
|
||||||
|
workspace.downgrade(),
|
||||||
|
workspace.read(cx).project().clone(),
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Run a search that matches two files with the same relative path.
|
// Run a search that matches two files with the same relative path.
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| f.spawn_search("a.t".into(), cx))
|
.update(cx, |f, cx| f.delegate_mut().spawn_search("a.t".into(), cx))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Can switch between different matches with the same relative path.
|
// Can switch between different matches with the same relative path.
|
||||||
finder.update(cx, |f, cx| {
|
finder.update(cx, |finder, cx| {
|
||||||
assert_eq!(f.matches.len(), 2);
|
let delegate = finder.delegate_mut();
|
||||||
assert_eq!(f.selected_index(), 0);
|
assert_eq!(delegate.matches.len(), 2);
|
||||||
f.set_selected_index(1, cx);
|
assert_eq!(delegate.selected_index(), 0);
|
||||||
assert_eq!(f.selected_index(), 1);
|
delegate.set_selected_index(1, cx);
|
||||||
f.set_selected_index(0, cx);
|
assert_eq!(delegate.selected_index(), 1);
|
||||||
assert_eq!(f.selected_index(), 0);
|
delegate.set_selected_index(0, cx);
|
||||||
|
assert_eq!(delegate.selected_index(), 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,16 +600,27 @@ mod tests {
|
|||||||
// so that one should be sorted earlier
|
// so that one should be sorted earlier
|
||||||
let b_path = Some(Arc::from(Path::new("/root/dir2/b.txt")));
|
let b_path = Some(Arc::from(Path::new("/root/dir2/b.txt")));
|
||||||
let (_, finder) = cx.add_window(|cx| {
|
let (_, finder) = cx.add_window(|cx| {
|
||||||
FileFinderDelegate::new(workspace.read(cx).project().clone(), b_path, cx)
|
Picker::new(
|
||||||
|
FileFinderDelegate::new(
|
||||||
|
workspace.downgrade(),
|
||||||
|
workspace.read(cx).project().clone(),
|
||||||
|
b_path,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| f.spawn_search("a.txt".into(), cx))
|
.update(cx, |f, cx| {
|
||||||
|
f.delegate_mut().spawn_search("a.txt".into(), cx)
|
||||||
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
finder.read_with(cx, |f, _| {
|
finder.read_with(cx, |f, _| {
|
||||||
assert_eq!(f.matches[0].path.as_ref(), Path::new("dir2/a.txt"));
|
let delegate = f.delegate();
|
||||||
assert_eq!(f.matches[1].path.as_ref(), Path::new("dir1/a.txt"));
|
assert_eq!(delegate.matches[0].path.as_ref(), Path::new("dir2/a.txt"));
|
||||||
|
assert_eq!(delegate.matches[1].path.as_ref(), Path::new("dir1/a.txt"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,14 +644,22 @@ mod tests {
|
|||||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||||
let (_, finder) = cx.add_window(|cx| {
|
let (_, finder) = cx.add_window(|cx| {
|
||||||
FileFinderDelegate::new(workspace.read(cx).project().clone(), None, cx)
|
Picker::new(
|
||||||
|
FileFinderDelegate::new(
|
||||||
|
workspace.downgrade(),
|
||||||
|
workspace.read(cx).project().clone(),
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
finder
|
finder
|
||||||
.update(cx, |f, cx| f.spawn_search("dir".into(), cx))
|
.update(cx, |f, cx| f.delegate_mut().spawn_search("dir".into(), cx))
|
||||||
.await;
|
.await;
|
||||||
cx.read(|cx| {
|
cx.read(|cx| {
|
||||||
let finder = finder.read(cx);
|
let finder = finder.read(cx);
|
||||||
assert_eq!(finder.matches.len(), 0);
|
assert_eq!(finder.delegate().matches.len(), 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use gpui::{
|
|||||||
use menu::{Cancel, Confirm};
|
use menu::{Cancel, Confirm};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use text::{Bias, Point};
|
use text::{Bias, Point};
|
||||||
use workspace::Workspace;
|
use workspace::{Modal, Workspace};
|
||||||
|
|
||||||
actions!(go_to_line, [Toggle]);
|
actions!(go_to_line, [Toggle]);
|
||||||
|
|
||||||
@ -65,11 +65,7 @@ impl GoToLine {
|
|||||||
.active_item(cx)
|
.active_item(cx)
|
||||||
.and_then(|active_item| active_item.downcast::<Editor>())
|
.and_then(|active_item| active_item.downcast::<Editor>())
|
||||||
{
|
{
|
||||||
workspace.toggle_modal(cx, |_, cx| {
|
workspace.toggle_modal(cx, |_, cx| cx.add_view(|cx| GoToLine::new(editor, cx)));
|
||||||
let view = cx.add_view(|cx| GoToLine::new(editor, cx));
|
|
||||||
cx.subscribe(&view, Self::on_event).detach();
|
|
||||||
view
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,17 +87,6 @@ impl GoToLine {
|
|||||||
cx.emit(Event::Dismissed);
|
cx.emit(Event::Dismissed);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
|
||||||
workspace: &mut Workspace,
|
|
||||||
_: ViewHandle<Self>,
|
|
||||||
event: &Event,
|
|
||||||
cx: &mut ViewContext<Workspace>,
|
|
||||||
) {
|
|
||||||
match event {
|
|
||||||
Event::Dismissed => workspace.dismiss_modal(cx),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_line_editor_event(
|
fn on_line_editor_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: ViewHandle<Editor>,
|
_: ViewHandle<Editor>,
|
||||||
@ -194,3 +179,9 @@ impl View for GoToLine {
|
|||||||
cx.focus(&self.line_editor);
|
cx.focus(&self.line_editor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Modal for GoToLine {
|
||||||
|
fn dismiss_on_event(event: &Self::Event) -> bool {
|
||||||
|
matches!(event, Event::Dismissed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,12 +4,9 @@ pub use active_buffer_language::ActiveBufferLanguage;
|
|||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
|
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{actions, elements::*, AppContext, ModelHandle, MouseState, ViewContext};
|
||||||
actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MouseState, View,
|
|
||||||
ViewContext, ViewHandle,
|
|
||||||
};
|
|
||||||
use language::{Buffer, LanguageRegistry};
|
use language::{Buffer, LanguageRegistry};
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -19,39 +16,49 @@ use workspace::{AppState, Workspace};
|
|||||||
actions!(language_selector, [Toggle]);
|
actions!(language_selector, [Toggle]);
|
||||||
|
|
||||||
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
Picker::<LanguageSelector>::init(cx);
|
Picker::<LanguageSelectorDelegate>::init(cx);
|
||||||
cx.add_action({
|
cx.add_action({
|
||||||
let language_registry = app_state.languages.clone();
|
let language_registry = app_state.languages.clone();
|
||||||
move |workspace, _: &Toggle, cx| {
|
move |workspace, _: &Toggle, cx| toggle(workspace, language_registry.clone(), cx)
|
||||||
LanguageSelector::toggle(workspace, language_registry.clone(), cx)
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Event {
|
fn toggle(
|
||||||
Dismissed,
|
workspace: &mut Workspace,
|
||||||
|
registry: Arc<LanguageRegistry>,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Option<()> {
|
||||||
|
let (_, buffer, _) = workspace
|
||||||
|
.active_item(cx)?
|
||||||
|
.act_as::<Editor>(cx)?
|
||||||
|
.read(cx)
|
||||||
|
.active_excerpt(cx)?;
|
||||||
|
workspace.toggle_modal(cx, |workspace, cx| {
|
||||||
|
cx.add_view(|cx| {
|
||||||
|
Picker::new(
|
||||||
|
LanguageSelectorDelegate::new(buffer, workspace.project().clone(), registry),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LanguageSelector {
|
pub struct LanguageSelectorDelegate {
|
||||||
buffer: ModelHandle<Buffer>,
|
buffer: ModelHandle<Buffer>,
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
candidates: Vec<StringMatchCandidate>,
|
candidates: Vec<StringMatchCandidate>,
|
||||||
matches: Vec<StringMatch>,
|
matches: Vec<StringMatch>,
|
||||||
picker: ViewHandle<Picker<Self>>,
|
|
||||||
selected_index: usize,
|
selected_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanguageSelector {
|
impl LanguageSelectorDelegate {
|
||||||
fn new(
|
fn new(
|
||||||
buffer: ModelHandle<Buffer>,
|
buffer: ModelHandle<Buffer>,
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let handle = cx.weak_handle();
|
|
||||||
let picker = cx.add_view(|cx| Picker::new("Select Language...", handle, cx));
|
|
||||||
|
|
||||||
let candidates = language_registry
|
let candidates = language_registry
|
||||||
.language_names()
|
.language_names()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -75,70 +82,21 @@ impl LanguageSelector {
|
|||||||
language_registry,
|
language_registry,
|
||||||
candidates,
|
candidates,
|
||||||
matches,
|
matches,
|
||||||
picker,
|
|
||||||
selected_index: 0,
|
selected_index: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle(
|
|
||||||
workspace: &mut Workspace,
|
|
||||||
registry: Arc<LanguageRegistry>,
|
|
||||||
cx: &mut ViewContext<Workspace>,
|
|
||||||
) {
|
|
||||||
if let Some((_, buffer, _)) = workspace
|
|
||||||
.active_item(cx)
|
|
||||||
.and_then(|active_item| active_item.act_as::<Editor>(cx))
|
|
||||||
.and_then(|editor| editor.read(cx).active_excerpt(cx))
|
|
||||||
{
|
|
||||||
workspace.toggle_modal(cx, |workspace, cx| {
|
|
||||||
let project = workspace.project().clone();
|
|
||||||
let this = cx.add_view(|cx| Self::new(buffer, project, registry, cx));
|
|
||||||
cx.subscribe(&this, Self::on_event).detach();
|
|
||||||
this
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
impl PickerDelegate for LanguageSelectorDelegate {
|
||||||
workspace: &mut Workspace,
|
fn placeholder_text(&self) -> Arc<str> {
|
||||||
_: ViewHandle<LanguageSelector>,
|
"Select a language...".into()
|
||||||
event: &Event,
|
|
||||||
cx: &mut ViewContext<Workspace>,
|
|
||||||
) {
|
|
||||||
match event {
|
|
||||||
Event::Dismissed => {
|
|
||||||
workspace.dismiss_modal(cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity for LanguageSelector {
|
|
||||||
type Event = Event;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl View for LanguageSelector {
|
|
||||||
fn ui_name() -> &'static str {
|
|
||||||
"LanguageSelector"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Element<Self> {
|
|
||||||
ChildView::new(&self.picker, cx).boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
|
||||||
if cx.is_self_focused() {
|
|
||||||
cx.focus(&self.picker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PickerDelegate for LanguageSelector {
|
|
||||||
fn match_count(&self) -> usize {
|
fn match_count(&self) -> usize {
|
||||||
self.matches.len()
|
self.matches.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm(&mut self, cx: &mut ViewContext<Self>) {
|
fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
|
||||||
if let Some(mat) = self.matches.get(self.selected_index) {
|
if let Some(mat) = self.matches.get(self.selected_index) {
|
||||||
let language_name = &self.candidates[mat.candidate_id].string;
|
let language_name = &self.candidates[mat.candidate_id].string;
|
||||||
let language = self.language_registry.language_for_name(language_name);
|
let language = self.language_registry.language_for_name(language_name);
|
||||||
@ -160,22 +118,24 @@ impl PickerDelegate for LanguageSelector {
|
|||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.emit(Event::Dismissed);
|
cx.emit(PickerEvent::Dismiss);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dismissed(&mut self, cx: &mut ViewContext<Self>) {
|
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
|
||||||
cx.emit(Event::Dismissed);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn selected_index(&self) -> usize {
|
fn selected_index(&self) -> usize {
|
||||||
self.selected_index
|
self.selected_index
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Self>) {
|
fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
|
||||||
self.selected_index = ix;
|
self.selected_index = ix;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) -> gpui::Task<()> {
|
fn update_matches(
|
||||||
|
&mut self,
|
||||||
|
query: String,
|
||||||
|
cx: &mut ViewContext<Picker<Self>>,
|
||||||
|
) -> gpui::Task<()> {
|
||||||
let background = cx.background().clone();
|
let background = cx.background().clone();
|
||||||
let candidates = self.candidates.clone();
|
let candidates = self.candidates.clone();
|
||||||
cx.spawn_weak(|this, mut cx| async move {
|
cx.spawn_weak(|this, mut cx| async move {
|
||||||
@ -204,10 +164,11 @@ impl PickerDelegate for LanguageSelector {
|
|||||||
|
|
||||||
if let Some(this) = this.upgrade(&cx) {
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.matches = matches;
|
let delegate = this.delegate_mut();
|
||||||
this.selected_index = this
|
delegate.matches = matches;
|
||||||
|
delegate.selected_index = delegate
|
||||||
.selected_index
|
.selected_index
|
||||||
.min(this.matches.len().saturating_sub(1));
|
.min(delegate.matches.len().saturating_sub(1));
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
.log_err();
|
.log_err();
|
||||||
|
@ -4,81 +4,24 @@ use editor::{
|
|||||||
};
|
};
|
||||||
use fuzzy::StringMatch;
|
use fuzzy::StringMatch;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, elements::*, geometry::vector::Vector2F, AnyViewHandle, AppContext, Entity,
|
actions, elements::*, geometry::vector::Vector2F, AppContext, MouseState, Task, ViewContext,
|
||||||
MouseState, Task, View, ViewContext, ViewHandle, WindowContext,
|
ViewHandle, WindowContext,
|
||||||
};
|
};
|
||||||
use language::Outline;
|
use language::Outline;
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::cmp::{self, Reverse};
|
use std::{
|
||||||
|
cmp::{self, Reverse},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
actions!(outline, [Toggle]);
|
actions!(outline, [Toggle]);
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(OutlineView::toggle);
|
cx.add_action(toggle);
|
||||||
Picker::<OutlineView>::init(cx);
|
OutlineView::init(cx);
|
||||||
}
|
|
||||||
|
|
||||||
struct OutlineView {
|
|
||||||
picker: ViewHandle<Picker<Self>>,
|
|
||||||
active_editor: ViewHandle<Editor>,
|
|
||||||
outline: Outline<Anchor>,
|
|
||||||
selected_match_index: usize,
|
|
||||||
prev_scroll_position: Option<Vector2F>,
|
|
||||||
matches: Vec<StringMatch>,
|
|
||||||
last_query: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Event {
|
|
||||||
Dismissed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Entity for OutlineView {
|
|
||||||
type Event = Event;
|
|
||||||
|
|
||||||
fn release(&mut self, cx: &mut AppContext) {
|
|
||||||
cx.update_window(self.active_editor.window_id(), |cx| {
|
|
||||||
self.restore_active_editor(cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl View for OutlineView {
|
|
||||||
fn ui_name() -> &'static str {
|
|
||||||
"OutlineView"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Element<Self> {
|
|
||||||
ChildView::new(&self.picker, cx).boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
|
||||||
if cx.is_self_focused() {
|
|
||||||
cx.focus(&self.picker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutlineView {
|
|
||||||
fn new(
|
|
||||||
outline: Outline<Anchor>,
|
|
||||||
editor: ViewHandle<Editor>,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) -> Self {
|
|
||||||
let handle = cx.weak_handle();
|
|
||||||
Self {
|
|
||||||
picker: cx.add_view(|cx| {
|
|
||||||
Picker::new("Search buffer symbols...", handle, cx).with_max_size(800., 1200.)
|
|
||||||
}),
|
|
||||||
last_query: Default::default(),
|
|
||||||
matches: Default::default(),
|
|
||||||
selected_match_index: 0,
|
|
||||||
prev_scroll_position: Some(editor.update(cx, |editor, cx| editor.scroll_position(cx))),
|
|
||||||
active_editor: editor,
|
|
||||||
outline,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
|
fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
|
||||||
@ -94,14 +37,42 @@ impl OutlineView {
|
|||||||
.outline(Some(cx.global::<Settings>().theme.editor.syntax.as_ref()));
|
.outline(Some(cx.global::<Settings>().theme.editor.syntax.as_ref()));
|
||||||
if let Some(outline) = outline {
|
if let Some(outline) = outline {
|
||||||
workspace.toggle_modal(cx, |_, cx| {
|
workspace.toggle_modal(cx, |_, cx| {
|
||||||
let view = cx.add_view(|cx| OutlineView::new(outline, editor, cx));
|
cx.add_view(|cx| {
|
||||||
cx.subscribe(&view, Self::on_event).detach();
|
OutlineView::new(OutlineViewDelegate::new(outline, editor, cx), cx)
|
||||||
view
|
.with_max_size(800., 1200.)
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OutlineView = Picker<OutlineViewDelegate>;
|
||||||
|
|
||||||
|
struct OutlineViewDelegate {
|
||||||
|
active_editor: ViewHandle<Editor>,
|
||||||
|
outline: Outline<Anchor>,
|
||||||
|
selected_match_index: usize,
|
||||||
|
prev_scroll_position: Option<Vector2F>,
|
||||||
|
matches: Vec<StringMatch>,
|
||||||
|
last_query: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutlineViewDelegate {
|
||||||
|
fn new(
|
||||||
|
outline: Outline<Anchor>,
|
||||||
|
editor: ViewHandle<Editor>,
|
||||||
|
cx: &mut ViewContext<OutlineView>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
last_query: Default::default(),
|
||||||
|
matches: Default::default(),
|
||||||
|
selected_match_index: 0,
|
||||||
|
prev_scroll_position: Some(editor.update(cx, |editor, cx| editor.scroll_position(cx))),
|
||||||
|
active_editor: editor,
|
||||||
|
outline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn restore_active_editor(&mut self, cx: &mut WindowContext) {
|
fn restore_active_editor(&mut self, cx: &mut WindowContext) {
|
||||||
self.active_editor.update(cx, |editor, cx| {
|
self.active_editor.update(cx, |editor, cx| {
|
||||||
editor.highlight_rows(None);
|
editor.highlight_rows(None);
|
||||||
@ -111,7 +82,7 @@ impl OutlineView {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_selected_index(&mut self, ix: usize, navigate: bool, cx: &mut ViewContext<Self>) {
|
fn set_selected_index(&mut self, ix: usize, navigate: bool, cx: &mut ViewContext<OutlineView>) {
|
||||||
self.selected_match_index = ix;
|
self.selected_match_index = ix;
|
||||||
if navigate && !self.matches.is_empty() {
|
if navigate && !self.matches.is_empty() {
|
||||||
let selected_match = &self.matches[self.selected_match_index];
|
let selected_match = &self.matches[self.selected_match_index];
|
||||||
@ -127,22 +98,14 @@ impl OutlineView {
|
|||||||
active_editor.request_autoscroll(Autoscroll::center(), cx);
|
active_editor.request_autoscroll(Autoscroll::center(), cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_event(
|
|
||||||
workspace: &mut Workspace,
|
|
||||||
_: ViewHandle<Self>,
|
|
||||||
event: &Event,
|
|
||||||
cx: &mut ViewContext<Workspace>,
|
|
||||||
) {
|
|
||||||
match event {
|
|
||||||
Event::Dismissed => workspace.dismiss_modal(cx),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PickerDelegate for OutlineView {
|
impl PickerDelegate for OutlineViewDelegate {
|
||||||
|
fn placeholder_text(&self) -> Arc<str> {
|
||||||
|
"Search buffer symbols...".into()
|
||||||
|
}
|
||||||
|
|
||||||
fn match_count(&self) -> usize {
|
fn match_count(&self) -> usize {
|
||||||
self.matches.len()
|
self.matches.len()
|
||||||
}
|
}
|
||||||
@ -151,7 +114,7 @@ impl PickerDelegate for OutlineView {
|
|||||||
self.selected_match_index
|
self.selected_match_index
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Self>) {
|
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<OutlineView>) {
|
||||||
self.set_selected_index(ix, true, cx);
|
self.set_selected_index(ix, true, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +122,7 @@ impl PickerDelegate for OutlineView {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) -> Task<()> {
|
fn update_matches(&mut self, query: String, cx: &mut ViewContext<OutlineView>) -> Task<()> {
|
||||||
let selected_index;
|
let selected_index;
|
||||||
if query.is_empty() {
|
if query.is_empty() {
|
||||||
self.restore_active_editor(cx);
|
self.restore_active_editor(cx);
|
||||||
@ -215,7 +178,7 @@ impl PickerDelegate for OutlineView {
|
|||||||
Task::ready(())
|
Task::ready(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm(&mut self, cx: &mut ViewContext<Self>) {
|
fn confirm(&mut self, cx: &mut ViewContext<OutlineView>) {
|
||||||
self.prev_scroll_position.take();
|
self.prev_scroll_position.take();
|
||||||
self.active_editor.update(cx, |active_editor, cx| {
|
self.active_editor.update(cx, |active_editor, cx| {
|
||||||
if let Some(rows) = active_editor.highlighted_rows() {
|
if let Some(rows) = active_editor.highlighted_rows() {
|
||||||
@ -226,12 +189,11 @@ impl PickerDelegate for OutlineView {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cx.emit(Event::Dismissed);
|
cx.emit(PickerEvent::Dismiss);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dismissed(&mut self, cx: &mut ViewContext<Self>) {
|
fn dismissed(&mut self, cx: &mut ViewContext<OutlineView>) {
|
||||||
self.restore_active_editor(cx);
|
self.restore_active_editor(cx);
|
||||||
cx.emit(Event::Dismissed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_match(
|
fn render_match(
|
||||||
|
@ -24,6 +24,7 @@ pub struct Picker<D: PickerDelegate> {
|
|||||||
max_size: Vector2F,
|
max_size: Vector2F,
|
||||||
theme: Arc<Mutex<Box<dyn Fn(&theme::Theme) -> theme::Picker>>>,
|
theme: Arc<Mutex<Box<dyn Fn(&theme::Theme) -> theme::Picker>>>,
|
||||||
confirmed: bool,
|
confirmed: bool,
|
||||||
|
pending_update_matches: Task<Option<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PickerDelegate: Sized + 'static {
|
pub trait PickerDelegate: Sized + 'static {
|
||||||
@ -184,6 +185,7 @@ impl<D: PickerDelegate> Picker<D> {
|
|||||||
max_size: vec2f(540., 420.),
|
max_size: vec2f(540., 420.),
|
||||||
theme,
|
theme,
|
||||||
confirmed: false,
|
confirmed: false,
|
||||||
|
pending_update_matches: Task::ready(None),
|
||||||
};
|
};
|
||||||
// TODO! How can the delegate notify the picker to update?
|
// TODO! How can the delegate notify the picker to update?
|
||||||
// cx.observe(&delegate, |_, _, cx| cx.notify()).detach();
|
// cx.observe(&delegate, |_, _, cx| cx.notify()).detach();
|
||||||
@ -238,22 +240,24 @@ impl<D: PickerDelegate> Picker<D> {
|
|||||||
|
|
||||||
pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
|
pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
|
||||||
let update = self.delegate.update_matches(query, cx);
|
let update = self.delegate.update_matches(query, cx);
|
||||||
cx.spawn_weak(|this, mut cx| async move {
|
self.matches_updated(cx);
|
||||||
|
self.pending_update_matches = cx.spawn_weak(|this, mut cx| async move {
|
||||||
update.await;
|
update.await;
|
||||||
this.upgrade(&cx)?
|
this.upgrade(&cx)?
|
||||||
.update(&mut cx, |this, cx| {
|
.update(&mut cx, |this, cx| this.matches_updated(cx))
|
||||||
let index = this.delegate.selected_index();
|
.log_err()
|
||||||
let target = if this.delegate.center_selection_after_match_updates() {
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matches_updated(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
let index = self.delegate.selected_index();
|
||||||
|
let target = if self.delegate.center_selection_after_match_updates() {
|
||||||
ScrollTarget::Center(index)
|
ScrollTarget::Center(index)
|
||||||
} else {
|
} else {
|
||||||
ScrollTarget::Show(index)
|
ScrollTarget::Show(index)
|
||||||
};
|
};
|
||||||
this.list_state.scroll_to(target);
|
self.list_state.scroll_to(target);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
|
||||||
.log_err()
|
|
||||||
})
|
|
||||||
.detach()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
|
pub fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
|
||||||
@ -306,7 +310,7 @@ impl<D: PickerDelegate> Picker<D> {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
|
pub fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
|
||||||
self.confirmed = true;
|
self.confirmed = true;
|
||||||
self.delegate.confirm(cx);
|
self.delegate.confirm(cx);
|
||||||
}
|
}
|
||||||
|
@ -1,90 +1,63 @@
|
|||||||
|
use anyhow::anyhow;
|
||||||
use editor::{
|
use editor::{
|
||||||
combine_syntax_and_fuzzy_match_highlights, scroll::autoscroll::Autoscroll,
|
combine_syntax_and_fuzzy_match_highlights, scroll::autoscroll::Autoscroll,
|
||||||
styled_runs_for_code_label, Bias, Editor,
|
styled_runs_for_code_label, Bias, Editor,
|
||||||
};
|
};
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MouseState, Task, View,
|
actions, elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext, WeakViewHandle,
|
||||||
ViewContext, ViewHandle,
|
|
||||||
};
|
};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||||
use project::{Project, Symbol};
|
use project::{Project, Symbol};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::{borrow::Cow, cmp::Reverse};
|
use std::{borrow::Cow, cmp::Reverse, sync::Arc};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
actions!(project_symbols, [Toggle]);
|
actions!(project_symbols, [Toggle]);
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(ProjectSymbolsView::toggle);
|
cx.add_action(toggle);
|
||||||
Picker::<ProjectSymbolsView>::init(cx);
|
ProjectSymbols::init(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProjectSymbolsView {
|
fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
|
||||||
picker: ViewHandle<Picker<Self>>,
|
workspace.toggle_modal(cx, |workspace, cx| {
|
||||||
|
let project = workspace.project().clone();
|
||||||
|
let workspace = cx.weak_handle();
|
||||||
|
cx.add_view(|cx| ProjectSymbols::new(ProjectSymbolsDelegate::new(workspace, project), cx))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ProjectSymbols = Picker<ProjectSymbolsDelegate>;
|
||||||
|
|
||||||
|
pub struct ProjectSymbolsDelegate {
|
||||||
|
workspace: WeakViewHandle<Workspace>,
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
selected_match_index: usize,
|
selected_match_index: usize,
|
||||||
symbols: Vec<Symbol>,
|
symbols: Vec<Symbol>,
|
||||||
visible_match_candidates: Vec<StringMatchCandidate>,
|
visible_match_candidates: Vec<StringMatchCandidate>,
|
||||||
external_match_candidates: Vec<StringMatchCandidate>,
|
external_match_candidates: Vec<StringMatchCandidate>,
|
||||||
show_worktree_root_name: bool,
|
show_worktree_root_name: bool,
|
||||||
pending_update: Task<()>,
|
|
||||||
matches: Vec<StringMatch>,
|
matches: Vec<StringMatch>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Event {
|
impl ProjectSymbolsDelegate {
|
||||||
Dismissed,
|
fn new(workspace: WeakViewHandle<Workspace>, project: ModelHandle<Project>) -> Self {
|
||||||
Selected(Symbol),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Entity for ProjectSymbolsView {
|
|
||||||
type Event = Event;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl View for ProjectSymbolsView {
|
|
||||||
fn ui_name() -> &'static str {
|
|
||||||
"ProjectSymbolsView"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Element<Self> {
|
|
||||||
ChildView::new(&self.picker, cx).boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
|
||||||
if cx.is_self_focused() {
|
|
||||||
cx.focus(&self.picker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProjectSymbolsView {
|
|
||||||
fn new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
|
|
||||||
let handle = cx.weak_handle();
|
|
||||||
Self {
|
Self {
|
||||||
|
workspace,
|
||||||
project,
|
project,
|
||||||
picker: cx.add_view(|cx| Picker::new("Search project symbols...", handle, cx)),
|
|
||||||
selected_match_index: 0,
|
selected_match_index: 0,
|
||||||
symbols: Default::default(),
|
symbols: Default::default(),
|
||||||
visible_match_candidates: Default::default(),
|
visible_match_candidates: Default::default(),
|
||||||
external_match_candidates: Default::default(),
|
external_match_candidates: Default::default(),
|
||||||
matches: Default::default(),
|
matches: Default::default(),
|
||||||
show_worktree_root_name: false,
|
show_worktree_root_name: false,
|
||||||
pending_update: Task::ready(()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
|
fn filter(&mut self, query: &str, cx: &mut ViewContext<ProjectSymbols>) {
|
||||||
workspace.toggle_modal(cx, |workspace, cx| {
|
|
||||||
let project = workspace.project().clone();
|
|
||||||
let symbols = cx.add_view(|cx| Self::new(project, cx));
|
|
||||||
cx.subscribe(&symbols, Self::on_event).detach();
|
|
||||||
symbols
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn filter(&mut self, query: &str, cx: &mut ViewContext<Self>) {
|
|
||||||
const MAX_MATCHES: usize = 100;
|
const MAX_MATCHES: usize = 100;
|
||||||
let mut visible_matches = cx.background_executor().block(fuzzy::match_strings(
|
let mut visible_matches = cx.background_executor().block(fuzzy::match_strings(
|
||||||
&self.visible_match_candidates,
|
&self.visible_match_candidates,
|
||||||
@ -125,25 +98,30 @@ impl ProjectSymbolsView {
|
|||||||
|
|
||||||
self.matches = matches;
|
self.matches = matches;
|
||||||
self.set_selected_index(0, cx);
|
self.set_selected_index(0, cx);
|
||||||
cx.notify();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
impl PickerDelegate for ProjectSymbolsDelegate {
|
||||||
workspace: &mut Workspace,
|
fn placeholder_text(&self) -> Arc<str> {
|
||||||
_: ViewHandle<Self>,
|
"Search project symbols...".into()
|
||||||
event: &Event,
|
}
|
||||||
cx: &mut ViewContext<Workspace>,
|
|
||||||
) {
|
|
||||||
match event {
|
|
||||||
Event::Dismissed => workspace.dismiss_modal(cx),
|
|
||||||
Event::Selected(symbol) => {
|
|
||||||
let buffer = workspace
|
|
||||||
.project()
|
|
||||||
.update(cx, |project, cx| project.open_buffer_for_symbol(symbol, cx));
|
|
||||||
|
|
||||||
|
fn confirm(&mut self, cx: &mut ViewContext<ProjectSymbols>) {
|
||||||
|
if let Some(symbol) = self
|
||||||
|
.matches
|
||||||
|
.get(self.selected_match_index)
|
||||||
|
.map(|mat| self.symbols[mat.candidate_id].clone())
|
||||||
|
{
|
||||||
|
let buffer = self.project.update(cx, |project, cx| {
|
||||||
|
project.open_buffer_for_symbol(&symbol, cx)
|
||||||
|
});
|
||||||
let symbol = symbol.clone();
|
let symbol = symbol.clone();
|
||||||
cx.spawn(|workspace, mut cx| async move {
|
let workspace = self.workspace.clone();
|
||||||
|
cx.spawn_weak(|_, mut cx| async move {
|
||||||
let buffer = buffer.await?;
|
let buffer = buffer.await?;
|
||||||
|
let workspace = workspace
|
||||||
|
.upgrade(&cx)
|
||||||
|
.ok_or_else(|| anyhow!("workspace was dropped"))?;
|
||||||
workspace.update(&mut cx, |workspace, cx| {
|
workspace.update(&mut cx, |workspace, cx| {
|
||||||
let position = buffer
|
let position = buffer
|
||||||
.read(cx)
|
.read(cx)
|
||||||
@ -159,26 +137,11 @@ impl ProjectSymbolsView {
|
|||||||
Ok::<_, anyhow::Error>(())
|
Ok::<_, anyhow::Error>(())
|
||||||
})
|
})
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
workspace.dismiss_modal(cx);
|
cx.emit(PickerEvent::Dismiss);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PickerDelegate for ProjectSymbolsView {
|
fn dismissed(&mut self, _cx: &mut ViewContext<ProjectSymbols>) {}
|
||||||
fn confirm(&mut self, cx: &mut ViewContext<Self>) {
|
|
||||||
if let Some(symbol) = self
|
|
||||||
.matches
|
|
||||||
.get(self.selected_match_index)
|
|
||||||
.map(|mat| self.symbols[mat.candidate_id].clone())
|
|
||||||
{
|
|
||||||
cx.emit(Event::Selected(symbol));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dismissed(&mut self, cx: &mut ViewContext<Self>) {
|
|
||||||
cx.emit(Event::Dismissed);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn match_count(&self) -> usize {
|
fn match_count(&self) -> usize {
|
||||||
self.matches.len()
|
self.matches.len()
|
||||||
@ -188,23 +151,23 @@ impl PickerDelegate for ProjectSymbolsView {
|
|||||||
self.selected_match_index
|
self.selected_match_index
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Self>) {
|
fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<ProjectSymbols>) {
|
||||||
self.selected_match_index = ix;
|
self.selected_match_index = ix;
|
||||||
cx.notify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) -> Task<()> {
|
fn update_matches(&mut self, query: String, cx: &mut ViewContext<ProjectSymbols>) -> Task<()> {
|
||||||
self.filter(&query, cx);
|
self.filter(&query, cx);
|
||||||
self.show_worktree_root_name = self.project.read(cx).visible_worktrees(cx).count() > 1;
|
self.show_worktree_root_name = self.project.read(cx).visible_worktrees(cx).count() > 1;
|
||||||
let symbols = self
|
let symbols = self
|
||||||
.project
|
.project
|
||||||
.update(cx, |project, cx| project.symbols(&query, cx));
|
.update(cx, |project, cx| project.symbols(&query, cx));
|
||||||
self.pending_update = cx.spawn_weak(|this, mut cx| async move {
|
cx.spawn_weak(|this, mut cx| async move {
|
||||||
let symbols = symbols.await.log_err();
|
let symbols = symbols.await.log_err();
|
||||||
if let Some(this) = this.upgrade(&cx) {
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
if let Some(symbols) = symbols {
|
if let Some(symbols) = symbols {
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
let project = this.project.read(cx);
|
let delegate = this.delegate_mut();
|
||||||
|
let project = delegate.project.read(cx);
|
||||||
let (visible_match_candidates, external_match_candidates) = symbols
|
let (visible_match_candidates, external_match_candidates) = symbols
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -221,16 +184,15 @@ impl PickerDelegate for ProjectSymbolsView {
|
|||||||
.map_or(false, |e| !e.is_ignored)
|
.map_or(false, |e| !e.is_ignored)
|
||||||
});
|
});
|
||||||
|
|
||||||
this.visible_match_candidates = visible_match_candidates;
|
delegate.visible_match_candidates = visible_match_candidates;
|
||||||
this.external_match_candidates = external_match_candidates;
|
delegate.external_match_candidates = external_match_candidates;
|
||||||
this.symbols = symbols;
|
delegate.symbols = symbols;
|
||||||
this.filter(&query, cx);
|
delegate.filter(&query, cx);
|
||||||
})
|
})
|
||||||
.log_err();
|
.log_err();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
Task::ready(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_match(
|
fn render_match(
|
||||||
@ -364,46 +326,53 @@ mod tests {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
|
|
||||||
// Create the project symbols view.
|
// Create the project symbols view.
|
||||||
let (_, symbols_view) = cx.add_window(|cx| ProjectSymbolsView::new(project.clone(), cx));
|
let symbols = cx.add_view(&workspace, |cx| {
|
||||||
let picker = symbols_view.read_with(cx, |symbols_view, _| symbols_view.picker.clone());
|
ProjectSymbols::new(
|
||||||
|
ProjectSymbolsDelegate::new(workspace.downgrade(), project.clone()),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// Spawn multiples updates before the first update completes,
|
// Spawn multiples updates before the first update completes,
|
||||||
// such that in the end, there are no matches. Testing for regression:
|
// such that in the end, there are no matches. Testing for regression:
|
||||||
// https://github.com/zed-industries/zed/issues/861
|
// https://github.com/zed-industries/zed/issues/861
|
||||||
picker.update(cx, |p, cx| {
|
symbols.update(cx, |p, cx| {
|
||||||
p.update_matches("o".to_string(), cx);
|
p.update_matches("o".to_string(), cx);
|
||||||
p.update_matches("on".to_string(), cx);
|
p.update_matches("on".to_string(), cx);
|
||||||
p.update_matches("onex".to_string(), cx);
|
p.update_matches("onex".to_string(), cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
symbols_view.read_with(cx, |symbols_view, _| {
|
symbols.read_with(cx, |symbols, _| {
|
||||||
assert_eq!(symbols_view.matches.len(), 0);
|
assert_eq!(symbols.delegate().matches.len(), 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Spawn more updates such that in the end, there are matches.
|
// Spawn more updates such that in the end, there are matches.
|
||||||
picker.update(cx, |p, cx| {
|
symbols.update(cx, |p, cx| {
|
||||||
p.update_matches("one".to_string(), cx);
|
p.update_matches("one".to_string(), cx);
|
||||||
p.update_matches("on".to_string(), cx);
|
p.update_matches("on".to_string(), cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
symbols_view.read_with(cx, |symbols_view, _| {
|
symbols.read_with(cx, |symbols, _| {
|
||||||
assert_eq!(symbols_view.matches.len(), 2);
|
let delegate = symbols.delegate();
|
||||||
assert_eq!(symbols_view.matches[0].string, "ton");
|
assert_eq!(delegate.matches.len(), 2);
|
||||||
assert_eq!(symbols_view.matches[1].string, "one");
|
assert_eq!(delegate.matches[0].string, "ton");
|
||||||
|
assert_eq!(delegate.matches[1].string, "one");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Spawn more updates such that in the end, there are again no matches.
|
// Spawn more updates such that in the end, there are again no matches.
|
||||||
picker.update(cx, |p, cx| {
|
symbols.update(cx, |p, cx| {
|
||||||
p.update_matches("o".to_string(), cx);
|
p.update_matches("o".to_string(), cx);
|
||||||
p.update_matches("".to_string(), cx);
|
p.update_matches("".to_string(), cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
symbols_view.read_with(cx, |symbols_view, _| {
|
symbols.read_with(cx, |symbols, _| {
|
||||||
assert_eq!(symbols_view.matches.len(), 0);
|
assert_eq!(symbols.delegate().matches.len(), 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,14 +3,15 @@ mod highlighted_workspace_location;
|
|||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
elements::{ChildView, Flex, ParentElement},
|
anyhow::Result,
|
||||||
AnyViewHandle, AppContext, Drawable, Element, Entity, Task, View, ViewContext, ViewHandle,
|
elements::{Flex, ParentElement},
|
||||||
|
AppContext, Drawable, Element, Task, ViewContext,
|
||||||
};
|
};
|
||||||
use highlighted_workspace_location::HighlightedWorkspaceLocation;
|
use highlighted_workspace_location::HighlightedWorkspaceLocation;
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use util::ResultExt;
|
use std::sync::Arc;
|
||||||
use workspace::{
|
use workspace::{
|
||||||
notifications::simple_message_notification::MessageNotification, OpenPaths, Workspace,
|
notifications::simple_message_notification::MessageNotification, OpenPaths, Workspace,
|
||||||
WorkspaceLocation, WORKSPACE_DB,
|
WorkspaceLocation, WORKSPACE_DB,
|
||||||
@ -19,32 +20,16 @@ use workspace::{
|
|||||||
actions!(projects, [OpenRecent]);
|
actions!(projects, [OpenRecent]);
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(RecentProjectsView::toggle);
|
cx.add_async_action(toggle);
|
||||||
Picker::<RecentProjectsView>::init(cx);
|
RecentProjects::init(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RecentProjectsView {
|
fn toggle(
|
||||||
picker: ViewHandle<Picker<Self>>,
|
_: &mut Workspace,
|
||||||
workspace_locations: Vec<WorkspaceLocation>,
|
_: &OpenRecent,
|
||||||
selected_match_index: usize,
|
cx: &mut ViewContext<Workspace>,
|
||||||
matches: Vec<StringMatch>,
|
) -> Option<Task<Result<()>>> {
|
||||||
}
|
Some(cx.spawn(|workspace, mut cx| async move {
|
||||||
|
|
||||||
impl RecentProjectsView {
|
|
||||||
fn new(workspace_locations: Vec<WorkspaceLocation>, cx: &mut ViewContext<Self>) -> Self {
|
|
||||||
let handle = cx.weak_handle();
|
|
||||||
Self {
|
|
||||||
picker: cx.add_view(|cx| {
|
|
||||||
Picker::new("Recent Projects...", handle, cx).with_max_size(800., 1200.)
|
|
||||||
}),
|
|
||||||
workspace_locations,
|
|
||||||
selected_match_index: 0,
|
|
||||||
matches: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toggle(_: &mut Workspace, _: &OpenRecent, cx: &mut ViewContext<Workspace>) {
|
|
||||||
cx.spawn(|workspace, mut cx| async move {
|
|
||||||
let workspace_locations: Vec<_> = cx
|
let workspace_locations: Vec<_> = cx
|
||||||
.background()
|
.background()
|
||||||
.spawn(async {
|
.spawn(async {
|
||||||
@ -58,64 +43,47 @@ impl RecentProjectsView {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
workspace
|
workspace.update(&mut cx, |workspace, cx| {
|
||||||
.update(&mut cx, |workspace, cx| {
|
|
||||||
if !workspace_locations.is_empty() {
|
if !workspace_locations.is_empty() {
|
||||||
workspace.toggle_modal(cx, |_, cx| {
|
workspace.toggle_modal(cx, |_, cx| {
|
||||||
let view = cx.add_view(|cx| Self::new(workspace_locations, cx));
|
cx.add_view(|cx| {
|
||||||
cx.subscribe(&view, Self::on_event).detach();
|
RecentProjects::new(RecentProjectsDelegate::new(workspace_locations), cx)
|
||||||
view
|
.with_max_size(800., 1200.)
|
||||||
|
})
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
workspace.show_notification(0, cx, |cx| {
|
workspace.show_notification(0, cx, |cx| {
|
||||||
cx.add_view(|_| {
|
cx.add_view(|_| MessageNotification::new_message("No recent projects to open."))
|
||||||
MessageNotification::new_message("No recent projects to open.")
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})?;
|
||||||
.log_err();
|
Ok(())
|
||||||
})
|
}))
|
||||||
.detach();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
type RecentProjects = Picker<RecentProjectsDelegate>;
|
||||||
workspace: &mut Workspace,
|
|
||||||
_: ViewHandle<Self>,
|
struct RecentProjectsDelegate {
|
||||||
event: &Event,
|
workspace_locations: Vec<WorkspaceLocation>,
|
||||||
cx: &mut ViewContext<Workspace>,
|
selected_match_index: usize,
|
||||||
) {
|
matches: Vec<StringMatch>,
|
||||||
match event {
|
}
|
||||||
Event::Dismissed => workspace.dismiss_modal(cx),
|
|
||||||
|
impl RecentProjectsDelegate {
|
||||||
|
fn new(workspace_locations: Vec<WorkspaceLocation>) -> Self {
|
||||||
|
Self {
|
||||||
|
workspace_locations,
|
||||||
|
selected_match_index: 0,
|
||||||
|
matches: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Event {
|
impl PickerDelegate for RecentProjectsDelegate {
|
||||||
Dismissed,
|
fn placeholder_text(&self) -> Arc<str> {
|
||||||
|
"Recent Projects...".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity for RecentProjectsView {
|
|
||||||
type Event = Event;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl View for RecentProjectsView {
|
|
||||||
fn ui_name() -> &'static str {
|
|
||||||
"RecentProjectsView"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Element<Self> {
|
|
||||||
ChildView::new(&self.picker, cx).boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
|
||||||
if cx.is_self_focused() {
|
|
||||||
cx.focus(&self.picker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PickerDelegate for RecentProjectsView {
|
|
||||||
fn match_count(&self) -> usize {
|
fn match_count(&self) -> usize {
|
||||||
self.matches.len()
|
self.matches.len()
|
||||||
}
|
}
|
||||||
@ -124,11 +92,15 @@ impl PickerDelegate for RecentProjectsView {
|
|||||||
self.selected_match_index
|
self.selected_match_index
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<Self>) {
|
fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<RecentProjects>) {
|
||||||
self.selected_match_index = ix;
|
self.selected_match_index = ix;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) -> gpui::Task<()> {
|
fn update_matches(
|
||||||
|
&mut self,
|
||||||
|
query: String,
|
||||||
|
cx: &mut ViewContext<RecentProjects>,
|
||||||
|
) -> gpui::Task<()> {
|
||||||
let query = query.trim_start();
|
let query = query.trim_start();
|
||||||
let smart_case = query.chars().any(|c| c.is_uppercase());
|
let smart_case = query.chars().any(|c| c.is_uppercase());
|
||||||
let candidates = self
|
let candidates = self
|
||||||
@ -166,19 +138,17 @@ impl PickerDelegate for RecentProjectsView {
|
|||||||
Task::ready(())
|
Task::ready(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm(&mut self, cx: &mut ViewContext<Self>) {
|
fn confirm(&mut self, cx: &mut ViewContext<RecentProjects>) {
|
||||||
if let Some(selected_match) = &self.matches.get(self.selected_index()) {
|
if let Some(selected_match) = &self.matches.get(self.selected_index()) {
|
||||||
let workspace_location = &self.workspace_locations[selected_match.candidate_id];
|
let workspace_location = &self.workspace_locations[selected_match.candidate_id];
|
||||||
cx.dispatch_global_action(OpenPaths {
|
cx.dispatch_global_action(OpenPaths {
|
||||||
paths: workspace_location.paths().as_ref().clone(),
|
paths: workspace_location.paths().as_ref().clone(),
|
||||||
});
|
});
|
||||||
cx.emit(Event::Dismissed);
|
cx.emit(PickerEvent::Dismiss);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dismissed(&mut self, cx: &mut ViewContext<Self>) {
|
fn dismissed(&mut self, _cx: &mut ViewContext<RecentProjects>) {}
|
||||||
cx.emit(Event::Dismissed);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_match(
|
fn render_match(
|
||||||
&self,
|
&self,
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
|
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{actions, elements::*, AppContext, Drawable, Element, MouseState, ViewContext};
|
||||||
actions, elements::*, AnyViewHandle, AppContext, Drawable, Element, Entity, MouseState, View,
|
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||||
ViewContext, ViewHandle,
|
|
||||||
};
|
|
||||||
use picker::{Picker, PickerDelegate};
|
|
||||||
use settings::{settings_file::SettingsFile, Settings};
|
use settings::{settings_file::SettingsFile, Settings};
|
||||||
use staff_mode::StaffMode;
|
use staff_mode::StaffMode;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -11,36 +8,50 @@ use theme::{Theme, ThemeMeta, ThemeRegistry};
|
|||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::{AppState, Workspace};
|
use workspace::{AppState, Workspace};
|
||||||
|
|
||||||
pub struct ThemeSelector {
|
actions!(theme_selector, [Toggle, Reload]);
|
||||||
|
|
||||||
|
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
|
cx.add_action({
|
||||||
|
let theme_registry = app_state.themes.clone();
|
||||||
|
move |workspace, _: &Toggle, cx| toggle(workspace, theme_registry.clone(), cx)
|
||||||
|
});
|
||||||
|
ThemeSelector::init(cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle(workspace: &mut Workspace, themes: Arc<ThemeRegistry>, cx: &mut ViewContext<Workspace>) {
|
||||||
|
workspace.toggle_modal(cx, |_, cx| {
|
||||||
|
cx.add_view(|cx| ThemeSelector::new(ThemeSelectorDelegate::new(themes, cx), cx))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub fn reload(themes: Arc<ThemeRegistry>, cx: &mut AppContext) {
|
||||||
|
let current_theme_name = cx.global::<Settings>().theme.meta.name.clone();
|
||||||
|
themes.clear();
|
||||||
|
match themes.get(¤t_theme_name) {
|
||||||
|
Ok(theme) => {
|
||||||
|
ThemeSelectorDelegate::set_theme(theme, cx);
|
||||||
|
log::info!("reloaded theme {}", current_theme_name);
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
log::error!("failed to load theme {}: {:?}", current_theme_name, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ThemeSelector = Picker<ThemeSelectorDelegate>;
|
||||||
|
|
||||||
|
pub struct ThemeSelectorDelegate {
|
||||||
registry: Arc<ThemeRegistry>,
|
registry: Arc<ThemeRegistry>,
|
||||||
theme_data: Vec<ThemeMeta>,
|
theme_data: Vec<ThemeMeta>,
|
||||||
matches: Vec<StringMatch>,
|
matches: Vec<StringMatch>,
|
||||||
original_theme: Arc<Theme>,
|
original_theme: Arc<Theme>,
|
||||||
picker: ViewHandle<Picker<Self>>,
|
|
||||||
selection_completed: bool,
|
selection_completed: bool,
|
||||||
selected_index: usize,
|
selected_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
actions!(theme_selector, [Toggle, Reload]);
|
impl ThemeSelectorDelegate {
|
||||||
|
fn new(registry: Arc<ThemeRegistry>, cx: &mut ViewContext<ThemeSelector>) -> Self {
|
||||||
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
|
||||||
Picker::<ThemeSelector>::init(cx);
|
|
||||||
cx.add_action({
|
|
||||||
let theme_registry = app_state.themes.clone();
|
|
||||||
move |workspace, _: &Toggle, cx| {
|
|
||||||
ThemeSelector::toggle(workspace, theme_registry.clone(), cx)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Event {
|
|
||||||
Dismissed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ThemeSelector {
|
|
||||||
fn new(registry: Arc<ThemeRegistry>, cx: &mut ViewContext<Self>) -> Self {
|
|
||||||
let handle = cx.weak_handle();
|
|
||||||
let picker = cx.add_view(|cx| Picker::new("Select Theme...", handle, cx));
|
|
||||||
let settings = cx.global::<Settings>();
|
let settings = cx.global::<Settings>();
|
||||||
|
|
||||||
let original_theme = settings.theme.clone();
|
let original_theme = settings.theme.clone();
|
||||||
@ -62,7 +73,6 @@ impl ThemeSelector {
|
|||||||
registry,
|
registry,
|
||||||
theme_data: theme_names,
|
theme_data: theme_names,
|
||||||
matches,
|
matches,
|
||||||
picker,
|
|
||||||
original_theme: original_theme.clone(),
|
original_theme: original_theme.clone(),
|
||||||
selected_index: 0,
|
selected_index: 0,
|
||||||
selection_completed: false,
|
selection_completed: false,
|
||||||
@ -71,34 +81,7 @@ impl ThemeSelector {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle(
|
fn show_selected_theme(&mut self, cx: &mut ViewContext<ThemeSelector>) {
|
||||||
workspace: &mut Workspace,
|
|
||||||
themes: Arc<ThemeRegistry>,
|
|
||||||
cx: &mut ViewContext<Workspace>,
|
|
||||||
) {
|
|
||||||
workspace.toggle_modal(cx, |_, cx| {
|
|
||||||
let this = cx.add_view(|cx| Self::new(themes, cx));
|
|
||||||
cx.subscribe(&this, Self::on_event).detach();
|
|
||||||
this
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
pub fn reload(themes: Arc<ThemeRegistry>, cx: &mut AppContext) {
|
|
||||||
let current_theme_name = cx.global::<Settings>().theme.meta.name.clone();
|
|
||||||
themes.clear();
|
|
||||||
match themes.get(¤t_theme_name) {
|
|
||||||
Ok(theme) => {
|
|
||||||
Self::set_theme(theme, cx);
|
|
||||||
log::info!("reloaded theme {}", current_theme_name);
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
log::error!("failed to load theme {}: {:?}", current_theme_name, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn show_selected_theme(&mut self, cx: &mut ViewContext<Self>) {
|
|
||||||
if let Some(mat) = self.matches.get(self.selected_index) {
|
if let Some(mat) = self.matches.get(self.selected_index) {
|
||||||
match self.registry.get(&mat.string) {
|
match self.registry.get(&mat.string) {
|
||||||
Ok(theme) => {
|
Ok(theme) => {
|
||||||
@ -119,19 +102,6 @@ impl ThemeSelector {
|
|||||||
.unwrap_or(self.selected_index);
|
.unwrap_or(self.selected_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
|
||||||
workspace: &mut Workspace,
|
|
||||||
_: ViewHandle<ThemeSelector>,
|
|
||||||
event: &Event,
|
|
||||||
cx: &mut ViewContext<Workspace>,
|
|
||||||
) {
|
|
||||||
match event {
|
|
||||||
Event::Dismissed => {
|
|
||||||
workspace.dismiss_modal(cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_theme(theme: Arc<Theme>, cx: &mut AppContext) {
|
fn set_theme(theme: Arc<Theme>, cx: &mut AppContext) {
|
||||||
cx.update_global::<Settings, _, _>(|settings, cx| {
|
cx.update_global::<Settings, _, _>(|settings, cx| {
|
||||||
settings.theme = theme;
|
settings.theme = theme;
|
||||||
@ -140,12 +110,16 @@ impl ThemeSelector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PickerDelegate for ThemeSelector {
|
impl PickerDelegate for ThemeSelectorDelegate {
|
||||||
|
fn placeholder_text(&self) -> Arc<str> {
|
||||||
|
"Select Theme...".into()
|
||||||
|
}
|
||||||
|
|
||||||
fn match_count(&self) -> usize {
|
fn match_count(&self) -> usize {
|
||||||
self.matches.len()
|
self.matches.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm(&mut self, cx: &mut ViewContext<Self>) {
|
fn confirm(&mut self, cx: &mut ViewContext<ThemeSelector>) {
|
||||||
self.selection_completed = true;
|
self.selection_completed = true;
|
||||||
|
|
||||||
let theme_name = cx.global::<Settings>().theme.meta.name.clone();
|
let theme_name = cx.global::<Settings>().theme.meta.name.clone();
|
||||||
@ -153,27 +127,30 @@ impl PickerDelegate for ThemeSelector {
|
|||||||
settings_content.theme = Some(theme_name);
|
settings_content.theme = Some(theme_name);
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.emit(Event::Dismissed);
|
cx.emit(PickerEvent::Dismiss);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dismissed(&mut self, cx: &mut ViewContext<Self>) {
|
fn dismissed(&mut self, cx: &mut ViewContext<ThemeSelector>) {
|
||||||
if !self.selection_completed {
|
if !self.selection_completed {
|
||||||
Self::set_theme(self.original_theme.clone(), cx);
|
Self::set_theme(self.original_theme.clone(), cx);
|
||||||
self.selection_completed = true;
|
self.selection_completed = true;
|
||||||
}
|
}
|
||||||
cx.emit(Event::Dismissed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn selected_index(&self) -> usize {
|
fn selected_index(&self) -> usize {
|
||||||
self.selected_index
|
self.selected_index
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Self>) {
|
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<ThemeSelector>) {
|
||||||
self.selected_index = ix;
|
self.selected_index = ix;
|
||||||
self.show_selected_theme(cx);
|
self.show_selected_theme(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) -> gpui::Task<()> {
|
fn update_matches(
|
||||||
|
&mut self,
|
||||||
|
query: String,
|
||||||
|
cx: &mut ViewContext<ThemeSelector>,
|
||||||
|
) -> gpui::Task<()> {
|
||||||
let background = cx.background().clone();
|
let background = cx.background().clone();
|
||||||
let candidates = self
|
let candidates = self
|
||||||
.theme_data
|
.theme_data
|
||||||
@ -212,12 +189,12 @@ impl PickerDelegate for ThemeSelector {
|
|||||||
|
|
||||||
if let Some(this) = this.upgrade(&cx) {
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.matches = matches;
|
let delegate = this.delegate_mut();
|
||||||
this.selected_index = this
|
delegate.matches = matches;
|
||||||
|
delegate.selected_index = delegate
|
||||||
.selected_index
|
.selected_index
|
||||||
.min(this.matches.len().saturating_sub(1));
|
.min(delegate.matches.len().saturating_sub(1));
|
||||||
this.show_selected_theme(cx);
|
delegate.show_selected_theme(cx);
|
||||||
cx.notify();
|
|
||||||
})
|
})
|
||||||
.log_err();
|
.log_err();
|
||||||
}
|
}
|
||||||
@ -243,29 +220,3 @@ impl PickerDelegate for ThemeSelector {
|
|||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity for ThemeSelector {
|
|
||||||
type Event = Event;
|
|
||||||
|
|
||||||
fn release(&mut self, cx: &mut AppContext) {
|
|
||||||
if !self.selection_completed {
|
|
||||||
Self::set_theme(self.original_theme.clone(), cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl View for ThemeSelector {
|
|
||||||
fn ui_name() -> &'static str {
|
|
||||||
"ThemeSelector"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Element<Self> {
|
|
||||||
ChildView::new(&self.picker, cx).boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
|
||||||
if cx.is_self_focused() {
|
|
||||||
cx.focus(&self.picker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,92 +1,59 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
|
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
elements::{ChildView, Drawable as _, Label},
|
elements::{Drawable as _, Label},
|
||||||
AnyViewHandle, AppContext, Entity, View, ViewContext, ViewHandle,
|
AppContext, Task, ViewContext,
|
||||||
};
|
};
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||||
use settings::{settings_file::SettingsFile, BaseKeymap, Settings};
|
use settings::{settings_file::SettingsFile, BaseKeymap, Settings};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub struct BaseKeymapSelector {
|
|
||||||
matches: Vec<StringMatch>,
|
|
||||||
picker: ViewHandle<Picker<Self>>,
|
|
||||||
selected_index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
actions!(welcome, [ToggleBaseKeymapSelector]);
|
actions!(welcome, [ToggleBaseKeymapSelector]);
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
Picker::<BaseKeymapSelector>::init(cx);
|
cx.add_action(toggle);
|
||||||
cx.add_action({
|
BaseKeymapSelector::init(cx);
|
||||||
move |workspace, _: &ToggleBaseKeymapSelector, cx| BaseKeymapSelector::toggle(workspace, cx)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Event {
|
fn toggle(
|
||||||
Dismissed,
|
workspace: &mut Workspace,
|
||||||
}
|
_: &ToggleBaseKeymapSelector,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
impl BaseKeymapSelector {
|
) {
|
||||||
fn toggle(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
|
|
||||||
workspace.toggle_modal(cx, |_, cx| {
|
workspace.toggle_modal(cx, |_, cx| {
|
||||||
let this = cx.add_view(|cx| Self::new(cx));
|
cx.add_view(|cx| BaseKeymapSelector::new(BaseKeymapSelectorDelegate::new(cx), cx))
|
||||||
cx.subscribe(&this, Self::on_event).detach();
|
|
||||||
this
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(cx: &mut ViewContext<Self>) -> Self {
|
pub type BaseKeymapSelector = Picker<BaseKeymapSelectorDelegate>;
|
||||||
|
|
||||||
|
pub struct BaseKeymapSelectorDelegate {
|
||||||
|
matches: Vec<StringMatch>,
|
||||||
|
selected_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BaseKeymapSelectorDelegate {
|
||||||
|
fn new(cx: &mut ViewContext<BaseKeymapSelector>) -> Self {
|
||||||
let base = cx.global::<Settings>().base_keymap;
|
let base = cx.global::<Settings>().base_keymap;
|
||||||
let selected_index = BaseKeymap::OPTIONS
|
let selected_index = BaseKeymap::OPTIONS
|
||||||
.iter()
|
.iter()
|
||||||
.position(|(_, value)| *value == base)
|
.position(|(_, value)| *value == base)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let this = cx.weak_handle();
|
|
||||||
Self {
|
Self {
|
||||||
picker: cx.add_view(|cx| Picker::new("Select a base keymap", this, cx)),
|
|
||||||
matches: Vec::new(),
|
matches: Vec::new(),
|
||||||
selected_index,
|
selected_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
|
||||||
workspace: &mut Workspace,
|
|
||||||
_: ViewHandle<BaseKeymapSelector>,
|
|
||||||
event: &Event,
|
|
||||||
cx: &mut ViewContext<Workspace>,
|
|
||||||
) {
|
|
||||||
match event {
|
|
||||||
Event::Dismissed => {
|
|
||||||
workspace.dismiss_modal(cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity for BaseKeymapSelector {
|
impl PickerDelegate for BaseKeymapSelectorDelegate {
|
||||||
type Event = Event;
|
fn placeholder_text(&self) -> Arc<str> {
|
||||||
|
"Select a base keymap...".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View for BaseKeymapSelector {
|
|
||||||
fn ui_name() -> &'static str {
|
|
||||||
"BaseKeymapSelector"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> gpui::Element<Self> {
|
|
||||||
ChildView::new(&self.picker, cx).boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
|
||||||
if cx.is_self_focused() {
|
|
||||||
cx.focus(&self.picker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PickerDelegate for BaseKeymapSelector {
|
|
||||||
fn match_count(&self) -> usize {
|
fn match_count(&self) -> usize {
|
||||||
self.matches.len()
|
self.matches.len()
|
||||||
}
|
}
|
||||||
@ -95,11 +62,15 @@ impl PickerDelegate for BaseKeymapSelector {
|
|||||||
self.selected_index
|
self.selected_index
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Self>) {
|
fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<BaseKeymapSelector>) {
|
||||||
self.selected_index = ix;
|
self.selected_index = ix;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) -> gpui::Task<()> {
|
fn update_matches(
|
||||||
|
&mut self,
|
||||||
|
query: String,
|
||||||
|
cx: &mut ViewContext<BaseKeymapSelector>,
|
||||||
|
) -> Task<()> {
|
||||||
let background = cx.background().clone();
|
let background = cx.background().clone();
|
||||||
let candidates = BaseKeymap::names()
|
let candidates = BaseKeymap::names()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -135,29 +106,27 @@ impl PickerDelegate for BaseKeymapSelector {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(this) = this.upgrade(&cx) {
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, _| {
|
||||||
this.matches = matches;
|
let delegate = this.delegate_mut();
|
||||||
this.selected_index = this
|
delegate.matches = matches;
|
||||||
|
delegate.selected_index = delegate
|
||||||
.selected_index
|
.selected_index
|
||||||
.min(this.matches.len().saturating_sub(1));
|
.min(delegate.matches.len().saturating_sub(1));
|
||||||
cx.notify();
|
|
||||||
})
|
})
|
||||||
.log_err();
|
.log_err();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm(&mut self, cx: &mut ViewContext<Self>) {
|
fn confirm(&mut self, cx: &mut ViewContext<BaseKeymapSelector>) {
|
||||||
if let Some(selection) = self.matches.get(self.selected_index) {
|
if let Some(selection) = self.matches.get(self.selected_index) {
|
||||||
let base_keymap = BaseKeymap::from_names(&selection.string);
|
let base_keymap = BaseKeymap::from_names(&selection.string);
|
||||||
SettingsFile::update(cx, move |settings| settings.base_keymap = Some(base_keymap));
|
SettingsFile::update(cx, move |settings| settings.base_keymap = Some(base_keymap));
|
||||||
}
|
}
|
||||||
cx.emit(Event::Dismissed);
|
cx.emit(PickerEvent::Dismiss);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dismissed(&mut self, cx: &mut ViewContext<Self>) {
|
fn dismissed(&mut self, _cx: &mut ViewContext<BaseKeymapSelector>) {}
|
||||||
cx.emit(Event::Dismissed)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_match(
|
fn render_match(
|
||||||
&self,
|
&self,
|
||||||
|
@ -520,7 +520,7 @@ async fn watch_themes(
|
|||||||
.await
|
.await
|
||||||
.log_err()?;
|
.log_err()?;
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
cx.update(|cx| theme_selector::ThemeSelector::reload(themes.clone(), cx))
|
cx.update(|cx| theme_selector::reload(themes.clone(), cx))
|
||||||
} else {
|
} else {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"build script failed {}",
|
"build script failed {}",
|
||||||
|
Loading…
Reference in New Issue
Block a user