mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Use Horizontal ranges everywhere
This commit is contained in:
parent
002e2cc42c
commit
ab050d1890
@ -15,7 +15,7 @@ use gpui::{
|
|||||||
color::Color,
|
color::Color,
|
||||||
fonts::{FontId, HighlightStyle, Underline},
|
fonts::{FontId, HighlightStyle, Underline},
|
||||||
text_layout::{Line, RunStyle},
|
text_layout::{Line, RunStyle},
|
||||||
AppContext, Entity, FontCache, ModelContext, ModelHandle, TextLayoutCache,
|
Entity, ModelContext, ModelHandle,
|
||||||
};
|
};
|
||||||
use inlay_map::InlayMap;
|
use inlay_map::InlayMap;
|
||||||
use language::{
|
use language::{
|
||||||
@ -576,7 +576,6 @@ impl DisplaySnapshot {
|
|||||||
|
|
||||||
let range = display_row..display_row + 1;
|
let range = display_row..display_row + 1;
|
||||||
for chunk in self.highlighted_chunks(range, editor_style) {
|
for chunk in self.highlighted_chunks(range, editor_style) {
|
||||||
dbg!(chunk.chunk);
|
|
||||||
line.push_str(chunk.chunk);
|
line.push_str(chunk.chunk);
|
||||||
|
|
||||||
let text_style = if let Some(style) = chunk.style {
|
let text_style = if let Some(style) = chunk.style {
|
||||||
@ -600,7 +599,6 @@ impl DisplaySnapshot {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg!(&line, &editor_style.text.font_size, &styles);
|
|
||||||
text_layout_cache.layout_str(&line, editor_style.text.font_size, &styles)
|
text_layout_cache.layout_str(&line, editor_style.text.font_size, &styles)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -623,49 +621,6 @@ impl DisplaySnapshot {
|
|||||||
layout_line.closest_index_for_x(x_coordinate) as u32
|
layout_line.closest_index_for_x(x_coordinate) as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
// column_for_x(row, x)
|
|
||||||
|
|
||||||
fn point(
|
|
||||||
&self,
|
|
||||||
display_point: DisplayPoint,
|
|
||||||
text_layout_cache: &TextLayoutCache,
|
|
||||||
editor_style: &EditorStyle,
|
|
||||||
cx: &AppContext,
|
|
||||||
) -> f32 {
|
|
||||||
let mut styles = Vec::new();
|
|
||||||
let mut line = String::new();
|
|
||||||
|
|
||||||
let range = display_point.row()..display_point.row() + 1;
|
|
||||||
for chunk in self.highlighted_chunks(range, editor_style) {
|
|
||||||
dbg!(chunk.chunk);
|
|
||||||
line.push_str(chunk.chunk);
|
|
||||||
|
|
||||||
let text_style = if let Some(style) = chunk.style {
|
|
||||||
editor_style
|
|
||||||
.text
|
|
||||||
.clone()
|
|
||||||
.highlight(style, cx.font_cache())
|
|
||||||
.map(Cow::Owned)
|
|
||||||
.unwrap_or_else(|_| Cow::Borrowed(&editor_style.text))
|
|
||||||
} else {
|
|
||||||
Cow::Borrowed(&editor_style.text)
|
|
||||||
};
|
|
||||||
|
|
||||||
styles.push((
|
|
||||||
chunk.chunk.len(),
|
|
||||||
RunStyle {
|
|
||||||
font_id: text_style.font_id,
|
|
||||||
color: text_style.color,
|
|
||||||
underline: text_style.underline,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
dbg!(&line, &editor_style.text.font_size, &styles);
|
|
||||||
let layout_line = text_layout_cache.layout_str(&line, editor_style.text.font_size, &styles);
|
|
||||||
layout_line.x_for_index(display_point.column() as usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chars_at(
|
pub fn chars_at(
|
||||||
&self,
|
&self,
|
||||||
mut point: DisplayPoint,
|
mut point: DisplayPoint,
|
||||||
@ -1374,7 +1329,6 @@ pub mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let x = snapshot.x_for_point(DisplayPoint::new(1, 10), &text_layout_details);
|
let x = snapshot.x_for_point(DisplayPoint::new(1, 10), &text_layout_details);
|
||||||
dbg!(x);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
movement::up(
|
movement::up(
|
||||||
&snapshot,
|
&snapshot,
|
||||||
@ -1401,7 +1355,6 @@ pub mod tests {
|
|||||||
SelectionGoal::HorizontalPosition(x)
|
SelectionGoal::HorizontalPosition(x)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
dbg!("starting down...");
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
movement::down(
|
movement::down(
|
||||||
&snapshot,
|
&snapshot,
|
||||||
|
@ -48,9 +48,9 @@ use gpui::{
|
|||||||
impl_actions,
|
impl_actions,
|
||||||
keymap_matcher::KeymapContext,
|
keymap_matcher::KeymapContext,
|
||||||
platform::{CursorStyle, MouseButton},
|
platform::{CursorStyle, MouseButton},
|
||||||
serde_json, text_layout, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem,
|
serde_json, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element,
|
||||||
Element, Entity, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle,
|
Entity, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||||
WeakViewHandle, WindowContext,
|
WindowContext,
|
||||||
};
|
};
|
||||||
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
||||||
use hover_popover::{hide_hover, HoverState};
|
use hover_popover::{hide_hover, HoverState};
|
||||||
@ -5953,11 +5953,14 @@ impl Editor {
|
|||||||
fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
|
fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
|
||||||
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 mut selections = self.selections.all::<Point>(cx);
|
let mut selections = self.selections.all::<Point>(cx);
|
||||||
|
let text_layout_details = TextLayoutDetails::new(self, cx);
|
||||||
let mut state = self.add_selections_state.take().unwrap_or_else(|| {
|
let mut state = self.add_selections_state.take().unwrap_or_else(|| {
|
||||||
let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
|
let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
|
||||||
let range = oldest_selection.display_range(&display_map).sorted();
|
let range = oldest_selection.display_range(&display_map).sorted();
|
||||||
let columns = cmp::min(range.start.column(), range.end.column())
|
|
||||||
..cmp::max(range.start.column(), range.end.column());
|
let start_x = display_map.x_for_point(range.start, &text_layout_details);
|
||||||
|
let end_x = display_map.x_for_point(range.end, &text_layout_details);
|
||||||
|
let positions = start_x.min(end_x)..start_x.max(end_x);
|
||||||
|
|
||||||
selections.clear();
|
selections.clear();
|
||||||
let mut stack = Vec::new();
|
let mut stack = Vec::new();
|
||||||
@ -5965,8 +5968,9 @@ impl Editor {
|
|||||||
if let Some(selection) = self.selections.build_columnar_selection(
|
if let Some(selection) = self.selections.build_columnar_selection(
|
||||||
&display_map,
|
&display_map,
|
||||||
row,
|
row,
|
||||||
&columns,
|
&positions,
|
||||||
oldest_selection.reversed,
|
oldest_selection.reversed,
|
||||||
|
&text_layout_details,
|
||||||
) {
|
) {
|
||||||
stack.push(selection.id);
|
stack.push(selection.id);
|
||||||
selections.push(selection);
|
selections.push(selection);
|
||||||
@ -5994,12 +5998,15 @@ impl Editor {
|
|||||||
let range = selection.display_range(&display_map).sorted();
|
let range = selection.display_range(&display_map).sorted();
|
||||||
debug_assert_eq!(range.start.row(), range.end.row());
|
debug_assert_eq!(range.start.row(), range.end.row());
|
||||||
let mut row = range.start.row();
|
let mut row = range.start.row();
|
||||||
let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
|
let positions = if let SelectionGoal::HorizontalRange { start, end } =
|
||||||
|
selection.goal
|
||||||
{
|
{
|
||||||
start..end
|
start..end
|
||||||
} else {
|
} else {
|
||||||
cmp::min(range.start.column(), range.end.column())
|
let start_x = display_map.x_for_point(range.start, &text_layout_details);
|
||||||
..cmp::max(range.start.column(), range.end.column())
|
let end_x = display_map.x_for_point(range.end, &text_layout_details);
|
||||||
|
|
||||||
|
start_x.min(end_x)..start_x.max(end_x)
|
||||||
};
|
};
|
||||||
|
|
||||||
while row != end_row {
|
while row != end_row {
|
||||||
@ -6012,8 +6019,9 @@ impl Editor {
|
|||||||
if let Some(new_selection) = self.selections.build_columnar_selection(
|
if let Some(new_selection) = self.selections.build_columnar_selection(
|
||||||
&display_map,
|
&display_map,
|
||||||
row,
|
row,
|
||||||
&columns,
|
&positions,
|
||||||
selection.reversed,
|
selection.reversed,
|
||||||
|
&text_layout_details,
|
||||||
) {
|
) {
|
||||||
state.stack.push(new_selection.id);
|
state.stack.push(new_selection.id);
|
||||||
if above {
|
if above {
|
||||||
|
@ -22,7 +22,7 @@ use git::diff::DiffHunkStatus;
|
|||||||
use gpui::{
|
use gpui::{
|
||||||
color::Color,
|
color::Color,
|
||||||
elements::*,
|
elements::*,
|
||||||
fonts::{HighlightStyle, TextStyle, Underline},
|
fonts::TextStyle,
|
||||||
geometry::{
|
geometry::{
|
||||||
rect::RectF,
|
rect::RectF,
|
||||||
vector::{vec2f, Vector2F},
|
vector::{vec2f, Vector2F},
|
||||||
@ -37,8 +37,7 @@ use gpui::{
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use json::json;
|
use json::json;
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::ShowWhitespaceSetting, Bias, CursorShape, DiagnosticSeverity, OffsetUtf16,
|
language_settings::ShowWhitespaceSetting, Bias, CursorShape, OffsetUtf16, Selection,
|
||||||
Selection,
|
|
||||||
};
|
};
|
||||||
use project::{
|
use project::{
|
||||||
project_settings::{GitGutterSetting, ProjectSettings},
|
project_settings::{GitGutterSetting, ProjectSettings},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
|
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
|
||||||
use crate::{char_kind, CharKind, Editor, EditorStyle, ToOffset, ToPoint};
|
use crate::{char_kind, CharKind, Editor, EditorStyle, ToOffset, ToPoint};
|
||||||
use gpui::{text_layout, FontCache, TextLayoutCache, WindowContext};
|
use gpui::{FontCache, TextLayoutCache, WindowContext};
|
||||||
use language::Point;
|
use language::Point;
|
||||||
use std::{ops::Range, sync::Arc};
|
use std::{ops::Range, sync::Arc};
|
||||||
|
|
||||||
@ -105,7 +105,9 @@ pub fn up_by_rows(
|
|||||||
) -> (DisplayPoint, SelectionGoal) {
|
) -> (DisplayPoint, SelectionGoal) {
|
||||||
let mut goal_x = match goal {
|
let mut goal_x = match goal {
|
||||||
SelectionGoal::HorizontalPosition(x) => x,
|
SelectionGoal::HorizontalPosition(x) => x,
|
||||||
|
SelectionGoal::WrappedHorizontalPosition((_, x)) => x,
|
||||||
SelectionGoal::HorizontalRange { end, .. } => end,
|
SelectionGoal::HorizontalRange { end, .. } => end,
|
||||||
|
SelectionGoal::WrappedHorizontalRange { end: (_, end), .. } => end,
|
||||||
_ => map.x_for_point(start, text_layout_details),
|
_ => map.x_for_point(start, text_layout_details),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -140,7 +142,9 @@ pub fn down_by_rows(
|
|||||||
) -> (DisplayPoint, SelectionGoal) {
|
) -> (DisplayPoint, SelectionGoal) {
|
||||||
let mut goal_x = match goal {
|
let mut goal_x = match goal {
|
||||||
SelectionGoal::HorizontalPosition(x) => x,
|
SelectionGoal::HorizontalPosition(x) => x,
|
||||||
|
SelectionGoal::WrappedHorizontalPosition((_, x)) => x,
|
||||||
SelectionGoal::HorizontalRange { end, .. } => end,
|
SelectionGoal::HorizontalRange { end, .. } => end,
|
||||||
|
SelectionGoal::WrappedHorizontalRange { end: (_, end), .. } => end,
|
||||||
_ => map.x_for_point(start, text_layout_details),
|
_ => map.x_for_point(start, text_layout_details),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cell::Ref,
|
cell::Ref,
|
||||||
cmp, iter, mem,
|
iter, mem,
|
||||||
ops::{Deref, DerefMut, Range, Sub},
|
ops::{Deref, DerefMut, Range, Sub},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
@ -13,6 +13,7 @@ use util::post_inc;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
|
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
|
||||||
|
movement::TextLayoutDetails,
|
||||||
Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset,
|
Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -305,23 +306,27 @@ impl SelectionsCollection {
|
|||||||
&mut self,
|
&mut self,
|
||||||
display_map: &DisplaySnapshot,
|
display_map: &DisplaySnapshot,
|
||||||
row: u32,
|
row: u32,
|
||||||
columns: &Range<u32>,
|
positions: &Range<f32>,
|
||||||
reversed: bool,
|
reversed: bool,
|
||||||
|
text_layout_details: &TextLayoutDetails,
|
||||||
) -> Option<Selection<Point>> {
|
) -> Option<Selection<Point>> {
|
||||||
let is_empty = columns.start == columns.end;
|
let is_empty = positions.start == positions.end;
|
||||||
let line_len = display_map.line_len(row);
|
let line_len = display_map.line_len(row);
|
||||||
if columns.start < line_len || (is_empty && columns.start == line_len) {
|
|
||||||
let start = DisplayPoint::new(row, columns.start);
|
let start_col = display_map.column_for_x(row, positions.start, text_layout_details);
|
||||||
let end = DisplayPoint::new(row, cmp::min(columns.end, line_len));
|
if start_col < line_len || (is_empty && start_col == line_len) {
|
||||||
|
let start = DisplayPoint::new(row, start_col);
|
||||||
|
let end_col = display_map.column_for_x(row, positions.end, text_layout_details);
|
||||||
|
let end = DisplayPoint::new(row, end_col);
|
||||||
|
|
||||||
Some(Selection {
|
Some(Selection {
|
||||||
id: post_inc(&mut self.next_selection_id),
|
id: post_inc(&mut self.next_selection_id),
|
||||||
start: start.to_point(display_map),
|
start: start.to_point(display_map),
|
||||||
end: end.to_point(display_map),
|
end: end.to_point(display_map),
|
||||||
reversed,
|
reversed,
|
||||||
goal: SelectionGoal::ColumnRange {
|
goal: SelectionGoal::HorizontalRange {
|
||||||
start: columns.start,
|
start: positions.start,
|
||||||
end: columns.end,
|
end: positions.end,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,8 +7,8 @@ pub enum SelectionGoal {
|
|||||||
None,
|
None,
|
||||||
HorizontalPosition(f32),
|
HorizontalPosition(f32),
|
||||||
HorizontalRange { start: f32, end: f32 },
|
HorizontalRange { start: f32, end: f32 },
|
||||||
Column(u32),
|
WrappedHorizontalPosition((u32, f32)),
|
||||||
ColumnRange { start: u32, end: u32 },
|
WrappedHorizontalRange { start: (u32, f32), end: (u32, f32) },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
use std::cmp;
|
|
||||||
|
|
||||||
use editor::{
|
use editor::{
|
||||||
char_kind,
|
char_kind,
|
||||||
display_map::{DisplaySnapshot, FoldPoint, ToDisplayPoint},
|
display_map::{DisplaySnapshot, FoldPoint, ToDisplayPoint},
|
||||||
@ -371,13 +369,13 @@ impl Motion {
|
|||||||
Backspace => (backspace(map, point, times), SelectionGoal::None),
|
Backspace => (backspace(map, point, times), SelectionGoal::None),
|
||||||
Down {
|
Down {
|
||||||
display_lines: false,
|
display_lines: false,
|
||||||
} => down(map, point, goal, times),
|
} => up_down_buffer_rows(map, point, goal, times as isize, &text_layout_details),
|
||||||
Down {
|
Down {
|
||||||
display_lines: true,
|
display_lines: true,
|
||||||
} => down_display(map, point, goal, times, &text_layout_details),
|
} => down_display(map, point, goal, times, &text_layout_details),
|
||||||
Up {
|
Up {
|
||||||
display_lines: false,
|
display_lines: false,
|
||||||
} => up(map, point, goal, times),
|
} => up_down_buffer_rows(map, point, goal, 0 - times as isize, &text_layout_details),
|
||||||
Up {
|
Up {
|
||||||
display_lines: true,
|
display_lines: true,
|
||||||
} => up_display(map, point, goal, times, &text_layout_details),
|
} => up_display(map, point, goal, times, &text_layout_details),
|
||||||
@ -536,35 +534,86 @@ fn backspace(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> Di
|
|||||||
point
|
point
|
||||||
}
|
}
|
||||||
|
|
||||||
fn down(
|
pub(crate) fn start_of_relative_buffer_row(
|
||||||
|
map: &DisplaySnapshot,
|
||||||
|
point: DisplayPoint,
|
||||||
|
times: isize,
|
||||||
|
) -> DisplayPoint {
|
||||||
|
let start = map.display_point_to_fold_point(point, Bias::Left);
|
||||||
|
let target = start.row() as isize + times;
|
||||||
|
let new_row = (target.max(0) as u32).min(map.fold_snapshot.max_point().row());
|
||||||
|
|
||||||
|
map.clip_point(
|
||||||
|
map.fold_point_to_display_point(
|
||||||
|
map.fold_snapshot
|
||||||
|
.clip_point(FoldPoint::new(new_row, 0), Bias::Right),
|
||||||
|
),
|
||||||
|
Bias::Right,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn up_down_buffer_rows(
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
point: DisplayPoint,
|
point: DisplayPoint,
|
||||||
mut goal: SelectionGoal,
|
mut goal: SelectionGoal,
|
||||||
times: usize,
|
times: isize,
|
||||||
|
text_layout_details: &TextLayoutDetails,
|
||||||
) -> (DisplayPoint, SelectionGoal) {
|
) -> (DisplayPoint, SelectionGoal) {
|
||||||
let start = map.display_point_to_fold_point(point, Bias::Left);
|
let start = map.display_point_to_fold_point(point, Bias::Left);
|
||||||
|
let begin_folded_line = map.fold_point_to_display_point(
|
||||||
|
map.fold_snapshot
|
||||||
|
.clip_point(FoldPoint::new(start.row(), 0), Bias::Left),
|
||||||
|
);
|
||||||
|
let select_nth_wrapped_row = point.row() - begin_folded_line.row();
|
||||||
|
|
||||||
let goal_column = match goal {
|
let (goal_wrap, goal_x) = match goal {
|
||||||
SelectionGoal::Column(column) => column,
|
SelectionGoal::WrappedHorizontalPosition((row, x)) => (row, x),
|
||||||
SelectionGoal::ColumnRange { end, .. } => end,
|
SelectionGoal::WrappedHorizontalRange { end: (row, x), .. } => (row, x),
|
||||||
|
SelectionGoal::HorizontalRange { end, .. } => (select_nth_wrapped_row, end),
|
||||||
|
SelectionGoal::HorizontalPosition(x) => (select_nth_wrapped_row, x),
|
||||||
_ => {
|
_ => {
|
||||||
goal = SelectionGoal::Column(start.column());
|
let x = map.x_for_point(point, text_layout_details);
|
||||||
start.column()
|
goal = SelectionGoal::WrappedHorizontalPosition((select_nth_wrapped_row, x));
|
||||||
|
(select_nth_wrapped_row, x)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_row = cmp::min(
|
let target = start.row() as isize + times;
|
||||||
start.row() + times as u32,
|
let new_row = (target.max(0) as u32).min(map.fold_snapshot.max_point().row());
|
||||||
map.fold_snapshot.max_point().row(),
|
|
||||||
);
|
let mut begin_folded_line = map.fold_point_to_display_point(
|
||||||
let new_col = cmp::min(goal_column, map.fold_snapshot.line_len(new_row));
|
|
||||||
let point = map.fold_point_to_display_point(
|
|
||||||
map.fold_snapshot
|
map.fold_snapshot
|
||||||
.clip_point(FoldPoint::new(new_row, new_col), Bias::Left),
|
.clip_point(FoldPoint::new(new_row, 0), Bias::Left),
|
||||||
);
|
);
|
||||||
|
|
||||||
// clip twice to "clip at end of line"
|
let mut i = 0;
|
||||||
(map.clip_point(point, Bias::Left), goal)
|
while i < goal_wrap && begin_folded_line.row() < map.max_point().row() {
|
||||||
|
let next_folded_line = DisplayPoint::new(begin_folded_line.row() + 1, 0);
|
||||||
|
if map
|
||||||
|
.display_point_to_fold_point(next_folded_line, Bias::Right)
|
||||||
|
.row()
|
||||||
|
== new_row
|
||||||
|
{
|
||||||
|
i += 1;
|
||||||
|
begin_folded_line = next_folded_line;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_col = if i == goal_wrap {
|
||||||
|
map.column_for_x(begin_folded_line.row(), goal_x, text_layout_details)
|
||||||
|
} else {
|
||||||
|
map.line_len(begin_folded_line.row())
|
||||||
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
map.clip_point(
|
||||||
|
DisplayPoint::new(begin_folded_line.row(), new_col),
|
||||||
|
Bias::Left,
|
||||||
|
),
|
||||||
|
goal,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn down_display(
|
fn down_display(
|
||||||
@ -581,33 +630,6 @@ fn down_display(
|
|||||||
(point, goal)
|
(point, goal)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn up(
|
|
||||||
map: &DisplaySnapshot,
|
|
||||||
point: DisplayPoint,
|
|
||||||
mut goal: SelectionGoal,
|
|
||||||
times: usize,
|
|
||||||
) -> (DisplayPoint, SelectionGoal) {
|
|
||||||
let start = map.display_point_to_fold_point(point, Bias::Left);
|
|
||||||
|
|
||||||
let goal_column = match goal {
|
|
||||||
SelectionGoal::Column(column) => column,
|
|
||||||
SelectionGoal::ColumnRange { end, .. } => end,
|
|
||||||
_ => {
|
|
||||||
goal = SelectionGoal::Column(start.column());
|
|
||||||
start.column()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_row = start.row().saturating_sub(times as u32);
|
|
||||||
let new_col = cmp::min(goal_column, map.fold_snapshot.line_len(new_row));
|
|
||||||
let point = map.fold_point_to_display_point(
|
|
||||||
map.fold_snapshot
|
|
||||||
.clip_point(FoldPoint::new(new_row, new_col), Bias::Left),
|
|
||||||
);
|
|
||||||
|
|
||||||
(map.clip_point(point, Bias::Left), goal)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn up_display(
|
fn up_display(
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
mut point: DisplayPoint,
|
mut point: DisplayPoint,
|
||||||
@ -894,7 +916,7 @@ fn find_backward(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn next_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> DisplayPoint {
|
fn next_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> DisplayPoint {
|
||||||
let correct_line = down(map, point, SelectionGoal::None, times).0;
|
let correct_line = start_of_relative_buffer_row(map, point, times as isize);
|
||||||
first_non_whitespace(map, false, correct_line)
|
first_non_whitespace(map, false, correct_line)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -904,7 +926,7 @@ pub(crate) fn next_line_end(
|
|||||||
times: usize,
|
times: usize,
|
||||||
) -> DisplayPoint {
|
) -> DisplayPoint {
|
||||||
if times > 1 {
|
if times > 1 {
|
||||||
point = down(map, point, SelectionGoal::None, times - 1).0;
|
point = start_of_relative_buffer_row(map, point, times as isize - 1);
|
||||||
}
|
}
|
||||||
end_of_line(map, false, point)
|
end_of_line(map, false, point)
|
||||||
}
|
}
|
||||||
|
@ -194,9 +194,7 @@ fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspa
|
|||||||
vim.switch_mode(Mode::Insert, false, cx);
|
vim.switch_mode(Mode::Insert, false, cx);
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.move_cursors_with(|map, cursor, goal| {
|
s.move_cursors_with(|map, cursor, _| (right(map, cursor, 1), SelectionGoal::None));
|
||||||
(right(map, cursor, 1), SelectionGoal::None)
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -219,7 +217,7 @@ fn insert_first_non_whitespace(
|
|||||||
vim.switch_mode(Mode::Insert, false, cx);
|
vim.switch_mode(Mode::Insert, false, cx);
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.move_cursors_with(|map, cursor, goal| {
|
s.move_cursors_with(|map, cursor, _| {
|
||||||
(
|
(
|
||||||
first_non_whitespace(map, false, cursor),
|
first_non_whitespace(map, false, cursor),
|
||||||
SelectionGoal::None,
|
SelectionGoal::None,
|
||||||
@ -236,7 +234,7 @@ fn insert_end_of_line(_: &mut Workspace, _: &InsertEndOfLine, cx: &mut ViewConte
|
|||||||
vim.switch_mode(Mode::Insert, false, cx);
|
vim.switch_mode(Mode::Insert, false, cx);
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.move_cursors_with(|map, cursor, goal| {
|
s.move_cursors_with(|map, cursor, _| {
|
||||||
(next_line_end(map, cursor, 1), SelectionGoal::None)
|
(next_line_end(map, cursor, 1), SelectionGoal::None)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -267,7 +265,7 @@ fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContex
|
|||||||
editor.edit_with_autoindent(edits, cx);
|
editor.edit_with_autoindent(edits, cx);
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.move_cursors_with(|map, cursor, _| {
|
s.move_cursors_with(|map, cursor, _| {
|
||||||
let previous_line = motion::up(map, cursor, SelectionGoal::None, 1).0;
|
let previous_line = motion::start_of_relative_buffer_row(map, cursor, -1);
|
||||||
let insert_point = motion::end_of_line(map, false, previous_line);
|
let insert_point = motion::end_of_line(map, false, previous_line);
|
||||||
(insert_point, SelectionGoal::None)
|
(insert_point, SelectionGoal::None)
|
||||||
});
|
});
|
||||||
@ -398,12 +396,26 @@ mod test {
|
|||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_j(cx: &mut gpui::TestAppContext) {
|
async fn test_j(cx: &mut gpui::TestAppContext) {
|
||||||
let mut cx = NeovimBackedTestContext::new(cx).await.binding(["j"]);
|
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||||
cx.assert_all(indoc! {"
|
|
||||||
ˇThe qˇuick broˇwn
|
cx.set_shared_state(indoc! {"
|
||||||
ˇfox jumps"
|
aaˇaa
|
||||||
|
😃😃"
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
cx.simulate_shared_keystrokes(["j"]).await;
|
||||||
|
cx.assert_shared_state(indoc! {"
|
||||||
|
aaaa
|
||||||
|
😃ˇ😃"
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
for marked_position in cx.each_marked_position(indoc! {"
|
||||||
|
ˇThe qˇuick broˇwn
|
||||||
|
ˇfox jumps"
|
||||||
|
}) {
|
||||||
|
cx.assert_neovim_compatible(&marked_position, ["j"]).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -3,11 +3,7 @@ use gpui::{actions, AppContext, WindowContext};
|
|||||||
use language::Point;
|
use language::Point;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
use crate::{
|
use crate::{motion::Motion, utils::copy_selections_content, Mode, Vim};
|
||||||
motion::{right, Motion},
|
|
||||||
utils::copy_selections_content,
|
|
||||||
Mode, Vim,
|
|
||||||
};
|
|
||||||
|
|
||||||
actions!(vim, [Substitute, SubstituteLine]);
|
actions!(vim, [Substitute, SubstituteLine]);
|
||||||
|
|
||||||
|
@ -652,3 +652,59 @@ async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
|
|||||||
Lorem Ipsum"})
|
Lorem Ipsum"})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
|
||||||
|
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||||
|
|
||||||
|
cx.set_shared_wrap(12).await;
|
||||||
|
|
||||||
|
cx.set_shared_state(indoc! {"
|
||||||
|
aaˇaa
|
||||||
|
😃😃"
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
cx.simulate_shared_keystrokes(["j"]).await;
|
||||||
|
cx.assert_shared_state(indoc! {"
|
||||||
|
aaaa
|
||||||
|
😃ˇ😃"
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
cx.set_shared_state(indoc! {"
|
||||||
|
123456789012aaˇaa
|
||||||
|
123456789012😃😃"
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
cx.simulate_shared_keystrokes(["j"]).await;
|
||||||
|
cx.assert_shared_state(indoc! {"
|
||||||
|
123456789012aaaa
|
||||||
|
123456789012😃ˇ😃"
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
cx.set_shared_state(indoc! {"
|
||||||
|
123456789012aaˇaa
|
||||||
|
123456789012😃😃"
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
cx.simulate_shared_keystrokes(["j"]).await;
|
||||||
|
cx.assert_shared_state(indoc! {"
|
||||||
|
123456789012aaaa
|
||||||
|
123456789012😃ˇ😃"
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
cx.set_shared_state(indoc! {"
|
||||||
|
123456789012aaaaˇaaaaaaaa123456789012
|
||||||
|
wow
|
||||||
|
123456789012😃😃😃😃😃😃123456789012"
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
cx.simulate_shared_keystrokes(["j", "j"]).await;
|
||||||
|
cx.assert_shared_state(indoc! {"
|
||||||
|
123456789012aaaaaaaaaaaa123456789012
|
||||||
|
wow
|
||||||
|
123456789012😃😃ˇ😃😃😃😃123456789012"
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
@ -581,7 +581,7 @@ impl Setting for VimModeSetting {
|
|||||||
fn local_selections_changed(newest: Selection<usize>, cx: &mut WindowContext) {
|
fn local_selections_changed(newest: Selection<usize>, cx: &mut WindowContext) {
|
||||||
Vim::update(cx, |vim, cx| {
|
Vim::update(cx, |vim, cx| {
|
||||||
if vim.enabled && vim.state().mode == Mode::Normal && !newest.is_empty() {
|
if vim.enabled && vim.state().mode == Mode::Normal && !newest.is_empty() {
|
||||||
if matches!(newest.goal, SelectionGoal::ColumnRange { .. }) {
|
if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
|
||||||
vim.switch_mode(Mode::VisualBlock, false, cx);
|
vim.switch_mode(Mode::VisualBlock, false, cx);
|
||||||
} else {
|
} else {
|
||||||
vim.switch_mode(Mode::Visual, false, cx)
|
vim.switch_mode(Mode::Visual, false, cx)
|
||||||
|
@ -140,17 +140,21 @@ pub fn visual_block_motion(
|
|||||||
SelectionGoal,
|
SelectionGoal,
|
||||||
) -> Option<(DisplayPoint, SelectionGoal)>,
|
) -> Option<(DisplayPoint, SelectionGoal)>,
|
||||||
) {
|
) {
|
||||||
|
let text_layout_details = TextLayoutDetails::new(editor, cx);
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
let map = &s.display_map();
|
let map = &s.display_map();
|
||||||
let mut head = s.newest_anchor().head().to_display_point(map);
|
let mut head = s.newest_anchor().head().to_display_point(map);
|
||||||
let mut tail = s.oldest_anchor().tail().to_display_point(map);
|
let mut tail = s.oldest_anchor().tail().to_display_point(map);
|
||||||
|
|
||||||
let (start, end) = match s.newest_anchor().goal {
|
let (start, end) = match s.newest_anchor().goal {
|
||||||
SelectionGoal::ColumnRange { start, end } if preserve_goal => (start, end),
|
SelectionGoal::HorizontalRange { start, end } if preserve_goal => (start, end),
|
||||||
SelectionGoal::Column(start) if preserve_goal => (start, start + 1),
|
SelectionGoal::HorizontalPosition(start) if preserve_goal => (start, start + 10.0),
|
||||||
_ => (tail.column(), head.column()),
|
_ => (
|
||||||
|
map.x_for_point(tail, &text_layout_details),
|
||||||
|
map.x_for_point(head, &text_layout_details),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
let goal = SelectionGoal::ColumnRange { start, end };
|
let goal = SelectionGoal::HorizontalRange { start, end };
|
||||||
|
|
||||||
let was_reversed = tail.column() > head.column();
|
let was_reversed = tail.column() > head.column();
|
||||||
if !was_reversed && !preserve_goal {
|
if !was_reversed && !preserve_goal {
|
||||||
@ -172,21 +176,39 @@ pub fn visual_block_motion(
|
|||||||
head = movement::saturating_right(map, head)
|
head = movement::saturating_right(map, head)
|
||||||
}
|
}
|
||||||
|
|
||||||
let columns = if is_reversed {
|
let positions = if is_reversed {
|
||||||
head.column()..tail.column()
|
map.x_for_point(head, &text_layout_details)..map.x_for_point(tail, &text_layout_details)
|
||||||
} else if head.column() == tail.column() {
|
} else if head.column() == tail.column() {
|
||||||
head.column()..(head.column() + 1)
|
map.x_for_point(head, &text_layout_details)
|
||||||
|
..map.x_for_point(head, &text_layout_details) + 10.0
|
||||||
} else {
|
} else {
|
||||||
tail.column()..head.column()
|
map.x_for_point(tail, &text_layout_details)..map.x_for_point(head, &text_layout_details)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut selections = Vec::new();
|
let mut selections = Vec::new();
|
||||||
let mut row = tail.row();
|
let mut row = tail.row();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let start = map.clip_point(DisplayPoint::new(row, columns.start), Bias::Left);
|
let start = map.clip_point(
|
||||||
let end = map.clip_point(DisplayPoint::new(row, columns.end), Bias::Left);
|
DisplayPoint::new(
|
||||||
if columns.start <= map.line_len(row) {
|
row,
|
||||||
|
map.column_for_x(row, positions.start, &text_layout_details),
|
||||||
|
),
|
||||||
|
Bias::Left,
|
||||||
|
);
|
||||||
|
let end = map.clip_point(
|
||||||
|
DisplayPoint::new(
|
||||||
|
row,
|
||||||
|
map.column_for_x(row, positions.end, &text_layout_details),
|
||||||
|
),
|
||||||
|
Bias::Left,
|
||||||
|
);
|
||||||
|
if positions.start
|
||||||
|
<= map.x_for_point(
|
||||||
|
DisplayPoint::new(row, map.line_len(row)),
|
||||||
|
&text_layout_details,
|
||||||
|
)
|
||||||
|
{
|
||||||
let selection = Selection {
|
let selection = Selection {
|
||||||
id: s.new_selection_id(),
|
id: s.new_selection_id(),
|
||||||
start: start.to_point(map),
|
start: start.to_point(map),
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
{"Put":{"state":"aaˇaa\n😃😃"}}
|
||||||
|
{"Key":"j"}
|
||||||
|
{"Get":{"state":"aaaa\n😃ˇ😃","mode":"Normal"}}
|
||||||
{"Put":{"state":"ˇThe quick brown\nfox jumps"}}
|
{"Put":{"state":"ˇThe quick brown\nfox jumps"}}
|
||||||
{"Key":"j"}
|
{"Key":"j"}
|
||||||
{"Get":{"state":"The quick brown\nˇfox jumps","mode":"Normal"}}
|
{"Get":{"state":"The quick brown\nˇfox jumps","mode":"Normal"}}
|
||||||
|
Loading…
Reference in New Issue
Block a user