vim: gq (#18156)
Closes #ISSUE Release Notes: - vim: Added gq/gw for rewrapping lines
This commit is contained in:
parent
7dac5594cd
commit
45388805ad
7 changed files with 177 additions and 3 deletions
|
@ -168,6 +168,7 @@ impl Vim {
|
|||
Some(Operator::Yank) => self.yank_motion(motion, times, cx),
|
||||
Some(Operator::AddSurrounds { target: None }) => {}
|
||||
Some(Operator::Indent) => self.indent_motion(motion, times, IndentDirection::In, cx),
|
||||
Some(Operator::Rewrap) => self.rewrap_motion(motion, times, cx),
|
||||
Some(Operator::Outdent) => self.indent_motion(motion, times, IndentDirection::Out, cx),
|
||||
Some(Operator::Lowercase) => {
|
||||
self.change_case_motion(motion, times, CaseTarget::Lowercase, cx)
|
||||
|
@ -199,6 +200,7 @@ impl Vim {
|
|||
Some(Operator::Outdent) => {
|
||||
self.indent_object(object, around, IndentDirection::Out, cx)
|
||||
}
|
||||
Some(Operator::Rewrap) => self.rewrap_object(object, around, cx),
|
||||
Some(Operator::Lowercase) => {
|
||||
self.change_case_object(object, around, CaseTarget::Lowercase, cx)
|
||||
}
|
||||
|
@ -478,8 +480,9 @@ impl Vim {
|
|||
}
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use gpui::{KeyBinding, TestAppContext};
|
||||
use gpui::{KeyBinding, TestAppContext, UpdateGlobal};
|
||||
use indoc::indoc;
|
||||
use language::language_settings::AllLanguageSettings;
|
||||
use settings::SettingsStore;
|
||||
|
||||
use crate::{
|
||||
|
@ -1386,4 +1389,29 @@ mod test {
|
|||
cx.simulate_shared_keystrokes("2 0 r - ").await;
|
||||
cx.shared_state().await.assert_eq("ˇhello world\n");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_gq(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
cx.set_neovim_option("textwidth=5").await;
|
||||
|
||||
cx.update(|cx| {
|
||||
SettingsStore::update_global(cx, |settings, cx| {
|
||||
settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
settings.defaults.preferred_line_length = Some(5);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
cx.set_shared_state("ˇth th th th th th\n").await;
|
||||
cx.simulate_shared_keystrokes("g q q").await;
|
||||
cx.shared_state().await.assert_eq("th th\nth th\nˇth th\n");
|
||||
|
||||
cx.set_shared_state("ˇth th th th th th\nth th th th th th\n")
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes("v j g q").await;
|
||||
cx.shared_state()
|
||||
.await
|
||||
.assert_eq("th th\nth th\nth th\nth th\nth th\nˇth th\n");
|
||||
}
|
||||
}
|
||||
|
|
114
crates/vim/src/rewrap.rs
Normal file
114
crates/vim/src/rewrap.rs
Normal file
|
@ -0,0 +1,114 @@
|
|||
use crate::{motion::Motion, object::Object, state::Mode, Vim};
|
||||
use collections::HashMap;
|
||||
use editor::{display_map::ToDisplayPoint, scroll::Autoscroll, Bias, Editor};
|
||||
use gpui::actions;
|
||||
use language::SelectionGoal;
|
||||
use ui::ViewContext;
|
||||
|
||||
actions!(vim, [Rewrap]);
|
||||
|
||||
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
|
||||
Vim::action(editor, cx, |vim, _: &Rewrap, cx| {
|
||||
vim.record_current_action(cx);
|
||||
vim.take_count(cx);
|
||||
vim.store_visual_marks(cx);
|
||||
vim.update_editor(cx, |vim, editor, cx| {
|
||||
editor.transact(cx, |editor, cx| {
|
||||
let mut positions = vim.save_selection_starts(editor, cx);
|
||||
editor.rewrap_impl(false, cx);
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
if let Some(anchor) = positions.remove(&selection.id) {
|
||||
let mut point = anchor.to_display_point(map);
|
||||
*point.column_mut() = 0;
|
||||
selection.collapse_to(point, SelectionGoal::None);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
if vim.mode.is_visual() {
|
||||
vim.switch_mode(Mode::Normal, true, cx)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl Vim {
|
||||
pub(crate) fn rewrap_motion(
|
||||
&mut self,
|
||||
motion: Motion,
|
||||
times: Option<usize>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.stop_recording(cx);
|
||||
self.update_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::Right);
|
||||
selection_starts.insert(selection.id, anchor);
|
||||
motion.expand_selection(map, selection, times, false, &text_layout_details);
|
||||
});
|
||||
});
|
||||
editor.rewrap_impl(false, cx);
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
let anchor = selection_starts.remove(&selection.id).unwrap();
|
||||
let mut point = anchor.to_display_point(map);
|
||||
*point.column_mut() = 0;
|
||||
selection.collapse_to(point, SelectionGoal::None);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn rewrap_object(
|
||||
&mut self,
|
||||
object: Object,
|
||||
around: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.stop_recording(cx);
|
||||
self.update_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| {
|
||||
let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
|
||||
original_positions.insert(selection.id, anchor);
|
||||
object.expand_selection(map, selection, around);
|
||||
});
|
||||
});
|
||||
editor.rewrap_impl(false, cx);
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
let anchor = original_positions.remove(&selection.id).unwrap();
|
||||
let mut point = anchor.to_display_point(map);
|
||||
*point.column_mut() = 0;
|
||||
selection.collapse_to(point, SelectionGoal::None);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::test::NeovimBackedTestContext;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_indent_gv(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
cx.set_neovim_option("shiftwidth=4").await;
|
||||
|
||||
cx.set_shared_state("ˇhello\nworld\n").await;
|
||||
cx.simulate_shared_keystrokes("v j > g v").await;
|
||||
cx.shared_state()
|
||||
.await
|
||||
.assert_eq("« hello\n ˇ» world\n");
|
||||
}
|
||||
}
|
|
@ -72,6 +72,7 @@ pub enum Operator {
|
|||
Jump { line: bool },
|
||||
Indent,
|
||||
Outdent,
|
||||
Rewrap,
|
||||
Lowercase,
|
||||
Uppercase,
|
||||
OppositeCase,
|
||||
|
@ -454,6 +455,7 @@ impl Operator {
|
|||
Operator::Jump { line: true } => "'",
|
||||
Operator::Jump { line: false } => "`",
|
||||
Operator::Indent => ">",
|
||||
Operator::Rewrap => "gq",
|
||||
Operator::Outdent => "<",
|
||||
Operator::Uppercase => "gU",
|
||||
Operator::Lowercase => "gu",
|
||||
|
@ -482,6 +484,7 @@ impl Operator {
|
|||
Operator::Change
|
||||
| Operator::Delete
|
||||
| Operator::Yank
|
||||
| Operator::Rewrap
|
||||
| Operator::Indent
|
||||
| Operator::Outdent
|
||||
| Operator::Lowercase
|
||||
|
|
|
@ -13,6 +13,7 @@ mod motion;
|
|||
mod normal;
|
||||
mod object;
|
||||
mod replace;
|
||||
mod rewrap;
|
||||
mod state;
|
||||
mod surrounds;
|
||||
mod visual;
|
||||
|
@ -291,6 +292,7 @@ impl Vim {
|
|||
command::register(editor, cx);
|
||||
replace::register(editor, cx);
|
||||
indent::register(editor, cx);
|
||||
rewrap::register(editor, cx);
|
||||
object::register(editor, cx);
|
||||
visual::register(editor, cx);
|
||||
change_list::register(editor, cx);
|
||||
|
|
12
crates/vim/test_data/test_gq.json
Normal file
12
crates/vim/test_data/test_gq.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{"SetOption":{"value":"textwidth=5"}}
|
||||
{"Put":{"state":"ˇth th th th th th\n"}}
|
||||
{"Key":"g"}
|
||||
{"Key":"q"}
|
||||
{"Key":"q"}
|
||||
{"Get":{"state":"th th\nth th\nˇth th\n","mode":"Normal"}}
|
||||
{"Put":{"state":"ˇth th th th th th\nth th th th th th\n"}}
|
||||
{"Key":"v"}
|
||||
{"Key":"j"}
|
||||
{"Key":"g"}
|
||||
{"Key":"q"}
|
||||
{"Get":{"state":"th th\nth th\nth th\nth th\nth th\nˇth th\n","mode":"Normal"}}
|
Loading…
Add table
Add a link
Reference in a new issue