mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-25 01:34:02 +00:00
WIP
This commit is contained in:
parent
030de803fe
commit
24639ec900
8 changed files with 378 additions and 388 deletions
279
gpui/src/app.rs
279
gpui/src/app.rs
|
@ -92,6 +92,104 @@ pub trait UpdateView {
|
|||
F: FnOnce(&mut T, &mut ViewContext<T>) -> S;
|
||||
}
|
||||
|
||||
pub trait Action: 'static + AnyAction {
|
||||
type Argument: 'static + Clone;
|
||||
|
||||
const NAME: &'static str;
|
||||
}
|
||||
|
||||
pub trait AnyAction {
|
||||
fn id(&self) -> TypeId;
|
||||
fn arg_as_any(&self) -> &dyn Any;
|
||||
fn boxed_clone(&self) -> Box<dyn AnyAction>;
|
||||
fn boxed_clone_as_any(&self) -> Box<dyn Any>;
|
||||
}
|
||||
|
||||
impl Action for () {
|
||||
type Argument = ();
|
||||
|
||||
const NAME: &'static str = "()";
|
||||
}
|
||||
|
||||
impl AnyAction for () {
|
||||
fn id(&self) -> TypeId {
|
||||
TypeId::of::<()>()
|
||||
}
|
||||
|
||||
fn arg_as_any(&self) -> &dyn Any {
|
||||
&()
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn AnyAction> {
|
||||
Box::new(())
|
||||
}
|
||||
|
||||
fn boxed_clone_as_any(&self) -> Box<dyn Any> {
|
||||
Box::new(())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! action {
|
||||
($name:ident, $arg:ty) => {
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct $name(pub $arg);
|
||||
|
||||
impl $crate::Action for $name {
|
||||
type Argument = $arg;
|
||||
|
||||
const NAME: &'static str = stringify!($name);
|
||||
}
|
||||
|
||||
impl $crate::AnyAction for $name {
|
||||
fn id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<$name>()
|
||||
}
|
||||
|
||||
fn arg_as_any(&self) -> &dyn std::any::Any {
|
||||
&self.0
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn $crate::AnyAction> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn boxed_clone_as_any(&self) -> Box<dyn std::any::Any> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident) => {
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct $name;
|
||||
|
||||
impl $crate::Action for $name {
|
||||
type Argument = ();
|
||||
|
||||
const NAME: &'static str = stringify!($name);
|
||||
}
|
||||
|
||||
impl $crate::AnyAction for $name {
|
||||
fn id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<$name>()
|
||||
}
|
||||
|
||||
fn arg_as_any(&self) -> &dyn std::any::Any {
|
||||
&()
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn $crate::AnyAction> {
|
||||
Box::new(())
|
||||
}
|
||||
|
||||
fn boxed_clone_as_any(&self) -> Box<dyn std::any::Any> {
|
||||
Box::new(())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Menu<'a> {
|
||||
pub name: &'a str,
|
||||
pub items: Vec<MenuItem<'a>>,
|
||||
|
@ -101,8 +199,7 @@ pub enum MenuItem<'a> {
|
|||
Action {
|
||||
name: &'a str,
|
||||
keystroke: Option<&'a str>,
|
||||
action: &'a str,
|
||||
arg: Option<Box<dyn Any + 'static>>,
|
||||
action: Box<dyn AnyAction>,
|
||||
},
|
||||
Separator,
|
||||
}
|
||||
|
@ -136,19 +233,19 @@ impl App {
|
|||
))));
|
||||
|
||||
let cx = app.0.clone();
|
||||
foreground_platform.on_menu_command(Box::new(move |command, arg| {
|
||||
foreground_platform.on_menu_command(Box::new(move |action| {
|
||||
let mut cx = cx.borrow_mut();
|
||||
if let Some(key_window_id) = cx.cx.platform.key_window_id() {
|
||||
if let Some((presenter, _)) = cx.presenters_and_platform_windows.get(&key_window_id)
|
||||
{
|
||||
let presenter = presenter.clone();
|
||||
let path = presenter.borrow().dispatch_path(cx.as_ref());
|
||||
cx.dispatch_action_any(key_window_id, &path, command, arg.unwrap_or(&()));
|
||||
cx.dispatch_action_any(key_window_id, &path, action);
|
||||
} else {
|
||||
cx.dispatch_global_action_any(command, arg.unwrap_or(&()));
|
||||
cx.dispatch_global_action_any(action);
|
||||
}
|
||||
} else {
|
||||
cx.dispatch_global_action_any(command, arg.unwrap_or(&()));
|
||||
cx.dispatch_global_action_any(action);
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -258,23 +355,19 @@ impl TestAppContext {
|
|||
cx
|
||||
}
|
||||
|
||||
pub fn dispatch_action<T: 'static + Any>(
|
||||
pub fn dispatch_action<A: Action>(
|
||||
&self,
|
||||
window_id: usize,
|
||||
responder_chain: Vec<usize>,
|
||||
name: &str,
|
||||
arg: T,
|
||||
action: A,
|
||||
) {
|
||||
self.cx.borrow_mut().dispatch_action_any(
|
||||
window_id,
|
||||
&responder_chain,
|
||||
name,
|
||||
Box::new(arg).as_ref(),
|
||||
);
|
||||
self.cx
|
||||
.borrow_mut()
|
||||
.dispatch_action_any(window_id, &responder_chain, &action);
|
||||
}
|
||||
|
||||
pub fn dispatch_global_action<T: 'static + Any>(&self, name: &str, arg: T) {
|
||||
self.cx.borrow_mut().dispatch_global_action(name, arg);
|
||||
pub fn dispatch_global_action<A: Action>(&self, action: A) {
|
||||
self.cx.borrow_mut().dispatch_global_action(action);
|
||||
}
|
||||
|
||||
pub fn dispatch_keystroke(
|
||||
|
@ -563,17 +656,17 @@ impl ReadViewWith for TestAppContext {
|
|||
}
|
||||
|
||||
type ActionCallback =
|
||||
dyn FnMut(&mut dyn AnyView, &dyn Any, &mut MutableAppContext, usize, usize) -> bool;
|
||||
dyn FnMut(&mut dyn AnyView, &dyn AnyAction, &mut MutableAppContext, usize, usize) -> bool;
|
||||
|
||||
type GlobalActionCallback = dyn FnMut(&dyn Any, &mut MutableAppContext);
|
||||
type GlobalActionCallback = dyn FnMut(&dyn AnyAction, &mut MutableAppContext);
|
||||
|
||||
pub struct MutableAppContext {
|
||||
weak_self: Option<rc::Weak<RefCell<Self>>>,
|
||||
foreground_platform: Rc<dyn platform::ForegroundPlatform>,
|
||||
assets: Arc<AssetCache>,
|
||||
cx: AppContext,
|
||||
actions: HashMap<TypeId, HashMap<String, Vec<Box<ActionCallback>>>>,
|
||||
global_actions: HashMap<String, Vec<Box<GlobalActionCallback>>>,
|
||||
actions: HashMap<TypeId, HashMap<TypeId, Vec<Box<ActionCallback>>>>,
|
||||
global_actions: HashMap<TypeId, Vec<Box<GlobalActionCallback>>>,
|
||||
keystroke_matcher: keymap::Matcher,
|
||||
next_entity_id: usize,
|
||||
next_window_id: usize,
|
||||
|
@ -663,69 +756,53 @@ impl MutableAppContext {
|
|||
.map(|debug_elements| debug_elements(&self.cx))
|
||||
}
|
||||
|
||||
pub fn add_action<S, V, T, F>(&mut self, name: S, mut handler: F)
|
||||
pub fn add_action<A, V, F>(&mut self, mut handler: F)
|
||||
where
|
||||
S: Into<String>,
|
||||
A: Action,
|
||||
V: View,
|
||||
T: Any,
|
||||
F: 'static + FnMut(&mut V, &T, &mut ViewContext<V>),
|
||||
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>),
|
||||
{
|
||||
let name = name.into();
|
||||
let name_clone = name.clone();
|
||||
let handler = Box::new(
|
||||
move |view: &mut dyn AnyView,
|
||||
arg: &dyn Any,
|
||||
action: &dyn AnyAction,
|
||||
cx: &mut MutableAppContext,
|
||||
window_id: usize,
|
||||
view_id: usize| {
|
||||
match arg.downcast_ref() {
|
||||
Some(arg) => {
|
||||
let mut cx = ViewContext::new(cx, window_id, view_id);
|
||||
handler(
|
||||
view.as_any_mut()
|
||||
.downcast_mut()
|
||||
.expect("downcast is type safe"),
|
||||
arg,
|
||||
&mut cx,
|
||||
);
|
||||
cx.halt_action_dispatch
|
||||
}
|
||||
None => {
|
||||
log::error!("Could not downcast argument for action {}", name_clone);
|
||||
false
|
||||
}
|
||||
}
|
||||
let arg = action.arg_as_any().downcast_ref().unwrap();
|
||||
let mut cx = ViewContext::new(cx, window_id, view_id);
|
||||
handler(
|
||||
view.as_any_mut()
|
||||
.downcast_mut()
|
||||
.expect("downcast is type safe"),
|
||||
arg,
|
||||
&mut cx,
|
||||
);
|
||||
cx.halt_action_dispatch
|
||||
},
|
||||
);
|
||||
|
||||
self.actions
|
||||
.entry(TypeId::of::<V>())
|
||||
.or_default()
|
||||
.entry(name)
|
||||
.entry(TypeId::of::<A>())
|
||||
.or_default()
|
||||
.push(handler);
|
||||
}
|
||||
|
||||
pub fn add_global_action<S, T, F>(&mut self, name: S, mut handler: F)
|
||||
pub fn add_global_action<A, F>(&mut self, mut handler: F)
|
||||
where
|
||||
S: Into<String>,
|
||||
T: 'static + Any,
|
||||
F: 'static + FnMut(&T, &mut MutableAppContext),
|
||||
A: Action,
|
||||
F: 'static + FnMut(&A, &mut MutableAppContext),
|
||||
{
|
||||
let name = name.into();
|
||||
let name_clone = name.clone();
|
||||
let handler = Box::new(move |arg: &dyn Any, cx: &mut MutableAppContext| {
|
||||
if let Some(arg) = arg.downcast_ref() {
|
||||
handler(arg, cx);
|
||||
} else {
|
||||
log::error!(
|
||||
"Could not downcast argument for global action {}",
|
||||
name_clone
|
||||
);
|
||||
}
|
||||
let handler = Box::new(move |action: &dyn AnyAction, cx: &mut MutableAppContext| {
|
||||
let arg = action.arg_as_any().downcast_ref().unwrap();
|
||||
handler(arg, cx);
|
||||
});
|
||||
|
||||
self.global_actions.entry(name).or_default().push(handler);
|
||||
self.global_actions
|
||||
.entry(TypeId::of::<A>())
|
||||
.or_default()
|
||||
.push(handler);
|
||||
}
|
||||
|
||||
pub fn window_ids(&self) -> impl Iterator<Item = usize> + '_ {
|
||||
|
@ -838,22 +915,20 @@ impl MutableAppContext {
|
|||
self.pending_effects.extend(notifications);
|
||||
}
|
||||
|
||||
pub fn dispatch_action<T: 'static + Any>(
|
||||
pub fn dispatch_action<A: Action>(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
responder_chain: Vec<usize>,
|
||||
name: &str,
|
||||
arg: T,
|
||||
action: &A,
|
||||
) {
|
||||
self.dispatch_action_any(window_id, &responder_chain, name, Box::new(arg).as_ref());
|
||||
self.dispatch_action_any(window_id, &responder_chain, action);
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_action_any(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
path: &[usize],
|
||||
name: &str,
|
||||
arg: &dyn Any,
|
||||
action: &dyn AnyAction,
|
||||
) -> bool {
|
||||
self.pending_flushes += 1;
|
||||
let mut halted_dispatch = false;
|
||||
|
@ -865,10 +940,11 @@ impl MutableAppContext {
|
|||
if let Some((name, mut handlers)) = self
|
||||
.actions
|
||||
.get_mut(&type_id)
|
||||
.and_then(|h| h.remove_entry(name))
|
||||
.and_then(|h| h.remove_entry(&action.id()))
|
||||
{
|
||||
for handler in handlers.iter_mut().rev() {
|
||||
let halt_dispatch = handler(view.as_mut(), arg, self, window_id, *view_id);
|
||||
let halt_dispatch =
|
||||
handler(view.as_mut(), action, self, window_id, *view_id);
|
||||
if halt_dispatch {
|
||||
halted_dispatch = true;
|
||||
break;
|
||||
|
@ -889,22 +965,22 @@ impl MutableAppContext {
|
|||
}
|
||||
|
||||
if !halted_dispatch {
|
||||
self.dispatch_global_action_any(name, arg);
|
||||
self.dispatch_global_action_any(action);
|
||||
}
|
||||
|
||||
self.flush_effects();
|
||||
halted_dispatch
|
||||
}
|
||||
|
||||
pub fn dispatch_global_action<T: 'static + Any>(&mut self, name: &str, arg: T) {
|
||||
self.dispatch_global_action_any(name, Box::new(arg).as_ref());
|
||||
pub fn dispatch_global_action<A: Action>(&mut self, action: A) {
|
||||
self.dispatch_global_action_any(&action);
|
||||
}
|
||||
|
||||
fn dispatch_global_action_any(&mut self, name: &str, arg: &dyn Any) {
|
||||
if let Some((name, mut handlers)) = self.global_actions.remove_entry(name) {
|
||||
fn dispatch_global_action_any(&mut self, action: &dyn AnyAction) {
|
||||
if let Some((name, mut handlers)) = self.global_actions.remove_entry(&action.id()) {
|
||||
self.pending_flushes += 1;
|
||||
for handler in handlers.iter_mut().rev() {
|
||||
handler(arg, self);
|
||||
handler(action, self);
|
||||
}
|
||||
self.global_actions.insert(name, handlers);
|
||||
self.flush_effects();
|
||||
|
@ -943,13 +1019,9 @@ impl MutableAppContext {
|
|||
{
|
||||
MatchResult::None => {}
|
||||
MatchResult::Pending => pending = true,
|
||||
MatchResult::Action { name, arg } => {
|
||||
if self.dispatch_action_any(
|
||||
window_id,
|
||||
&responder_chain[0..=i],
|
||||
&name,
|
||||
arg.as_ref().map(|arg| arg.as_ref()).unwrap_or(&()),
|
||||
) {
|
||||
MatchResult::Action(action) => {
|
||||
if self.dispatch_action_any(window_id, &responder_chain[0..=i], action.as_ref())
|
||||
{
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
@ -3575,31 +3647,29 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
struct ActionArg {
|
||||
foo: String,
|
||||
}
|
||||
action!(Action, &'static str);
|
||||
|
||||
let actions = Rc::new(RefCell::new(Vec::new()));
|
||||
|
||||
let actions_clone = actions.clone();
|
||||
cx.add_global_action("action", move |_: &ActionArg, _: &mut MutableAppContext| {
|
||||
cx.add_global_action(move |_: &Action, _: &mut MutableAppContext| {
|
||||
actions_clone.borrow_mut().push("global a".to_string());
|
||||
});
|
||||
|
||||
let actions_clone = actions.clone();
|
||||
cx.add_global_action("action", move |_: &ActionArg, _: &mut MutableAppContext| {
|
||||
cx.add_global_action(move |_: &Action, _: &mut MutableAppContext| {
|
||||
actions_clone.borrow_mut().push("global b".to_string());
|
||||
});
|
||||
|
||||
let actions_clone = actions.clone();
|
||||
cx.add_action("action", move |view: &mut ViewA, arg: &ActionArg, cx| {
|
||||
assert_eq!(arg.foo, "bar");
|
||||
cx.add_action(move |view: &mut ViewA, action: &Action, cx| {
|
||||
assert_eq!(action.0, "bar");
|
||||
cx.propagate_action();
|
||||
actions_clone.borrow_mut().push(format!("{} a", view.id));
|
||||
});
|
||||
|
||||
let actions_clone = actions.clone();
|
||||
cx.add_action("action", move |view: &mut ViewA, _: &ActionArg, cx| {
|
||||
cx.add_action(move |view: &mut ViewA, _: &Action, cx| {
|
||||
if view.id != 1 {
|
||||
cx.propagate_action();
|
||||
}
|
||||
|
@ -3607,13 +3677,13 @@ mod tests {
|
|||
});
|
||||
|
||||
let actions_clone = actions.clone();
|
||||
cx.add_action("action", move |view: &mut ViewB, _: &ActionArg, cx| {
|
||||
cx.add_action(move |view: &mut ViewB, action: &Action, cx| {
|
||||
cx.propagate_action();
|
||||
actions_clone.borrow_mut().push(format!("{} c", view.id));
|
||||
});
|
||||
|
||||
let actions_clone = actions.clone();
|
||||
cx.add_action("action", move |view: &mut ViewB, _: &ActionArg, cx| {
|
||||
cx.add_action(move |view: &mut ViewB, action: &Action, cx| {
|
||||
cx.propagate_action();
|
||||
actions_clone.borrow_mut().push(format!("{} d", view.id));
|
||||
});
|
||||
|
@ -3626,8 +3696,7 @@ mod tests {
|
|||
cx.dispatch_action(
|
||||
window_id,
|
||||
vec![view_1.id(), view_2.id(), view_3.id(), view_4.id()],
|
||||
"action",
|
||||
ActionArg { foo: "bar".into() },
|
||||
&Action("bar"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -3640,8 +3709,7 @@ mod tests {
|
|||
cx.dispatch_action(
|
||||
window_id,
|
||||
vec![view_2.id(), view_3.id(), view_4.id()],
|
||||
"action",
|
||||
ActionArg { foo: "bar".into() },
|
||||
&Action("bar"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -3654,10 +3722,7 @@ mod tests {
|
|||
fn test_dispatch_keystroke(cx: &mut MutableAppContext) {
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ActionArg {
|
||||
key: String,
|
||||
}
|
||||
action!(Action, &'static str);
|
||||
|
||||
struct View {
|
||||
id: usize,
|
||||
|
@ -3704,16 +3769,18 @@ mod tests {
|
|||
|
||||
// This keymap's only binding dispatches an action on view 2 because that view will have
|
||||
// "a" and "b" in its context, but not "c".
|
||||
let binding = keymap::Binding::new("a", "action", Some("a && b && !c"))
|
||||
.with_arg(ActionArg { key: "a".into() });
|
||||
cx.add_bindings(vec![binding]);
|
||||
cx.add_bindings(vec![keymap::Binding::new(
|
||||
"a",
|
||||
Action("a"),
|
||||
Some("a && b && !c"),
|
||||
)]);
|
||||
|
||||
let handled_action = Rc::new(Cell::new(false));
|
||||
let handled_action_clone = handled_action.clone();
|
||||
cx.add_action("action", move |view: &mut View, arg: &ActionArg, _| {
|
||||
cx.add_action(move |view: &mut View, action: &Action, _| {
|
||||
handled_action_clone.set(true);
|
||||
assert_eq!(view.id, 2);
|
||||
assert_eq!(arg.key, "a");
|
||||
assert_eq!(action.0, "a");
|
||||
});
|
||||
|
||||
cx.dispatch_keystroke(
|
||||
|
|
|
@ -5,6 +5,8 @@ use std::{
|
|||
};
|
||||
use tree_sitter::{Language, Node, Parser};
|
||||
|
||||
use crate::{Action, AnyAction};
|
||||
|
||||
extern "C" {
|
||||
fn tree_sitter_context_predicate() -> Language;
|
||||
}
|
||||
|
@ -24,8 +26,7 @@ pub struct Keymap(Vec<Binding>);
|
|||
|
||||
pub struct Binding {
|
||||
keystrokes: Vec<Keystroke>,
|
||||
action: String,
|
||||
action_arg: Option<Box<dyn ActionArg>>,
|
||||
action: Box<dyn AnyAction>,
|
||||
context: Option<ContextPredicate>,
|
||||
}
|
||||
|
||||
|
@ -70,10 +71,7 @@ where
|
|||
pub enum MatchResult {
|
||||
None,
|
||||
Pending,
|
||||
Action {
|
||||
name: String,
|
||||
arg: Option<Box<dyn Any>>,
|
||||
},
|
||||
Action(Box<dyn AnyAction>),
|
||||
}
|
||||
|
||||
impl Matcher {
|
||||
|
@ -117,10 +115,7 @@ impl Matcher {
|
|||
{
|
||||
if binding.keystrokes.len() == pending.keystrokes.len() {
|
||||
self.pending.remove(&view_id);
|
||||
return MatchResult::Action {
|
||||
name: binding.action.clone(),
|
||||
arg: binding.action_arg.as_ref().map(|arg| (*arg).boxed_clone()),
|
||||
};
|
||||
return MatchResult::Action(binding.action.boxed_clone());
|
||||
} else {
|
||||
retain_pending = true;
|
||||
pending.context = Some(cx.clone());
|
||||
|
@ -153,19 +148,26 @@ impl Keymap {
|
|||
}
|
||||
}
|
||||
|
||||
mod menu {
|
||||
use crate::action;
|
||||
|
||||
action!(SelectPrev);
|
||||
action!(SelectNext);
|
||||
}
|
||||
|
||||
impl Default for Keymap {
|
||||
fn default() -> Self {
|
||||
Self(vec![
|
||||
Binding::new("up", "menu:select_prev", Some("menu")),
|
||||
Binding::new("ctrl-p", "menu:select_prev", Some("menu")),
|
||||
Binding::new("down", "menu:select_next", Some("menu")),
|
||||
Binding::new("ctrl-n", "menu:select_next", Some("menu")),
|
||||
Binding::new("up", menu::SelectPrev, Some("menu")),
|
||||
Binding::new("ctrl-p", menu::SelectPrev, Some("menu")),
|
||||
Binding::new("down", menu::SelectNext, Some("menu")),
|
||||
Binding::new("ctrl-n", menu::SelectNext, Some("menu")),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl Binding {
|
||||
pub fn new<S: Into<String>>(keystrokes: &str, action: S, context: Option<&str>) -> Self {
|
||||
pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
|
||||
let context = if let Some(context) = context {
|
||||
Some(ContextPredicate::parse(context).unwrap())
|
||||
} else {
|
||||
|
@ -177,16 +179,10 @@ impl Binding {
|
|||
.split_whitespace()
|
||||
.map(|key| Keystroke::parse(key).unwrap())
|
||||
.collect(),
|
||||
action: action.into(),
|
||||
action_arg: None,
|
||||
action: Box::new(action),
|
||||
context,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_arg<T: 'static + Any + Clone>(mut self, arg: T) -> Self {
|
||||
self.action_arg = Some(Box::new(arg));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Keystroke {
|
||||
|
@ -328,6 +324,8 @@ impl ContextPredicate {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::action;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
@ -417,15 +415,19 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_matcher() -> anyhow::Result<()> {
|
||||
action!(A, &'static str);
|
||||
action!(B);
|
||||
action!(Ab);
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct ActionArg {
|
||||
a: &'static str,
|
||||
}
|
||||
|
||||
let keymap = Keymap(vec![
|
||||
Binding::new("a", "a", Some("a")).with_arg(ActionArg { a: "b" }),
|
||||
Binding::new("b", "b", Some("a")),
|
||||
Binding::new("a b", "a_b", Some("a || b")),
|
||||
Binding::new("a", A("x"), Some("a")),
|
||||
Binding::new("b", B, Some("a")),
|
||||
Binding::new("a b", Ab, Some("a || b")),
|
||||
]);
|
||||
|
||||
let mut ctx_a = Context::default();
|
||||
|
@ -437,31 +439,19 @@ mod tests {
|
|||
let mut matcher = Matcher::new(keymap);
|
||||
|
||||
// Basic match
|
||||
assert_eq!(
|
||||
matcher.test_keystroke("a", 1, &ctx_a),
|
||||
Some(("a".to_string(), Some(ActionArg { a: "b" })))
|
||||
);
|
||||
assert_eq!(matcher.test_keystroke("a", 1, &ctx_a), Some(A("x")));
|
||||
|
||||
// Multi-keystroke match
|
||||
assert_eq!(matcher.test_keystroke::<()>("a", 1, &ctx_b), None);
|
||||
assert_eq!(
|
||||
matcher.test_keystroke::<()>("b", 1, &ctx_b),
|
||||
Some(("a_b".to_string(), None))
|
||||
);
|
||||
assert_eq!(matcher.test_keystroke("b", 1, &ctx_b), Some(Ab));
|
||||
|
||||
// Failed matches don't interfere with matching subsequent keys
|
||||
assert_eq!(matcher.test_keystroke::<()>("x", 1, &ctx_a), None);
|
||||
assert_eq!(
|
||||
matcher.test_keystroke("a", 1, &ctx_a),
|
||||
Some(("a".to_string(), Some(ActionArg { a: "b" })))
|
||||
);
|
||||
assert_eq!(matcher.test_keystroke("a", 1, &ctx_a), Some(A("x")));
|
||||
|
||||
// Pending keystrokes are cleared when the context changes
|
||||
assert_eq!(matcher.test_keystroke::<()>("a", 1, &ctx_b), None);
|
||||
assert_eq!(
|
||||
matcher.test_keystroke::<()>("b", 1, &ctx_a),
|
||||
Some(("b".to_string(), None))
|
||||
);
|
||||
assert_eq!(matcher.test_keystroke("b", 1, &ctx_a), Some(B));
|
||||
|
||||
let mut ctx_c = Context::default();
|
||||
ctx_c.set.insert("c".into());
|
||||
|
@ -469,25 +459,22 @@ mod tests {
|
|||
// Pending keystrokes are maintained per-view
|
||||
assert_eq!(matcher.test_keystroke::<()>("a", 1, &ctx_b), None);
|
||||
assert_eq!(matcher.test_keystroke::<()>("a", 2, &ctx_c), None);
|
||||
assert_eq!(
|
||||
matcher.test_keystroke::<()>("b", 1, &ctx_b),
|
||||
Some(("a_b".to_string(), None))
|
||||
);
|
||||
assert_eq!(matcher.test_keystroke("b", 1, &ctx_b), Some(Ab));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Matcher {
|
||||
fn test_keystroke<A: Any + Clone>(
|
||||
fn test_keystroke<A: Action>(
|
||||
&mut self,
|
||||
keystroke: &str,
|
||||
view_id: usize,
|
||||
cx: &Context,
|
||||
) -> Option<(String, Option<A>)> {
|
||||
if let MatchResult::Action { name, arg } =
|
||||
) -> Option<A> {
|
||||
if let MatchResult::Action(action) =
|
||||
self.push_keystroke(Keystroke::parse(keystroke).unwrap(), view_id, cx)
|
||||
{
|
||||
Some((name, arg.and_then(|arg| arg.downcast_ref::<A>().cloned())))
|
||||
Some(*action.boxed_clone_as_any().downcast().unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
text_layout::LineLayout,
|
||||
ClipboardItem, Menu, Scene,
|
||||
AnyAction, ClipboardItem, Menu, Scene,
|
||||
};
|
||||
use async_task::Runnable;
|
||||
pub use event::Event;
|
||||
|
@ -56,7 +56,7 @@ pub(crate) trait ForegroundPlatform {
|
|||
fn on_open_files(&self, callback: Box<dyn FnMut(Vec<PathBuf>)>);
|
||||
fn run(&self, on_finish_launching: Box<dyn FnOnce() -> ()>);
|
||||
|
||||
fn on_menu_command(&self, callback: Box<dyn FnMut(&str, Option<&dyn Any>)>);
|
||||
fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn AnyAction)>);
|
||||
fn set_menus(&self, menus: Vec<Menu>);
|
||||
fn prompt_for_paths(
|
||||
&self,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use super::{BoolExt as _, Dispatcher, FontSystem, Window};
|
||||
use crate::{executor, keymap::Keystroke, platform, ClipboardItem, Event, Menu, MenuItem};
|
||||
use crate::{
|
||||
executor, keymap::Keystroke, platform, AnyAction, ClipboardItem, Event, Menu, MenuItem,
|
||||
};
|
||||
use block::ConcreteBlock;
|
||||
use cocoa::{
|
||||
appkit::{
|
||||
|
@ -90,10 +92,10 @@ pub struct MacForegroundPlatformState {
|
|||
become_active: Option<Box<dyn FnMut()>>,
|
||||
resign_active: Option<Box<dyn FnMut()>>,
|
||||
event: Option<Box<dyn FnMut(crate::Event) -> bool>>,
|
||||
menu_command: Option<Box<dyn FnMut(&str, Option<&dyn Any>)>>,
|
||||
menu_command: Option<Box<dyn FnMut(&dyn AnyAction)>>,
|
||||
open_files: Option<Box<dyn FnMut(Vec<PathBuf>)>>,
|
||||
finish_launching: Option<Box<dyn FnOnce() -> ()>>,
|
||||
menu_actions: Vec<(String, Option<Box<dyn Any>>)>,
|
||||
menu_actions: Vec<Box<dyn AnyAction>>,
|
||||
}
|
||||
|
||||
impl MacForegroundPlatform {
|
||||
|
@ -121,7 +123,6 @@ impl MacForegroundPlatform {
|
|||
name,
|
||||
keystroke,
|
||||
action,
|
||||
arg,
|
||||
} => {
|
||||
if let Some(keystroke) = keystroke {
|
||||
let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| {
|
||||
|
@ -162,7 +163,7 @@ impl MacForegroundPlatform {
|
|||
|
||||
let tag = state.menu_actions.len() as NSInteger;
|
||||
let _: () = msg_send![item, setTag: tag];
|
||||
state.menu_actions.push((action.to_string(), arg));
|
||||
state.menu_actions.push(action);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +216,7 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
|
|||
}
|
||||
}
|
||||
|
||||
fn on_menu_command(&self, callback: Box<dyn FnMut(&str, Option<&dyn Any>)>) {
|
||||
fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn AnyAction)>) {
|
||||
self.0.borrow_mut().menu_command = Some(callback);
|
||||
}
|
||||
|
||||
|
@ -623,8 +624,8 @@ extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
|
|||
if let Some(mut callback) = platform.menu_command.take() {
|
||||
let tag: NSInteger = msg_send![item, tag];
|
||||
let index = tag as usize;
|
||||
if let Some((action, arg)) = platform.menu_actions.get(index) {
|
||||
callback(action, arg.as_ref().map(Box::as_ref));
|
||||
if let Some(action) = platform.menu_actions.get(index) {
|
||||
callback(action.as_ref());
|
||||
}
|
||||
platform.menu_command = Some(callback);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ClipboardItem;
|
||||
use crate::{AnyAction, ClipboardItem};
|
||||
use parking_lot::Mutex;
|
||||
use pathfinder_geometry::vector::Vector2F;
|
||||
use std::{
|
||||
|
@ -62,7 +62,7 @@ impl super::ForegroundPlatform for ForegroundPlatform {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_menu_command(&self, _: Box<dyn FnMut(&str, Option<&dyn Any>)>) {}
|
||||
fn on_menu_command(&self, _: Box<dyn FnMut(&dyn AnyAction)>) {}
|
||||
|
||||
fn set_menus(&self, _: Vec<crate::Menu>) {}
|
||||
|
||||
|
|
|
@ -5,12 +5,11 @@ use crate::{
|
|||
json::{self, ToJson},
|
||||
platform::Event,
|
||||
text_layout::TextLayoutCache,
|
||||
AssetCache, ElementBox, Scene,
|
||||
Action, AnyAction, AssetCache, ElementBox, Scene,
|
||||
};
|
||||
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
||||
use serde_json::json;
|
||||
use std::{
|
||||
any::Any,
|
||||
collections::{HashMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
|
@ -144,7 +143,7 @@ impl Presenter {
|
|||
|
||||
let mut event_cx = EventContext {
|
||||
rendered_views: &mut self.rendered_views,
|
||||
actions: Default::default(),
|
||||
dispatched_actions: Default::default(),
|
||||
font_cache: &self.font_cache,
|
||||
text_layout_cache: &self.text_layout_cache,
|
||||
view_stack: Default::default(),
|
||||
|
@ -154,18 +153,13 @@ impl Presenter {
|
|||
event_cx.dispatch_event(root_view_id, &event);
|
||||
|
||||
let invalidated_views = event_cx.invalidated_views;
|
||||
let actions = event_cx.actions;
|
||||
let dispatch_directives = event_cx.dispatched_actions;
|
||||
|
||||
for view_id in invalidated_views {
|
||||
cx.notify_view(self.window_id, view_id);
|
||||
}
|
||||
for action in actions {
|
||||
cx.dispatch_action_any(
|
||||
self.window_id,
|
||||
&action.path,
|
||||
action.name,
|
||||
action.arg.as_ref(),
|
||||
);
|
||||
for directive in dispatch_directives {
|
||||
cx.dispatch_action_any(self.window_id, &directive.path, directive.action.as_ref());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -183,10 +177,9 @@ impl Presenter {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ActionToDispatch {
|
||||
pub struct DispatchDirective {
|
||||
pub path: Vec<usize>,
|
||||
pub name: &'static str,
|
||||
pub arg: Box<dyn Any>,
|
||||
pub action: Box<dyn AnyAction>,
|
||||
}
|
||||
|
||||
pub struct LayoutContext<'a> {
|
||||
|
@ -249,7 +242,7 @@ impl<'a> PaintContext<'a> {
|
|||
|
||||
pub struct EventContext<'a> {
|
||||
rendered_views: &'a mut HashMap<usize, ElementBox>,
|
||||
actions: Vec<ActionToDispatch>,
|
||||
dispatched_actions: Vec<DispatchDirective>,
|
||||
pub font_cache: &'a FontCache,
|
||||
pub text_layout_cache: &'a TextLayoutCache,
|
||||
pub app: &'a mut MutableAppContext,
|
||||
|
@ -270,11 +263,10 @@ impl<'a> EventContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn dispatch_action<A: 'static + Any>(&mut self, name: &'static str, arg: A) {
|
||||
self.actions.push(ActionToDispatch {
|
||||
pub fn dispatch_action<A: Action>(&mut self, action: A) {
|
||||
self.dispatched_actions.push(DispatchDirective {
|
||||
path: self.view_stack.clone(),
|
||||
name,
|
||||
arg: Box::new(arg),
|
||||
action: Box::new(action),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ pub use display_map::DisplayPoint;
|
|||
use display_map::*;
|
||||
pub use element::*;
|
||||
use gpui::{
|
||||
color::Color, font_cache::FamilyId, fonts::Properties as FontProperties,
|
||||
action, color::Color, font_cache::FamilyId, fonts::Properties as FontProperties,
|
||||
geometry::vector::Vector2F, keymap::Binding, text_layout, AppContext, ClipboardItem, Element,
|
||||
ElementBox, Entity, FontCache, ModelHandle, MutableAppContext, RenderContext, Task,
|
||||
TextLayoutCache, View, ViewContext, WeakViewHandle,
|
||||
|
@ -40,224 +40,167 @@ use std::{
|
|||
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
||||
const MAX_LINE_LEN: usize = 1024;
|
||||
|
||||
action!(Cancel);
|
||||
action!(Backspace);
|
||||
action!(Delete);
|
||||
action!(Newline);
|
||||
action!(Insert, String);
|
||||
action!(DeleteLine);
|
||||
action!(DeleteToPreviousWordBoundary);
|
||||
action!(DeleteToNextWordBoundary);
|
||||
action!(DeleteToBeginningOfLine);
|
||||
action!(DeleteToEndOfLine);
|
||||
action!(CutToEndOfLine);
|
||||
action!(DuplicateLine);
|
||||
action!(MoveLineUp);
|
||||
action!(MoveLineDown);
|
||||
action!(Cut);
|
||||
action!(Copy);
|
||||
action!(Paste);
|
||||
action!(Undo);
|
||||
action!(Redo);
|
||||
action!(MoveUp);
|
||||
action!(MoveDown);
|
||||
action!(MoveLeft);
|
||||
action!(MoveRight);
|
||||
action!(MoveToPreviousWordBoundary);
|
||||
action!(MoveToNextWordBoundary);
|
||||
action!(MoveToBeginningOfLine);
|
||||
action!(MoveToEndOfLine);
|
||||
action!(MoveToBeginning);
|
||||
action!(MoveToEnd);
|
||||
action!(SelectUp);
|
||||
action!(SelectDown);
|
||||
action!(SelectLeft);
|
||||
action!(SelectRight);
|
||||
action!(SelectToPreviousWordBoundary);
|
||||
action!(SelectToNextWordBoundary);
|
||||
action!(SelectToBeginningOfLine, bool);
|
||||
action!(SelectToEndOfLine);
|
||||
action!(SelectToBeginning);
|
||||
action!(SelectToEnd);
|
||||
action!(SelectAll);
|
||||
action!(SelectLine);
|
||||
action!(SplitSelectionIntoLines);
|
||||
action!(AddSelectionAbove);
|
||||
action!(AddSelectionBelow);
|
||||
action!(SelectLargerSyntaxNode);
|
||||
action!(SelectSmallerSyntaxNode);
|
||||
action!(MoveToEnclosingBracket);
|
||||
action!(PageUp);
|
||||
action!(PageDown);
|
||||
action!(Fold);
|
||||
action!(Unfold);
|
||||
action!(FoldSelectedRanges);
|
||||
action!(Scroll, Vector2F);
|
||||
action!(Select, SelectPhase);
|
||||
|
||||
pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_bindings(vec![
|
||||
Binding::new("escape", "buffer:cancel", Some("BufferView")),
|
||||
Binding::new("backspace", "buffer:backspace", Some("BufferView")),
|
||||
Binding::new("ctrl-h", "buffer:backspace", Some("BufferView")),
|
||||
Binding::new("delete", "buffer:delete", Some("BufferView")),
|
||||
Binding::new("ctrl-d", "buffer:delete", Some("BufferView")),
|
||||
Binding::new("enter", "buffer:newline", Some("BufferView")),
|
||||
Binding::new("tab", "buffer:insert", Some("BufferView")).with_arg("\t".to_string()),
|
||||
Binding::new("ctrl-shift-K", "buffer:delete_line", Some("BufferView")),
|
||||
Binding::new("escape", Cancel, Some("BufferView")),
|
||||
Binding::new("backspace", Backspace, Some("BufferView")),
|
||||
Binding::new("ctrl-h", Backspace, Some("BufferView")),
|
||||
Binding::new("delete", Delete, Some("BufferView")),
|
||||
Binding::new("ctrl-d", Delete, Some("BufferView")),
|
||||
Binding::new("enter", Newline, Some("BufferView")),
|
||||
Binding::new("tab", Insert("\t".into()), Some("BufferView")),
|
||||
Binding::new("ctrl-shift-K", DeleteLine, Some("BufferView")),
|
||||
Binding::new(
|
||||
"alt-backspace",
|
||||
"buffer:delete_to_previous_word_boundary",
|
||||
DeleteToPreviousWordBoundary,
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"alt-h",
|
||||
"buffer:delete_to_previous_word_boundary",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"alt-delete",
|
||||
"buffer:delete_to_next_word_boundary",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"alt-d",
|
||||
"buffer:delete_to_next_word_boundary",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"cmd-backspace",
|
||||
"buffer:delete_to_beginning_of_line",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"cmd-delete",
|
||||
"buffer:delete_to_end_of_line",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new("ctrl-k", "buffer:cut_to_end_of_line", Some("BufferView")),
|
||||
Binding::new("cmd-shift-D", "buffer:duplicate_line", Some("BufferView")),
|
||||
Binding::new("ctrl-cmd-up", "buffer:move_line_up", Some("BufferView")),
|
||||
Binding::new("ctrl-cmd-down", "buffer:move_line_down", Some("BufferView")),
|
||||
Binding::new("cmd-x", "buffer:cut", Some("BufferView")),
|
||||
Binding::new("cmd-c", "buffer:copy", Some("BufferView")),
|
||||
Binding::new("cmd-v", "buffer:paste", Some("BufferView")),
|
||||
Binding::new("cmd-z", "buffer:undo", Some("BufferView")),
|
||||
Binding::new("cmd-shift-Z", "buffer:redo", Some("BufferView")),
|
||||
Binding::new("up", "buffer:move_up", Some("BufferView")),
|
||||
Binding::new("down", "buffer:move_down", Some("BufferView")),
|
||||
Binding::new("left", "buffer:move_left", Some("BufferView")),
|
||||
Binding::new("right", "buffer:move_right", Some("BufferView")),
|
||||
Binding::new("ctrl-p", "buffer:move_up", Some("BufferView")),
|
||||
Binding::new("ctrl-n", "buffer:move_down", Some("BufferView")),
|
||||
Binding::new("ctrl-b", "buffer:move_left", Some("BufferView")),
|
||||
Binding::new("ctrl-f", "buffer:move_right", Some("BufferView")),
|
||||
Binding::new(
|
||||
"alt-left",
|
||||
"buffer:move_to_previous_word_boundary",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"alt-b",
|
||||
"buffer:move_to_previous_word_boundary",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"alt-right",
|
||||
"buffer:move_to_next_word_boundary",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"alt-f",
|
||||
"buffer:move_to_next_word_boundary",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"cmd-left",
|
||||
"buffer:move_to_beginning_of_line",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"ctrl-a",
|
||||
"buffer:move_to_beginning_of_line",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"cmd-right",
|
||||
"buffer:move_to_end_of_line",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new("ctrl-e", "buffer:move_to_end_of_line", Some("BufferView")),
|
||||
Binding::new("cmd-up", "buffer:move_to_beginning", Some("BufferView")),
|
||||
Binding::new("cmd-down", "buffer:move_to_end", Some("BufferView")),
|
||||
Binding::new("shift-up", "buffer:select_up", Some("BufferView")),
|
||||
Binding::new("ctrl-shift-P", "buffer:select_up", Some("BufferView")),
|
||||
Binding::new("shift-down", "buffer:select_down", Some("BufferView")),
|
||||
Binding::new("ctrl-shift-N", "buffer:select_down", Some("BufferView")),
|
||||
Binding::new("shift-left", "buffer:select_left", Some("BufferView")),
|
||||
Binding::new("ctrl-shift-B", "buffer:select_left", Some("BufferView")),
|
||||
Binding::new("shift-right", "buffer:select_right", Some("BufferView")),
|
||||
Binding::new("ctrl-shift-F", "buffer:select_right", Some("BufferView")),
|
||||
Binding::new("alt-h", DeleteToPreviousWordBoundary, Some("BufferView")),
|
||||
Binding::new("alt-delete", DeleteToNextWordBoundary, Some("BufferView")),
|
||||
Binding::new("alt-d", DeleteToNextWordBoundary, Some("BufferView")),
|
||||
Binding::new("cmd-backspace", DeleteToBeginningOfLine, Some("BufferView")),
|
||||
Binding::new("cmd-delete", DeleteToEndOfLine, Some("BufferView")),
|
||||
Binding::new("ctrl-k", CutToEndOfLine, Some("BufferView")),
|
||||
Binding::new("cmd-shift-D", DuplicateLine, Some("BufferView")),
|
||||
Binding::new("ctrl-cmd-up", MoveLineUp, Some("BufferView")),
|
||||
Binding::new("ctrl-cmd-down", MoveLineDown, Some("BufferView")),
|
||||
Binding::new("cmd-x", Cut, Some("BufferView")),
|
||||
Binding::new("cmd-c", Copy, Some("BufferView")),
|
||||
Binding::new("cmd-v", Paste, Some("BufferView")),
|
||||
Binding::new("cmd-z", Undo, Some("BufferView")),
|
||||
Binding::new("cmd-shift-Z", Redo, Some("BufferView")),
|
||||
Binding::new("up", MoveUp, Some("BufferView")),
|
||||
Binding::new("down", MoveDown, Some("BufferView")),
|
||||
Binding::new("left", MoveLeft, Some("BufferView")),
|
||||
Binding::new("right", MoveRight, Some("BufferView")),
|
||||
Binding::new("ctrl-p", MoveUp, Some("BufferView")),
|
||||
Binding::new("ctrl-n", MoveDown, Some("BufferView")),
|
||||
Binding::new("ctrl-b", MoveLeft, Some("BufferView")),
|
||||
Binding::new("ctrl-f", MoveRight, Some("BufferView")),
|
||||
Binding::new("alt-left", MoveToPreviousWordBoundary, Some("BufferView")),
|
||||
Binding::new("alt-b", MoveToPreviousWordBoundary, Some("BufferView")),
|
||||
Binding::new("alt-right", MoveToNextWordBoundary, Some("BufferView")),
|
||||
Binding::new("alt-f", MoveToNextWordBoundary, Some("BufferView")),
|
||||
Binding::new("cmd-left", MoveToBeginningOfLine, Some("BufferView")),
|
||||
Binding::new("ctrl-a", MoveToBeginningOfLine, Some("BufferView")),
|
||||
Binding::new("cmd-right", MoveToEndOfLine, Some("BufferView")),
|
||||
Binding::new("ctrl-e", MoveToEndOfLine, Some("BufferView")),
|
||||
Binding::new("cmd-up", MoveToBeginning, Some("BufferView")),
|
||||
Binding::new("cmd-down", MoveToEnd, Some("BufferView")),
|
||||
Binding::new("shift-up", SelectUp, Some("BufferView")),
|
||||
Binding::new("ctrl-shift-P", SelectUp, Some("BufferView")),
|
||||
Binding::new("shift-down", SelectDown, Some("BufferView")),
|
||||
Binding::new("ctrl-shift-N", SelectDown, Some("BufferView")),
|
||||
Binding::new("shift-left", SelectLeft, Some("BufferView")),
|
||||
Binding::new("ctrl-shift-B", SelectLeft, Some("BufferView")),
|
||||
Binding::new("shift-right", SelectRight, Some("BufferView")),
|
||||
Binding::new("ctrl-shift-F", SelectRight, Some("BufferView")),
|
||||
Binding::new(
|
||||
"alt-shift-left",
|
||||
"buffer:select_to_previous_word_boundary",
|
||||
SelectToPreviousWordBoundary,
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"alt-shift-B",
|
||||
"buffer:select_to_previous_word_boundary",
|
||||
SelectToPreviousWordBoundary,
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"alt-shift-right",
|
||||
"buffer:select_to_next_word_boundary",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"alt-shift-F",
|
||||
"buffer:select_to_next_word_boundary",
|
||||
SelectToNextWordBoundary,
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new("alt-shift-F", SelectToNextWordBoundary, Some("BufferView")),
|
||||
Binding::new(
|
||||
"cmd-shift-left",
|
||||
"buffer:select_to_beginning_of_line",
|
||||
SelectToBeginningOfLine(true),
|
||||
Some("BufferView"),
|
||||
)
|
||||
.with_arg(true),
|
||||
),
|
||||
Binding::new(
|
||||
"ctrl-shift-A",
|
||||
"buffer:select_to_beginning_of_line",
|
||||
Some("BufferView"),
|
||||
)
|
||||
.with_arg(true),
|
||||
Binding::new(
|
||||
"cmd-shift-right",
|
||||
"buffer:select_to_end_of_line",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"ctrl-shift-E",
|
||||
"buffer:select_to_end_of_line",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"cmd-shift-up",
|
||||
"buffer:select_to_beginning",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new("cmd-shift-down", "buffer:select_to_end", Some("BufferView")),
|
||||
Binding::new("cmd-a", "buffer:select_all", Some("BufferView")),
|
||||
Binding::new("cmd-l", "buffer:select_line", Some("BufferView")),
|
||||
Binding::new(
|
||||
"cmd-shift-L",
|
||||
"buffer:split_selection_into_lines",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"cmd-alt-up",
|
||||
"buffer:add_selection_above",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"cmd-ctrl-p",
|
||||
"buffer:add_selection_above",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"cmd-alt-down",
|
||||
"buffer:add_selection_below",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"cmd-ctrl-n",
|
||||
"buffer:add_selection_below",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"alt-up",
|
||||
"buffer:select_larger_syntax_node",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"ctrl-w",
|
||||
"buffer:select_larger_syntax_node",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"alt-down",
|
||||
"buffer:select_smaller_syntax_node",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"ctrl-shift-W",
|
||||
"buffer:select_smaller_syntax_node",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new(
|
||||
"ctrl-m",
|
||||
"buffer:move_to_enclosing_bracket",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new("pageup", "buffer:page_up", Some("BufferView")),
|
||||
Binding::new("pagedown", "buffer:page_down", Some("BufferView")),
|
||||
Binding::new("alt-cmd-[", "buffer:fold", Some("BufferView")),
|
||||
Binding::new("alt-cmd-]", "buffer:unfold", Some("BufferView")),
|
||||
Binding::new(
|
||||
"alt-cmd-f",
|
||||
"buffer:fold_selected_ranges",
|
||||
SelectToBeginningOfLine(true),
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new("cmd-shift-right", SelectToEndOfLine, Some("BufferView")),
|
||||
Binding::new("ctrl-shift-E", SelectToEndOfLine, Some("BufferView")),
|
||||
Binding::new("cmd-shift-up", SelectToBeginning, Some("BufferView")),
|
||||
Binding::new("cmd-shift-down", SelectToEnd, Some("BufferView")),
|
||||
Binding::new("cmd-a", SelectAll, Some("BufferView")),
|
||||
Binding::new("cmd-l", SelectLine, Some("BufferView")),
|
||||
Binding::new("cmd-shift-L", SplitSelectionIntoLines, Some("BufferView")),
|
||||
Binding::new("cmd-alt-up", AddSelectionAbove, Some("BufferView")),
|
||||
Binding::new("cmd-ctrl-p", AddSelectionAbove, Some("BufferView")),
|
||||
Binding::new("cmd-alt-down", AddSelectionBelow, Some("BufferView")),
|
||||
Binding::new("cmd-ctrl-n", AddSelectionBelow, Some("BufferView")),
|
||||
Binding::new("alt-up", SelectLargerSyntaxNode, Some("BufferView")),
|
||||
Binding::new("ctrl-w", SelectLargerSyntaxNode, Some("BufferView")),
|
||||
Binding::new("alt-down", SelectSmallerSyntaxNode, Some("BufferView")),
|
||||
Binding::new("ctrl-shift-W", SelectSmallerSyntaxNode, Some("BufferView")),
|
||||
Binding::new("ctrl-m", MoveToEnclosingBracket, Some("BufferView")),
|
||||
Binding::new("pageup", PageUp, Some("BufferView")),
|
||||
Binding::new("pagedown", PageDown, Some("BufferView")),
|
||||
Binding::new("alt-cmd-[", Fold, Some("BufferView")),
|
||||
Binding::new("alt-cmd-]", Unfold, Some("BufferView")),
|
||||
Binding::new("alt-cmd-f", FoldSelectedRanges, Some("BufferView")),
|
||||
]);
|
||||
|
||||
cx.add_action("buffer:scroll", |this: &mut Editor, scroll_position, cx| {
|
||||
this.set_scroll_position(*scroll_position, cx)
|
||||
});
|
||||
cx.add_action("buffer:select", Editor::select);
|
||||
cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx));
|
||||
cx.add_action(Editor::select);
|
||||
cx.add_action("buffer:cancel", Editor::cancel);
|
||||
cx.add_action("buffer:insert", Editor::insert);
|
||||
cx.add_action("buffer:newline", Editor::newline);
|
||||
|
@ -357,7 +300,7 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
cx.add_action("buffer:fold_selected_ranges", Editor::fold_selected_ranges);
|
||||
}
|
||||
|
||||
pub enum SelectAction {
|
||||
pub enum SelectPhase {
|
||||
Begin {
|
||||
position: DisplayPoint,
|
||||
add: bool,
|
||||
|
@ -612,14 +555,14 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
fn select(&mut self, arg: &SelectAction, cx: &mut ViewContext<Self>) {
|
||||
match arg {
|
||||
SelectAction::Begin { position, add } => self.begin_selection(*position, *add, cx),
|
||||
SelectAction::Update {
|
||||
fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
|
||||
match phase {
|
||||
SelectPhase::Begin { position, add } => self.begin_selection(*position, *add, cx),
|
||||
SelectPhase::Update {
|
||||
position,
|
||||
scroll_position,
|
||||
} => self.update_selection(*position, *scroll_position, cx),
|
||||
SelectAction::End => self.end_selection(cx),
|
||||
SelectPhase::End => self.end_selection(cx),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::{DisplayPoint, Editor, SelectAction, Snapshot};
|
||||
use super::{DisplayPoint, Editor, SelectPhase, Snapshot};
|
||||
use crate::time::ReplicaId;
|
||||
use gpui::{
|
||||
color::Color,
|
||||
|
@ -55,7 +55,7 @@ impl EditorElement {
|
|||
if paint.text_bounds.contains_point(position) {
|
||||
let snapshot = self.snapshot(cx.app);
|
||||
let position = paint.point_for_position(&snapshot, layout, position);
|
||||
cx.dispatch_action("buffer:select", SelectAction::Begin { position, add: cmd });
|
||||
cx.dispatch_action("buffer:select", SelectPhase::Begin { position, add: cmd });
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -64,7 +64,7 @@ impl EditorElement {
|
|||
|
||||
fn mouse_up(&self, _position: Vector2F, cx: &mut EventContext) -> bool {
|
||||
if self.view(cx.app.as_ref()).is_selecting() {
|
||||
cx.dispatch_action("buffer:select", SelectAction::End);
|
||||
cx.dispatch_action("buffer:select", SelectPhase::End);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -115,7 +115,7 @@ impl EditorElement {
|
|||
|
||||
cx.dispatch_action(
|
||||
"buffer:select",
|
||||
SelectAction::Update {
|
||||
SelectPhase::Update {
|
||||
position,
|
||||
scroll_position: (snapshot.scroll_position() + scroll_delta).clamp(
|
||||
Vector2F::zero(),
|
||||
|
|
Loading…
Reference in a new issue