@@ -352,6 +352,29 @@ pub fn surrounding_word(map: &DisplaySnapshot, position: DisplayPoint) -> Range<
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)]
mod tests {
use super::*;
@@ -429,14 +429,42 @@ pub(crate) fn normal_replace(text: &str, cx: &mut MutableAppContext) {
vim.update_active_editor(cx, |editor, cx| {
editor.transact(cx, |editor, cx| {
editor.set_clip_at_line_ends(false, cx);
- editor.change_selections(None, cx, |s| {
- s.move_with(|map, selection| {
- *selection.end.column_mut() += 1;
- selection.end = map.clip_point(selection.end, Bias::Right);
- });
+ let (map, display_selections) = editor.selections.all_display(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(&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.change_selections(None, cx, |s| {
+ s.select_anchor_ranges(stable_anchors);
+ });
});
});
vim.pop_operator(cx)
@@ -487,6 +515,16 @@ mod test {
.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]
async fn test_k(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await.binding(["k"]);
@@ -2,7 +2,7 @@ use std::borrow::Cow;
use collections::HashMap;
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 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| {
editor.transact(cx, |editor, 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
- .map(|p| p.to_offset(&display_map, Bias::Right))
- .range();
- new_selections.push(range.start..range.start);
+ // 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();
+ 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());
edits.push((range, text));
}
+ }
+ editor.buffer().update(cx, |buffer, 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);
});
}