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,8 +318,20 @@ 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| { // Selections are biased right at the start. So we need to store
// anchors that are biased left so that we can restore the selections
// after the change
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(); let mut edits = Vec::new();
for selection in selections.iter() { for selection in selections.iter() {
let mut selection = selection.clone(); let mut selection = selection.clone();
@ -330,20 +342,23 @@ pub(crate) fn visual_replace(text: &str, line: bool, cx: &mut MutableAppContext)
selection.end = display_map.clip_point(selection.end, Bias::Right); selection.end = display_map.clip_point(selection.end, Bias::Right);
} }
let range = selection for row_range in
.map(|p| p.to_offset(&display_map, Bias::Right)) movement::split_display_range_by_lines(&display_map, selection.range())
.range(); {
new_selections.push(range.start..range.start); 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);
}); });
} }