mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
vim: Add gU/gu/g~ (#12782)
Co-Authored-By: ethanmsl@gmail.com Release Notes: - vim: Added `gu`/`gU`/`g~` for changing case. (#12565)
This commit is contained in:
parent
3eac83eece
commit
5548773b2e
@ -381,6 +381,9 @@
|
||||
"shift-s": "vim::SubstituteLine",
|
||||
">": ["vim::PushOperator", "Indent"],
|
||||
"<": ["vim::PushOperator", "Outdent"],
|
||||
"g u": ["vim::PushOperator", "Lowercase"],
|
||||
"g shift-u": ["vim::PushOperator", "Uppercase"],
|
||||
"g ~": ["vim::PushOperator", "OppositeCase"],
|
||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||
// tree-sitter related commands
|
||||
|
@ -21,6 +21,7 @@ use crate::{
|
||||
surrounds::{check_and_move_to_valid_bracket_pair, SurroundsType},
|
||||
Vim,
|
||||
};
|
||||
use case::{change_case_motion, change_case_object, CaseTarget};
|
||||
use collections::BTreeSet;
|
||||
use editor::display_map::ToDisplayPoint;
|
||||
use editor::scroll::Autoscroll;
|
||||
@ -198,6 +199,15 @@ pub fn normal_motion(
|
||||
Some(Operator::AddSurrounds { target: None }) => {}
|
||||
Some(Operator::Indent) => indent_motion(vim, motion, times, IndentDirection::In, cx),
|
||||
Some(Operator::Outdent) => indent_motion(vim, motion, times, IndentDirection::Out, cx),
|
||||
Some(Operator::Lowercase) => {
|
||||
change_case_motion(vim, motion, times, CaseTarget::Lowercase, cx)
|
||||
}
|
||||
Some(Operator::Uppercase) => {
|
||||
change_case_motion(vim, motion, times, CaseTarget::Uppercase, cx)
|
||||
}
|
||||
Some(Operator::OppositeCase) => {
|
||||
change_case_motion(vim, motion, times, CaseTarget::OppositeCase, cx)
|
||||
}
|
||||
Some(operator) => {
|
||||
// Can't do anything for text objects, Ignoring
|
||||
error!("Unexpected normal mode motion operator: {:?}", operator)
|
||||
@ -220,6 +230,15 @@ pub fn normal_object(object: Object, cx: &mut WindowContext) {
|
||||
Some(Operator::Outdent) => {
|
||||
indent_object(vim, object, around, IndentDirection::Out, cx)
|
||||
}
|
||||
Some(Operator::Lowercase) => {
|
||||
change_case_object(vim, object, around, CaseTarget::Lowercase, cx)
|
||||
}
|
||||
Some(Operator::Uppercase) => {
|
||||
change_case_object(vim, object, around, CaseTarget::Uppercase, cx)
|
||||
}
|
||||
Some(Operator::OppositeCase) => {
|
||||
change_case_object(vim, object, around, CaseTarget::OppositeCase, cx)
|
||||
}
|
||||
Some(Operator::AddSurrounds { target: None }) => {
|
||||
waiting_operator = Some(Operator::AddSurrounds {
|
||||
target: Some(SurroundsType::Object(object)),
|
||||
|
@ -1,13 +1,98 @@
|
||||
use editor::scroll::Autoscroll;
|
||||
use collections::HashMap;
|
||||
use editor::{display_map::ToDisplayPoint, scroll::Autoscroll};
|
||||
use gpui::ViewContext;
|
||||
use language::{Bias, Point};
|
||||
use language::{Bias, Point, SelectionGoal};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use ui::WindowContext;
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::{
|
||||
normal::ChangeCase, normal::ConvertToLowerCase, normal::ConvertToUpperCase, state::Mode, Vim,
|
||||
motion::Motion,
|
||||
normal::{ChangeCase, ConvertToLowerCase, ConvertToUpperCase},
|
||||
object::Object,
|
||||
state::Mode,
|
||||
Vim,
|
||||
};
|
||||
|
||||
pub enum CaseTarget {
|
||||
Lowercase,
|
||||
Uppercase,
|
||||
OppositeCase,
|
||||
}
|
||||
|
||||
pub fn change_case_motion(
|
||||
vim: &mut Vim,
|
||||
motion: Motion,
|
||||
times: Option<usize>,
|
||||
mode: CaseTarget,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
vim.stop_recording();
|
||||
vim.update_active_editor(cx, |_, editor, cx| {
|
||||
let text_layout_details = editor.text_layout_details(cx);
|
||||
editor.transact(cx, |editor, cx| {
|
||||
let mut selection_starts: HashMap<_, _> = Default::default();
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
let anchor = map.display_point_to_anchor(selection.head(), Bias::Left);
|
||||
selection_starts.insert(selection.id, anchor);
|
||||
motion.expand_selection(map, selection, times, false, &text_layout_details);
|
||||
});
|
||||
});
|
||||
match mode {
|
||||
CaseTarget::Lowercase => editor.convert_to_lower_case(&Default::default(), cx),
|
||||
CaseTarget::Uppercase => editor.convert_to_upper_case(&Default::default(), cx),
|
||||
CaseTarget::OppositeCase => {
|
||||
editor.convert_to_opposite_case(&Default::default(), cx)
|
||||
}
|
||||
}
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
let anchor = selection_starts.remove(&selection.id).unwrap();
|
||||
selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn change_case_object(
|
||||
vim: &mut Vim,
|
||||
object: Object,
|
||||
around: bool,
|
||||
mode: CaseTarget,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
vim.stop_recording();
|
||||
vim.update_active_editor(cx, |_, editor, cx| {
|
||||
editor.transact(cx, |editor, cx| {
|
||||
let mut original_positions: HashMap<_, _> = Default::default();
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
object.expand_selection(map, selection, around);
|
||||
original_positions.insert(
|
||||
selection.id,
|
||||
map.display_point_to_anchor(selection.start, Bias::Left),
|
||||
);
|
||||
});
|
||||
});
|
||||
match mode {
|
||||
CaseTarget::Lowercase => editor.convert_to_lower_case(&Default::default(), cx),
|
||||
CaseTarget::Uppercase => editor.convert_to_upper_case(&Default::default(), cx),
|
||||
CaseTarget::OppositeCase => {
|
||||
editor.convert_to_opposite_case(&Default::default(), cx)
|
||||
}
|
||||
}
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
let anchor = original_positions.remove(&selection.id).unwrap();
|
||||
selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn change_case(_: &mut Workspace, _: &ChangeCase, cx: &mut ViewContext<Workspace>) {
|
||||
manipulate_text(cx, |c| {
|
||||
if c.is_lowercase() {
|
||||
@ -180,4 +265,29 @@ mod test {
|
||||
cx.simulate_shared_keystrokes("ctrl-v j u").await;
|
||||
cx.shared_state().await.assert_eq("ˇaa\nbb\nCc");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_change_case_motion(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
// works in visual mode
|
||||
cx.set_shared_state("ˇabc def").await;
|
||||
cx.simulate_shared_keystrokes("g shift-u w").await;
|
||||
cx.shared_state().await.assert_eq("ˇABC def");
|
||||
|
||||
cx.simulate_shared_keystrokes("g u w").await;
|
||||
cx.shared_state().await.assert_eq("ˇabc def");
|
||||
|
||||
cx.simulate_shared_keystrokes("g ~ w").await;
|
||||
cx.shared_state().await.assert_eq("ˇABC def");
|
||||
|
||||
cx.simulate_shared_keystrokes(".").await;
|
||||
cx.shared_state().await.assert_eq("ˇabc def");
|
||||
|
||||
cx.set_shared_state("abˇc def").await;
|
||||
cx.simulate_shared_keystrokes("g ~ i w").await;
|
||||
cx.shared_state().await.assert_eq("ˇABC def");
|
||||
|
||||
cx.simulate_shared_keystrokes(".").await;
|
||||
cx.shared_state().await.assert_eq("ˇabc def");
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,10 @@ pub enum Operator {
|
||||
Jump { line: bool },
|
||||
Indent,
|
||||
Outdent,
|
||||
|
||||
Lowercase,
|
||||
Uppercase,
|
||||
OppositeCase,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
@ -270,6 +274,9 @@ impl Operator {
|
||||
Operator::Jump { line: false } => "`",
|
||||
Operator::Indent => ">",
|
||||
Operator::Outdent => "<",
|
||||
Operator::Uppercase => "gU",
|
||||
Operator::Lowercase => "gu",
|
||||
Operator::OppositeCase => "g~",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -539,6 +539,9 @@ impl Vim {
|
||||
| Operator::Replace
|
||||
| Operator::Indent
|
||||
| Operator::Outdent
|
||||
| Operator::Lowercase
|
||||
| Operator::Uppercase
|
||||
| Operator::OppositeCase
|
||||
) {
|
||||
self.start_recording(cx)
|
||||
};
|
||||
|
23
crates/vim/test_data/test_change_case_motion.json
Normal file
23
crates/vim/test_data/test_change_case_motion.json
Normal file
@ -0,0 +1,23 @@
|
||||
{"Put":{"state":"ˇabc def"}}
|
||||
{"Key":"g"}
|
||||
{"Key":"shift-u"}
|
||||
{"Key":"w"}
|
||||
{"Get":{"state":"ˇABC def","mode":"Normal"}}
|
||||
{"Key":"g"}
|
||||
{"Key":"u"}
|
||||
{"Key":"w"}
|
||||
{"Get":{"state":"ˇabc def","mode":"Normal"}}
|
||||
{"Key":"g"}
|
||||
{"Key":"~"}
|
||||
{"Key":"w"}
|
||||
{"Get":{"state":"ˇABC def","mode":"Normal"}}
|
||||
{"Key":"."}
|
||||
{"Get":{"state":"ˇabc def","mode":"Normal"}}
|
||||
{"Put":{"state":"abˇc def"}}
|
||||
{"Key":"g"}
|
||||
{"Key":"~"}
|
||||
{"Key":"i"}
|
||||
{"Key":"w"}
|
||||
{"Get":{"state":"ˇABC def","mode":"Normal"}}
|
||||
{"Key":"."}
|
||||
{"Get":{"state":"ˇabc def","mode":"Normal"}}
|
Loading…
Reference in New Issue
Block a user