vim: Improve lifecycle (#16477)
Closes #13579 A major painpoint in the Vim crate has been life-cycle management. We used to have one global Vim instance that tried to track per-editor state; this led to a number of subtle issues (e.g. #13579, the mode indicator being global, and quick toggling between windows letting vim mode's notion of the active editor get out of sync). This PR changes the internal structure of the code so that there is now one `Vim` instance per `Editor` (stored as an `Addon`); and the global stuff is separated out. This fixes the above problems, and tidies up a bunch of the mess in the codebase. Release Notes: * vim: Fixed accidental visual mode in project search and go to references ([#13579](https://github.com/zed-industries/zed/issues/13579)).
This commit is contained in:
parent
c4c07583c3
commit
36d51fe4a5
32 changed files with 3362 additions and 3585 deletions
|
@ -1,85 +1,87 @@
|
|||
use editor::movement;
|
||||
use gpui::{actions, ViewContext, WindowContext};
|
||||
use editor::{movement, Editor};
|
||||
use gpui::{actions, ViewContext};
|
||||
use language::Point;
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::{motion::Motion, normal::yank::copy_selections_content, Mode, Vim};
|
||||
use crate::{motion::Motion, Mode, Vim};
|
||||
|
||||
actions!(vim, [Substitute, SubstituteLine]);
|
||||
|
||||
pub(crate) fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
|
||||
workspace.register_action(|_: &mut Workspace, _: &Substitute, cx| {
|
||||
Vim::update(cx, |vim, cx| {
|
||||
vim.start_recording(cx);
|
||||
let count = vim.take_count(cx);
|
||||
substitute(vim, count, vim.state().mode == Mode::VisualLine, cx);
|
||||
})
|
||||
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
|
||||
Vim::action(editor, cx, |vim, _: &Substitute, cx| {
|
||||
vim.start_recording(cx);
|
||||
let count = vim.take_count(cx);
|
||||
vim.substitute(count, vim.mode == Mode::VisualLine, cx);
|
||||
});
|
||||
|
||||
workspace.register_action(|_: &mut Workspace, _: &SubstituteLine, cx| {
|
||||
Vim::update(cx, |vim, cx| {
|
||||
vim.start_recording(cx);
|
||||
if matches!(vim.state().mode, Mode::VisualBlock | Mode::Visual) {
|
||||
vim.switch_mode(Mode::VisualLine, false, cx)
|
||||
}
|
||||
let count = vim.take_count(cx);
|
||||
substitute(vim, count, true, cx)
|
||||
})
|
||||
Vim::action(editor, cx, |vim, _: &SubstituteLine, cx| {
|
||||
vim.start_recording(cx);
|
||||
if matches!(vim.mode, Mode::VisualBlock | Mode::Visual) {
|
||||
vim.switch_mode(Mode::VisualLine, false, cx)
|
||||
}
|
||||
let count = vim.take_count(cx);
|
||||
vim.substitute(count, true, cx)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn substitute(vim: &mut Vim, count: Option<usize>, line_mode: bool, cx: &mut WindowContext) {
|
||||
vim.store_visual_marks(cx);
|
||||
vim.update_active_editor(cx, |vim, editor, cx| {
|
||||
editor.set_clip_at_line_ends(false, cx);
|
||||
editor.transact(cx, |editor, cx| {
|
||||
let text_layout_details = editor.text_layout_details(cx);
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
if selection.start == selection.end {
|
||||
Motion::Right.expand_selection(
|
||||
map,
|
||||
selection,
|
||||
count,
|
||||
true,
|
||||
&text_layout_details,
|
||||
);
|
||||
}
|
||||
if line_mode {
|
||||
// in Visual mode when the selection contains the newline at the end
|
||||
// of the line, we should exclude it.
|
||||
if !selection.is_empty() && selection.end.column() == 0 {
|
||||
selection.end = movement::left(map, selection.end);
|
||||
impl Vim {
|
||||
pub fn substitute(
|
||||
&mut self,
|
||||
count: Option<usize>,
|
||||
line_mode: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.store_visual_marks(cx);
|
||||
self.update_editor(cx, |vim, editor, cx| {
|
||||
editor.set_clip_at_line_ends(false, cx);
|
||||
editor.transact(cx, |editor, cx| {
|
||||
let text_layout_details = editor.text_layout_details(cx);
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
if selection.start == selection.end {
|
||||
Motion::Right.expand_selection(
|
||||
map,
|
||||
selection,
|
||||
count,
|
||||
true,
|
||||
&text_layout_details,
|
||||
);
|
||||
}
|
||||
Motion::CurrentLine.expand_selection(
|
||||
map,
|
||||
selection,
|
||||
None,
|
||||
false,
|
||||
&text_layout_details,
|
||||
);
|
||||
if let Some((point, _)) = (Motion::FirstNonWhitespace {
|
||||
display_lines: false,
|
||||
})
|
||||
.move_point(
|
||||
map,
|
||||
selection.start,
|
||||
selection.goal,
|
||||
None,
|
||||
&text_layout_details,
|
||||
) {
|
||||
selection.start = point;
|
||||
if line_mode {
|
||||
// in Visual mode when the selection contains the newline at the end
|
||||
// of the line, we should exclude it.
|
||||
if !selection.is_empty() && selection.end.column() == 0 {
|
||||
selection.end = movement::left(map, selection.end);
|
||||
}
|
||||
Motion::CurrentLine.expand_selection(
|
||||
map,
|
||||
selection,
|
||||
None,
|
||||
false,
|
||||
&text_layout_details,
|
||||
);
|
||||
if let Some((point, _)) = (Motion::FirstNonWhitespace {
|
||||
display_lines: false,
|
||||
})
|
||||
.move_point(
|
||||
map,
|
||||
selection.start,
|
||||
selection.goal,
|
||||
None,
|
||||
&text_layout_details,
|
||||
) {
|
||||
selection.start = point;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
vim.copy_selections_content(editor, line_mode, cx);
|
||||
let selections = editor.selections.all::<Point>(cx).into_iter();
|
||||
let edits = selections.map(|selection| (selection.start..selection.end, ""));
|
||||
editor.edit(edits, cx);
|
||||
});
|
||||
copy_selections_content(vim, editor, line_mode, cx);
|
||||
let selections = editor.selections.all::<Point>(cx).into_iter();
|
||||
let edits = selections.map(|selection| (selection.start..selection.end, ""));
|
||||
editor.edit(edits, cx);
|
||||
});
|
||||
});
|
||||
vim.switch_mode(Mode::Insert, true, cx);
|
||||
self.switch_mode(Mode::Insert, true, cx);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue