diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index cb6b020aff1b9df80adfcc7fb3c6036127c4df12..06a06ed28fc4f83d6eecad2a49a9c763428d1a7b 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -226,6 +226,19 @@ impl DisplaySnapshot { } } + pub fn next_line_boundary(&self, mut point: Point) -> Point { + loop { + point.column = self.buffer_snapshot.line_len(point.row); + let mut display_point = self.point_to_display_point(point, Bias::Right); + *display_point.column_mut() = self.line_len(display_point.row()); + let next_point = self.display_point_to_point(display_point, Bias::Right); + if next_point == point { + return point; + } + point = next_point; + } + } + fn point_to_display_point(&self, point: Point, bias: Bias) -> DisplayPoint { let fold_point = point.to_fold_point(&self.folds_snapshot, bias); let tab_point = self.tabs_snapshot.to_tab_point(fold_point); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index faee89b545bed6054318ef2291837d3c03bdf314..fdf76b24a74d1ddb8d632f45e5856854799207cc 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1783,95 +1783,88 @@ impl Editor { } pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext) { - let selections = self.local_selections::(cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = self.buffer.read(cx).snapshot(cx); let mut edits = Vec::new(); - let mut new_selection_ranges = Vec::new(); - let mut old_folds = Vec::new(); - let mut new_folds = Vec::new(); + let mut unfold_ranges = Vec::new(); + let mut refold_ranges = Vec::new(); + let selections = self.local_selections::(cx); let mut selections = selections.iter().peekable(); - let mut contiguous_selections = Vec::new(); - while let Some(selection) = selections.next() { - // Accumulate contiguous regions of rows that we want to move. - contiguous_selections.push(selection.point_range(&buffer)); + let mut contiguous_row_selections = Vec::new(); + let mut new_selections = Vec::new(); - let SpannedRows { - mut buffer_rows, - mut display_rows, - } = selection.spanned_rows(false, &display_map); + while let Some(selection) = selections.next() { + // Find all the selections that span a contiguous row range + contiguous_row_selections.push(selection.clone()); + let start_row = selection.start.row; + let mut end_row = if selection.end.column > 0 || selection.is_empty() { + display_map.next_line_boundary(selection.end).row + 1 + } else { + selection.end.row + }; while let Some(next_selection) = selections.peek() { - let SpannedRows { - buffer_rows: next_buffer_rows, - display_rows: next_display_rows, - } = next_selection.spanned_rows(false, &display_map); - if next_buffer_rows.start <= buffer_rows.end { - buffer_rows.end = next_buffer_rows.end; - display_rows.end = next_display_rows.end; - contiguous_selections.push(next_selection.point_range(&buffer)); - selections.next().unwrap(); + if next_selection.start.row <= end_row { + end_row = if next_selection.end.column > 0 || next_selection.is_empty() { + display_map.next_line_boundary(next_selection.end).row + 1 + } else { + next_selection.end.row + }; + contiguous_row_selections.push(selections.next().unwrap().clone()); } else { break; } } - // Cut the text from the selected rows and paste it at the end of the next line. - if display_rows.end <= display_map.max_point().row() { - let start = Point::new(buffer_rows.start, 0).to_offset(&buffer); - let end = Point::new(buffer_rows.end - 1, buffer.line_len(buffer_rows.end - 1)) - .to_offset(&buffer); - - let next_row_display_end = - DisplayPoint::new(display_rows.end, display_map.line_len(display_rows.end)); - - let next_row_buffer_end = display_map.next_row_boundary(next_row_display_end).1; - let next_row_buffer_end_offset = next_row_buffer_end.to_offset(&buffer); - - if buffer.range_contains_excerpt_boundary(start..next_row_buffer_end_offset) { - new_selection_ranges.extend(contiguous_selections.drain(..)); - continue; - } - - let mut text = String::new(); - text.push('\n'); - text.extend(buffer.text_for_range(start..end)); - edits.push((start..end + 1, String::new())); - edits.push((next_row_buffer_end_offset..next_row_buffer_end_offset, text)); - - // Move selections down. - let display_row_delta = next_row_display_end.row() - display_rows.end + 1; - for range in &mut contiguous_selections { - range.start.row += display_row_delta; - range.end.row += display_row_delta; - } - - // Move folds down. - old_folds.push(start..end); - let buffer_row_delta = next_row_buffer_end.row - buffer_rows.end + 1; - for fold in display_map.folds_in_range(start..end) { + // Move the text spanned by the row range to be after the last line of the row range + if end_row <= buffer.max_point().row { + let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0); + let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)); + let mut text = String::from("\n"); + text.extend(buffer.text_for_range(range_to_move.clone())); + text.pop(); // Drop trailing newline + edits.push((range_to_move.clone(), String::new())); + edits.push((insertion_point..insertion_point, text)); + + let row_delta = insertion_point.row - range_to_move.end.row + 1; + + // Make the selections relative to the insertion row + new_selections.extend(contiguous_row_selections.drain(..).map(|mut selection| { + selection.start.row += row_delta; + selection.end.row += row_delta; + selection + })); + + // Unfold all the folds spanned by these rows + unfold_ranges.push(range_to_move.clone()); + + // Refold ranges relative to the insertion row + for fold in display_map.folds_in_range( + buffer.anchor_before(range_to_move.start) + ..buffer.anchor_after(range_to_move.end), + ) { let mut start = fold.start.to_point(&buffer); let mut end = fold.end.to_point(&buffer); - start.row += buffer_row_delta; - end.row += buffer_row_delta; - new_folds.push(start..end); + start.row += row_delta; + end.row += row_delta; + refold_ranges.push(start..end); } + } else { + new_selections.extend(contiguous_row_selections.drain(..)); } - - new_selection_ranges.extend(contiguous_selections.drain(..)); } self.start_transaction(cx); - self.unfold_ranges(old_folds, cx); + self.unfold_ranges(unfold_ranges, cx); self.buffer.update(cx, |buffer, cx| { for (range, text) in edits.into_iter().rev() { - buffer.edit(Some(range), text, cx); + buffer.edit([range], text, cx); } }); - self.fold_ranges(new_folds, cx); - self.select_ranges(new_selection_ranges, Some(Autoscroll::Fit), cx); + self.fold_ranges(refold_ranges, cx); + self.update_selections(new_selections, Some(Autoscroll::Fit), cx); self.end_transaction(cx); }