Detailed changes
@@ -1043,7 +1043,12 @@ impl EditorElement {
.chars_at(cursor_position)
.next()
.and_then(|(character, _)| {
- let text = SharedString::from(character.to_string());
+ // todo!() currently shape_line panics if text conatins newlines
+ let text = if character == '\n' {
+ SharedString::from(" ")
+ } else {
+ SharedString::from(character.to_string())
+ };
let len = text.len();
cx.text_system()
.shape_line(
@@ -124,17 +124,6 @@ pub fn update_settings_file<T: Settings>(
pub fn load_default_keymap(cx: &mut AppContext) {
for path in ["keymaps/default.json", "keymaps/vim.json"] {
- // TODO: Remove this conditional when we're ready to add Vim support.
- // Right now we're avoiding loading the Vim keymap to silence the warnings
- // about invalid action bindings.
- if path.contains("vim") {
- let _: Option<()> = Err(format!(
- "TODO: Skipping {path} until we're ready to add Vim support"
- ))
- .log_err();
- continue;
- }
-
KeymapFile::load_asset(path, cx).unwrap();
}
@@ -1,70 +1,79 @@
use crate::{Vim, VimEvent};
-use editor::{EditorBlurred, EditorFocused, EditorReleased};
-use gpui::AppContext;
+use editor::{Editor, EditorBlurred, EditorEvent, EditorFocused, EditorReleased};
+use gpui::{AppContext, Entity, EntityId, View, ViewContext, WindowContext};
+use workspace::item::WeakItemHandle;
pub fn init(cx: &mut AppContext) {
+ cx.observe_new_views(|_, cx: &mut ViewContext<Editor>| {
+ let editor = cx.view().clone();
+ cx.subscribe(&editor, |_, editor, event: &EditorEvent, cx| match event {
+ EditorEvent::Focused => cx.window_context().defer(|cx| focused(editor, cx)),
+ EditorEvent::Blurred => cx.window_context().defer(|cx| blurred(editor, cx)),
+ _ => {}
+ })
+ .detach();
+
+ let id = cx.view().entity_id();
+ cx.on_release(move |_, cx| released(id, cx)).detach();
+ })
+ .detach();
// todo!()
// cx.subscribe_global(focused).detach();
// cx.subscribe_global(blurred).detach();
// cx.subscribe_global(released).detach();
}
-fn focused(EditorFocused(editor): &EditorFocused, cx: &mut AppContext) {
- todo!();
- // if let Some(previously_active_editor) = Vim::read(cx).active_editor.clone() {
- // previously_active_editor.window_handle().update(cx, |cx| {
- // Vim::update(cx, |vim, cx| {
- // vim.update_active_editor(cx, |previously_active_editor, cx| {
- // vim.unhook_vim_settings(previously_active_editor, cx)
- // });
- // });
- // });
- // }
+fn focused(editor: View<Editor>, cx: &mut WindowContext) {
+ if let Some(previously_active_editor) = Vim::read(cx).active_editor.clone() {
+ Vim::update(cx, |vim, cx| {
+ vim.update_active_editor(cx, |previously_active_editor, cx| {
+ vim.unhook_vim_settings(previously_active_editor, cx)
+ });
+ });
+ }
- // editor.window().update(cx, |cx| {
- // Vim::update(cx, |vim, cx| {
- // vim.set_active_editor(editor.clone(), cx);
- // if vim.enabled {
- // cx.emit_global(VimEvent::ModeChanged {
- // mode: vim.state().mode,
- // });
- // }
- // });
- // });
+ Vim::update(cx, |vim, cx| {
+ vim.set_active_editor(editor.clone(), cx);
+ if vim.enabled {
+ // todo!()
+ // cx.emit_global(VimEvent::ModeChanged {
+ // mode: vim.state().mode,
+ // });
+ }
+ });
}
-fn blurred(EditorBlurred(editor): &EditorBlurred, cx: &mut AppContext) {
- todo!();
- // editor.window().update(cx, |cx| {
- // Vim::update(cx, |vim, cx| {
- // vim.workspace_state.recording = false;
- // vim.workspace_state.recorded_actions.clear();
- // if let Some(previous_editor) = vim.active_editor.clone() {
- // if previous_editor == editor.clone() {
- // vim.clear_operator(cx);
- // vim.active_editor = None;
- // vim.editor_subscription = None;
- // }
- // }
+fn blurred(editor: View<Editor>, cx: &mut WindowContext) {
+ Vim::update(cx, |vim, cx| {
+ vim.workspace_state.recording = false;
+ vim.workspace_state.recorded_actions.clear();
+ if let Some(previous_editor) = vim.active_editor.clone() {
+ if previous_editor
+ .upgrade()
+ .is_some_and(|previous| previous == editor.clone())
+ {
+ vim.clear_operator(cx);
+ vim.active_editor = None;
+ vim.editor_subscription = None;
+ }
+ }
- // editor.update(cx, |editor, cx| vim.unhook_vim_settings(editor, cx))
- // });
- // });
+ editor.update(cx, |editor, cx| vim.unhook_vim_settings(editor, cx))
+ });
}
-fn released(EditorReleased(editor): &EditorReleased, cx: &mut AppContext) {
- todo!();
- // editor.window().update(cx, |cx| {
- // Vim::update(cx, |vim, _| {
- // if let Some(previous_editor) = vim.active_editor.clone() {
- // if previous_editor == editor.clone() {
- // vim.active_editor = None;
- // vim.editor_subscription = None;
- // }
- // }
- // vim.editor_states.remove(&editor.id())
- // });
- // });
+fn released(entity_id: EntityId, cx: &mut WindowContext) {
+ Vim::update(cx, |vim, _| {
+ if vim
+ .active_editor
+ .as_ref()
+ .is_some_and(|previous| previous.entity_id() == entity_id)
+ {
+ vim.active_editor = None;
+ vim.editor_subscription = None;
+ }
+ vim.editor_states.remove(&entity_id)
+ });
}
// #[cfg(test)]
@@ -11,21 +11,8 @@ pub struct ModeIndicator {
impl ModeIndicator {
pub fn new(cx: &mut ViewContext<Self>) -> Self {
- let handle = cx.view().downgrade();
-
- // let _subscription = cx.subscribe_global::<VimEvent, _>(move |&event, cx| {
- // if let Some(mode_indicator) = handle.upgrade(cx) {
- // match event {
- // VimEvent::ModeChanged { mode } => {
- // mode_indicator.window().update(cx, |cx| {
- // mode_indicator.update(cx, move |mode_indicator, cx| {
- // mode_indicator.set_mode(mode, cx);
- // })
- // });
- // }
- // }
- // }
- // });
+ cx.observe_global::<Vim>(|this, cx| this.set_mode(Vim::read(cx).state().mode, cx))
+ .detach();
cx.observe_global::<SettingsStore>(move |mode_indicator, cx| {
if VimModeSetting::get_global(cx).0 {
@@ -49,85 +49,86 @@ actions!(
JoinLines,
);
-pub fn init(cx: &mut AppContext) {
- paste::init(cx);
- repeat::init(cx);
- scroll::init(cx);
- search::init(cx);
- substitute::init(cx);
- increment::init(cx);
-
- // cx.add_action(insert_after);
- // cx.add_action(insert_before);
- // cx.add_action(insert_first_non_whitespace);
- // cx.add_action(insert_end_of_line);
- // cx.add_action(insert_line_above);
- // cx.add_action(insert_line_below);
- // cx.add_action(change_case);
- // cx.add_action(yank_line);
-
- // cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
- // Vim::update(cx, |vim, cx| {
- // vim.record_current_action(cx);
- // let times = vim.take_count(cx);
- // delete_motion(vim, Motion::Left, times, cx);
- // })
- // });
- // cx.add_action(|_: &mut Workspace, _: &DeleteRight, cx| {
- // Vim::update(cx, |vim, cx| {
- // vim.record_current_action(cx);
- // let times = vim.take_count(cx);
- // delete_motion(vim, Motion::Right, times, cx);
- // })
- // });
- // cx.add_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| {
- // Vim::update(cx, |vim, cx| {
- // vim.start_recording(cx);
- // let times = vim.take_count(cx);
- // change_motion(
- // vim,
- // Motion::EndOfLine {
- // display_lines: false,
- // },
- // times,
- // cx,
- // );
- // })
- // });
- // cx.add_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| {
- // Vim::update(cx, |vim, cx| {
- // vim.record_current_action(cx);
- // let times = vim.take_count(cx);
- // delete_motion(
- // vim,
- // Motion::EndOfLine {
- // display_lines: false,
- // },
- // times,
- // cx,
- // );
- // })
- // });
- // cx.add_action(|_: &mut Workspace, _: &JoinLines, cx| {
- // Vim::update(cx, |vim, cx| {
- // vim.record_current_action(cx);
- // let mut times = vim.take_count(cx).unwrap_or(1);
- // if vim.state().mode.is_visual() {
- // times = 1;
- // } else if times > 1 {
- // // 2J joins two lines together (same as J or 1J)
- // times -= 1;
- // }
-
- // vim.update_active_editor(cx, |editor, cx| {
- // editor.transact(cx, |editor, cx| {
- // for _ in 0..times {
- // editor.join_lines(&Default::default(), cx)
- // }
- // })
- // })
- // })
- // })
+pub(crate) fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
+ dbg!("registering");
+ workspace.register_action(insert_after);
+ workspace.register_action(insert_before);
+ workspace.register_action(insert_first_non_whitespace);
+ workspace.register_action(insert_end_of_line);
+ workspace.register_action(insert_line_above);
+ workspace.register_action(insert_line_below);
+ workspace.register_action(change_case);
+ workspace.register_action(yank_line);
+
+ workspace.register_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
+ Vim::update(cx, |vim, cx| {
+ vim.record_current_action(cx);
+ let times = vim.take_count(cx);
+ delete_motion(vim, Motion::Left, times, cx);
+ })
+ });
+ workspace.register_action(|_: &mut Workspace, _: &DeleteRight, cx| {
+ Vim::update(cx, |vim, cx| {
+ vim.record_current_action(cx);
+ let times = vim.take_count(cx);
+ delete_motion(vim, Motion::Right, times, cx);
+ })
+ });
+ workspace.register_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| {
+ Vim::update(cx, |vim, cx| {
+ vim.start_recording(cx);
+ let times = vim.take_count(cx);
+ change_motion(
+ vim,
+ Motion::EndOfLine {
+ display_lines: false,
+ },
+ times,
+ cx,
+ );
+ })
+ });
+ workspace.register_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| {
+ Vim::update(cx, |vim, cx| {
+ vim.record_current_action(cx);
+ let times = vim.take_count(cx);
+ delete_motion(
+ vim,
+ Motion::EndOfLine {
+ display_lines: false,
+ },
+ times,
+ cx,
+ );
+ })
+ });
+ workspace.register_action(|_: &mut Workspace, _: &JoinLines, cx| {
+ Vim::update(cx, |vim, cx| {
+ vim.record_current_action(cx);
+ let mut times = vim.take_count(cx).unwrap_or(1);
+ if vim.state().mode.is_visual() {
+ times = 1;
+ } else if times > 1 {
+ // 2J joins two lines together (same as J or 1J)
+ times -= 1;
+ }
+
+ vim.update_active_editor(cx, |editor, cx| {
+ editor.transact(cx, |editor, cx| {
+ for _ in 0..times {
+ editor.join_lines(&Default::default(), cx)
+ }
+ })
+ })
+ });
+ });
+
+ // paste::init(cx);
+ // repeat::init(cx);
+ // scroll::init(cx);
+ // search::init(cx);
+ // substitute::init(cx);
+ // increment::init(cx);
}
pub fn normal_motion(
@@ -200,6 +201,7 @@ fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspa
}
fn insert_before(_: &mut Workspace, _: &InsertBefore, cx: &mut ViewContext<Workspace>) {
+ dbg!("insert before!");
Vim::update(cx, |vim, cx| {
vim.start_recording(cx);
vim.switch_mode(Mode::Insert, false, cx);
@@ -44,9 +44,7 @@ pub struct PushOperator(pub Operator);
#[derive(Action, Clone, Deserialize, PartialEq)]
struct Number(usize);
-actions!(Tab, Enter, Object, InnerObject, FindForward, FindBackward);
-// todo!
-// actions!(workspace, [ToggleVimMode]);
+actions!(Tab, Enter, Object, InnerObject, FindForward, FindBackward,);
#[derive(Copy, Clone, Debug)]
enum VimEvent {
@@ -58,43 +56,16 @@ pub fn init(cx: &mut AppContext) {
VimModeSetting::register(cx);
editor_events::init(cx);
- normal::init(cx);
+
+ cx.observe_new_views(|workspace: &mut Workspace, cx| register(workspace, cx))
+ .detach();
+
visual::init(cx);
insert::init(cx);
object::init(cx);
motion::init(cx);
command::init(cx);
- // Vim Actions
- // todo!()
- // cx.add_action(|_: &mut Workspace, &SwitchMode(mode): &SwitchMode, cx| {
- // Vim::update(cx, |vim, cx| vim.switch_mode(mode, false, cx))
- // });
- // cx.add_action(
- // |_: &mut Workspace, &PushOperator(operator): &PushOperator, cx| {
- // Vim::update(cx, |vim, cx| vim.push_operator(operator, cx))
- // },
- // );
- // cx.add_action(|_: &mut Workspace, n: &Number, cx: _| {
- // Vim::update(cx, |vim, cx| vim.push_count_digit(n.0, cx));
- // });
-
- // cx.add_action(|_: &mut Workspace, _: &Tab, cx| {
- // Vim::active_editor_input_ignored(" ".into(), cx)
- // });
-
- // cx.add_action(|_: &mut Workspace, _: &Enter, cx| {
- // Vim::active_editor_input_ignored("\n".into(), cx)
- // });
-
- // cx.add_action(|workspace: &mut Workspace, _: &ToggleVimMode, cx| {
- // let fs = workspace.app_state().fs.clone();
- // let currently_enabled = settings::get::<VimModeSetting>(cx).0;
- // update_settings_file::<VimModeSetting>(fs, cx, move |setting| {
- // *setting = Some(!currently_enabled)
- // })
- // });
-
// Any time settings change, update vim mode to match. The Vim struct
// will be initialized as disabled by default, so we filter its commands
// out when starting up.
@@ -112,6 +83,40 @@ pub fn init(cx: &mut AppContext) {
.detach();
}
+fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
+ workspace.register_action(|_: &mut Workspace, &SwitchMode(mode): &SwitchMode, cx| {
+ Vim::update(cx, |vim, cx| vim.switch_mode(mode, false, cx))
+ });
+ workspace.register_action(
+ |_: &mut Workspace, &PushOperator(operator): &PushOperator, cx| {
+ Vim::update(cx, |vim, cx| vim.push_operator(operator, cx))
+ },
+ );
+ workspace.register_action(|_: &mut Workspace, n: &Number, cx: _| {
+ Vim::update(cx, |vim, cx| vim.push_count_digit(n.0, cx));
+ });
+
+ workspace.register_action(|_: &mut Workspace, _: &Tab, cx| {
+ Vim::active_editor_input_ignored(" ".into(), cx)
+ });
+
+ workspace.register_action(|_: &mut Workspace, _: &Enter, cx| {
+ Vim::active_editor_input_ignored("\n".into(), cx)
+ });
+
+ workspace.register_action(
+ |workspace: &mut Workspace, _: &workspace::ToggleVimMode, cx| {
+ let fs = workspace.app_state().fs.clone();
+ let currently_enabled = VimModeSetting::get_global(cx).0;
+ update_settings_file::<VimModeSetting>(fs, cx, move |setting| {
+ *setting = Some(!currently_enabled)
+ })
+ },
+ );
+
+ normal::register(workspace, cx)
+}
+
pub fn observe_keystrokes(cx: &mut WindowContext) {
// todo!()
@@ -165,7 +170,7 @@ pub struct Vim {
impl Vim {
fn read(cx: &mut AppContext) -> &Self {
- cx.default_global()
+ cx.global::<Self>()
}
fn update<F, S>(cx: &mut WindowContext, update: F) -> S
@@ -114,6 +114,7 @@ actions!(
ToggleLeftDock,
ToggleRightDock,
ToggleBottomDock,
+ ToggleVimMode,
CloseAllDocks,
);