Compare commits

...

3 Commits

Author SHA1 Message Date
Max Brunsfeld
d063874f49 Merge branch 'main' into always-adjust-selections 2024-10-28 11:38:03 -07:00
Antonio Scandurra
5d7b63ca87 WIP 2024-10-25 19:09:13 +02:00
Antonio Scandurra
29db144434 Remove SelectionsCollection::all_adjusted in favor of using all
We still need to re-implement the adjustment logic in `all`, but this commit
paves the way for that.

Co-Authored-By: Nathan <nathan@zed.dev>
2024-10-25 16:45:30 +02:00
14 changed files with 190 additions and 88 deletions

View File

@@ -1667,8 +1667,10 @@ impl ContextEditor {
});
}
fn cursors(&self, cx: &AppContext) -> Vec<usize> {
let selections = self.editor.read(cx).selections.all::<usize>(cx);
fn cursors(&self, cx: &mut WindowContext) -> Vec<usize> {
let selections = self
.editor
.update(cx, |editor, cx| editor.selections.all::<usize>(cx));
selections
.into_iter()
.map(|selection| selection.head())
@@ -2964,7 +2966,7 @@ impl ContextEditor {
let mut creases = vec![];
editor.update(cx, |editor, cx| {
let selections = editor.selections.all_adjusted(cx);
let selections = editor.selections.all::<Point>(cx);
let buffer = editor.buffer().read(cx).snapshot(cx);
for selection in selections {
let range = editor::ToOffset::to_offset(&selection.start, &buffer)

View File

@@ -189,11 +189,16 @@ impl InlineAssistant {
initial_prompt: Option<String>,
cx: &mut WindowContext,
) {
let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx);
let (snapshot, initial_selections) = editor.update(cx, |editor, cx| {
(
editor.buffer().read(cx).snapshot(cx),
editor.selections.all::<Point>(cx),
)
});
let mut selections = Vec::<Selection<Point>>::new();
let mut newest_selection = None;
for mut selection in editor.read(cx).selections.all::<Point>(cx) {
for mut selection in initial_selections {
if selection.end > selection.start {
selection.start.column = 0;
// If the selection ends at the start of the line, we don't want to include it.

View File

@@ -1957,9 +1957,10 @@ async fn test_following_to_channel_notes_without_a_shared_project(
});
channel_notes_1_b.update(cx_b, |notes, cx| {
assert_eq!(notes.channel(cx).unwrap().name, "channel-1");
let editor = notes.editor.read(cx);
assert_eq!(editor.text(cx), "Hello from A.");
assert_eq!(editor.selections.ranges::<usize>(cx), &[3..4]);
notes.editor.update(cx, |editor, cx| {
assert_eq!(editor.text(cx), "Hello from A.");
assert_eq!(editor.selections.ranges::<usize>(cx), &[3..4]);
})
});
// Client A opens the notes for channel 2.

View File

@@ -3218,7 +3218,7 @@ impl Editor {
return;
}
let selections = self.selections.all_adjusted(cx);
let selections = self.selections.all::<Point>(cx);
let mut bracket_inserted = false;
let mut edits = Vec::new();
let mut linked_edits = HashMap::<_, Vec<_>>::default();
@@ -3731,7 +3731,7 @@ impl Editor {
let mut edits = Vec::new();
let mut rows = Vec::new();
for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
for (rows_inserted, selection) in self.selections.all::<Point>(cx).into_iter().enumerate() {
let cursor = selection.head();
let row = cursor.row;
@@ -3789,7 +3789,7 @@ impl Editor {
let mut rows = Vec::new();
let mut rows_inserted = 0;
for selection in self.selections.all_adjusted(cx) {
for selection in self.selections.all::<Point>(cx) {
let cursor = selection.head();
let row = cursor.row;
@@ -3860,7 +3860,7 @@ impl Editor {
let text: Arc<str> = text.into();
self.transact(cx, |this, cx| {
let old_selections = this.selections.all_adjusted(cx);
let old_selections = this.selections.all::<Point>(cx);
let selection_anchors = this.buffer.update(cx, |buffer, cx| {
let anchors = {
let snapshot = buffer.read(cx);
@@ -5818,7 +5818,7 @@ impl Editor {
return;
}
let mut selections = self.selections.all_adjusted(cx);
let mut selections = self.selections.all::<Point>(cx);
let buffer = self.buffer.read(cx);
let snapshot = buffer.snapshot(cx);
let rows_iter = selections.iter().map(|s| s.head().row);
@@ -10392,7 +10392,7 @@ impl Editor {
let selections = self
.selections
.all_adjusted(cx)
.all::<Point>(cx)
.into_iter()
.filter(|s| !s.is_empty())
.collect_vec();
@@ -10701,7 +10701,7 @@ impl Editor {
pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
let mut fold_ranges = Vec::new();
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all_adjusted(cx);
let selections = self.selections.all::<Point>(cx);
for selection in selections {
let range = selection.range().sorted();
@@ -10790,7 +10790,7 @@ impl Editor {
pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
let mut fold_ranges = Vec::new();
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all_adjusted(cx);
let selections = self.selections.all::<Point>(cx);
for selection in selections {
let range = selection.range().sorted();
@@ -12372,9 +12372,10 @@ impl Editor {
return;
};
let selections = self.selections.all::<usize>(cx);
let buffer = self.buffer.read(cx);
let mut new_selections_by_buffer = HashMap::default();
for selection in self.selections.all::<usize>(cx) {
for selection in selections {
for (buffer, range, _) in
buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
{
@@ -12419,6 +12420,7 @@ impl Editor {
}
fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
let selections = self.selections.all::<usize>(cx);
let buffer = self.buffer.read(cx);
if buffer.is_singleton() {
cx.propagate();
@@ -12431,7 +12433,7 @@ impl Editor {
};
let mut new_selections_by_buffer = HashMap::default();
for selection in self.selections.all::<usize>(cx) {
for selection in selections {
for (mut buffer_handle, mut range, _) in
buffer.range_to_buffer_ranges(selection.range(), cx)
{
@@ -12547,7 +12549,7 @@ impl Editor {
fn selection_replacement_ranges(
&self,
range: Range<OffsetUtf16>,
cx: &AppContext,
cx: &mut AppContext,
) -> Vec<Range<OffsetUtf16>> {
let selections = self.selections.all::<OffsetUtf16>(cx);
let newest_selection = selections

View File

@@ -41,9 +41,9 @@ pub(super) fn refresh_linked_ranges(this: &mut Editor, cx: &mut ViewContext<Edit
return None;
}
let project = this.project.clone()?;
let selections = this.selections.all::<usize>(cx);
let buffer = this.buffer.read(cx);
let mut applicable_selections = vec![];
let selections = this.selections.all::<usize>(cx);
let snapshot = buffer.snapshot(cx);
for selection in selections {
let cursor_position = selection.head();

View File

@@ -8,14 +8,15 @@ use std::{
use collections::HashMap;
use gpui::{AppContext, Model, Pixels};
use itertools::Itertools;
use language::{Bias, Point, Selection, SelectionGoal, TextDimension, ToPoint};
use language::{Bias, Point, Selection, SelectionGoal, TextDimension};
use multi_buffer::AnchorRangeExt as _;
use util::post_inc;
use crate::{
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
movement::TextLayoutDetails,
Anchor, DisplayPoint, DisplayRow, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode,
ToOffset,
ToOffset, ToPoint,
};
#[derive(Debug, Clone)]
@@ -96,28 +97,68 @@ impl SelectionsCollection {
pub fn pending<D: TextDimension + Ord + Sub<D, Output = D>>(
&self,
cx: &AppContext,
cx: &mut AppContext,
) -> Option<Selection<D>> {
self.pending_anchor()
.as_ref()
.map(|pending| pending.map(|p| p.summary::<D>(&self.buffer(cx))))
let selection = self.pending_anchor()?;
if self.line_mode {
let map = self.display_map(cx);
let range = selection.range().to_point(&map.buffer_snapshot);
let expanded_range = map.expand_to_line(range);
let mut endpoints = map
.buffer_snapshot
.dimensions_from_points::<D>([expanded_range.start, expanded_range.end]);
let start = endpoints.next().unwrap();
let end = endpoints.next().unwrap();
Some(Selection {
id: selection.id,
start,
end,
reversed: selection.reversed,
goal: selection.goal,
})
} else {
Some(selection.map(|p| p.summary::<D>(&self.buffer(cx))))
}
}
pub(crate) fn pending_mode(&self) -> Option<SelectMode> {
self.pending.as_ref().map(|pending| pending.mode.clone())
}
pub fn all<'a, D>(&self, cx: &AppContext) -> Vec<Selection<D>>
pub fn all<'a, D>(&self, cx: &mut AppContext) -> Vec<Selection<D>>
where
D: 'a + TextDimension + Ord + Sub<D, Output = D>,
{
// todo!()
// let mut selections = self.all::<Point>(cx);
// if self.line_mode {
// let map = self.display_map(cx);
// for selection in &mut selections {
// let new_range = map.expand_to_line(selection.range());
// selection.start = new_range.start;
// selection.end = new_range.end;
// }
// }
let map = self.display_map(cx);
let disjoint_anchors = &self.disjoint;
let mut disjoint =
resolve_multiple::<D, _>(disjoint_anchors.iter(), &self.buffer(cx)).peekable();
let mut disjoint = resolve_multiple::<Point, _>(disjoint_anchors.iter(), &self.buffer(cx))
.map(|mut selection| {
if self.line_mode {
let new_range = map.expand_to_line(selection.range());
selection.start = new_range.start;
selection.end = new_range.end;
}
// todo!(expand selection to encompass folds and blocks)
// let start = map.display_point_to_point(map.point_to_display_point(selection.start, Bias::Left), Bias::Left);
// let end = map.display_point_to_point(map.point_to_display_point(selection.end, Bias::Right), Bias::Right);
selection
})
.peekable();
let mut pending_opt = self.pending::<D>(cx);
let mut pending_opt = self.pending::<Point>(cx);
iter::from_fn(move || {
// todo!(return merged selections)
if let Some(pending) = pending_opt.as_mut() {
while let Some(next_selection) = disjoint.peek() {
if pending.start <= next_selection.end && pending.end >= next_selection.start {
@@ -140,23 +181,10 @@ impl SelectionsCollection {
disjoint.next()
}
})
// todo!("convert selections to D in a batched way")
.collect()
}
/// Returns all of the selections, adjusted to take into account the selection line_mode
pub fn all_adjusted(&self, cx: &mut AppContext) -> Vec<Selection<Point>> {
let mut selections = self.all::<Point>(cx);
if self.line_mode {
let map = self.display_map(cx);
for selection in &mut selections {
let new_range = map.expand_to_line(selection.range());
selection.start = new_range.start;
selection.end = new_range.end;
}
}
selections
}
/// Returns the newest selection, adjusted to take into account the selection line_mode
pub fn newest_adjusted(&self, cx: &mut AppContext) -> Selection<Point> {
let mut selection = self.newest::<Point>(cx);
@@ -276,14 +304,14 @@ impl SelectionsCollection {
pub fn first<D: TextDimension + Ord + Sub<D, Output = D>>(
&self,
cx: &AppContext,
cx: &mut AppContext,
) -> Selection<D> {
self.all(cx).first().unwrap().clone()
}
pub fn last<D: TextDimension + Ord + Sub<D, Output = D>>(
&self,
cx: &AppContext,
cx: &mut AppContext,
) -> Selection<D> {
self.all(cx).last().unwrap().clone()
}
@@ -298,7 +326,7 @@ impl SelectionsCollection {
#[cfg(any(test, feature = "test-support"))]
pub fn ranges<D: TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug>(
&self,
cx: &AppContext,
cx: &mut AppContext,
) -> Vec<Range<D>> {
self.all::<D>(cx)
.iter()
@@ -475,7 +503,7 @@ impl<'a> MutableSelectionsCollection<'a> {
where
T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub<T, Output = T> + std::marker::Copy,
{
let mut selections = self.all(self.cx);
let mut selections = self.collection.all(self.cx);
let mut start = range.start.to_offset(&self.buffer());
let mut end = range.end.to_offset(&self.buffer());
let reversed = if start > end {
@@ -649,6 +677,7 @@ impl<'a> MutableSelectionsCollection<'a> {
let mut changed = false;
let display_map = self.display_map();
let selections = self
.collection
.all::<Point>(self.cx)
.into_iter()
.map(|selection| {
@@ -676,6 +705,7 @@ impl<'a> MutableSelectionsCollection<'a> {
let mut changed = false;
let snapshot = self.buffer().clone();
let selections = self
.collection
.all::<usize>(self.cx)
.into_iter()
.map(|selection| {

View File

@@ -242,7 +242,7 @@ async fn test_row_column_numbers_query_inside_file(cx: &mut TestAppContext) {
cx.executor().advance_clock(Duration::from_secs(2));
editor.update(cx, |editor, cx| {
let all_selections = editor.selections.all_adjusted(cx);
let all_selections = editor.selections.all::<Point>(cx);
assert_eq!(
all_selections.len(),
1,
@@ -317,7 +317,7 @@ async fn test_row_column_numbers_query_outside_file(cx: &mut TestAppContext) {
cx.executor().advance_clock(Duration::from_secs(2));
editor.update(cx, |editor, cx| {
let all_selections = editor.selections.all_adjusted(cx);
let all_selections = editor.selections.all::<Point>(cx);
assert_eq!(
all_selections.len(),
1,

View File

@@ -37,34 +37,34 @@ impl CursorPosition {
}
fn update_position(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
let editor = editor.read(cx);
let buffer = editor.buffer().read(cx).snapshot(cx);
editor.update(cx, |editor, cx| {
let buffer = editor.buffer().read(cx).snapshot(cx);
self.selected_count = Default::default();
self.selected_count.selections = editor.selections.count();
let mut last_selection: Option<Selection<usize>> = None;
for selection in editor.selections.all::<usize>(cx) {
self.selected_count.characters += buffer
.text_for_range(selection.start..selection.end)
.map(|t| t.chars().count())
.sum::<usize>();
if last_selection
.as_ref()
.map_or(true, |last_selection| selection.id > last_selection.id)
{
last_selection = Some(selection);
}
}
for selection in editor.selections.all::<Point>(cx) {
if selection.end != selection.start {
self.selected_count.lines += (selection.end.row - selection.start.row) as usize;
if selection.end.column != 0 {
self.selected_count.lines += 1;
self.selected_count = Default::default();
self.selected_count.selections = editor.selections.count();
let mut last_selection: Option<Selection<usize>> = None;
for selection in editor.selections.all::<usize>(cx) {
self.selected_count.characters += buffer
.text_for_range(selection.start..selection.end)
.map(|t| t.chars().count())
.sum::<usize>();
if last_selection
.as_ref()
.map_or(true, |last_selection| selection.id > last_selection.id)
{
last_selection = Some(selection);
}
}
}
self.position = last_selection.map(|s| s.head().to_point(&buffer));
for selection in editor.selections.all::<Point>(cx) {
if selection.end != selection.start {
self.selected_count.lines += (selection.end.row - selection.start.row) as usize;
if selection.end.column != 0 {
self.selected_count.lines += 1;
}
}
}
self.position = last_selection.map(|s| s.head().to_point(&buffer));
});
cx.notify();
}

View File

@@ -56,8 +56,8 @@ impl GoToLine {
}
pub fn new(active_editor: View<Editor>, cx: &mut ViewContext<Self>) -> Self {
let editor = active_editor.read(cx);
let cursor = editor.selections.last::<Point>(cx).head();
let cursor =
active_editor.update(cx, |editor, cx| editor.selections.last::<Point>(cx).head());
let line = cursor.row + 1;
let column = cursor.column + 1;

View File

@@ -128,12 +128,16 @@ impl SyntaxTreeView {
fn editor_updated(&mut self, did_reparse: bool, cx: &mut ViewContext<Self>) -> Option<()> {
// Find which excerpt the cursor is in, and the position within that excerpted buffer.
let editor_state = self.editor.as_mut()?;
let editor = &editor_state.editor.read(cx);
let selection_range = editor.selections.last::<usize>(cx).range();
let multibuffer = editor.buffer().read(cx);
let (buffer, range, excerpt_id) = multibuffer
.range_to_buffer_ranges(selection_range, cx)
.pop()?;
let (buffer, range, excerpt_id) = editor_state.editor.update(cx, |editor, cx| {
let selection_range = editor.selections.last::<usize>(cx).range();
Some(
editor
.buffer()
.read(cx)
.range_to_buffer_ranges(selection_range, cx)
.pop()?,
)
})?;
// If the cursor has moved into a different excerpt, retrieve a new syntax layer
// from that buffer.

View File

@@ -301,8 +301,8 @@ impl MarkdownPreviewView {
this.parse_markdown_from_active_editor(true, cx);
}
EditorEvent::SelectionsChanged { .. } => {
let editor = editor.read(cx);
let selection_range = editor.selections.last::<usize>(cx).range();
let selection_range =
editor.update(cx, |editor, cx| editor.selections.last::<usize>(cx).range());
this.selected_block = this.get_block_index_under_cursor(selection_range);
this.list_state.scroll_to_reveal_item(this.selected_block);
cx.notify();

View File

@@ -2937,6 +2937,58 @@ impl MultiBufferSnapshot {
self.excerpts.summary().text.clone()
}
pub fn dimensions_from_points<'a, D>(
&'a self,
points: impl 'a + IntoIterator<Item = Point>,
) -> impl 'a + Iterator<Item = D>
where
D: TextDimension,
{
let mut cursor = self.excerpts.cursor::<TextSummary>(&());
let mut memoized_source_start: Option<Point> = None;
let mut points = points.into_iter();
std::iter::from_fn(move || {
let point = points.next()?;
// Clear the memoized source start if the point is in a different excerpt than previous.
if memoized_source_start.map_or(false, |_| point >= cursor.end(&()).lines) {
memoized_source_start = None;
}
// Now determine where the excerpt containing the point starts in its source buffer.
// We'll use this value to calculate overshoot next.
let source_start = if let Some(source_start) = memoized_source_start {
source_start
} else {
cursor.seek_forward(&point, Bias::Right, &());
if let Some(excerpt) = cursor.item() {
let source_start = excerpt.range.context.start.to_point(&excerpt.buffer);
memoized_source_start = Some(source_start);
source_start
} else {
return Some(D::from_text_summary(cursor.start()));
}
};
// First, assume the output dimension is at least the start of the excerpt containing the point
let mut output = D::from_text_summary(cursor.start());
// If the point lands within its excerpt, calculate and add the overshoot in dimension D.
if let Some(excerpt) = cursor.item() {
let overshoot = point - cursor.start().lines;
if !overshoot.is_zero() {
let end_in_excerpt = source_start + overshoot;
output.add_assign(
&excerpt
.buffer
.text_summary_for_range::<D, _>(source_start..end_in_excerpt),
);
}
}
Some(output)
})
}
pub fn text_summary_for_range<D, O>(&self, range: Range<O>) -> D
where
D: TextDimension,
@@ -4706,6 +4758,12 @@ impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
}
}
impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, TextSummary> for Point {
fn cmp(&self, cursor_location: &TextSummary, _: &()) -> cmp::Ordering {
Ord::cmp(self, &cursor_location.lines)
}
}
impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, Option<&'a Locator>> for Locator {
fn cmp(&self, cursor_location: &Option<&'a Locator>, _: &()) -> cmp::Ordering {
Ord::cmp(&Some(self), cursor_location)

View File

@@ -46,7 +46,7 @@ impl Vim {
let mut new_anchors = Vec::new();
let snapshot = editor.buffer().read(cx).snapshot(cx);
for selection in editor.selections.all_adjusted(cx) {
for selection in editor.selections.all::<Point>(cx) {
if !selection.is_empty()
&& (vim.mode != Mode::VisualBlock || new_anchors.is_empty())
{

View File

@@ -79,7 +79,7 @@ impl Vim {
true,
editor
.selections
.all_adjusted(cx)
.all::<Point>(cx)
.iter()
.map(|s| s.range())
.collect(),
@@ -99,7 +99,7 @@ impl Vim {
false,
editor
.selections
.all_adjusted(cx)
.all::<Point>(cx)
.iter()
.map(|s| s.range())
.collect(),