@@ -44,7 +44,7 @@ fn object(object: Object, cx: &mut MutableAppContext) {
}
impl Object {
- pub fn object_range(
+ pub fn range(
self,
map: &DisplaySnapshot,
relative_to: DisplayPoint,
@@ -68,7 +68,7 @@ impl Object {
selection: &mut Selection<DisplayPoint>,
around: bool,
) {
- let range = self.object_range(map, selection.head(), around);
+ let range = self.range(map, selection.head(), around);
selection.start = range.start;
selection.end = range.end;
}
@@ -328,43 +328,58 @@ mod test {
"};
#[gpui::test]
- async fn test_change_in_word(cx: &mut gpui::TestAppContext) {
- let mut cx = NeovimBackedTestContext::new(cx)
- .await
- .binding(["c", "i", "w"]);
- cx.assert_all(WORD_LOCATIONS).await;
- let mut cx = cx.consume().binding(["c", "i", "shift-w"]);
- cx.assert_all(WORD_LOCATIONS).await;
- }
-
- #[gpui::test]
- async fn test_delete_in_word(cx: &mut gpui::TestAppContext) {
- let mut cx = NeovimBackedTestContext::new(cx)
- .await
- .binding(["d", "i", "w"]);
- cx.assert_all(WORD_LOCATIONS).await;
- let mut cx = cx.consume().binding(["d", "i", "shift-w"]);
- cx.assert_all(WORD_LOCATIONS).await;
+ async fn test_change_word_object(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.assert_binding_matches_all(["c", "i", "w"], WORD_LOCATIONS)
+ .await;
+ cx.assert_binding_matches_all(["c", "i", "shift-w"], WORD_LOCATIONS)
+ .await;
+ cx.assert_binding_matches_all(["c", "a", "w"], WORD_LOCATIONS)
+ .await;
+ cx.assert_binding_matches_all(["c", "a", "shift-w"], WORD_LOCATIONS)
+ .await;
}
#[gpui::test]
- async fn test_change_around_word(cx: &mut gpui::TestAppContext) {
- let mut cx = NeovimBackedTestContext::new(cx)
- .await
- .binding(["c", "a", "w"]);
- cx.assert_all(WORD_LOCATIONS).await;
- let mut cx = cx.consume().binding(["c", "a", "shift-w"]);
- cx.assert_all(WORD_LOCATIONS).await;
+ async fn test_delete_word_object(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.assert_binding_matches_all(["d", "i", "w"], WORD_LOCATIONS)
+ .await;
+ cx.assert_binding_matches_all(["d", "i", "shift-w"], WORD_LOCATIONS)
+ .await;
+ cx.assert_binding_matches_all(["d", "a", "w"], WORD_LOCATIONS)
+ .await;
+ cx.assert_binding_matches_all(["d", "a", "shift-w"], WORD_LOCATIONS)
+ .await;
}
#[gpui::test]
- async fn test_delete_around_word(cx: &mut gpui::TestAppContext) {
- let mut cx = NeovimBackedTestContext::new(cx)
- .await
- .binding(["d", "a", "w"]);
- cx.assert_all(WORD_LOCATIONS).await;
- let mut cx = cx.consume().binding(["d", "a", "shift-w"]);
- cx.assert_all(WORD_LOCATIONS).await;
+ async fn test_visual_word_object(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.assert_binding_matches_all(["v", "i", "w"], WORD_LOCATIONS)
+ .await;
+ // Visual text objects are slightly broken when used with non empty selections
+ // cx.assert_binding_matches_all(["v", "h", "i", "w"], WORD_LOCATIONS)
+ // .await;
+ // cx.assert_binding_matches_all(["v", "l", "i", "w"], WORD_LOCATIONS)
+ // .await;
+ cx.assert_binding_matches_all(["v", "i", "shift-w"], WORD_LOCATIONS)
+ .await;
+
+ // Visual text objects are slightly broken when used with non empty selections
+ // cx.assert_binding_matches_all(["v", "i", "h", "shift-w"], WORD_LOCATIONS)
+ // .await;
+ // cx.assert_binding_matches_all(["v", "i", "l", "shift-w"], WORD_LOCATIONS)
+ // .await;
+
+ // Visual around words is somewhat broken right now when it comes to newlines
+ // cx.assert_binding_matches_all(["v", "a", "w"], WORD_LOCATIONS)
+ // .await;
+ // cx.assert_binding_matches_all(["v", "a", "shift-w"], WORD_LOCATIONS)
+ // .await;
}
const SENTENCE_EXAMPLES: &[&'static str] = &[
@@ -390,20 +405,19 @@ mod test {
];
#[gpui::test]
- async fn test_change_in_sentence(cx: &mut gpui::TestAppContext) {
+ async fn test_change_sentence_object(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx)
.await
.binding(["c", "i", "s"]);
for sentence_example in SENTENCE_EXAMPLES {
cx.assert_all(sentence_example).await;
}
- }
- #[gpui::test]
- async fn test_delete_in_sentence(cx: &mut gpui::TestAppContext) {
- let mut cx = NeovimBackedTestContext::new(cx)
- .await
- .binding(["d", "i", "s"]);
+ let mut cx = cx.binding(["c", "a", "s"]);
+ // Resulting position is slightly incorrect for unintuitive reasons.
+ cx.add_initial_state_exemption("The quick brown?ˇ Fox Jumps! Over the lazy.");
+ // Changing around the sentence at the end of the line doesn't remove whitespace.'
+ cx.add_initial_state_exemption("The quick brown.)]\'\" Brown fox jumps.ˇ ");
for sentence_example in SENTENCE_EXAMPLES {
cx.assert_all(sentence_example).await;
@@ -411,11 +425,15 @@ mod test {
}
#[gpui::test]
- async fn test_change_around_sentence(cx: &mut gpui::TestAppContext) {
+ async fn test_delete_sentence_object(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx)
.await
- .binding(["c", "a", "s"]);
+ .binding(["d", "i", "s"]);
+ for sentence_example in SENTENCE_EXAMPLES {
+ cx.assert_all(sentence_example).await;
+ }
+ let mut cx = cx.binding(["d", "a", "s"]);
// Resulting position is slightly incorrect for unintuitive reasons.
cx.add_initial_state_exemption("The quick brown?ˇ Fox Jumps! Over the lazy.");
// Changing around the sentence at the end of the line doesn't remove whitespace.'
@@ -427,18 +445,18 @@ mod test {
}
#[gpui::test]
- async fn test_delete_around_sentence(cx: &mut gpui::TestAppContext) {
+ async fn test_visual_sentence_object(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx)
.await
- .binding(["d", "a", "s"]);
-
- // Resulting position is slightly incorrect for unintuitive reasons.
- cx.add_initial_state_exemption("The quick brown?ˇ Fox Jumps! Over the lazy.");
- // Changing around the sentence at the end of the line doesn't remove whitespace.'
- cx.add_initial_state_exemption("The quick brown.)]\'\" Brown fox jumps.ˇ ");
-
+ .binding(["v", "i", "s"]);
for sentence_example in SENTENCE_EXAMPLES {
cx.assert_all(sentence_example).await;
}
+
+ // Visual around sentences is somewhat broken right now when it comes to newlines
+ // let mut cx = cx.binding(["d", "a", "s"]);
+ // for sentence_example in SENTENCE_EXAMPLES {
+ // cx.assert_all(sentence_example).await;
+ // }
}
}
@@ -6,7 +6,13 @@ use gpui::{actions, MutableAppContext, ViewContext};
use language::{AutoindentMode, SelectionGoal};
use workspace::Workspace;
-use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim};
+use crate::{
+ motion::Motion,
+ object::Object,
+ state::{Mode, Operator},
+ utils::copy_selections_content,
+ Vim,
+};
actions!(vim, [VisualDelete, VisualChange, VisualYank, VisualPaste]);
@@ -47,7 +53,34 @@ pub fn visual_motion(motion: Motion, times: usize, cx: &mut MutableAppContext) {
});
}
-pub fn visual_object(_object: Object, _cx: &mut MutableAppContext) {}
+pub fn visual_object(object: Object, cx: &mut MutableAppContext) {
+ Vim::update(cx, |vim, cx| {
+ if let Operator::Object { around } = vim.pop_operator(cx) {
+ vim.update_active_editor(cx, |editor, cx| {
+ editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
+ s.move_with(|map, selection| {
+ let head = selection.head();
+ let mut range = object.range(map, head, around);
+ if !range.is_empty() {
+ if let Some((_, end)) = map.reverse_chars_at(range.end).next() {
+ range.end = end;
+ }
+
+ if selection.is_empty() {
+ selection.start = range.start;
+ selection.end = range.end;
+ } else if selection.reversed {
+ selection.start = range.start;
+ } else {
+ selection.end = range.end;
+ }
+ }
+ })
+ });
+ });
+ }
+ });
+}
pub fn change(_: &mut Workspace, _: &VisualChange, cx: &mut ViewContext<Workspace>) {
Vim::update(cx, |vim, cx| {
@@ -1 +1 @@
-[{"Text":"test"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}]
+[{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"test"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}]