diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a3c9f73c3f..9ba9a659e0 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2147,6 +2147,7 @@ impl Editor { self.push_to_nav_history( *old_cursor_position, Some(new_cursor_position.to_point(buffer)), + false, cx, ); @@ -10809,10 +10810,15 @@ impl Editor { self.nav_history.as_ref() } + pub fn create_nav_history_entry(&mut self, cx: &mut Context) { + self.push_to_nav_history(self.selections.newest_anchor().head(), None, false, cx); + } + fn push_to_nav_history( &mut self, cursor_anchor: Anchor, new_position: Option, + is_deactivate: bool, cx: &mut Context, ) { if let Some(nav_history) = self.nav_history.as_mut() { @@ -10838,6 +10844,10 @@ impl Editor { }), cx, ); + cx.emit(EditorEvent::PushedToNavHistory { + anchor: cursor_anchor, + is_deactivate, + }) } } @@ -18617,6 +18627,10 @@ pub enum EditorEvent { }, Reloaded, CursorShapeChanged, + PushedToNavHistory { + anchor: Anchor, + is_deactivate: bool, + }, } impl EventEmitter for Editor {} diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index bd558c429f..b504ede9bc 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -737,7 +737,7 @@ impl Item for Editor { fn deactivated(&mut self, _: &mut Window, cx: &mut Context) { let selection = self.selections.newest_anchor(); - self.push_to_nav_history(selection.head(), None, cx); + self.push_to_nav_history(selection.head(), None, true, cx); } fn workspace_deactivated(&mut self, _: &mut Window, cx: &mut Context) { diff --git a/crates/vim/src/normal/mark.rs b/crates/vim/src/normal/mark.rs index 3fffa5f583..f873e4a6d5 100644 --- a/crates/vim/src/normal/mark.rs +++ b/crates/vim/src/normal/mark.rs @@ -210,6 +210,9 @@ impl Vim { let Some(mut anchors) = anchors else { return }; + self.update_editor(window, cx, |_, editor, _, cx| { + editor.create_nav_history_entry(cx); + }); let is_active_operator = self.active_operator().is_some(); if is_active_operator { if let Some(anchor) = anchors.last() { @@ -264,7 +267,7 @@ impl Vim { pub fn set_mark( &mut self, - name: String, + mut name: String, anchors: Vec, buffer_entity: &Entity, window: &mut Window, @@ -273,6 +276,9 @@ impl Vim { let Some(workspace) = self.workspace(window) else { return; }; + if name == "`" { + name = "'".to_string(); + } let entity_id = workspace.entity_id(); Vim::update_globals(cx, |vim_globals, cx| { let Some(marks_state) = vim_globals.marks.get(&entity_id) else { @@ -286,11 +292,14 @@ impl Vim { pub fn get_mark( &self, - name: &str, + mut name: &str, editor: &mut Editor, window: &mut Window, cx: &mut App, ) -> Option { + if name == "`" { + name = "'"; + } if matches!(name, "{" | "}" | "(" | ")") { let (map, selections) = editor.selections.all_display(cx); let anchors = selections @@ -331,3 +340,29 @@ pub fn jump_motion( (point, SelectionGoal::None) } + +#[cfg(test)] +mod test { + use gpui::TestAppContext; + + use crate::test::NeovimBackedTestContext; + + #[gpui::test] + async fn test_quote_mark(cx: &mut TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state("ˇHello, world!").await; + cx.simulate_shared_keystrokes("w m o").await; + cx.shared_state().await.assert_eq("Helloˇ, world!"); + cx.simulate_shared_keystrokes("$ ` o").await; + cx.shared_state().await.assert_eq("Helloˇ, world!"); + cx.simulate_shared_keystrokes("` `").await; + cx.shared_state().await.assert_eq("Hello, worldˇ!"); + cx.simulate_shared_keystrokes("` `").await; + cx.shared_state().await.assert_eq("Helloˇ, world!"); + cx.simulate_shared_keystrokes("$ m '").await; + cx.shared_state().await.assert_eq("Hello, worldˇ!"); + cx.simulate_shared_keystrokes("^ ` `").await; + cx.shared_state().await.assert_eq("Hello, worldˇ!"); + } +} diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 4743347b37..c4f9f11005 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -822,6 +822,19 @@ impl Vim { EditorEvent::Edited { .. } => self.push_to_change_list(window, cx), EditorEvent::FocusedIn => self.sync_vim_settings(window, cx), EditorEvent::CursorShapeChanged => self.cursor_shape_changed(window, cx), + EditorEvent::PushedToNavHistory { + anchor, + is_deactivate, + } => { + self.update_editor(window, cx, |vim, editor, window, cx| { + let mark = if *is_deactivate { + "\"".to_string() + } else { + "'".to_string() + }; + vim.set_mark(mark, vec![*anchor], editor.buffer(), window, cx); + }); + } _ => {} } } diff --git a/crates/vim/test_data/test_quote_mark.json b/crates/vim/test_data/test_quote_mark.json new file mode 100644 index 0000000000..6eb85580e8 --- /dev/null +++ b/crates/vim/test_data/test_quote_mark.json @@ -0,0 +1,23 @@ +{"Put":{"state":"ˇHello, world!"}} +{"Key":"w"} +{"Key":"m"} +{"Key":"o"} +{"Get":{"state":"Helloˇ, world!","mode":"Normal"}} +{"Key":"$"} +{"Key":"`"} +{"Key":"o"} +{"Get":{"state":"Helloˇ, world!","mode":"Normal"}} +{"Key":"`"} +{"Key":"`"} +{"Get":{"state":"Hello, worldˇ!","mode":"Normal"}} +{"Key":"`"} +{"Key":"`"} +{"Get":{"state":"Helloˇ, world!","mode":"Normal"}} +{"Key":"$"} +{"Key":"m"} +{"Key":"'"} +{"Get":{"state":"Hello, worldˇ!","mode":"Normal"}} +{"Key":"^"} +{"Key":"`"} +{"Key":"`"} +{"Get":{"state":"Hello, worldˇ!","mode":"Normal"}}