Add support for visual ranges ending with a newline
These primarily happen when first entering visual mode, but can also be created with objects like `vi{`. Along the way fix the way ranges like `vi{` are selected to be more similar to nvim.
This commit is contained in:
parent
b53fb8633e
commit
5edcb74760
21 changed files with 301 additions and 135 deletions
|
@ -35,12 +35,6 @@ pub enum FoldStatus {
|
|||
Foldable,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Clip {
|
||||
None,
|
||||
EndOfLine,
|
||||
}
|
||||
|
||||
pub trait ToDisplayPoint {
|
||||
fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
|
||||
}
|
||||
|
@ -56,7 +50,7 @@ pub struct DisplayMap {
|
|||
wrap_map: ModelHandle<WrapMap>,
|
||||
block_map: BlockMap,
|
||||
text_highlights: TextHighlights,
|
||||
pub default_clip: Clip,
|
||||
pub clip_at_line_ends: bool,
|
||||
}
|
||||
|
||||
impl Entity for DisplayMap {
|
||||
|
@ -91,7 +85,7 @@ impl DisplayMap {
|
|||
wrap_map,
|
||||
block_map,
|
||||
text_highlights: Default::default(),
|
||||
default_clip: Clip::None,
|
||||
clip_at_line_ends: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +109,7 @@ impl DisplayMap {
|
|||
wrap_snapshot,
|
||||
block_snapshot,
|
||||
text_highlights: self.text_highlights.clone(),
|
||||
default_clip: self.default_clip,
|
||||
clip_at_line_ends: self.clip_at_line_ends,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,7 +296,7 @@ pub struct DisplaySnapshot {
|
|||
wrap_snapshot: wrap_map::WrapSnapshot,
|
||||
block_snapshot: block_map::BlockSnapshot,
|
||||
text_highlights: TextHighlights,
|
||||
default_clip: Clip,
|
||||
clip_at_line_ends: bool,
|
||||
}
|
||||
|
||||
impl DisplaySnapshot {
|
||||
|
@ -361,6 +355,9 @@ impl DisplaySnapshot {
|
|||
|
||||
pub fn expand_to_line(&self, range: Range<Point>) -> Range<Point> {
|
||||
let mut new_start = self.prev_line_boundary(range.start).0;
|
||||
if range.end.column == 0 {
|
||||
return new_start..range.end;
|
||||
}
|
||||
let mut new_end = self.next_line_boundary(range.end).0;
|
||||
|
||||
if new_start.row == range.start.row && new_end.row == range.end.row {
|
||||
|
@ -583,33 +580,21 @@ impl DisplaySnapshot {
|
|||
column
|
||||
}
|
||||
|
||||
pub fn move_left(&self, point: DisplayPoint, clip: Clip) -> DisplayPoint {
|
||||
self.clip_point_with(
|
||||
DisplayPoint::new(point.row(), point.column().saturating_sub(1)),
|
||||
Bias::Left,
|
||||
clip,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn move_right(&self, point: DisplayPoint, clip: Clip) -> DisplayPoint {
|
||||
self.clip_point_with(
|
||||
DisplayPoint::new(point.row(), point.column() + 1),
|
||||
Bias::Right,
|
||||
clip,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn clip_point_with(&self, point: DisplayPoint, bias: Bias, clip: Clip) -> DisplayPoint {
|
||||
let new_point = DisplayPoint(self.block_snapshot.clip_point(point.0, bias));
|
||||
if clip == Clip::EndOfLine && new_point.column() == self.line_len(new_point.row()) {
|
||||
self.move_left(new_point, Clip::None)
|
||||
} else {
|
||||
new_point
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
|
||||
self.clip_point_with(point, bias, self.default_clip)
|
||||
let mut clipped = self.block_snapshot.clip_point(point.0, bias);
|
||||
if self.clip_at_line_ends {
|
||||
clipped = self.clip_at_line_end(DisplayPoint(clipped)).0
|
||||
}
|
||||
DisplayPoint(clipped)
|
||||
}
|
||||
|
||||
pub fn clip_at_line_end(&self, point: DisplayPoint) -> DisplayPoint {
|
||||
let mut point = point.0;
|
||||
if point.column == self.line_len(point.row) {
|
||||
point.column = point.column.saturating_sub(1);
|
||||
point = self.block_snapshot.clip_point(point, Bias::Left);
|
||||
}
|
||||
DisplayPoint(point)
|
||||
}
|
||||
|
||||
pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Range<Anchor>>
|
||||
|
@ -1598,7 +1583,7 @@ pub mod tests {
|
|||
|
||||
fn assert(text: &str, cx: &mut gpui::AppContext) {
|
||||
let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
|
||||
unmarked_snapshot.default_clip = Clip::EndOfLine;
|
||||
unmarked_snapshot.clip_at_line_ends = true;
|
||||
assert_eq!(
|
||||
unmarked_snapshot.clip_point(markers[1], Bias::Left),
|
||||
markers[0]
|
||||
|
|
|
@ -1544,10 +1544,10 @@ impl Editor {
|
|||
range.clone()
|
||||
}
|
||||
|
||||
pub fn set_default_clip(&mut self, clip: Clip, cx: &mut ViewContext<Self>) {
|
||||
if self.display_map.read(cx).default_clip != clip {
|
||||
pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
|
||||
if self.display_map.read(cx).clip_at_line_ends != clip {
|
||||
self.display_map
|
||||
.update(cx, |map, _| map.default_clip = clip);
|
||||
.update(cx, |map, _| map.clip_at_line_ends = clip);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -847,23 +847,26 @@ impl EditorElement {
|
|||
|
||||
if editor.show_local_cursors(cx) || replica_id != local_replica_id {
|
||||
let cursor_position = selection.head;
|
||||
let mut cursor_column = cursor_position.column() as usize;
|
||||
let mut cursor_row = cursor_position.row();
|
||||
|
||||
if layout
|
||||
.visible_display_row_range
|
||||
.contains(&cursor_position.row())
|
||||
if CursorShape::Block == selection.cursor_shape
|
||||
&& !selection.range.is_empty()
|
||||
&& !selection.reversed
|
||||
{
|
||||
let cursor_row_layout = &layout.position_map.line_layouts
|
||||
[(cursor_position.row() - start_row) as usize]
|
||||
.line;
|
||||
let mut cursor_column = cursor_position.column() as usize;
|
||||
|
||||
if CursorShape::Block == selection.cursor_shape
|
||||
&& !selection.range.is_empty()
|
||||
&& !selection.reversed
|
||||
&& cursor_column > 0
|
||||
{
|
||||
if cursor_column > 0 {
|
||||
cursor_column -= 1;
|
||||
} else if cursor_row > 0 {
|
||||
cursor_row -= 1;
|
||||
cursor_column =
|
||||
layout.position_map.snapshot.line_len(cursor_row) as usize;
|
||||
}
|
||||
}
|
||||
|
||||
if layout.visible_display_row_range.contains(&cursor_row) {
|
||||
let cursor_row_layout = &layout.position_map.line_layouts
|
||||
[(cursor_row - start_row) as usize]
|
||||
.line;
|
||||
|
||||
let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
|
||||
let mut block_width =
|
||||
|
@ -876,7 +879,7 @@ impl EditorElement {
|
|||
.position_map
|
||||
.snapshot
|
||||
.chars_at(DisplayPoint::new(
|
||||
cursor_position.row(),
|
||||
cursor_row as u32,
|
||||
cursor_column as u32,
|
||||
))
|
||||
.next()
|
||||
|
@ -903,8 +906,7 @@ impl EditorElement {
|
|||
};
|
||||
|
||||
let x = cursor_character_x - scroll_left;
|
||||
let y = cursor_position.row() as f32 * layout.position_map.line_height
|
||||
- scroll_top;
|
||||
let y = cursor_row as f32 * layout.position_map.line_height - scroll_top;
|
||||
if selection.is_newest {
|
||||
editor.pixel_position_of_newest_cursor = Some(vec2f(
|
||||
bounds.origin_x() + x + block_width / 2.,
|
||||
|
|
|
@ -13,6 +13,13 @@ pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
|
|||
map.clip_point(point, Bias::Left)
|
||||
}
|
||||
|
||||
pub fn saturating_left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
|
||||
if point.column() > 0 {
|
||||
*point.column_mut() -= 1;
|
||||
}
|
||||
map.clip_point(point, Bias::Left)
|
||||
}
|
||||
|
||||
pub fn right(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
|
||||
let max_column = map.line_len(point.row());
|
||||
if point.column() < max_column {
|
||||
|
@ -24,6 +31,11 @@ pub fn right(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
|
|||
map.clip_point(point, Bias::Right)
|
||||
}
|
||||
|
||||
pub fn saturating_right(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
|
||||
*point.column_mut() += 1;
|
||||
map.clip_point(point, Bias::Right)
|
||||
}
|
||||
|
||||
pub fn up(
|
||||
map: &DisplaySnapshot,
|
||||
start: DisplayPoint,
|
||||
|
|
|
@ -498,6 +498,7 @@ impl<'a> MutableSelectionsCollection<'a> {
|
|||
T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug,
|
||||
{
|
||||
let buffer = self.buffer.read(self.cx).snapshot(self.cx);
|
||||
|
||||
selections.sort_unstable_by_key(|s| s.start);
|
||||
// Merge overlapping selections.
|
||||
let mut i = 1;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue