mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Fix visual objects
Adds 'a'/'i' in visual mode
This commit is contained in:
parent
d4276acab8
commit
b0ba0f8851
@ -366,7 +366,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && vim_mode == visual && !VimWaiting",
|
||||
"context": "Editor && vim_mode == visual && !VimWaiting && !VimObject",
|
||||
"bindings": {
|
||||
"u": "editor::Undo",
|
||||
"o": "vim::OtherEnd",
|
||||
@ -400,7 +400,23 @@
|
||||
"Normal"
|
||||
],
|
||||
">": "editor::Indent",
|
||||
"<": "editor::Outdent"
|
||||
"<": "editor::Outdent",
|
||||
"i": [
|
||||
"vim::PushOperator",
|
||||
{
|
||||
"Object": {
|
||||
"around": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"a": [
|
||||
"vim::PushOperator",
|
||||
{
|
||||
"Object": {
|
||||
"around": true
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -84,6 +84,35 @@ impl Object {
|
||||
| Object::SquareBrackets => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn always_expands_both_ways(self) -> bool {
|
||||
match self {
|
||||
Object::Word { .. } | Object::Sentence => false,
|
||||
Object::Quotes
|
||||
| Object::BackQuotes
|
||||
| Object::DoubleQuotes
|
||||
| Object::Parentheses
|
||||
| Object::SquareBrackets
|
||||
| Object::CurlyBrackets
|
||||
| Object::AngleBrackets => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn target_visual_mode(self, current_mode: Mode) -> Mode {
|
||||
match self {
|
||||
Object::Word { .. } if current_mode == Mode::VisualLine => Mode::Visual,
|
||||
Object::Word { .. } => current_mode,
|
||||
Object::Sentence
|
||||
| Object::Quotes
|
||||
| Object::BackQuotes
|
||||
| Object::DoubleQuotes
|
||||
| Object::Parentheses
|
||||
| Object::SquareBrackets
|
||||
| Object::CurlyBrackets
|
||||
| Object::AngleBrackets => Mode::Visual,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn range(
|
||||
self,
|
||||
map: &DisplaySnapshot,
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
use std::{borrow::Cow, cmp, sync::Arc};
|
||||
|
||||
use collections::HashMap;
|
||||
use editor::{
|
||||
@ -198,6 +198,11 @@ pub fn visual_object(object: Object, cx: &mut WindowContext) {
|
||||
Vim::update(cx, |vim, cx| {
|
||||
if let Some(Operator::Object { around }) = vim.active_operator() {
|
||||
vim.pop_operator(cx);
|
||||
let current_mode = vim.state().mode;
|
||||
let target_mode = object.target_visual_mode(current_mode);
|
||||
if target_mode != current_mode {
|
||||
vim.switch_mode(target_mode, true, cx);
|
||||
}
|
||||
|
||||
vim.update_active_editor(cx, |editor, cx| {
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
@ -213,20 +218,21 @@ pub fn visual_object(object: Object, cx: &mut WindowContext) {
|
||||
|
||||
if let Some(range) = object.range(map, head, around) {
|
||||
if !range.is_empty() {
|
||||
let expand_both_ways = if selection.is_empty() {
|
||||
true
|
||||
// contains only one character
|
||||
} else if let Some((_, start)) =
|
||||
map.reverse_chars_at(selection.end).next()
|
||||
{
|
||||
selection.start == start
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let expand_both_ways =
|
||||
if object.always_expands_both_ways() || selection.is_empty() {
|
||||
true
|
||||
// contains only one character
|
||||
} else if let Some((_, start)) =
|
||||
map.reverse_chars_at(selection.end).next()
|
||||
{
|
||||
selection.start == start
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if expand_both_ways {
|
||||
selection.start = range.start;
|
||||
selection.end = range.end;
|
||||
selection.start = cmp::min(selection.start, range.start);
|
||||
selection.end = cmp::max(selection.end, range.end);
|
||||
} else if selection.reversed {
|
||||
selection.start = range.start;
|
||||
} else {
|
||||
@ -1030,6 +1036,28 @@ mod test {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_visual_object(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
cx.set_shared_state("hello (in [parˇens] o)").await;
|
||||
cx.simulate_shared_keystrokes(["ctrl-v", "l"]).await;
|
||||
cx.simulate_shared_keystrokes(["a", "]"]).await;
|
||||
cx.assert_shared_state("hello (in «[parens]ˇ» o)").await;
|
||||
assert_eq!(cx.mode(), Mode::Visual);
|
||||
cx.simulate_shared_keystrokes(["i", "("]).await;
|
||||
cx.assert_shared_state("hello («in [parens] oˇ»)").await;
|
||||
|
||||
cx.set_shared_state("hello in a wˇord again.").await;
|
||||
cx.simulate_shared_keystrokes(["ctrl-v", "l", "i", "w"])
|
||||
.await;
|
||||
cx.assert_shared_state("hello in a w«ordˇ» again.").await;
|
||||
assert_eq!(cx.mode(), Mode::VisualBlock);
|
||||
cx.simulate_shared_keystrokes(["o", "a", "s"]).await;
|
||||
cx.assert_shared_state("«ˇhello in a word» again.").await;
|
||||
assert_eq!(cx.mode(), Mode::Visual);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_mode_across_command(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
|
19
crates/vim/test_data/test_visual_object.json
Normal file
19
crates/vim/test_data/test_visual_object.json
Normal file
@ -0,0 +1,19 @@
|
||||
{"Put":{"state":"hello (in [parˇens] o)"}}
|
||||
{"Key":"ctrl-v"}
|
||||
{"Key":"l"}
|
||||
{"Key":"a"}
|
||||
{"Key":"]"}
|
||||
{"Get":{"state":"hello (in «[parens]ˇ» o)","mode":"Visual"}}
|
||||
{"Key":"i"}
|
||||
{"Key":"("}
|
||||
{"Get":{"state":"hello («in [parens] oˇ»)","mode":"Visual"}}
|
||||
{"Put":{"state":"hello in a wˇord again."}}
|
||||
{"Key":"ctrl-v"}
|
||||
{"Key":"l"}
|
||||
{"Key":"i"}
|
||||
{"Key":"w"}
|
||||
{"Get":{"state":"hello in a w«ordˇ» again.","mode":"VisualBlock"}}
|
||||
{"Key":"o"}
|
||||
{"Key":"a"}
|
||||
{"Key":"s"}
|
||||
{"Get":{"state":"«ˇhello in a word» again.","mode":"VisualBlock"}}
|
Loading…
Reference in New Issue
Block a user