mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 10:40:54 +00:00
Allow actions to be deserialized from JSON
Introduce separate macro for implementing 'internal' actions which are not intended to be loaded from keymaps.
This commit is contained in:
parent
1778622960
commit
fd4b81c8fc
26 changed files with 559 additions and 335 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -4269,6 +4269,7 @@ dependencies = [
|
|||
"log",
|
||||
"postage",
|
||||
"project",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"settings",
|
||||
"theme",
|
||||
|
@ -5748,6 +5749,7 @@ dependencies = [
|
|||
"language",
|
||||
"log",
|
||||
"project",
|
||||
"serde",
|
||||
"settings",
|
||||
"util",
|
||||
"workspace",
|
||||
|
@ -6133,6 +6135,7 @@ dependencies = [
|
|||
"sha-1 0.9.6",
|
||||
"sqlx 0.5.5",
|
||||
"surf",
|
||||
"theme",
|
||||
"tide",
|
||||
"tide-compress",
|
||||
"time 0.2.27",
|
||||
|
|
|
@ -22,7 +22,7 @@ use gpui::{
|
|||
executor,
|
||||
fonts::{self, HighlightStyle, TextStyle},
|
||||
geometry::vector::{vec2f, Vector2F},
|
||||
impl_actions,
|
||||
impl_actions, impl_internal_actions,
|
||||
keymap::Binding,
|
||||
platform::CursorStyle,
|
||||
text_layout, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity,
|
||||
|
@ -66,8 +66,11 @@ const MAX_LINE_LEN: usize = 1024;
|
|||
const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
|
||||
const MAX_SELECTION_HISTORY_LEN: usize = 1024;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SelectNext(pub bool);
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct SelectNext {
|
||||
#[serde(default)]
|
||||
pub replace_newest: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GoToDiagnostic(pub Direction);
|
||||
|
@ -81,44 +84,26 @@ pub struct Select(pub SelectPhase);
|
|||
#[derive(Clone)]
|
||||
pub struct Input(pub String);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Tab(pub Direction);
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct SelectToBeginningOfLine {
|
||||
#[serde(default)]
|
||||
stop_at_soft_wraps: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct SelectToEndOfLine {
|
||||
#[serde(default)]
|
||||
stop_at_soft_wraps: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ToggleCodeActions(pub bool);
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct ToggleCodeActions(#[serde(default)] pub bool);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConfirmCompletion(pub Option<usize>);
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct ConfirmCompletion(#[serde(default)] pub Option<usize>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConfirmCodeAction(pub Option<usize>);
|
||||
|
||||
impl_actions!(
|
||||
editor,
|
||||
[
|
||||
SelectNext,
|
||||
GoToDiagnostic,
|
||||
Scroll,
|
||||
Select,
|
||||
Input,
|
||||
Tab,
|
||||
SelectToBeginningOfLine,
|
||||
SelectToEndOfLine,
|
||||
ToggleCodeActions,
|
||||
ConfirmCompletion,
|
||||
ConfirmCodeAction,
|
||||
]
|
||||
);
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct ConfirmCodeAction(#[serde(default)] pub Option<usize>);
|
||||
|
||||
actions!(
|
||||
editor,
|
||||
|
@ -127,6 +112,8 @@ actions!(
|
|||
Backspace,
|
||||
Delete,
|
||||
Newline,
|
||||
GoToNextDiagnostic,
|
||||
GoToPrevDiagnostic,
|
||||
Indent,
|
||||
Outdent,
|
||||
DeleteLine,
|
||||
|
@ -172,6 +159,8 @@ actions!(
|
|||
SplitSelectionIntoLines,
|
||||
AddSelectionAbove,
|
||||
AddSelectionBelow,
|
||||
Tab,
|
||||
TabPrev,
|
||||
ToggleComments,
|
||||
SelectLargerSyntaxNode,
|
||||
SelectSmallerSyntaxNode,
|
||||
|
@ -193,6 +182,20 @@ actions!(
|
|||
]
|
||||
);
|
||||
|
||||
impl_actions!(
|
||||
editor,
|
||||
[
|
||||
SelectNext,
|
||||
SelectToBeginningOfLine,
|
||||
SelectToEndOfLine,
|
||||
ToggleCodeActions,
|
||||
ConfirmCompletion,
|
||||
ConfirmCodeAction,
|
||||
]
|
||||
);
|
||||
|
||||
impl_internal_actions!(editor, [Scroll, Select, Input]);
|
||||
|
||||
enum DocumentHighlightRead {}
|
||||
enum DocumentHighlightWrite {}
|
||||
|
||||
|
@ -226,8 +229,8 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
Some("Editor && showing_code_actions"),
|
||||
),
|
||||
Binding::new("enter", ConfirmRename, Some("Editor && renaming")),
|
||||
Binding::new("tab", Tab(Direction::Next), Some("Editor")),
|
||||
Binding::new("shift-tab", Tab(Direction::Prev), Some("Editor")),
|
||||
Binding::new("tab", Tab, Some("Editor")),
|
||||
Binding::new("shift-tab", TabPrev, Some("Editor")),
|
||||
Binding::new(
|
||||
"tab",
|
||||
ConfirmCompletion(None),
|
||||
|
@ -346,8 +349,20 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
Binding::new("cmd-ctrl-p", AddSelectionAbove, Some("Editor")),
|
||||
Binding::new("cmd-alt-down", AddSelectionBelow, Some("Editor")),
|
||||
Binding::new("cmd-ctrl-n", AddSelectionBelow, Some("Editor")),
|
||||
Binding::new("cmd-d", SelectNext(false), Some("Editor")),
|
||||
Binding::new("cmd-k cmd-d", SelectNext(true), Some("Editor")),
|
||||
Binding::new(
|
||||
"cmd-d",
|
||||
SelectNext {
|
||||
replace_newest: false,
|
||||
},
|
||||
Some("Editor"),
|
||||
),
|
||||
Binding::new(
|
||||
"cmd-k cmd-d",
|
||||
SelectNext {
|
||||
replace_newest: true,
|
||||
},
|
||||
Some("Editor"),
|
||||
),
|
||||
Binding::new("cmd-/", ToggleComments, Some("Editor")),
|
||||
Binding::new("alt-up", SelectLargerSyntaxNode, Some("Editor")),
|
||||
Binding::new("ctrl-w", SelectLargerSyntaxNode, Some("Editor")),
|
||||
|
@ -355,8 +370,8 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
Binding::new("ctrl-shift-W", SelectSmallerSyntaxNode, Some("Editor")),
|
||||
Binding::new("cmd-u", UndoSelection, Some("Editor")),
|
||||
Binding::new("cmd-shift-U", RedoSelection, Some("Editor")),
|
||||
Binding::new("f8", GoToDiagnostic(Direction::Next), Some("Editor")),
|
||||
Binding::new("shift-f8", GoToDiagnostic(Direction::Prev), Some("Editor")),
|
||||
Binding::new("f8", GoToNextDiagnostic, Some("Editor")),
|
||||
Binding::new("shift-f8", GoToPrevDiagnostic, Some("Editor")),
|
||||
Binding::new("f2", Rename, Some("Editor")),
|
||||
Binding::new("f12", GoToDefinition, Some("Editor")),
|
||||
Binding::new("alt-shift-f12", FindAllReferences, Some("Editor")),
|
||||
|
@ -435,7 +450,8 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
cx.add_action(Editor::move_to_enclosing_bracket);
|
||||
cx.add_action(Editor::undo_selection);
|
||||
cx.add_action(Editor::redo_selection);
|
||||
cx.add_action(Editor::go_to_diagnostic);
|
||||
cx.add_action(Editor::go_to_next_diagnostic);
|
||||
cx.add_action(Editor::go_to_prev_diagnostic);
|
||||
cx.add_action(Editor::go_to_definition);
|
||||
cx.add_action(Editor::page_up);
|
||||
cx.add_action(Editor::page_down);
|
||||
|
@ -2940,8 +2956,8 @@ impl Editor {
|
|||
self.move_to_snippet_tabstop(Bias::Right, cx)
|
||||
}
|
||||
|
||||
pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.move_to_snippet_tabstop(Bias::Left, cx);
|
||||
pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||
self.move_to_snippet_tabstop(Bias::Left, cx)
|
||||
}
|
||||
|
||||
pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
|
||||
|
@ -3046,54 +3062,46 @@ impl Editor {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn tab(&mut self, &Tab(direction): &Tab, cx: &mut ViewContext<Self>) {
|
||||
match direction {
|
||||
Direction::Prev => {
|
||||
if !self.snippet_stack.is_empty() {
|
||||
self.move_to_prev_snippet_tabstop(cx);
|
||||
return;
|
||||
}
|
||||
pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
|
||||
if self.move_to_prev_snippet_tabstop(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.outdent(&Outdent, cx);
|
||||
}
|
||||
Direction::Next => {
|
||||
if self.move_to_next_snippet_tabstop(cx) {
|
||||
return;
|
||||
}
|
||||
self.outdent(&Outdent, cx);
|
||||
}
|
||||
|
||||
let mut selections = self.local_selections::<Point>(cx);
|
||||
if selections.iter().all(|s| s.is_empty()) {
|
||||
self.transact(cx, |this, cx| {
|
||||
this.buffer.update(cx, |buffer, cx| {
|
||||
for selection in &mut selections {
|
||||
let language_name =
|
||||
buffer.language_at(selection.start, cx).map(|l| l.name());
|
||||
let tab_size =
|
||||
cx.global::<Settings>().tab_size(language_name.as_deref());
|
||||
let char_column = buffer
|
||||
.read(cx)
|
||||
.text_for_range(
|
||||
Point::new(selection.start.row, 0)..selection.start,
|
||||
)
|
||||
.flat_map(str::chars)
|
||||
.count();
|
||||
let chars_to_next_tab_stop =
|
||||
tab_size - (char_column as u32 % tab_size);
|
||||
buffer.edit(
|
||||
[selection.start..selection.start],
|
||||
" ".repeat(chars_to_next_tab_stop as usize),
|
||||
cx,
|
||||
);
|
||||
selection.start.column += chars_to_next_tab_stop;
|
||||
selection.end = selection.start;
|
||||
}
|
||||
});
|
||||
this.update_selections(selections, Some(Autoscroll::Fit), cx);
|
||||
});
|
||||
} else {
|
||||
self.indent(&Indent, cx);
|
||||
}
|
||||
}
|
||||
pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
|
||||
if self.move_to_next_snippet_tabstop(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut selections = self.local_selections::<Point>(cx);
|
||||
if selections.iter().all(|s| s.is_empty()) {
|
||||
self.transact(cx, |this, cx| {
|
||||
this.buffer.update(cx, |buffer, cx| {
|
||||
for selection in &mut selections {
|
||||
let language_name =
|
||||
buffer.language_at(selection.start, cx).map(|l| l.name());
|
||||
let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
|
||||
let char_column = buffer
|
||||
.read(cx)
|
||||
.text_for_range(Point::new(selection.start.row, 0)..selection.start)
|
||||
.flat_map(str::chars)
|
||||
.count();
|
||||
let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
|
||||
buffer.edit(
|
||||
[selection.start..selection.start],
|
||||
" ".repeat(chars_to_next_tab_stop as usize),
|
||||
cx,
|
||||
);
|
||||
selection.start.column += chars_to_next_tab_stop;
|
||||
selection.end = selection.start;
|
||||
}
|
||||
});
|
||||
this.update_selections(selections, Some(Autoscroll::Fit), cx);
|
||||
});
|
||||
} else {
|
||||
self.indent(&Indent, cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4237,7 +4245,6 @@ impl Editor {
|
|||
|
||||
pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
|
||||
self.push_to_selection_history();
|
||||
let replace_newest = action.0;
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let buffer = &display_map.buffer_snapshot;
|
||||
let mut selections = self.local_selections::<usize>(cx);
|
||||
|
@ -4276,7 +4283,7 @@ impl Editor {
|
|||
}
|
||||
|
||||
if let Some(next_selected_range) = next_selected_range {
|
||||
if replace_newest {
|
||||
if action.replace_newest {
|
||||
if let Some(newest_id) =
|
||||
selections.iter().max_by_key(|s| s.id).map(|s| s.id)
|
||||
{
|
||||
|
@ -4547,11 +4554,15 @@ impl Editor {
|
|||
self.selection_history.mode = SelectionHistoryMode::Normal;
|
||||
}
|
||||
|
||||
pub fn go_to_diagnostic(
|
||||
&mut self,
|
||||
&GoToDiagnostic(direction): &GoToDiagnostic,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
fn go_to_next_diagnostic(&mut self, _: &GoToNextDiagnostic, cx: &mut ViewContext<Self>) {
|
||||
self.go_to_diagnostic(Direction::Next, cx)
|
||||
}
|
||||
|
||||
fn go_to_prev_diagnostic(&mut self, _: &GoToNextDiagnostic, cx: &mut ViewContext<Self>) {
|
||||
self.go_to_diagnostic(Direction::Prev, cx)
|
||||
}
|
||||
|
||||
pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
|
||||
let buffer = self.buffer.read(cx).snapshot(cx);
|
||||
let selection = self.newest_selection_with_snapshot::<usize>(&buffer);
|
||||
let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
|
||||
|
@ -7771,7 +7782,7 @@ mod tests {
|
|||
);
|
||||
|
||||
// indent from mid-tabstop to full tabstop
|
||||
view.tab(&Tab(Direction::Next), cx);
|
||||
view.tab(&Tab, cx);
|
||||
assert_text_with_selections(
|
||||
view,
|
||||
indoc! {"
|
||||
|
@ -7782,7 +7793,7 @@ mod tests {
|
|||
);
|
||||
|
||||
// outdent from 1 tabstop to 0 tabstops
|
||||
view.tab(&Tab(Direction::Prev), cx);
|
||||
view.tab_prev(&TabPrev, cx);
|
||||
assert_text_with_selections(
|
||||
view,
|
||||
indoc! {"
|
||||
|
@ -7803,7 +7814,7 @@ mod tests {
|
|||
);
|
||||
|
||||
// indent and outdent affect only the preceding line
|
||||
view.tab(&Tab(Direction::Next), cx);
|
||||
view.tab(&Tab, cx);
|
||||
assert_text_with_selections(
|
||||
view,
|
||||
indoc! {"
|
||||
|
@ -7812,7 +7823,7 @@ mod tests {
|
|||
] four"},
|
||||
cx,
|
||||
);
|
||||
view.tab(&Tab(Direction::Prev), cx);
|
||||
view.tab_prev(&TabPrev, cx);
|
||||
assert_text_with_selections(
|
||||
view,
|
||||
indoc! {"
|
||||
|
@ -7831,7 +7842,7 @@ mod tests {
|
|||
four"},
|
||||
cx,
|
||||
);
|
||||
view.tab(&Tab(Direction::Next), cx);
|
||||
view.tab(&Tab, cx);
|
||||
assert_text_with_selections(
|
||||
view,
|
||||
indoc! {"
|
||||
|
@ -7849,7 +7860,7 @@ mod tests {
|
|||
four"},
|
||||
cx,
|
||||
);
|
||||
view.tab(&Tab(Direction::Prev), cx);
|
||||
view.tab_prev(&TabPrev, cx);
|
||||
assert_text_with_selections(
|
||||
view,
|
||||
indoc! {"
|
||||
|
@ -7939,7 +7950,7 @@ mod tests {
|
|||
cx,
|
||||
);
|
||||
|
||||
editor.tab(&Tab(Direction::Next), cx);
|
||||
editor.tab(&Tab, cx);
|
||||
assert_text_with_selections(
|
||||
&mut editor,
|
||||
indoc! {"
|
||||
|
@ -7950,7 +7961,7 @@ mod tests {
|
|||
"},
|
||||
cx,
|
||||
);
|
||||
editor.tab(&Tab(Direction::Prev), cx);
|
||||
editor.tab_prev(&TabPrev, cx);
|
||||
assert_text_with_selections(
|
||||
&mut editor,
|
||||
indoc! {"
|
||||
|
@ -8693,10 +8704,20 @@ mod tests {
|
|||
|
||||
view.update(cx, |view, cx| {
|
||||
view.select_ranges([ranges[1].start + 1..ranges[1].start + 1], None, cx);
|
||||
view.select_next(&SelectNext(false), cx);
|
||||
view.select_next(
|
||||
&SelectNext {
|
||||
replace_newest: false,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
assert_eq!(view.selected_ranges(cx), &ranges[1..2]);
|
||||
|
||||
view.select_next(&SelectNext(false), cx);
|
||||
view.select_next(
|
||||
&SelectNext {
|
||||
replace_newest: false,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
assert_eq!(view.selected_ranges(cx), &ranges[1..3]);
|
||||
|
||||
view.undo_selection(&UndoSelection, cx);
|
||||
|
@ -8705,10 +8726,20 @@ mod tests {
|
|||
view.redo_selection(&RedoSelection, cx);
|
||||
assert_eq!(view.selected_ranges(cx), &ranges[1..3]);
|
||||
|
||||
view.select_next(&SelectNext(false), cx);
|
||||
view.select_next(
|
||||
&SelectNext {
|
||||
replace_newest: false,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
assert_eq!(view.selected_ranges(cx), &ranges[1..4]);
|
||||
|
||||
view.select_next(&SelectNext(false), cx);
|
||||
view.select_next(
|
||||
&SelectNext {
|
||||
replace_newest: false,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
assert_eq!(view.selected_ranges(cx), &ranges[0..4]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use fuzzy::PathMatch;
|
|||
use gpui::{
|
||||
actions,
|
||||
elements::*,
|
||||
impl_actions,
|
||||
impl_internal_actions,
|
||||
keymap::{self, Binding},
|
||||
AppContext, Axis, Entity, ModelHandle, MutableAppContext, RenderContext, Task, View,
|
||||
ViewContext, ViewHandle, WeakViewHandle,
|
||||
|
@ -41,8 +41,8 @@ pub struct FileFinder {
|
|||
#[derive(Clone)]
|
||||
pub struct Select(pub ProjectPath);
|
||||
|
||||
impl_actions!(file_finder, [Select]);
|
||||
actions!(file_finder, [Toggle]);
|
||||
impl_internal_actions!(file_finder, [Select]);
|
||||
|
||||
pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_action(FileFinder::toggle);
|
||||
|
|
|
@ -715,12 +715,14 @@ type GlobalSubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext
|
|||
type ObservationCallback = Box<dyn FnMut(&mut MutableAppContext) -> bool>;
|
||||
type GlobalObservationCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>;
|
||||
type ReleaseObservationCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>;
|
||||
type DeserializeActionCallback = fn(json: &str) -> anyhow::Result<Box<dyn Action>>;
|
||||
|
||||
pub struct MutableAppContext {
|
||||
weak_self: Option<rc::Weak<RefCell<Self>>>,
|
||||
foreground_platform: Rc<dyn platform::ForegroundPlatform>,
|
||||
assets: Arc<AssetCache>,
|
||||
cx: AppContext,
|
||||
action_deserializers: HashMap<&'static str, DeserializeActionCallback>,
|
||||
capture_actions: HashMap<TypeId, HashMap<TypeId, Vec<Box<ActionCallback>>>>,
|
||||
actions: HashMap<TypeId, HashMap<TypeId, Vec<Box<ActionCallback>>>>,
|
||||
global_actions: HashMap<TypeId, Box<GlobalActionCallback>>,
|
||||
|
@ -773,6 +775,7 @@ impl MutableAppContext {
|
|||
font_cache,
|
||||
platform,
|
||||
},
|
||||
action_deserializers: HashMap::new(),
|
||||
capture_actions: HashMap::new(),
|
||||
actions: HashMap::new(),
|
||||
global_actions: HashMap::new(),
|
||||
|
@ -857,6 +860,18 @@ impl MutableAppContext {
|
|||
.and_then(|(presenter, _)| presenter.borrow().debug_elements(self))
|
||||
}
|
||||
|
||||
pub fn deserialize_action(
|
||||
&self,
|
||||
name: &str,
|
||||
argument: Option<&str>,
|
||||
) -> Result<Box<dyn Action>> {
|
||||
let callback = self
|
||||
.action_deserializers
|
||||
.get(name)
|
||||
.ok_or_else(|| anyhow!("unknown action {}", name))?;
|
||||
callback(argument.unwrap_or("{}"))
|
||||
}
|
||||
|
||||
pub fn add_action<A, V, F>(&mut self, handler: F)
|
||||
where
|
||||
A: Action,
|
||||
|
@ -899,6 +914,10 @@ impl MutableAppContext {
|
|||
},
|
||||
);
|
||||
|
||||
self.action_deserializers
|
||||
.entry(A::qualified_name())
|
||||
.or_insert(A::from_json_str);
|
||||
|
||||
let actions = if capture {
|
||||
&mut self.capture_actions
|
||||
} else {
|
||||
|
@ -934,6 +953,10 @@ impl MutableAppContext {
|
|||
handler(action, cx);
|
||||
});
|
||||
|
||||
self.action_deserializers
|
||||
.entry(A::qualified_name())
|
||||
.or_insert(A::from_json_str);
|
||||
|
||||
if self
|
||||
.global_actions
|
||||
.insert(TypeId::of::<A>(), handler)
|
||||
|
@ -4575,7 +4598,8 @@ impl RefCounts {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{elements::*, impl_actions};
|
||||
use crate::{actions, elements::*, impl_actions};
|
||||
use serde::Deserialize;
|
||||
use smol::future::poll_once;
|
||||
use std::{
|
||||
cell::Cell,
|
||||
|
@ -5683,6 +5707,42 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[crate::test(self)]
|
||||
fn test_deserialize_actions(cx: &mut MutableAppContext) {
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||
pub struct ComplexAction {
|
||||
arg: String,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
actions!(test::something, [SimpleAction]);
|
||||
impl_actions!(test::something, [ComplexAction]);
|
||||
|
||||
cx.add_global_action(move |_: &SimpleAction, _: &mut MutableAppContext| {});
|
||||
cx.add_global_action(move |_: &ComplexAction, _: &mut MutableAppContext| {});
|
||||
|
||||
let action1 = cx
|
||||
.deserialize_action(
|
||||
"test::something::ComplexAction",
|
||||
Some(r#"{"arg": "a", "count": 5}"#),
|
||||
)
|
||||
.unwrap();
|
||||
let action2 = cx
|
||||
.deserialize_action("test::something::SimpleAction", None)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
action1.as_any().downcast_ref::<ComplexAction>().unwrap(),
|
||||
&ComplexAction {
|
||||
arg: "a".to_string(),
|
||||
count: 5,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
action2.as_any().downcast_ref::<SimpleAction>().unwrap(),
|
||||
&SimpleAction
|
||||
);
|
||||
}
|
||||
|
||||
#[crate::test(self)]
|
||||
fn test_dispatch_action(cx: &mut MutableAppContext) {
|
||||
struct ViewA {
|
||||
|
@ -5721,32 +5781,32 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Action(pub &'static str);
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct Action(pub String);
|
||||
|
||||
impl_actions!(test, [Action]);
|
||||
|
||||
let actions = Rc::new(RefCell::new(Vec::new()));
|
||||
|
||||
{
|
||||
cx.add_global_action({
|
||||
let actions = actions.clone();
|
||||
cx.add_global_action(move |_: &Action, _: &mut MutableAppContext| {
|
||||
move |_: &Action, _: &mut MutableAppContext| {
|
||||
actions.borrow_mut().push("global".to_string());
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
cx.add_action({
|
||||
let actions = actions.clone();
|
||||
cx.add_action(move |view: &mut ViewA, action: &Action, cx| {
|
||||
move |view: &mut ViewA, action: &Action, cx| {
|
||||
assert_eq!(action.0, "bar");
|
||||
cx.propagate_action();
|
||||
actions.borrow_mut().push(format!("{} a", view.id));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
cx.add_action({
|
||||
let actions = actions.clone();
|
||||
cx.add_action(move |view: &mut ViewA, _: &Action, cx| {
|
||||
move |view: &mut ViewA, _: &Action, cx| {
|
||||
if view.id != 1 {
|
||||
cx.add_view(|cx| {
|
||||
cx.propagate_action(); // Still works on a nested ViewContext
|
||||
|
@ -5754,32 +5814,32 @@ mod tests {
|
|||
});
|
||||
}
|
||||
actions.borrow_mut().push(format!("{} b", view.id));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
cx.add_action({
|
||||
let actions = actions.clone();
|
||||
cx.add_action(move |view: &mut ViewB, _: &Action, cx| {
|
||||
move |view: &mut ViewB, _: &Action, cx| {
|
||||
cx.propagate_action();
|
||||
actions.borrow_mut().push(format!("{} c", view.id));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
cx.add_action({
|
||||
let actions = actions.clone();
|
||||
cx.add_action(move |view: &mut ViewB, _: &Action, cx| {
|
||||
move |view: &mut ViewB, _: &Action, cx| {
|
||||
cx.propagate_action();
|
||||
actions.borrow_mut().push(format!("{} d", view.id));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
cx.capture_action({
|
||||
let actions = actions.clone();
|
||||
cx.capture_action(move |view: &mut ViewA, _: &Action, cx| {
|
||||
move |view: &mut ViewA, _: &Action, cx| {
|
||||
cx.propagate_action();
|
||||
actions.borrow_mut().push(format!("{} capture", view.id));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let (window_id, view_1) = cx.add_window(Default::default(), |_| ViewA { id: 1 });
|
||||
let view_2 = cx.add_view(window_id, |_| ViewB { id: 2 });
|
||||
|
@ -5789,7 +5849,7 @@ mod tests {
|
|||
cx.dispatch_action(
|
||||
window_id,
|
||||
vec![view_1.id(), view_2.id(), view_3.id(), view_4.id()],
|
||||
&Action("bar"),
|
||||
&Action("bar".to_string()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -5812,7 +5872,7 @@ mod tests {
|
|||
cx.dispatch_action(
|
||||
window_id,
|
||||
vec![view_2.id(), view_3.id(), view_4.id()],
|
||||
&Action("bar"),
|
||||
&Action("bar".to_string()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -5832,8 +5892,8 @@ mod tests {
|
|||
|
||||
#[crate::test(self)]
|
||||
fn test_dispatch_keystroke(cx: &mut MutableAppContext) {
|
||||
#[derive(Clone)]
|
||||
pub struct Action(pub &'static str);
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct Action(String);
|
||||
|
||||
impl_actions!(test, [Action]);
|
||||
|
||||
|
@ -5887,16 +5947,20 @@ mod tests {
|
|||
// "a" and "b" in its context, but not "c".
|
||||
cx.add_bindings(vec![keymap::Binding::new(
|
||||
"a",
|
||||
Action("a"),
|
||||
Action("a".to_string()),
|
||||
Some("a && b && !c"),
|
||||
)]);
|
||||
|
||||
cx.add_bindings(vec![keymap::Binding::new("b", Action("b"), None)]);
|
||||
cx.add_bindings(vec![keymap::Binding::new(
|
||||
"b",
|
||||
Action("b".to_string()),
|
||||
None,
|
||||
)]);
|
||||
|
||||
let actions = Rc::new(RefCell::new(Vec::new()));
|
||||
{
|
||||
cx.add_action({
|
||||
let actions = actions.clone();
|
||||
cx.add_action(move |view: &mut View, action: &Action, cx| {
|
||||
move |view: &mut View, action: &Action, cx| {
|
||||
if action.0 == "a" {
|
||||
actions.borrow_mut().push(format!("{} a", view.id));
|
||||
} else {
|
||||
|
@ -5905,14 +5969,15 @@ mod tests {
|
|||
.push(format!("{} {}", view.id, action.0));
|
||||
cx.propagate_action();
|
||||
}
|
||||
});
|
||||
}
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
cx.add_global_action({
|
||||
let actions = actions.clone();
|
||||
cx.add_global_action(move |action: &Action, _| {
|
||||
move |action: &Action, _| {
|
||||
actions.borrow_mut().push(format!("global {}", action.0));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cx.dispatch_keystroke(
|
||||
window_id,
|
||||
|
|
|
@ -2,55 +2,108 @@ use std::any::{Any, TypeId};
|
|||
|
||||
pub trait Action: 'static {
|
||||
fn id(&self) -> TypeId;
|
||||
fn namespace(&self) -> &'static str;
|
||||
fn name(&self) -> &'static str;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn boxed_clone(&self) -> Box<dyn Action>;
|
||||
fn boxed_clone_as_any(&self) -> Box<dyn Any>;
|
||||
|
||||
fn qualified_name() -> &'static str
|
||||
where
|
||||
Self: Sized;
|
||||
fn from_json_str(json: &str) -> anyhow::Result<Box<dyn Action>>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
/// Define a set of unit struct types that all implement the `Action` trait.
|
||||
///
|
||||
/// The first argument is a namespace that will be associated with each of
|
||||
/// the given action types, to ensure that they have globally unique
|
||||
/// qualified names for use in keymap files.
|
||||
#[macro_export]
|
||||
macro_rules! impl_actions {
|
||||
macro_rules! actions {
|
||||
($namespace:path, [ $($name:ident),* $(,)? ]) => {
|
||||
$(
|
||||
impl $crate::action::Action for $name {
|
||||
fn id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<$name>()
|
||||
}
|
||||
|
||||
fn namespace(&self) -> &'static str {
|
||||
stringify!($namespace)
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
stringify!($name)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn $crate::action::Action> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn boxed_clone_as_any(&self) -> Box<dyn std::any::Any> {
|
||||
Box::new(self.clone())
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct $name;
|
||||
$crate::__impl_action! {
|
||||
$namespace,
|
||||
$name,
|
||||
fn from_json_str(_: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
|
||||
Ok(Box::new(Self))
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
/// Implement the `Action` trait for a set of existing types.
|
||||
///
|
||||
/// The first argument is a namespace that will be associated with each of
|
||||
/// the given action types, to ensure that they have globally unique
|
||||
/// qualified names for use in keymap files.
|
||||
#[macro_export]
|
||||
macro_rules! actions {
|
||||
macro_rules! impl_actions {
|
||||
($namespace:path, [ $($name:ident),* $(,)? ]) => {
|
||||
|
||||
$(
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct $name;
|
||||
$crate::__impl_action! {
|
||||
$namespace,
|
||||
$name,
|
||||
fn from_json_str(json: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
|
||||
Ok(Box::new($crate::serde_json::from_str::<Self>(json)?))
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
$crate::impl_actions!($namespace, [ $($name),* ]);
|
||||
};
|
||||
}
|
||||
|
||||
/// Implement the `Action` trait for a set of existing types that are
|
||||
/// not intended to be constructed via a keymap file, but only dispatched
|
||||
/// internally.
|
||||
#[macro_export]
|
||||
macro_rules! impl_internal_actions {
|
||||
($namespace:path, [ $($name:ident),* $(,)? ]) => {
|
||||
$(
|
||||
$crate::__impl_action! {
|
||||
$namespace,
|
||||
$name,
|
||||
fn from_json_str(_: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
|
||||
Err($crate::anyhow::anyhow!("internal action"))
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __impl_action {
|
||||
($namespace:path, $name:ident, $from_json_fn:item) => {
|
||||
impl $crate::action::Action for $name {
|
||||
fn name(&self) -> &'static str {
|
||||
stringify!($name)
|
||||
}
|
||||
|
||||
fn qualified_name() -> &'static str {
|
||||
concat!(
|
||||
stringify!($namespace),
|
||||
"::",
|
||||
stringify!($name),
|
||||
)
|
||||
}
|
||||
|
||||
fn id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<$name>()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn $crate::Action> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
$from_json_fn
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -33,3 +33,6 @@ pub use platform::{Event, NavigationDirection, PathPromptOptions, Platform, Prom
|
|||
pub use presenter::{
|
||||
Axis, DebugContext, EventContext, LayoutContext, PaintContext, SizeConstraint, Vector2FExt,
|
||||
};
|
||||
|
||||
pub use anyhow;
|
||||
pub use serde_json;
|
||||
|
|
|
@ -328,6 +328,8 @@ impl ContextPredicate {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{actions, impl_actions};
|
||||
|
||||
use super::*;
|
||||
|
@ -419,30 +421,18 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_matcher() -> anyhow::Result<()> {
|
||||
#[derive(Clone)]
|
||||
pub struct A(pub &'static str);
|
||||
#[derive(Clone, Deserialize, PartialEq, Eq, Debug)]
|
||||
pub struct A(pub String);
|
||||
impl_actions!(test, [A]);
|
||||
actions!(test, [B, Ab]);
|
||||
|
||||
impl PartialEq for A {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
impl Eq for A {}
|
||||
impl Debug for A {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "A({:?})", &self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct ActionArg {
|
||||
a: &'static str,
|
||||
}
|
||||
|
||||
let keymap = Keymap(vec![
|
||||
Binding::new("a", A("x"), Some("a")),
|
||||
Binding::new("a", A("x".to_string()), Some("a")),
|
||||
Binding::new("b", B, Some("a")),
|
||||
Binding::new("a b", Ab, Some("a || b")),
|
||||
]);
|
||||
|
@ -456,40 +446,54 @@ mod tests {
|
|||
let mut matcher = Matcher::new(keymap);
|
||||
|
||||
// Basic match
|
||||
assert_eq!(matcher.test_keystroke("a", 1, &ctx_a), Some(A("x")));
|
||||
assert_eq!(
|
||||
downcast(&matcher.test_keystroke("a", 1, &ctx_a)),
|
||||
Some(&A("x".to_string()))
|
||||
);
|
||||
|
||||
// Multi-keystroke match
|
||||
assert_eq!(matcher.test_keystroke::<A>("a", 1, &ctx_b), None);
|
||||
assert_eq!(matcher.test_keystroke("b", 1, &ctx_b), Some(Ab));
|
||||
assert!(matcher.test_keystroke("a", 1, &ctx_b).is_none());
|
||||
assert_eq!(downcast(&matcher.test_keystroke("b", 1, &ctx_b)), Some(&Ab));
|
||||
|
||||
// Failed matches don't interfere with matching subsequent keys
|
||||
assert_eq!(matcher.test_keystroke::<A>("x", 1, &ctx_a), None);
|
||||
assert_eq!(matcher.test_keystroke("a", 1, &ctx_a), Some(A("x")));
|
||||
assert!(matcher.test_keystroke("x", 1, &ctx_a).is_none());
|
||||
assert_eq!(
|
||||
downcast(&matcher.test_keystroke("a", 1, &ctx_a)),
|
||||
Some(&A("x".to_string()))
|
||||
);
|
||||
|
||||
// Pending keystrokes are cleared when the context changes
|
||||
assert_eq!(matcher.test_keystroke::<A>("a", 1, &ctx_b), None);
|
||||
assert_eq!(matcher.test_keystroke("b", 1, &ctx_a), Some(B));
|
||||
assert!(&matcher.test_keystroke("a", 1, &ctx_b).is_none());
|
||||
assert_eq!(downcast(&matcher.test_keystroke("b", 1, &ctx_a)), Some(&B));
|
||||
|
||||
let mut ctx_c = Context::default();
|
||||
ctx_c.set.insert("c".into());
|
||||
|
||||
// Pending keystrokes are maintained per-view
|
||||
assert_eq!(matcher.test_keystroke::<A>("a", 1, &ctx_b), None);
|
||||
assert_eq!(matcher.test_keystroke::<A>("a", 2, &ctx_c), None);
|
||||
assert_eq!(matcher.test_keystroke("b", 1, &ctx_b), Some(Ab));
|
||||
assert!(matcher.test_keystroke("a", 1, &ctx_b).is_none());
|
||||
assert!(matcher.test_keystroke("a", 2, &ctx_c).is_none());
|
||||
assert_eq!(downcast(&matcher.test_keystroke("b", 1, &ctx_b)), Some(&Ab));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn downcast<'a, A: Action>(action: &'a Option<Box<dyn Action>>) -> Option<&'a A> {
|
||||
action
|
||||
.as_ref()
|
||||
.and_then(|action| action.as_any().downcast_ref())
|
||||
}
|
||||
|
||||
impl Matcher {
|
||||
fn test_keystroke<A>(&mut self, keystroke: &str, view_id: usize, cx: &Context) -> Option<A>
|
||||
where
|
||||
A: Action + Debug + Eq,
|
||||
{
|
||||
fn test_keystroke(
|
||||
&mut self,
|
||||
keystroke: &str,
|
||||
view_id: usize,
|
||||
cx: &Context,
|
||||
) -> Option<Box<dyn Action>> {
|
||||
if let MatchResult::Action(action) =
|
||||
self.push_keystroke(Keystroke::parse(keystroke).unwrap(), view_id, cx)
|
||||
{
|
||||
Some(*action.boxed_clone_as_any().downcast().unwrap())
|
||||
Some(action.boxed_clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
actions, elements::*, impl_actions, AppContext, Entity, MutableAppContext, RenderContext, View,
|
||||
ViewContext, WeakViewHandle,
|
||||
|
@ -25,7 +27,7 @@ pub enum ItemType {
|
|||
Unselected,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct SelectItem(pub usize);
|
||||
|
||||
actions!(select, [ToggleSelect]);
|
||||
|
|
|
@ -4,7 +4,7 @@ use gpui::{
|
|||
Align, ConstrainedBox, Empty, Flex, Label, MouseEventHandler, ParentElement, ScrollTarget,
|
||||
Svg, UniformList, UniformListState,
|
||||
},
|
||||
impl_actions,
|
||||
impl_internal_actions,
|
||||
keymap::{self, Binding},
|
||||
platform::CursorStyle,
|
||||
AppContext, Element, ElementBox, Entity, ModelHandle, MutableAppContext, View, ViewContext,
|
||||
|
@ -54,7 +54,7 @@ pub struct ToggleExpanded(pub ProjectEntryId);
|
|||
pub struct Open(pub ProjectEntryId);
|
||||
|
||||
actions!(project_panel, [ExpandSelectedEntry, CollapseSelectedEntry]);
|
||||
impl_actions!(project_panel, [Open, ToggleExpanded]);
|
||||
impl_internal_actions!(project_panel, [Open, ToggleExpanded]);
|
||||
|
||||
pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_action(ProjectPanel::expand_selected_entry);
|
||||
|
|
|
@ -20,6 +20,7 @@ workspace = { path = "../workspace" }
|
|||
anyhow = "1.0"
|
||||
log = "0.4"
|
||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
editor = { path = "../editor", features = ["test-support"] }
|
||||
|
|
|
@ -1,25 +1,32 @@
|
|||
use crate::{active_match_index, match_index_for_direction, Direction, SearchOption, SelectMatch};
|
||||
use crate::{
|
||||
active_match_index, match_index_for_direction, Direction, SearchOption, SelectNextMatch,
|
||||
SelectPrevMatch,
|
||||
};
|
||||
use collections::HashMap;
|
||||
use editor::{display_map::ToDisplayPoint, Anchor, Autoscroll, Bias, Editor};
|
||||
use gpui::{
|
||||
actions, elements::*, impl_actions, keymap::Binding, platform::CursorStyle, AppContext, Entity,
|
||||
MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle,
|
||||
WeakViewHandle,
|
||||
actions, elements::*, impl_actions, impl_internal_actions, keymap::Binding,
|
||||
platform::CursorStyle, AppContext, Entity, MutableAppContext, RenderContext, Subscription,
|
||||
Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use language::OffsetRangeExt;
|
||||
use project::search::SearchQuery;
|
||||
use serde::Deserialize;
|
||||
use settings::Settings;
|
||||
use std::ops::Range;
|
||||
use workspace::{ItemHandle, Pane, ToolbarItemLocation, ToolbarItemView};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Deploy(pub bool);
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct Deploy {
|
||||
pub focus: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ToggleSearchOption(pub SearchOption);
|
||||
|
||||
actions!(buffer_search, [Dismiss, FocusEditor]);
|
||||
impl_actions!(buffer_search, [Deploy, ToggleSearchOption]);
|
||||
impl_actions!(buffer_search, [Deploy]);
|
||||
impl_internal_actions!(buffer_search, [ToggleSearchOption]);
|
||||
|
||||
pub enum Event {
|
||||
UpdateLocation,
|
||||
|
@ -27,29 +34,31 @@ pub enum Event {
|
|||
|
||||
pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_bindings([
|
||||
Binding::new("cmd-f", Deploy(true), Some("Editor && mode == full")),
|
||||
Binding::new("cmd-e", Deploy(false), Some("Editor && mode == full")),
|
||||
Binding::new(
|
||||
"cmd-f",
|
||||
Deploy { focus: true },
|
||||
Some("Editor && mode == full"),
|
||||
),
|
||||
Binding::new(
|
||||
"cmd-e",
|
||||
Deploy { focus: false },
|
||||
Some("Editor && mode == full"),
|
||||
),
|
||||
Binding::new("escape", Dismiss, Some("BufferSearchBar")),
|
||||
Binding::new("cmd-f", FocusEditor, Some("BufferSearchBar")),
|
||||
Binding::new(
|
||||
"enter",
|
||||
SelectMatch(Direction::Next),
|
||||
Some("BufferSearchBar"),
|
||||
),
|
||||
Binding::new(
|
||||
"shift-enter",
|
||||
SelectMatch(Direction::Prev),
|
||||
Some("BufferSearchBar"),
|
||||
),
|
||||
Binding::new("cmd-g", SelectMatch(Direction::Next), Some("Pane")),
|
||||
Binding::new("cmd-shift-G", SelectMatch(Direction::Prev), Some("Pane")),
|
||||
Binding::new("enter", SelectNextMatch, Some("BufferSearchBar")),
|
||||
Binding::new("shift-enter", SelectPrevMatch, Some("BufferSearchBar")),
|
||||
Binding::new("cmd-g", SelectNextMatch, Some("Pane")),
|
||||
Binding::new("cmd-shift-G", SelectPrevMatch, Some("Pane")),
|
||||
]);
|
||||
cx.add_action(BufferSearchBar::deploy);
|
||||
cx.add_action(BufferSearchBar::dismiss);
|
||||
cx.add_action(BufferSearchBar::focus_editor);
|
||||
cx.add_action(BufferSearchBar::toggle_search_option);
|
||||
cx.add_action(BufferSearchBar::select_match);
|
||||
cx.add_action(BufferSearchBar::select_match_on_pane);
|
||||
cx.add_action(BufferSearchBar::select_next_match);
|
||||
cx.add_action(BufferSearchBar::select_prev_match);
|
||||
cx.add_action(BufferSearchBar::select_next_match_on_pane);
|
||||
cx.add_action(BufferSearchBar::select_prev_match_on_pane);
|
||||
}
|
||||
|
||||
pub struct BufferSearchBar {
|
||||
|
@ -325,14 +334,17 @@ impl BufferSearchBar {
|
|||
.with_style(style.container)
|
||||
.boxed()
|
||||
})
|
||||
.on_click(move |cx| cx.dispatch_action(SelectMatch(direction)))
|
||||
.on_click(move |cx| match direction {
|
||||
Direction::Prev => cx.dispatch_action(SelectPrevMatch),
|
||||
Direction::Next => cx.dispatch_action(SelectNextMatch),
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn deploy(pane: &mut Pane, Deploy(focus): &Deploy, cx: &mut ViewContext<Pane>) {
|
||||
fn deploy(pane: &mut Pane, action: &Deploy, cx: &mut ViewContext<Pane>) {
|
||||
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
|
||||
if search_bar.update(cx, |search_bar, cx| search_bar.show(*focus, cx)) {
|
||||
if search_bar.update(cx, |search_bar, cx| search_bar.show(action.focus, cx)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -368,7 +380,15 @@ impl BufferSearchBar {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
fn select_match(&mut self, &SelectMatch(direction): &SelectMatch, cx: &mut ViewContext<Self>) {
|
||||
fn select_next_match(&mut self, _: &SelectNextMatch, cx: &mut ViewContext<Self>) {
|
||||
self.select_match(Direction::Next, cx);
|
||||
}
|
||||
|
||||
fn select_prev_match(&mut self, _: &SelectPrevMatch, cx: &mut ViewContext<Self>) {
|
||||
self.select_match(Direction::Prev, cx);
|
||||
}
|
||||
|
||||
fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
|
||||
if let Some(index) = self.active_match_index {
|
||||
if let Some(editor) = self.active_editor.as_ref() {
|
||||
editor.update(cx, |editor, cx| {
|
||||
|
@ -389,9 +409,23 @@ impl BufferSearchBar {
|
|||
}
|
||||
}
|
||||
|
||||
fn select_match_on_pane(pane: &mut Pane, action: &SelectMatch, cx: &mut ViewContext<Pane>) {
|
||||
fn select_next_match_on_pane(
|
||||
pane: &mut Pane,
|
||||
action: &SelectNextMatch,
|
||||
cx: &mut ViewContext<Pane>,
|
||||
) {
|
||||
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
|
||||
search_bar.update(cx, |search_bar, cx| search_bar.select_match(action, cx));
|
||||
search_bar.update(cx, |bar, cx| bar.select_next_match(action, cx));
|
||||
}
|
||||
}
|
||||
|
||||
fn select_prev_match_on_pane(
|
||||
pane: &mut Pane,
|
||||
action: &SelectPrevMatch,
|
||||
cx: &mut ViewContext<Pane>,
|
||||
) {
|
||||
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
|
||||
search_bar.update(cx, |bar, cx| bar.select_prev_match(action, cx));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -699,7 +733,7 @@ mod tests {
|
|||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.active_match_index, Some(0));
|
||||
search_bar.select_match(&SelectMatch(Direction::Next), cx);
|
||||
search_bar.select_next_match(&SelectNextMatch, cx);
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
|
||||
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
|
||||
|
@ -710,7 +744,7 @@ mod tests {
|
|||
});
|
||||
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.select_match(&SelectMatch(Direction::Next), cx);
|
||||
search_bar.select_next_match(&SelectNextMatch, cx);
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
|
||||
[DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)]
|
||||
|
@ -721,7 +755,7 @@ mod tests {
|
|||
});
|
||||
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.select_match(&SelectMatch(Direction::Next), cx);
|
||||
search_bar.select_next_match(&SelectNextMatch, cx);
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
|
||||
[DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
|
||||
|
@ -732,7 +766,7 @@ mod tests {
|
|||
});
|
||||
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.select_match(&SelectMatch(Direction::Next), cx);
|
||||
search_bar.select_next_match(&SelectNextMatch, cx);
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
|
||||
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
|
||||
|
@ -743,7 +777,7 @@ mod tests {
|
|||
});
|
||||
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.select_match(&SelectMatch(Direction::Prev), cx);
|
||||
search_bar.select_prev_match(&SelectPrevMatch, cx);
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
|
||||
[DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
|
||||
|
@ -754,7 +788,7 @@ mod tests {
|
|||
});
|
||||
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.select_match(&SelectMatch(Direction::Prev), cx);
|
||||
search_bar.select_prev_match(&SelectPrevMatch, cx);
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
|
||||
[DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)]
|
||||
|
@ -765,7 +799,7 @@ mod tests {
|
|||
});
|
||||
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.select_match(&SelectMatch(Direction::Prev), cx);
|
||||
search_bar.select_prev_match(&SelectPrevMatch, cx);
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
|
||||
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
|
||||
|
@ -782,7 +816,7 @@ mod tests {
|
|||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.active_match_index, Some(1));
|
||||
search_bar.select_match(&SelectMatch(Direction::Prev), cx);
|
||||
search_bar.select_prev_match(&SelectPrevMatch, cx);
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
|
||||
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
|
||||
|
@ -799,7 +833,7 @@ mod tests {
|
|||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.active_match_index, Some(1));
|
||||
search_bar.select_match(&SelectMatch(Direction::Next), cx);
|
||||
search_bar.select_next_match(&SelectNextMatch, cx);
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
|
||||
[DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)]
|
||||
|
@ -816,7 +850,7 @@ mod tests {
|
|||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.active_match_index, Some(2));
|
||||
search_bar.select_match(&SelectMatch(Direction::Prev), cx);
|
||||
search_bar.select_prev_match(&SelectPrevMatch, cx);
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
|
||||
[DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
|
||||
|
@ -833,7 +867,7 @@ mod tests {
|
|||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.active_match_index, Some(2));
|
||||
search_bar.select_match(&SelectMatch(Direction::Next), cx);
|
||||
search_bar.select_next_match(&SelectNextMatch, cx);
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
|
||||
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
|
||||
|
@ -850,7 +884,7 @@ mod tests {
|
|||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.active_match_index, Some(0));
|
||||
search_bar.select_match(&SelectMatch(Direction::Prev), cx);
|
||||
search_bar.select_prev_match(&SelectPrevMatch, cx);
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
|
||||
[DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
active_match_index, match_index_for_direction, Direction, SearchOption, SelectMatch,
|
||||
ToggleSearchOption,
|
||||
active_match_index, match_index_for_direction, Direction, SearchOption, SelectNextMatch,
|
||||
SelectPrevMatch, ToggleSearchOption,
|
||||
};
|
||||
use collections::HashMap;
|
||||
use editor::{Anchor, Autoscroll, Editor, MultiBuffer, SelectAll};
|
||||
|
@ -34,14 +34,15 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
Binding::new("cmd-shift-F", Deploy, Some("Workspace")),
|
||||
Binding::new("enter", Search, Some("ProjectSearchBar")),
|
||||
Binding::new("cmd-enter", SearchInNew, Some("ProjectSearchBar")),
|
||||
Binding::new("cmd-g", SelectMatch(Direction::Next), Some("Pane")),
|
||||
Binding::new("cmd-shift-G", SelectMatch(Direction::Prev), Some("Pane")),
|
||||
Binding::new("cmd-g", SelectNextMatch, Some("Pane")),
|
||||
Binding::new("cmd-shift-G", SelectPrevMatch, Some("Pane")),
|
||||
]);
|
||||
cx.add_action(ProjectSearchView::deploy);
|
||||
cx.add_action(ProjectSearchBar::search);
|
||||
cx.add_action(ProjectSearchBar::search_in_new);
|
||||
cx.add_action(ProjectSearchBar::toggle_search_option);
|
||||
cx.add_action(ProjectSearchBar::select_match);
|
||||
cx.add_action(ProjectSearchBar::select_next_match);
|
||||
cx.add_action(ProjectSearchBar::select_prev_match);
|
||||
cx.add_action(ProjectSearchBar::toggle_focus);
|
||||
cx.capture_action(ProjectSearchBar::tab);
|
||||
}
|
||||
|
@ -545,18 +546,23 @@ impl ProjectSearchBar {
|
|||
}
|
||||
}
|
||||
|
||||
fn select_match(
|
||||
pane: &mut Pane,
|
||||
&SelectMatch(direction): &SelectMatch,
|
||||
cx: &mut ViewContext<Pane>,
|
||||
) {
|
||||
fn select_next_match(pane: &mut Pane, _: &SelectNextMatch, cx: &mut ViewContext<Pane>) {
|
||||
if let Some(search_view) = pane
|
||||
.active_item()
|
||||
.and_then(|item| item.downcast::<ProjectSearchView>())
|
||||
{
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
search_view.select_match(direction, cx);
|
||||
});
|
||||
search_view.update(cx, |view, cx| view.select_match(Direction::Next, cx));
|
||||
} else {
|
||||
cx.propagate_action();
|
||||
}
|
||||
}
|
||||
|
||||
fn select_prev_match(pane: &mut Pane, _: &SelectPrevMatch, cx: &mut ViewContext<Pane>) {
|
||||
if let Some(search_view) = pane
|
||||
.active_item()
|
||||
.and_then(|item| item.downcast::<ProjectSearchView>())
|
||||
{
|
||||
search_view.update(cx, |view, cx| view.select_match(Direction::Prev, cx));
|
||||
} else {
|
||||
cx.propagate_action();
|
||||
}
|
||||
|
@ -635,7 +641,10 @@ impl ProjectSearchBar {
|
|||
.with_style(style.container)
|
||||
.boxed()
|
||||
})
|
||||
.on_click(move |cx| cx.dispatch_action(SelectMatch(direction)))
|
||||
.on_click(move |cx| match direction {
|
||||
Direction::Prev => cx.dispatch_action(SelectPrevMatch),
|
||||
Direction::Next => cx.dispatch_action(SelectNextMatch),
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.boxed()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
pub use buffer_search::BufferSearchBar;
|
||||
use editor::{Anchor, MultiBufferSnapshot};
|
||||
use gpui::{impl_actions, MutableAppContext};
|
||||
use gpui::{actions, impl_internal_actions, MutableAppContext};
|
||||
pub use project_search::{ProjectSearchBar, ProjectSearchView};
|
||||
use std::{
|
||||
cmp::{self, Ordering},
|
||||
|
@ -18,10 +18,8 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
#[derive(Clone)]
|
||||
pub struct ToggleSearchOption(pub SearchOption);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SelectMatch(pub Direction);
|
||||
|
||||
impl_actions!(search, [ToggleSearchOption, SelectMatch]);
|
||||
actions!(search, [SelectNextMatch, SelectPrevMatch]);
|
||||
impl_internal_actions!(search, [ToggleSearchOption]);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum SearchOption {
|
||||
|
|
|
@ -64,6 +64,7 @@ language = { path = "../language", features = ["test-support"] }
|
|||
lsp = { path = "../lsp", features = ["test-support"] }
|
||||
project = { path = "../project", features = ["test-support"] }
|
||||
settings = { path = "../settings", features = ["test-support"] }
|
||||
theme = { path = "../theme" }
|
||||
workspace = { path = "../workspace", features = ["test-support"] }
|
||||
ctor = "0.1"
|
||||
env_logger = "0.8"
|
||||
|
|
|
@ -1117,6 +1117,7 @@ mod tests {
|
|||
},
|
||||
time::Duration,
|
||||
};
|
||||
use theme::ThemeRegistry;
|
||||
use util::TryFutureExt;
|
||||
use workspace::{Item, SplitDirection, ToggleFollow, Workspace, WorkspaceParams};
|
||||
|
||||
|
@ -5633,6 +5634,7 @@ mod tests {
|
|||
project: project.clone(),
|
||||
user_store: self.user_store.clone(),
|
||||
languages: self.language_registry.clone(),
|
||||
themes: ThemeRegistry::new((), cx.font_cache().clone()),
|
||||
channel_list: cx.add_model(|cx| {
|
||||
ChannelList::new(self.user_store.clone(), self.client.clone(), cx)
|
||||
}),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use editor::Editor;
|
||||
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions,
|
||||
elements::*,
|
||||
impl_actions,
|
||||
keymap::{self, Binding},
|
||||
AppContext, Axis, Element, ElementBox, Entity, MutableAppContext, RenderContext, View,
|
||||
ViewContext, ViewHandle,
|
||||
|
@ -25,15 +25,9 @@ pub struct ThemeSelector {
|
|||
selection_completed: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Toggle(pub Arc<ThemeRegistry>);
|
||||
actions!(theme_selector, [Toggle, Reload]);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Reload(pub Arc<ThemeRegistry>);
|
||||
|
||||
impl_actions!(theme_selector, [Toggle, Reload]);
|
||||
|
||||
pub fn init(themes: Arc<ThemeRegistry>, cx: &mut MutableAppContext) {
|
||||
pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_action(ThemeSelector::confirm);
|
||||
cx.add_action(ThemeSelector::select_prev);
|
||||
cx.add_action(ThemeSelector::select_next);
|
||||
|
@ -41,9 +35,9 @@ pub fn init(themes: Arc<ThemeRegistry>, cx: &mut MutableAppContext) {
|
|||
cx.add_action(ThemeSelector::reload);
|
||||
|
||||
cx.add_bindings(vec![
|
||||
Binding::new("cmd-k cmd-t", Toggle(themes.clone()), None),
|
||||
Binding::new("cmd-k t", Reload(themes.clone()), None),
|
||||
Binding::new("escape", Toggle(themes.clone()), Some("ThemeSelector")),
|
||||
Binding::new("cmd-k cmd-t", Toggle, None),
|
||||
Binding::new("cmd-k t", Reload, None),
|
||||
Binding::new("escape", Toggle, Some("ThemeSelector")),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -79,18 +73,20 @@ impl ThemeSelector {
|
|||
this
|
||||
}
|
||||
|
||||
fn toggle(workspace: &mut Workspace, action: &Toggle, cx: &mut ViewContext<Workspace>) {
|
||||
fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
|
||||
let themes = workspace.themes();
|
||||
workspace.toggle_modal(cx, |cx, _| {
|
||||
let selector = cx.add_view(|cx| Self::new(action.0.clone(), cx));
|
||||
let selector = cx.add_view(|cx| Self::new(themes, cx));
|
||||
cx.subscribe(&selector, Self::on_event).detach();
|
||||
selector
|
||||
});
|
||||
}
|
||||
|
||||
fn reload(_: &mut Workspace, action: &Reload, cx: &mut ViewContext<Workspace>) {
|
||||
fn reload(workspace: &mut Workspace, _: &Reload, cx: &mut ViewContext<Workspace>) {
|
||||
let current_theme_name = cx.global::<Settings>().theme.name.clone();
|
||||
action.0.clear();
|
||||
match action.0.get(¤t_theme_name) {
|
||||
let themes = workspace.themes();
|
||||
themes.clear();
|
||||
match themes.get(¤t_theme_name) {
|
||||
Ok(theme) => {
|
||||
Self::set_theme(theme, cx);
|
||||
log::info!("reloaded theme {}", current_theme_name);
|
||||
|
|
|
@ -12,6 +12,7 @@ collections = { path = "../collections" }
|
|||
editor = { path = "../editor" }
|
||||
gpui = { path = "../gpui" }
|
||||
language = { path = "../language" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
settings = { path = "../settings" }
|
||||
workspace = { path = "../workspace" }
|
||||
log = "0.4"
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use editor::CursorShape;
|
||||
use gpui::keymap::Context;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
|
||||
pub enum Mode {
|
||||
Normal(NormalState),
|
||||
Insert,
|
||||
|
@ -44,7 +45,7 @@ impl Default for Mode {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
|
||||
pub enum NormalState {
|
||||
None,
|
||||
GPrefix,
|
||||
|
|
|
@ -4,15 +4,16 @@ use crate::{mode::NormalState, Mode, SwitchMode, VimState};
|
|||
use editor::{char_kind, movement, Bias};
|
||||
use gpui::{actions, impl_actions, keymap::Binding, MutableAppContext, ViewContext};
|
||||
use language::SelectionGoal;
|
||||
use serde::Deserialize;
|
||||
use workspace::Workspace;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
struct MoveToNextWordStart(pub bool);
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
struct MoveToNextWordEnd(pub bool);
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
struct MoveToPreviousWordStart(pub bool);
|
||||
|
||||
impl_actions!(
|
||||
|
|
|
@ -8,12 +8,13 @@ mod vim_test_context;
|
|||
use collections::HashMap;
|
||||
use editor::{CursorShape, Editor};
|
||||
use gpui::{impl_actions, MutableAppContext, ViewContext, WeakViewHandle};
|
||||
use serde::Deserialize;
|
||||
|
||||
use mode::Mode;
|
||||
use settings::Settings;
|
||||
use workspace::{self, Workspace};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct SwitchMode(pub Mode);
|
||||
|
||||
impl_actions!(vim, [SwitchMode]);
|
||||
|
|
|
@ -7,13 +7,14 @@ use gpui::{
|
|||
actions,
|
||||
elements::*,
|
||||
geometry::{rect::RectF, vector::vec2f},
|
||||
impl_actions,
|
||||
impl_actions, impl_internal_actions,
|
||||
keymap::Binding,
|
||||
platform::{CursorStyle, NavigationDirection},
|
||||
AppContext, Entity, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View,
|
||||
ViewContext, ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use project::{ProjectEntryId, ProjectPath};
|
||||
use serde::Deserialize;
|
||||
use settings::Settings;
|
||||
use std::{any::Any, cell::RefCell, cmp, mem, path::Path, rc::Rc};
|
||||
use util::ResultExt;
|
||||
|
@ -28,13 +29,16 @@ actions!(
|
|||
]
|
||||
);
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct Split(pub SplitDirection);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CloseItem(pub CloseItemParams);
|
||||
pub struct CloseItem {
|
||||
pub item_id: usize,
|
||||
pub pane: WeakViewHandle<Pane>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct ActivateItem(pub usize);
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -43,13 +47,8 @@ pub struct GoBack(pub Option<WeakViewHandle<Pane>>);
|
|||
#[derive(Clone)]
|
||||
pub struct GoForward(pub Option<WeakViewHandle<Pane>>);
|
||||
|
||||
impl_actions!(pane, [Split, CloseItem, ActivateItem, GoBack, GoForward,]);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CloseItemParams {
|
||||
pub item_id: usize,
|
||||
pub pane: WeakViewHandle<Pane>,
|
||||
}
|
||||
impl_actions!(pane, [Split]);
|
||||
impl_internal_actions!(pane, [CloseItem, ActivateItem, GoBack, GoForward]);
|
||||
|
||||
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
|
||||
|
||||
|
@ -66,8 +65,8 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
cx.add_async_action(Pane::close_active_item);
|
||||
cx.add_async_action(Pane::close_inactive_items);
|
||||
cx.add_async_action(|workspace: &mut Workspace, action: &CloseItem, cx| {
|
||||
let pane = action.0.pane.upgrade(cx)?;
|
||||
Some(Pane::close_item(workspace, pane, action.0.item_id, cx))
|
||||
let pane = action.pane.upgrade(cx)?;
|
||||
Some(Pane::close_item(workspace, pane, action.item_id, cx))
|
||||
});
|
||||
cx.add_action(|pane: &mut Pane, action: &Split, cx| {
|
||||
pane.split(action.0, cx);
|
||||
|
@ -747,10 +746,10 @@ impl Pane {
|
|||
.on_click({
|
||||
let pane = pane.clone();
|
||||
move |cx| {
|
||||
cx.dispatch_action(CloseItem(CloseItemParams {
|
||||
cx.dispatch_action(CloseItem {
|
||||
item_id,
|
||||
pane: pane.clone(),
|
||||
}))
|
||||
})
|
||||
}
|
||||
})
|
||||
.named("close-tab-icon")
|
||||
|
|
|
@ -4,6 +4,7 @@ use client::PeerId;
|
|||
use collections::HashMap;
|
||||
use gpui::{elements::*, Axis, Border, ViewHandle};
|
||||
use project::Collaborator;
|
||||
use serde::Deserialize;
|
||||
use theme::Theme;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
|
@ -254,7 +255,8 @@ impl PaneAxis {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SplitDirection {
|
||||
Up,
|
||||
Down,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use super::Workspace;
|
||||
use gpui::{elements::*, impl_actions, platform::CursorStyle, AnyViewHandle, RenderContext};
|
||||
use gpui::{
|
||||
elements::*, impl_internal_actions, platform::CursorStyle, AnyViewHandle, RenderContext,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use theme::Theme;
|
||||
|
||||
|
@ -27,7 +29,7 @@ pub struct ToggleSidebarItem(pub SidebarItemId);
|
|||
#[derive(Clone)]
|
||||
pub struct ToggleSidebarItemFocus(pub SidebarItemId);
|
||||
|
||||
impl_actions!(workspace, [ToggleSidebarItem, ToggleSidebarItemFocus]);
|
||||
impl_internal_actions!(workspace, [ToggleSidebarItem, ToggleSidebarItemFocus]);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SidebarItemId {
|
||||
|
|
|
@ -17,7 +17,7 @@ use gpui::{
|
|||
color::Color,
|
||||
elements::*,
|
||||
geometry::{rect::RectF, vector::vec2f, PathBuilder},
|
||||
impl_actions,
|
||||
impl_internal_actions,
|
||||
json::{self, to_string_pretty, ToJson},
|
||||
keymap::Binding,
|
||||
platform::{CursorStyle, WindowOptions},
|
||||
|
@ -101,7 +101,7 @@ pub struct ToggleFollow(pub PeerId);
|
|||
#[derive(Clone)]
|
||||
pub struct JoinProject(pub JoinProjectParams);
|
||||
|
||||
impl_actions!(
|
||||
impl_internal_actions!(
|
||||
workspace,
|
||||
[Open, OpenNew, OpenPaths, ToggleFollow, JoinProject]
|
||||
);
|
||||
|
@ -630,6 +630,7 @@ pub struct WorkspaceParams {
|
|||
pub client: Arc<Client>,
|
||||
pub fs: Arc<dyn Fs>,
|
||||
pub languages: Arc<LanguageRegistry>,
|
||||
pub themes: Arc<ThemeRegistry>,
|
||||
pub user_store: ModelHandle<UserStore>,
|
||||
pub channel_list: ModelHandle<ChannelList>,
|
||||
}
|
||||
|
@ -659,6 +660,7 @@ impl WorkspaceParams {
|
|||
channel_list: cx
|
||||
.add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx)),
|
||||
client,
|
||||
themes: ThemeRegistry::new((), cx.font_cache().clone()),
|
||||
fs,
|
||||
languages,
|
||||
user_store,
|
||||
|
@ -677,6 +679,7 @@ impl WorkspaceParams {
|
|||
),
|
||||
client: app_state.client.clone(),
|
||||
fs: app_state.fs.clone(),
|
||||
themes: app_state.themes.clone(),
|
||||
languages: app_state.languages.clone(),
|
||||
user_store: app_state.user_store.clone(),
|
||||
channel_list: app_state.channel_list.clone(),
|
||||
|
@ -694,6 +697,7 @@ pub struct Workspace {
|
|||
user_store: ModelHandle<client::UserStore>,
|
||||
remote_entity_subscription: Option<Subscription>,
|
||||
fs: Arc<dyn Fs>,
|
||||
themes: Arc<ThemeRegistry>,
|
||||
modal: Option<AnyViewHandle>,
|
||||
center: PaneGroup,
|
||||
left_sidebar: Sidebar,
|
||||
|
@ -802,6 +806,7 @@ impl Workspace {
|
|||
remote_entity_subscription: None,
|
||||
user_store: params.user_store.clone(),
|
||||
fs: params.fs.clone(),
|
||||
themes: params.themes.clone(),
|
||||
left_sidebar: Sidebar::new(Side::Left),
|
||||
right_sidebar: Sidebar::new(Side::Right),
|
||||
project: params.project.clone(),
|
||||
|
@ -834,6 +839,10 @@ impl Workspace {
|
|||
&self.project
|
||||
}
|
||||
|
||||
pub fn themes(&self) -> Arc<ThemeRegistry> {
|
||||
self.themes.clone()
|
||||
}
|
||||
|
||||
pub fn worktrees<'a>(
|
||||
&self,
|
||||
cx: &'a AppContext,
|
||||
|
|
|
@ -146,7 +146,7 @@ fn main() {
|
|||
});
|
||||
journal::init(app_state.clone(), cx);
|
||||
zed::init(&app_state, cx);
|
||||
theme_selector::init(app_state.themes.clone(), cx);
|
||||
theme_selector::init(cx);
|
||||
|
||||
cx.set_menus(menus::menus(&app_state.clone()));
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ pub use editor;
|
|||
use gpui::{
|
||||
actions,
|
||||
geometry::vector::vec2f,
|
||||
impl_actions,
|
||||
keymap::Binding,
|
||||
platform::{WindowBounds, WindowOptions},
|
||||
ModelHandle, ViewContext,
|
||||
|
@ -30,12 +29,16 @@ use std::{path::PathBuf, sync::Arc};
|
|||
pub use workspace;
|
||||
use workspace::{AppState, Workspace, WorkspaceParams};
|
||||
|
||||
actions!(zed, [About, Quit, OpenSettings]);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AdjustBufferFontSize(pub f32);
|
||||
|
||||
impl_actions!(zed, [AdjustBufferFontSize]);
|
||||
actions!(
|
||||
zed,
|
||||
[
|
||||
About,
|
||||
Quit,
|
||||
OpenSettings,
|
||||
IncreaseBufferFontSize,
|
||||
DecreaseBufferFontSize
|
||||
]
|
||||
);
|
||||
|
||||
const MIN_FONT_SIZE: f32 = 6.0;
|
||||
|
||||
|
@ -48,16 +51,18 @@ lazy_static! {
|
|||
|
||||
pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
|
||||
cx.add_global_action(quit);
|
||||
cx.add_global_action({
|
||||
move |action: &AdjustBufferFontSize, cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, cx| {
|
||||
settings.buffer_font_size =
|
||||
(settings.buffer_font_size + action.0).max(MIN_FONT_SIZE);
|
||||
cx.refresh_windows();
|
||||
});
|
||||
}
|
||||
cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, cx| {
|
||||
settings.buffer_font_size = (settings.buffer_font_size + 1.0).max(MIN_FONT_SIZE);
|
||||
cx.refresh_windows();
|
||||
});
|
||||
});
|
||||
cx.add_global_action(move |_: &DecreaseBufferFontSize, cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, cx| {
|
||||
settings.buffer_font_size = (settings.buffer_font_size - 1.0).max(MIN_FONT_SIZE);
|
||||
cx.refresh_windows();
|
||||
});
|
||||
});
|
||||
|
||||
cx.add_action({
|
||||
let app_state = app_state.clone();
|
||||
move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext<Workspace>| {
|
||||
|
@ -100,8 +105,8 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
|
|||
workspace::lsp_status::init(cx);
|
||||
|
||||
cx.add_bindings(vec![
|
||||
Binding::new("cmd-=", AdjustBufferFontSize(1.), None),
|
||||
Binding::new("cmd--", AdjustBufferFontSize(-1.), None),
|
||||
Binding::new("cmd-=", IncreaseBufferFontSize, None),
|
||||
Binding::new("cmd--", DecreaseBufferFontSize, None),
|
||||
Binding::new("cmd-,", OpenSettings, None),
|
||||
])
|
||||
}
|
||||
|
@ -134,6 +139,7 @@ pub fn build_workspace(
|
|||
client: app_state.client.clone(),
|
||||
fs: app_state.fs.clone(),
|
||||
languages: app_state.languages.clone(),
|
||||
themes: app_state.themes.clone(),
|
||||
user_store: app_state.user_store.clone(),
|
||||
channel_list: app_state.channel_list.clone(),
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue