WIP: Start moving toward a simpler interface for detecting prev/next line boundaries
This commit is contained in:
parent
89bbfb8154
commit
7f786ca8a6
3 changed files with 125 additions and 138 deletions
|
@ -226,6 +226,19 @@ impl DisplaySnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prev_line_boundary(&self, mut point: Point) -> Point {
|
||||||
|
loop {
|
||||||
|
point.column = 0;
|
||||||
|
let mut display_point = self.point_to_display_point(point, Bias::Left);
|
||||||
|
*display_point.column_mut() = 0;
|
||||||
|
let next_point = self.display_point_to_point(display_point, Bias::Left);
|
||||||
|
if next_point == point {
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
point = next_point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn next_line_boundary(&self, mut point: Point) -> Point {
|
pub fn next_line_boundary(&self, mut point: Point) -> Point {
|
||||||
loop {
|
loop {
|
||||||
point.column = self.buffer_snapshot.line_len(point.row);
|
point.column = self.buffer_snapshot.line_len(point.row);
|
||||||
|
@ -247,16 +260,6 @@ impl DisplaySnapshot {
|
||||||
DisplayPoint(block_point)
|
DisplayPoint(block_point)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn point_to_display_point_with_clipping(&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);
|
|
||||||
let wrap_point = self
|
|
||||||
.wraps_snapshot
|
|
||||||
.from_tab_point_with_clipping(tab_point, bias);
|
|
||||||
let block_point = self.blocks_snapshot.to_block_point(wrap_point);
|
|
||||||
DisplayPoint(block_point)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn display_point_to_point(&self, point: DisplayPoint, bias: Bias) -> Point {
|
fn display_point_to_point(&self, point: DisplayPoint, bias: Bias) -> Point {
|
||||||
let block_point = point.0;
|
let block_point = point.0;
|
||||||
let wrap_point = self.blocks_snapshot.to_wrap_point(block_point);
|
let wrap_point = self.blocks_snapshot.to_wrap_point(block_point);
|
||||||
|
|
|
@ -672,15 +672,6 @@ impl WrapSnapshot {
|
||||||
WrapPoint(cursor.start().1 .0 + (point.0 - cursor.start().0 .0))
|
WrapPoint(cursor.start().1 .0 + (point.0 - cursor.start().0 .0))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_tab_point_with_clipping(&self, point: TabPoint, bias: Bias) -> WrapPoint {
|
|
||||||
let mut cursor = self.transforms.cursor::<(TabPoint, WrapPoint)>();
|
|
||||||
cursor.seek(&point, bias, &());
|
|
||||||
self.clip_point(
|
|
||||||
WrapPoint(cursor.start().1 .0 + (point.0 - cursor.start().0 .0)),
|
|
||||||
bias,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clip_point(&self, mut point: WrapPoint, bias: Bias) -> WrapPoint {
|
pub fn clip_point(&self, mut point: WrapPoint, bias: Bias) -> WrapPoint {
|
||||||
if bias == Bias::Left {
|
if bias == Bias::Left {
|
||||||
let mut cursor = self.transforms.cursor::<WrapPoint>();
|
let mut cursor = self.transforms.cursor::<WrapPoint>();
|
||||||
|
|
|
@ -284,16 +284,8 @@ trait SelectionExt {
|
||||||
fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
|
fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
|
||||||
fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
|
fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
|
||||||
fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
|
fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
|
||||||
fn spanned_rows(
|
fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
|
||||||
&self,
|
-> Range<u32>;
|
||||||
include_end_if_at_line_start: bool,
|
|
||||||
map: &DisplaySnapshot,
|
|
||||||
) -> SpannedRows;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SpannedRows {
|
|
||||||
buffer_rows: Range<u32>,
|
|
||||||
display_rows: Range<u32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -1577,12 +1569,12 @@ impl Editor {
|
||||||
let mut edit_ranges = Vec::new();
|
let mut edit_ranges = Vec::new();
|
||||||
let mut selections = selections.iter().peekable();
|
let mut selections = selections.iter().peekable();
|
||||||
while let Some(selection) = selections.next() {
|
while let Some(selection) = selections.next() {
|
||||||
let mut rows = selection.spanned_rows(false, &display_map).buffer_rows;
|
let mut rows = selection.spanned_rows(false, &display_map);
|
||||||
let goal_display_column = selection.head().to_display_point(&display_map).column();
|
let goal_display_column = selection.head().to_display_point(&display_map).column();
|
||||||
|
|
||||||
// Accumulate contiguous regions of rows that we want to delete.
|
// Accumulate contiguous regions of rows that we want to delete.
|
||||||
while let Some(next_selection) = selections.peek() {
|
while let Some(next_selection) = selections.peek() {
|
||||||
let next_rows = next_selection.spanned_rows(false, &display_map).buffer_rows;
|
let next_rows = next_selection.spanned_rows(false, &display_map);
|
||||||
if next_rows.start <= rows.end {
|
if next_rows.start <= rows.end {
|
||||||
rows.end = next_rows.end;
|
rows.end = next_rows.end;
|
||||||
selections.next().unwrap();
|
selections.next().unwrap();
|
||||||
|
@ -1645,10 +1637,10 @@ impl Editor {
|
||||||
let mut selections_iter = selections.iter().peekable();
|
let mut selections_iter = selections.iter().peekable();
|
||||||
while let Some(selection) = selections_iter.next() {
|
while let Some(selection) = selections_iter.next() {
|
||||||
// Avoid duplicating the same lines twice.
|
// Avoid duplicating the same lines twice.
|
||||||
let mut rows = selection.spanned_rows(false, &display_map).buffer_rows;
|
let mut rows = selection.spanned_rows(false, &display_map);
|
||||||
|
|
||||||
while let Some(next_selection) = selections_iter.peek() {
|
while let Some(next_selection) = selections_iter.peek() {
|
||||||
let next_rows = next_selection.spanned_rows(false, &display_map).buffer_rows;
|
let next_rows = next_selection.spanned_rows(false, &display_map);
|
||||||
if next_rows.start <= rows.end - 1 {
|
if next_rows.start <= rows.end - 1 {
|
||||||
rows.end = next_rows.end;
|
rows.end = next_rows.end;
|
||||||
selections_iter.next().unwrap();
|
selections_iter.next().unwrap();
|
||||||
|
@ -1693,92 +1685,98 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
|
pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
|
||||||
self.start_transaction(cx);
|
|
||||||
|
|
||||||
let selections = self.local_selections::<Point>(cx);
|
|
||||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
let buffer = self.buffer.read(cx).snapshot(cx);
|
let buffer = self.buffer.read(cx).snapshot(cx);
|
||||||
|
|
||||||
let mut edits = Vec::new();
|
let mut edits = Vec::new();
|
||||||
let mut new_selection_ranges = Vec::new();
|
let mut unfold_ranges = Vec::new();
|
||||||
let mut old_folds = Vec::new();
|
let mut refold_ranges = Vec::new();
|
||||||
let mut new_folds = Vec::new();
|
|
||||||
|
|
||||||
|
let selections = self.local_selections::<Point>(cx);
|
||||||
let mut selections = selections.iter().peekable();
|
let mut selections = selections.iter().peekable();
|
||||||
let mut contiguous_selections = Vec::new();
|
let mut contiguous_row_selections = Vec::new();
|
||||||
|
let mut new_selections = Vec::new();
|
||||||
|
|
||||||
while let Some(selection) = selections.next() {
|
while let Some(selection) = selections.next() {
|
||||||
// Accumulate contiguous regions of rows that we want to move.
|
// Find all the selections that span a contiguous row range
|
||||||
contiguous_selections.push(selection.point_range(&buffer));
|
contiguous_row_selections.push(selection.clone());
|
||||||
let SpannedRows {
|
let start_row = selection.start.row;
|
||||||
mut buffer_rows,
|
let mut end_row = if selection.end.column > 0 || selection.is_empty() {
|
||||||
mut display_rows,
|
display_map.next_line_boundary(selection.end).row + 1
|
||||||
} = selection.spanned_rows(false, &display_map);
|
} else {
|
||||||
|
selection.end.row
|
||||||
|
};
|
||||||
|
|
||||||
while let Some(next_selection) = selections.peek() {
|
while let Some(next_selection) = selections.peek() {
|
||||||
let SpannedRows {
|
if next_selection.start.row <= end_row {
|
||||||
buffer_rows: next_buffer_rows,
|
end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
|
||||||
display_rows: next_display_rows,
|
display_map.next_line_boundary(next_selection.end).row + 1
|
||||||
} = next_selection.spanned_rows(false, &display_map);
|
} else {
|
||||||
if next_buffer_rows.start <= buffer_rows.end {
|
next_selection.end.row
|
||||||
buffer_rows.end = next_buffer_rows.end;
|
};
|
||||||
display_rows.end = next_display_rows.end;
|
contiguous_row_selections.push(selections.next().unwrap().clone());
|
||||||
contiguous_selections.push(next_selection.point_range(&buffer));
|
|
||||||
selections.next().unwrap();
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cut the text from the selected rows and paste it at the start of the previous line.
|
// Move the text spanned by the row range to be before the line preceding the row range
|
||||||
if display_rows.start != 0 {
|
if start_row > 0 {
|
||||||
let start = Point::new(buffer_rows.start, 0).to_offset(&buffer);
|
let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
|
||||||
let end = Point::new(buffer_rows.end - 1, buffer.line_len(buffer_rows.end - 1))
|
..Point::new(end_row - 1, buffer.line_len(end_row - 1));
|
||||||
.to_offset(&buffer);
|
let insertion_point = display_map.prev_line_boundary(Point::new(start_row - 1, 0));
|
||||||
|
|
||||||
let prev_row_display_start = DisplayPoint::new(display_rows.start - 1, 0);
|
// Don't move lines across excerpts
|
||||||
let prev_row_buffer_start = display_map.prev_row_boundary(prev_row_display_start).1;
|
if !buffer.range_contains_excerpt_boundary(insertion_point..range_to_move.end) {
|
||||||
let prev_row_buffer_start_offset = prev_row_buffer_start.to_offset(&buffer);
|
let text = buffer
|
||||||
|
.text_for_range(range_to_move.clone())
|
||||||
|
.flat_map(|s| s.chars())
|
||||||
|
.skip(1)
|
||||||
|
.chain(['\n'])
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
let mut text = String::new();
|
edits.push((insertion_point..insertion_point, text));
|
||||||
text.extend(buffer.text_for_range(start..end));
|
edits.push((range_to_move.clone(), String::new()));
|
||||||
text.push('\n');
|
|
||||||
edits.push((
|
|
||||||
prev_row_buffer_start_offset..prev_row_buffer_start_offset,
|
|
||||||
text,
|
|
||||||
));
|
|
||||||
edits.push((start - 1..end, String::new()));
|
|
||||||
|
|
||||||
let row_delta = buffer_rows.start - prev_row_buffer_start.row;
|
let row_delta = range_to_move.start.row - insertion_point.row + 1;
|
||||||
|
|
||||||
// Move selections up.
|
// Move selections up
|
||||||
for range in &mut contiguous_selections {
|
new_selections.extend(contiguous_row_selections.drain(..).map(
|
||||||
range.start.row -= row_delta;
|
|mut selection| {
|
||||||
range.end.row -= row_delta;
|
selection.start.row -= row_delta;
|
||||||
}
|
selection.end.row -= row_delta;
|
||||||
|
selection
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
// Move folds up.
|
// Move folds up
|
||||||
old_folds.push(start..end);
|
unfold_ranges.push(range_to_move.clone());
|
||||||
for fold in display_map.folds_in_range(start..end) {
|
for fold in display_map.folds_in_range(
|
||||||
let mut start = fold.start.to_point(&buffer);
|
buffer.anchor_before(range_to_move.start)
|
||||||
let mut end = fold.end.to_point(&buffer);
|
..buffer.anchor_after(range_to_move.end),
|
||||||
start.row -= row_delta;
|
) {
|
||||||
end.row -= row_delta;
|
let mut start = fold.start.to_point(&buffer);
|
||||||
new_folds.push(start..end);
|
let mut end = fold.end.to_point(&buffer);
|
||||||
|
start.row -= row_delta;
|
||||||
|
end.row -= row_delta;
|
||||||
|
refold_ranges.push(start..end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new_selection_ranges.extend(contiguous_selections.drain(..));
|
// If we didn't move line(s), preserve the existing selections
|
||||||
|
new_selections.extend(contiguous_row_selections.drain(..));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.unfold_ranges(old_folds, cx);
|
self.start_transaction(cx);
|
||||||
|
self.unfold_ranges(unfold_ranges, cx);
|
||||||
self.buffer.update(cx, |buffer, cx| {
|
self.buffer.update(cx, |buffer, cx| {
|
||||||
for (range, text) in edits.into_iter().rev() {
|
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.fold_ranges(refold_ranges, cx);
|
||||||
self.select_ranges(new_selection_ranges, Some(Autoscroll::Fit), cx);
|
self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
|
||||||
|
|
||||||
self.end_transaction(cx);
|
self.end_transaction(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1822,38 +1820,43 @@ impl Editor {
|
||||||
if end_row <= buffer.max_point().row {
|
if end_row <= buffer.max_point().row {
|
||||||
let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
|
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 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;
|
// Don't move lines across excerpt boundaries
|
||||||
|
if !buffer.range_contains_excerpt_boundary(range_to_move.start..insertion_point) {
|
||||||
|
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));
|
||||||
|
|
||||||
// Make the selections relative to the insertion row
|
let row_delta = insertion_point.row - range_to_move.end.row + 1;
|
||||||
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
|
// Move selections down
|
||||||
unfold_ranges.push(range_to_move.clone());
|
new_selections.extend(contiguous_row_selections.drain(..).map(
|
||||||
|
|mut selection| {
|
||||||
|
selection.start.row += row_delta;
|
||||||
|
selection.end.row += row_delta;
|
||||||
|
selection
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
// Refold ranges relative to the insertion row
|
// Move folds down
|
||||||
for fold in display_map.folds_in_range(
|
unfold_ranges.push(range_to_move.clone());
|
||||||
buffer.anchor_before(range_to_move.start)
|
for fold in display_map.folds_in_range(
|
||||||
..buffer.anchor_after(range_to_move.end),
|
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);
|
let mut start = fold.start.to_point(&buffer);
|
||||||
start.row += row_delta;
|
let mut end = fold.end.to_point(&buffer);
|
||||||
end.row += row_delta;
|
start.row += row_delta;
|
||||||
refold_ranges.push(start..end);
|
end.row += row_delta;
|
||||||
|
refold_ranges.push(start..end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
new_selections.extend(contiguous_row_selections.drain(..));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we didn't move line(s), preserve the existing selections
|
||||||
|
new_selections.extend(contiguous_row_selections.drain(..));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.start_transaction(cx);
|
self.start_transaction(cx);
|
||||||
|
@ -2405,7 +2408,7 @@ impl Editor {
|
||||||
let mut selections = self.local_selections::<Point>(cx);
|
let mut selections = self.local_selections::<Point>(cx);
|
||||||
let max_point = display_map.buffer_snapshot.max_point();
|
let max_point = display_map.buffer_snapshot.max_point();
|
||||||
for selection in &mut selections {
|
for selection in &mut selections {
|
||||||
let rows = selection.spanned_rows(true, &display_map).buffer_rows;
|
let rows = selection.spanned_rows(true, &display_map);
|
||||||
selection.start = Point::new(rows.start, 0);
|
selection.start = Point::new(rows.start, 0);
|
||||||
selection.end = cmp::min(max_point, Point::new(rows.end, 0));
|
selection.end = cmp::min(max_point, Point::new(rows.end, 0));
|
||||||
selection.reversed = false;
|
selection.reversed = false;
|
||||||
|
@ -3778,30 +3781,20 @@ impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
|
||||||
&self,
|
&self,
|
||||||
include_end_if_at_line_start: bool,
|
include_end_if_at_line_start: bool,
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
) -> SpannedRows {
|
) -> Range<u32> {
|
||||||
let display_start = self
|
let start = self.start.to_point(&map.buffer_snapshot);
|
||||||
.start
|
let mut end = self.end.to_point(&map.buffer_snapshot);
|
||||||
.to_point(&map.buffer_snapshot)
|
|
||||||
.to_display_point(map);
|
|
||||||
let mut display_end = self
|
|
||||||
.end
|
|
||||||
.to_point(&map.buffer_snapshot)
|
|
||||||
.to_display_point(map);
|
|
||||||
if !include_end_if_at_line_start
|
if !include_end_if_at_line_start
|
||||||
&& display_end.row() != map.max_point().row()
|
&& end.row != map.buffer_snapshot.max_point().row
|
||||||
&& display_start.row() != display_end.row()
|
&& start.row != end.row
|
||||||
&& display_end.column() == 0
|
&& end.column == 0
|
||||||
{
|
{
|
||||||
*display_end.row_mut() -= 1;
|
end.row -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (display_start, buffer_start) = map.prev_row_boundary(display_start);
|
let buffer_start = map.prev_line_boundary(start);
|
||||||
let (display_end, buffer_end) = map.next_row_boundary(display_end);
|
let buffer_end = map.next_line_boundary(end);
|
||||||
|
buffer_start.row..buffer_end.row + 1
|
||||||
SpannedRows {
|
|
||||||
buffer_rows: buffer_start.row..buffer_end.row + 1,
|
|
||||||
display_rows: display_start.row()..display_end.row() + 1,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue