fix replace in normal and visual modes

This commit is contained in:
Kay Simmons 2023-01-11 14:57:40 -08:00
parent 14eec66e38
commit 216b1aec08
3 changed files with 100 additions and 24 deletions

View file

@ -352,6 +352,29 @@ pub fn surrounding_word(map: &DisplaySnapshot, position: DisplayPoint) -> Range<
start..end start..end
} }
pub fn split_display_range_by_lines(
map: &DisplaySnapshot,
range: Range<DisplayPoint>,
) -> Vec<Range<DisplayPoint>> {
let mut result = Vec::new();
let mut start = range.start;
// Loop over all the covered rows until the one containing the range end
for row in range.start.row()..range.end.row() {
let row_end_column = map.line_len(row);
let end = map.clip_point(DisplayPoint::new(row, row_end_column), Bias::Left);
if start != end {
result.push(start..end);
}
start = map.clip_point(DisplayPoint::new(row + 1, 0), Bias::Left);
}
// Add the final range from the start of the last end to the original range end.
result.push(start..range.end);
result
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -429,14 +429,42 @@ pub(crate) fn normal_replace(text: &str, cx: &mut MutableAppContext) {
vim.update_active_editor(cx, |editor, cx| { vim.update_active_editor(cx, |editor, cx| {
editor.transact(cx, |editor, cx| { editor.transact(cx, |editor, cx| {
editor.set_clip_at_line_ends(false, cx); editor.set_clip_at_line_ends(false, cx);
editor.change_selections(None, cx, |s| { let (map, display_selections) = editor.selections.all_display(cx);
s.move_with(|map, selection| { // Selections are biased right at the start. So we need to store
*selection.end.column_mut() += 1; // anchors that are biased left so that we can restore the selections
selection.end = map.clip_point(selection.end, Bias::Right); // after the change
}); let stable_anchors = editor
.selections
.disjoint_anchors()
.into_iter()
.map(|selection| {
let start = selection.start.bias_left(&map.buffer_snapshot);
start..start
})
.collect::<Vec<_>>();
let edits = display_selections
.into_iter()
.map(|selection| {
let mut range = selection.range();
*range.end.column_mut() += 1;
range.end = map.clip_point(range.end, Bias::Right);
(
range.start.to_offset(&map, Bias::Left)
..range.end.to_offset(&map, Bias::Left),
text,
)
})
.collect::<Vec<_>>();
editor.buffer().update(cx, |buffer, cx| {
buffer.edit(edits, None, cx);
}); });
editor.insert(text, cx);
editor.set_clip_at_line_ends(true, cx); editor.set_clip_at_line_ends(true, cx);
editor.change_selections(None, cx, |s| {
s.select_anchor_ranges(stable_anchors);
});
}); });
}); });
vim.pop_operator(cx) vim.pop_operator(cx)
@ -487,6 +515,16 @@ mod test {
.await; .await;
} }
// #[gpui::test]
// async fn test_enter(cx: &mut gpui::TestAppContext) {
// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["enter"]);
// cx.assert_all(indoc! {"
// ˇThe qˇuick broˇwn
// ˇfox jumps"
// })
// .await;
// }
#[gpui::test] #[gpui::test]
async fn test_k(cx: &mut gpui::TestAppContext) { async fn test_k(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await.binding(["k"]); let mut cx = NeovimBackedTestContext::new(cx).await.binding(["k"]);

View file

@ -2,7 +2,7 @@ use std::borrow::Cow;
use collections::HashMap; use collections::HashMap;
use editor::{ use editor::{
display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Bias, ClipboardSelection, display_map::ToDisplayPoint, movement, scroll::autoscroll::Autoscroll, Bias, ClipboardSelection,
}; };
use gpui::{actions, MutableAppContext, ViewContext}; use gpui::{actions, MutableAppContext, ViewContext};
use language::{AutoindentMode, SelectionGoal}; use language::{AutoindentMode, SelectionGoal};
@ -318,32 +318,47 @@ pub(crate) fn visual_replace(text: &str, line: bool, cx: &mut MutableAppContext)
vim.update_active_editor(cx, |editor, cx| { vim.update_active_editor(cx, |editor, cx| {
editor.transact(cx, |editor, cx| { editor.transact(cx, |editor, cx| {
let (display_map, selections) = editor.selections.all_adjusted_display(cx); let (display_map, selections) = editor.selections.all_adjusted_display(cx);
let mut new_selections = Vec::new();
editor.buffer().update(cx, |buffer, cx| {
let mut edits = Vec::new();
for selection in selections.iter() {
let mut selection = selection.clone();
if !line && !selection.reversed {
// Head is at the end of the selection. Adjust the end position to
// to include the character under the cursor.
*selection.end.column_mut() = selection.end.column() + 1;
selection.end = display_map.clip_point(selection.end, Bias::Right);
}
let range = selection // Selections are biased right at the start. So we need to store
.map(|p| p.to_offset(&display_map, Bias::Right)) // anchors that are biased left so that we can restore the selections
.range(); // after the change
new_selections.push(range.start..range.start); let stable_anchors = editor
.selections
.disjoint_anchors()
.into_iter()
.map(|selection| {
let start = selection.start.bias_left(&display_map.buffer_snapshot);
start..start
})
.collect::<Vec<_>>();
let mut edits = Vec::new();
for selection in selections.iter() {
let mut selection = selection.clone();
if !line && !selection.reversed {
// Head is at the end of the selection. Adjust the end position to
// to include the character under the cursor.
*selection.end.column_mut() = selection.end.column() + 1;
selection.end = display_map.clip_point(selection.end, Bias::Right);
}
for row_range in
movement::split_display_range_by_lines(&display_map, selection.range())
{
let range = row_range.start.to_offset(&display_map, Bias::Right)
..row_range.end.to_offset(&display_map, Bias::Right);
let text = text.repeat(range.len()); let text = text.repeat(range.len());
edits.push((range, text)); edits.push((range, text));
} }
}
editor.buffer().update(cx, |buffer, cx| {
buffer.edit(edits, None, cx); buffer.edit(edits, None, cx);
}); });
editor.change_selections(None, cx, |s| s.select_ranges(new_selections)); editor.change_selections(None, cx, |s| s.select_ranges(stable_anchors));
}); });
}); });
vim.pop_operator(cx) vim.switch_mode(Mode::Normal, false, cx);
}); });
} }