Compare commits

...

7 Commits

Author SHA1 Message Date
Antonio Scandurra
5ec78700b6 Merge remote-tracking branch 'origin/main' into anchor-enum
# Conflicts:
#	crates/multi_buffer/src/multi_buffer.rs
2024-09-23 11:44:39 -06:00
Antonio Scandurra
250a27945c WIP 2024-09-19 09:10:34 -06:00
Antonio Scandurra
7a971e301c WIP 2024-09-18 10:40:19 -06:00
Antonio Scandurra
4cfe6469dc Merge remote-tracking branch 'origin/main' into anchor-enum
# Conflicts:
#	crates/text/src/anchor.rs
#	crates/text/src/text.rs
2024-09-18 09:26:53 -06:00
Antonio Scandurra
adcdc9cd89 WIP 2024-09-17 14:58:32 -06:00
Antonio Scandurra
fcd27b0019 WIP 2024-09-17 14:10:23 -06:00
Antonio Scandurra
013e12d3bc WIP: Make buffer_id not optional in text::Anchor 2024-09-17 13:49:28 -06:00
23 changed files with 572 additions and 394 deletions

View File

@@ -2794,7 +2794,7 @@ impl ContextEditor {
let multibuffer = editor.buffer().read(cx).snapshot(cx);
let (&excerpt_id, _, buffer) = multibuffer.as_singleton().unwrap();
let anchor = if group.context_range.start.to_offset(buffer) == 0 {
Anchor::min()
Anchor::Start
} else {
multibuffer
.anchor_in_excerpt(excerpt_id, group.context_range.start)

View File

@@ -1208,7 +1208,7 @@ impl InlineAssistant {
editor.set_read_only(true);
editor.set_show_inline_completions(Some(false), cx);
editor.highlight_rows::<DeletedLines>(
Anchor::min()..=Anchor::max(),
Anchor::Start..=Anchor::End,
Some(cx.theme().status().deleted_background),
false,
cx,

View File

@@ -138,7 +138,7 @@ impl WorkflowSuggestion {
}
Self::CreateFile { description } => {
initial_prompt = description.clone();
suggestion_range = editor::Anchor::min()..editor::Anchor::min();
suggestion_range = Anchor::Start..Anchor::Start;
}
Self::InsertBefore {
position,

View File

@@ -297,7 +297,7 @@ fn assert_remote_selections(
cx: &mut ViewContext<Editor>,
) {
let snapshot = editor.snapshot(cx);
let range = Anchor::min()..Anchor::max();
let range = Anchor::Start..Anchor::End;
let remote_selections = snapshot
.remote_selections_in_range(&range, editor.collaboration_hub().unwrap(), cx)
.map(|s| {

View File

@@ -271,7 +271,7 @@ pub struct ItemSummary {
impl Default for ItemSummary {
fn default() -> Self {
Self {
range: Anchor::min()..Anchor::min(),
range: Anchor::Start..Anchor::Start,
}
}
}

View File

@@ -376,7 +376,7 @@ impl FoldMap {
.folds
.cursor::<FoldRange>(&inlay_snapshot.buffer);
folds_cursor.seek(
&FoldRange(anchor..Anchor::max()),
&FoldRange(anchor..Anchor::End),
Bias::Left,
&inlay_snapshot.buffer,
);
@@ -997,7 +997,7 @@ impl DerefMut for FoldRange {
impl Default for FoldRange {
fn default() -> Self {
Self(Anchor::min()..Anchor::max())
Self(Anchor::Start..Anchor::End)
}
}
@@ -1027,10 +1027,10 @@ pub struct FoldSummary {
impl Default for FoldSummary {
fn default() -> Self {
Self {
start: Anchor::min(),
end: Anchor::max(),
min_start: Anchor::max(),
max_end: Anchor::min(),
start: Anchor::Start,
end: Anchor::End,
min_start: Anchor::End,
max_end: Anchor::Start,
count: 0,
}
}

View File

@@ -3101,9 +3101,19 @@ impl Editor {
if self.linked_edit_ranges.is_empty() {
return None;
}
let ((base_range, linked_ranges), buffer_snapshot, buffer) =
selection.end.buffer_id.and_then(|end_buffer_id| {
if selection.start.buffer_id != Some(end_buffer_id) {
let selection_start_buffer_id = match selection.start {
text::Anchor::Start | text::Anchor::End => None,
text::Anchor::Character { buffer_id, .. } => Some(buffer_id),
};
let selection_end_buffer_id = match selection.end {
text::Anchor::Start | text::Anchor::End => None,
text::Anchor::Character { buffer_id, .. } => Some(buffer_id),
};
let ((base_range, linked_ranges), buffer_snapshot, buffer) = selection_end_buffer_id
.and_then(|end_buffer_id| {
if selection_start_buffer_id != Some(end_buffer_id) {
return None;
}
let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
@@ -3130,8 +3140,8 @@ impl Editor {
continue;
}
if self.selections.disjoint_anchor_ranges().iter().any(|s| {
if s.start.buffer_id != selection.start.buffer_id
|| s.end.buffer_id != selection.end.buffer_id
if s.start.buffer_id != selection_start_buffer_id
|| s.end.buffer_id != selection_end_buffer_id
{
return false;
}
@@ -5014,12 +5024,10 @@ impl Editor {
continue;
}
let range = Anchor {
buffer_id,
let range = Anchor::Text {
excerpt_id,
text_anchor: start,
}..Anchor {
buffer_id,
}..Anchor::Text {
excerpt_id,
text_anchor: end,
};
@@ -5611,7 +5619,15 @@ impl Editor {
for selection in selections.iter() {
let selection_start = snapshot.anchor_before(selection.start).text_anchor;
let selection_end = snapshot.anchor_after(selection.end).text_anchor;
if selection_start.buffer_id != selection_end.buffer_id {
let selection_start_buffer_id = match selection_start {
text::Anchor::Start | text::Anchor::End => None,
text::Anchor::Character { buffer_id, .. } => Some(buffer_id),
};
let selection_end_buffer_id = match selection_end {
text::Anchor::Start | text::Anchor::End => None,
text::Anchor::Character { buffer_id, .. } => Some(buffer_id),
};
if selection_start_buffer_id != selection_end_buffer_id {
continue;
}
if let Some(ranges) =
@@ -8974,7 +8990,7 @@ impl Editor {
.spawn({
let snapshot = display_snapshot.clone();
async move {
Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
Self::fetch_runnable_ranges(&snapshot, Anchor::Start..Anchor::End)
}
})
.await;
@@ -12686,7 +12702,7 @@ fn snippet_completions(
return vec![];
}
let snapshot = buffer.read(cx).text_snapshot();
let chunks = snapshot.reversed_chunks_in_range(text::Anchor::MIN..buffer_position);
let chunks = snapshot.reversed_chunks_in_range(text::Anchor::Start..buffer_position);
let mut lines = chunks.lines();
let Some(line_at) = lines.next().filter(|line| !line.is_empty()) else {

View File

@@ -739,7 +739,9 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
// Ensure we don't panic when navigation data contains invalid anchors *and* points.
let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
invalid_anchor.text_anchor = text::Anchor::Start {
buffer_id: BufferId::new(999).unwrap(),
};
let invalid_point = Point::new(9999, 0);
editor.navigate(
Box::new(NavigationData {

View File

@@ -950,7 +950,7 @@ impl EditorElement {
// Remote cursors
if let Some(collaboration_hub) = &editor.collaboration_hub {
for remote_selection in snapshot.remote_selections_in_range(
&(Anchor::min()..Anchor::max()),
&(Anchor::Start..Anchor::End),
collaboration_hub.deref(),
cx,
) {
@@ -5087,14 +5087,14 @@ impl Element for EditorElement {
.collect::<Vec<_>>();
let start_anchor = if start_row == Default::default() {
Anchor::min()
Anchor::Start
} else {
snapshot.buffer_snapshot.anchor_before(
DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left),
)
};
let end_anchor = if end_row > max_row {
Anchor::max()
Anchor::End
} else {
snapshot.buffer_snapshot.anchor_before(
DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right),

View File

@@ -784,7 +784,7 @@ fn editor_with_deleted_text(
editor.set_read_only(true);
editor.set_show_inline_completions(Some(false), cx);
editor.highlight_rows::<DiffRowHighlight>(
Anchor::min()..=Anchor::max(),
Anchor::Start..=Anchor::End,
Some(deleted_color),
false,
cx,

View File

@@ -40,13 +40,13 @@ impl ScrollAnchor {
fn new() -> Self {
Self {
offset: gpui::Point::default(),
anchor: Anchor::min(),
anchor: Anchor::Start,
}
}
pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<f32> {
let mut scroll_position = self.offset;
if self.anchor == Anchor::min() {
if self.anchor == Anchor::Start {
scroll_position.y = 0.;
} else {
let scroll_top = self.anchor.to_display_point(snapshot).row().as_f32();
@@ -194,7 +194,7 @@ impl ScrollManager {
let (new_anchor, top_row) = if scroll_position.y <= 0. {
(
ScrollAnchor {
anchor: Anchor::min(),
anchor: Anchor::Start,
offset: scroll_position.max(&gpui::Point::default()),
},
0,

View File

@@ -48,8 +48,8 @@ impl SelectionsCollection {
pending: Some(PendingSelection {
selection: Selection {
id: 0,
start: Anchor::min(),
end: Anchor::min(),
start: Anchor::Start,
end: Anchor::Start,
reversed: false,
goal: SelectionGoal::None,
},

View File

@@ -41,7 +41,7 @@ impl sum_tree::Item for InternalDiffHunk {
}
}
#[derive(Debug, Default, Clone)]
#[derive(Debug, Clone)]
pub struct DiffHunkSummary {
buffer_range: Range<Anchor>,
}
@@ -49,8 +49,10 @@ pub struct DiffHunkSummary {
impl sum_tree::Summary for DiffHunkSummary {
type Context = text::BufferSnapshot;
fn zero(_cx: &Self::Context) -> Self {
Default::default()
fn zero(buffer: &Self::Context) -> Self {
Self {
buffer_range: buffer.min_anchor()..buffer.min_anchor(),
}
}
fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {

View File

@@ -2768,7 +2768,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
for buffer in &buffers {
let buffer = buffer.read(cx).snapshot();
let actual_remote_selections = buffer
.selections_in_range(Anchor::MIN..Anchor::MAX, false)
.selections_in_range(buffer.min_anchor()..buffer.max_anchor(), false)
.map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
.collect::<Vec<_>>();
let expected_remote_selections = active_selections

View File

@@ -246,23 +246,17 @@ impl DiagnosticEntry<Anchor> {
}
}
impl Default for Summary {
fn default() -> Self {
Self {
start: Anchor::MIN,
end: Anchor::MAX,
min_start: Anchor::MAX,
max_end: Anchor::MIN,
count: 0,
}
}
}
impl sum_tree::Summary for Summary {
type Context = text::BufferSnapshot;
fn zero(_cx: &Self::Context) -> Self {
Default::default()
fn zero(buffer: &Self::Context) -> Self {
Self {
start: buffer.min_anchor(),
end: buffer.max_anchor(),
min_start: buffer.max_anchor(),
max_end: buffer.min_anchor(),
count: 0,
}
}
fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {

View File

@@ -6,7 +6,7 @@ use clock::ReplicaId;
use lsp::{DiagnosticSeverity, LanguageServerId};
use rpc::proto;
use serde_json::Value;
use std::{ops::Range, str::FromStr, sync::Arc};
use std::{ops::Range, str::FromStr, sync::Arc, u32};
use text::*;
pub use proto::{BufferState, Operation};
@@ -221,15 +221,36 @@ pub fn serialize_diagnostics<'a>(
/// Serializes an [`Anchor`] to be sent over RPC.
pub fn serialize_anchor(anchor: &Anchor) -> proto::Anchor {
proto::Anchor {
replica_id: anchor.timestamp.replica_id as u32,
timestamp: anchor.timestamp.value,
offset: anchor.offset as u64,
bias: match anchor.bias {
Bias::Left => proto::Bias::Left as i32,
Bias::Right => proto::Bias::Right as i32,
match *anchor {
Anchor::Start { buffer_id } => proto::Anchor {
replica_id: 0,
timestamp: 0,
offset: 0,
bias: proto::Bias::Left as i32,
buffer_id: Some(buffer_id.into()),
},
Anchor::End { buffer_id } => proto::Anchor {
replica_id: u32::MAX,
timestamp: u32::MAX,
offset: u64::MAX,
bias: proto::Bias::Right as i32,
buffer_id: Some(buffer_id.into()),
},
Anchor::Character {
buffer_id,
insertion_id,
offset,
bias,
} => proto::Anchor {
replica_id: insertion_id.replica_id as u32,
timestamp: insertion_id.value,
offset: offset as u64,
bias: match bias {
Bias::Left => proto::Bias::Left as i32,
Bias::Right => proto::Bias::Right as i32,
},
buffer_id: Some(buffer_id.into()),
},
buffer_id: anchor.buffer_id.map(Into::into),
}
}
@@ -429,23 +450,33 @@ pub fn deserialize_diagnostics(
/// Deserializes an [`Anchor`] from the RPC representation.
pub fn deserialize_anchor(anchor: proto::Anchor) -> Option<Anchor> {
let buffer_id = if let Some(id) = anchor.buffer_id {
Some(BufferId::new(id).ok()?)
let buffer_id = BufferId::new(anchor.buffer_id?).ok()?;
if anchor.replica_id == 0
&& anchor.timestamp == 0
&& anchor.offset == 0
&& anchor.bias == proto::Bias::Left as i32
{
Some(Anchor::Start { buffer_id })
} else if anchor.replica_id == u32::MAX
&& anchor.timestamp == u32::MAX
&& anchor.offset == u64::MAX
&& anchor.bias == proto::Bias::Right as i32
{
Some(Anchor::End { buffer_id })
} else {
None
};
Some(Anchor {
timestamp: clock::Lamport {
replica_id: anchor.replica_id as ReplicaId,
value: anchor.timestamp,
},
offset: anchor.offset as usize,
bias: match proto::Bias::from_i32(anchor.bias)? {
proto::Bias::Left => Bias::Left,
proto::Bias::Right => Bias::Right,
},
buffer_id,
})
Some(Anchor::Character {
insertion_id: clock::Lamport {
replica_id: anchor.replica_id as ReplicaId,
value: anchor.timestamp,
},
offset: anchor.offset as usize,
bias: match proto::Bias::from_i32(anchor.bias)? {
proto::Bias::Left => Bias::Left,
proto::Bias::Right => Bias::Right,
},
buffer_id,
})
}
}
/// Returns a `[clock::Lamport`] timestamp for the given [`proto::Operation`].

View File

@@ -303,7 +303,7 @@ impl SyntaxSnapshot {
let slice = cursor.slice(
&SyntaxLayerPosition {
depth: depth + 1,
range: Anchor::MIN..Anchor::MAX,
range: text.min_anchor()..text.max_anchor(),
language: None,
},
Bias::Left,
@@ -459,7 +459,7 @@ impl SyntaxSnapshot {
start_point: Point::zero().to_ts_point(),
end_point: text.max_point().to_ts_point(),
}],
range: Anchor::MIN..Anchor::MAX,
range: text.min_anchor()..text.max_anchor(),
mode: ParseMode::Single,
});
@@ -474,7 +474,7 @@ impl SyntaxSnapshot {
} else {
SyntaxLayerPosition {
depth: max_depth + 1,
range: Anchor::MAX..Anchor::MAX,
range: text.max_anchor()..text.max_anchor(),
language: None,
}
};
@@ -485,7 +485,7 @@ impl SyntaxSnapshot {
let bounded_position = SyntaxLayerPositionBeforeChange {
position: position.clone(),
change: changed_regions.start_position(),
change: changed_regions.start_position(text),
};
if bounded_position.cmp(cursor.start(), text).is_gt() {
let slice = cursor.slice(&bounded_position, Bias::Left, text);
@@ -1608,11 +1608,11 @@ impl ChangedRegion {
}
impl ChangeRegionSet {
fn start_position(&self) -> ChangeStartPosition {
fn start_position(&self, text: &BufferSnapshot) -> ChangeStartPosition {
self.0.first().map_or(
ChangeStartPosition {
depth: usize::MAX,
position: Anchor::MAX,
position: text.max_anchor(),
},
|region| ChangeStartPosition {
depth: region.depth,
@@ -1661,24 +1661,18 @@ impl ChangeRegionSet {
}
}
impl Default for SyntaxLayerSummary {
fn default() -> Self {
Self {
max_depth: 0,
min_depth: 0,
range: Anchor::MAX..Anchor::MIN,
last_layer_range: Anchor::MIN..Anchor::MAX,
last_layer_language: None,
contains_unknown_injections: false,
}
}
}
impl sum_tree::Summary for SyntaxLayerSummary {
type Context = BufferSnapshot;
fn zero(_cx: &BufferSnapshot) -> Self {
Default::default()
fn zero(buffer: &BufferSnapshot) -> Self {
Self {
max_depth: 0,
min_depth: 0,
range: buffer.max_anchor()..buffer.min_anchor(),
last_layer_range: buffer.min_anchor()..buffer.max_anchor(),
last_layer_language: None,
contains_unknown_injections: false,
}
}
fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {
@@ -1686,7 +1680,7 @@ impl sum_tree::Summary for SyntaxLayerSummary {
self.max_depth = other.max_depth;
self.range = other.range.clone();
} else {
if self.range == (Anchor::MAX..Anchor::MAX) {
if self.range == (buffer.max_anchor()..buffer.max_anchor()) {
self.range.start = other.range.start;
}
if other.range.end.cmp(&self.range.end, buffer).is_gt() {

View File

@@ -5,75 +5,105 @@ use std::{
ops::{Range, Sub},
};
use sum_tree::Bias;
use text::BufferId;
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
pub struct Anchor {
pub buffer_id: Option<BufferId>,
pub excerpt_id: ExcerptId,
pub text_anchor: text::Anchor,
pub enum Anchor {
Start,
End,
Text {
excerpt_id: ExcerptId,
text_anchor: text::Anchor,
},
}
impl Anchor {
pub fn min() -> Self {
Self {
buffer_id: None,
excerpt_id: ExcerptId::min(),
text_anchor: text::Anchor::MIN,
}
}
pub fn max() -> Self {
Self {
buffer_id: None,
excerpt_id: ExcerptId::max(),
text_anchor: text::Anchor::MAX,
pub fn excerpt_id(&self) -> ExcerptId {
match self {
Anchor::Start => ExcerptId::min(),
Anchor::End => ExcerptId::max(),
Anchor::Text { excerpt_id, .. } => *excerpt_id,
}
}
pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering {
let excerpt_id_cmp = self.excerpt_id.cmp(&other.excerpt_id, snapshot);
if excerpt_id_cmp.is_eq() {
if self.excerpt_id == ExcerptId::min() || self.excerpt_id == ExcerptId::max() {
Ordering::Equal
} else if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) {
self.text_anchor.cmp(&other.text_anchor, &excerpt.buffer)
} else {
Ordering::Equal
match (self, other) {
(Anchor::Start, Anchor::Start) | (Anchor::End, Anchor::End) => Ordering::Equal,
(_, Anchor::Start) | (Anchor::End, _) => Ordering::Greater,
(Anchor::Start, _) | (_, Anchor::End) => Ordering::Less,
(
Anchor::Text {
excerpt_id: id1,
text_anchor: anchor1,
},
Anchor::Text {
excerpt_id: id2,
text_anchor: anchor2,
},
) => {
let excerpt_id_cmp = id1.cmp(id2, snapshot);
if excerpt_id_cmp.is_eq() {
if let Some(excerpt) = snapshot.excerpt(*id1) {
anchor1.cmp(anchor2, &excerpt.buffer)
} else {
Ordering::Equal
}
} else {
excerpt_id_cmp
}
}
} else {
excerpt_id_cmp
}
}
pub fn bias(&self) -> Bias {
self.text_anchor.bias
match self {
Anchor::Start => Bias::Left,
Anchor::End => Bias::Right,
Anchor::Text { text_anchor, .. } => match text_anchor {
text::Anchor::Start { .. } => Bias::Left,
text::Anchor::End { .. } => Bias::Right,
text::Anchor::Character { bias, .. } => *bias,
},
}
}
pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
if self.text_anchor.bias != Bias::Left {
if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) {
return Self {
buffer_id: self.buffer_id,
excerpt_id: self.excerpt_id,
text_anchor: self.text_anchor.bias_left(&excerpt.buffer),
};
match self {
Anchor::Start => *self,
Anchor::End => snapshot.anchor_before(snapshot.len()),
Anchor::Text {
excerpt_id,
text_anchor,
} => {
if let Some(excerpt) = snapshot.excerpt(*excerpt_id) {
Anchor::Text {
excerpt_id: *excerpt_id,
text_anchor: text_anchor.bias_left(&excerpt.buffer),
}
} else {
*self
}
}
}
*self
}
pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
if self.text_anchor.bias != Bias::Right {
if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) {
return Self {
buffer_id: self.buffer_id,
excerpt_id: self.excerpt_id,
text_anchor: self.text_anchor.bias_right(&excerpt.buffer),
};
match self {
Anchor::Start => snapshot.anchor_after(0),
Anchor::End => *self,
Anchor::Text {
excerpt_id,
text_anchor,
} => {
if let Some(excerpt) = snapshot.excerpt(*excerpt_id) {
Anchor::Text {
excerpt_id: *excerpt_id,
text_anchor: text_anchor.bias_right(&excerpt.buffer),
}
} else {
*self
}
}
}
*self
}
pub fn summary<D>(&self, snapshot: &MultiBufferSnapshot) -> D
@@ -84,15 +114,18 @@ impl Anchor {
}
pub fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool {
if *self == Anchor::min() || *self == Anchor::max() {
true
} else if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) {
excerpt.contains(self)
&& (self.text_anchor == excerpt.range.context.start
|| self.text_anchor == excerpt.range.context.end
|| self.text_anchor.is_valid(&excerpt.buffer))
} else {
false
match self {
Self::Start | Anchor::End => true,
Anchor::Text {
excerpt_id,
text_anchor,
} => {
if let Some(excerpt) = snapshot.excerpt(*excerpt_id) {
excerpt.contains(self) && text_anchor.is_valid(&excerpt.buffer)
} else {
false
}
}
}
}
}

View File

@@ -437,13 +437,14 @@ impl MultiBuffer {
self.capability == Capability::ReadOnly
}
pub fn singleton(buffer: Model<Buffer>, cx: &mut ModelContext<Self>) -> Self {
let mut this = Self::new(buffer.read(cx).capability());
pub fn singleton(buffer_model: Model<Buffer>, cx: &mut ModelContext<Self>) -> Self {
let buffer = buffer_model.read(cx);
let mut this = Self::new(buffer.capability());
this.singleton = true;
this.push_excerpts(
buffer,
buffer_model,
[ExcerptRange {
context: text::Anchor::MIN..text::Anchor::MAX,
context: buffer.min_anchor()..buffer.max_anchor(),
primary: None,
}],
cx,
@@ -962,9 +963,27 @@ impl MultiBuffer {
Default::default();
let snapshot = self.read(cx);
let mut cursor = snapshot.excerpts.cursor::<Option<&Locator>>(&());
for selection in selections {
let start_locator = snapshot.excerpt_locator_for_id(selection.start.excerpt_id);
let end_locator = snapshot.excerpt_locator_for_id(selection.end.excerpt_id);
for mut selection in selections {
if selection.start == Anchor::Start {
if let Some(first_excerpt) = snapshot.excerpts.first() {
selection.start = Anchor::Text {
excerpt_id: first_excerpt.id,
text_anchor: first_excerpt.range.context.start,
};
}
}
if selection.end == Anchor::End {
if let Some(last_excerpt) = snapshot.excerpts.last() {
selection.end = Anchor::Text {
excerpt_id: last_excerpt.id,
text_anchor: last_excerpt.range.context.end,
};
}
}
let start_locator = snapshot.excerpt_locator_for_id(selection.start.excerpt_id());
let end_locator = snapshot.excerpt_locator_for_id(selection.end.excerpt_id());
cursor.seek(&Some(start_locator), Bias::Left, &());
while let Some(excerpt) = cursor.item() {
@@ -974,11 +993,15 @@ impl MultiBuffer {
let mut start = excerpt.range.context.start;
let mut end = excerpt.range.context.end;
if excerpt.id == selection.start.excerpt_id {
start = selection.start.text_anchor;
if excerpt.id == selection.start.excerpt_id() {
if let Anchor::Text { text_anchor, .. } = selection.start {
start = text_anchor;
}
}
if excerpt.id == selection.end.excerpt_id {
end = selection.end.text_anchor;
if excerpt.id == selection.end.excerpt_id() {
if let Anchor::Text { text_anchor, .. } = selection.end {
end = text_anchor;
}
}
selections_by_buffer
.entry(excerpt.buffer_id)
@@ -1168,13 +1191,11 @@ impl MultiBuffer {
for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(range_counts.by_ref())
{
for range in ranges.by_ref().take(range_count) {
let start = Anchor {
buffer_id: Some(buffer_id),
let start = Anchor::Text {
excerpt_id,
text_anchor: range.start,
};
let end = Anchor {
buffer_id: Some(buffer_id),
let end = Anchor::Text {
excerpt_id,
text_anchor: range.end,
};
@@ -1223,13 +1244,11 @@ impl MultiBuffer {
let mut ranges = ranges.into_iter();
for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(range_counts.into_iter()) {
anchor_ranges.extend(ranges.by_ref().take(range_count).map(|range| {
let start = Anchor {
buffer_id: Some(buffer_id),
let start = Anchor::Text {
excerpt_id,
text_anchor: buffer_snapshot.anchor_after(range.start),
};
let end = Anchor {
buffer_id: Some(buffer_id),
let end = Anchor::Text {
excerpt_id,
text_anchor: buffer_snapshot.anchor_after(range.end),
};
@@ -1654,14 +1673,15 @@ impl MultiBuffer {
let mut error = None;
let mut futures = Vec::new();
for anchor in anchors {
if let Some(buffer_id) = anchor.buffer_id {
if let Some(buffer) = borrow.get(&buffer_id) {
if let Anchor::Text { text_anchor, .. } = anchor {
if let Some(buffer) = borrow.get(&text_anchor.buffer_id()) {
buffer.buffer.update(cx, |buffer, _| {
futures.push(buffer.wait_for_anchors([anchor.text_anchor]))
futures.push(buffer.wait_for_anchors([text_anchor]))
});
} else {
error = Some(anyhow!(
"buffer {buffer_id} is not part of this multi-buffer"
"buffer {:?} is not part of this multi-buffer",
text_anchor.buffer_id()
));
break;
}
@@ -1684,14 +1704,43 @@ impl MultiBuffer {
cx: &AppContext,
) -> Option<(Model<Buffer>, language::Anchor)> {
let snapshot = self.read(cx);
let anchor = snapshot.anchor_before(position);
let buffer = self
.buffers
.borrow()
.get(&anchor.buffer_id?)?
.buffer
.clone();
Some((buffer, anchor.text_anchor))
match snapshot.anchor_before(position) {
Anchor::Start => {
if let Some(first_excerpt) = snapshot.excerpts.first() {
let buffer = self
.buffers
.borrow()
.get(&first_excerpt.buffer_id)?
.buffer
.clone();
Some((buffer, first_excerpt.range.context.start))
} else {
None
}
}
Anchor::End => {
if let Some(last_excerpt) = snapshot.excerpts.last() {
let buffer = self
.buffers
.borrow()
.get(&last_excerpt.buffer_id)?
.buffer
.clone();
Some((buffer, last_excerpt.range.context.end))
} else {
None
}
}
Anchor::Text { text_anchor, .. } => {
let buffer = self
.buffers
.borrow()
.get(&text_anchor.buffer_id())?
.buffer
.clone();
Some((buffer, text_anchor))
}
}
}
fn on_buffer_event(
@@ -2944,7 +2993,7 @@ impl MultiBufferSnapshot {
D: TextDimension + Ord + Sub<D, Output = D>,
{
let mut cursor = self.excerpts.cursor::<ExcerptSummary>(&());
let locator = self.excerpt_locator_for_id(anchor.excerpt_id);
let locator = self.excerpt_locator_for_id(anchor.excerpt_id());
cursor.seek(locator, Bias::Left, &());
if cursor.item().is_none() {
@@ -2953,7 +3002,7 @@ impl MultiBufferSnapshot {
let mut position = D::from_text_summary(&cursor.start().text);
if let Some(excerpt) = cursor.item() {
if excerpt.id == anchor.excerpt_id {
if excerpt.id == anchor.excerpt_id() {
let excerpt_buffer_start =
excerpt.range.context.start.summary::<D>(&excerpt.buffer);
let excerpt_buffer_end = excerpt.range.context.end.summary::<D>(&excerpt.buffer);
@@ -3085,12 +3134,11 @@ impl MultiBufferSnapshot {
// If there's no adjacent excerpt that contains the anchor's position,
// then report that the anchor has lost its position.
if !kept_position {
let bias = anchor.bias();
anchor = if let Some(excerpt) = next_excerpt {
let mut text_anchor = excerpt
.range
.context
.start
.bias(anchor.text_anchor.bias, &excerpt.buffer);
let mut text_anchor =
excerpt.range.context.start.bias(bias, &excerpt.buffer);
if text_anchor
.cmp(&excerpt.range.context.end, &excerpt.buffer)
.is_gt()
@@ -3103,11 +3151,7 @@ impl MultiBufferSnapshot {
text_anchor,
}
} else if let Some(excerpt) = prev_excerpt {
let mut text_anchor = excerpt
.range
.context
.end
.bias(anchor.text_anchor.bias, &excerpt.buffer);
let mut text_anchor = excerpt.range.context.end.bias(bias, &excerpt.buffer);
if text_anchor
.cmp(&excerpt.range.context.start, &excerpt.buffer)
.is_lt()
@@ -3119,10 +3163,10 @@ impl MultiBufferSnapshot {
excerpt_id: excerpt.id,
text_anchor,
}
} else if anchor.text_anchor.bias == Bias::Left {
Anchor::min()
} else if bias == Bias::Left {
Anchor::Start
} else {
Anchor::max()
Anchor::End
};
}
@@ -3172,9 +3216,9 @@ impl MultiBufferSnapshot {
text_anchor,
}
} else if offset == 0 && bias == Bias::Left {
Anchor::min()
Anchor::Start
} else {
Anchor::max()
Anchor::End
}
}

View File

@@ -999,7 +999,7 @@ impl LspStore {
let actions = this
.update(cx, move |this, cx| {
let request = GetCodeActions {
range: text::Anchor::MIN..text::Anchor::MAX,
range: buffer.read(cx).min_anchor()..buffer.read(cx).max_anchor(),
kinds: Some(code_actions),
};
let server = LanguageServerToQuery::Other(language_server.server_id());
@@ -1323,9 +1323,7 @@ impl LspStore {
});
buffer
.update(&mut cx, |buffer, _| {
buffer.wait_for_edits(Some(position.timestamp))
})?
.update(&mut cx, |buffer, _| buffer.wait_for_anchors(Some(position)))?
.await?;
this.update(&mut cx, |this, cx| {
let position = position.to_point_utf16(buffer.read(cx));
@@ -1969,7 +1967,7 @@ impl LspStore {
cx.spawn(move |_, mut cx| async move {
buffer_handle
.update(&mut cx, |buffer, _| {
buffer.wait_for_edits(vec![range_start.timestamp, range_end.timestamp])
buffer.wait_for_anchors([range_start, range_end])
})?
.await
.context("waiting for inlay hint request range edits")?;

View File

@@ -537,7 +537,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
DiagnosticSet::from_sorted_entries(
vec![DiagnosticEntry {
diagnostic: Default::default(),
range: Anchor::MIN..Anchor::MAX,
range: buffer.min_anchor()..buffer.max_anchor(),
}],
&buffer.snapshot(),
),

View File

@@ -6,43 +6,68 @@ use std::{cmp::Ordering, fmt::Debug, ops::Range};
use sum_tree::Bias;
/// A timestamped position in a buffer
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Default)]
pub struct Anchor {
pub timestamp: clock::Lamport,
/// The byte offset in the buffer
pub offset: usize,
/// Describes which character the anchor is biased towards
pub bias: Bias,
pub buffer_id: Option<BufferId>,
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum Anchor {
Start {
buffer_id: BufferId,
},
End {
buffer_id: BufferId,
},
Character {
buffer_id: BufferId,
insertion_id: clock::Lamport,
offset: usize,
bias: Bias,
},
}
impl Anchor {
pub const MIN: Self = Self {
timestamp: clock::Lamport::MIN,
offset: usize::MIN,
bias: Bias::Left,
buffer_id: None,
};
pub const MAX: Self = Self {
timestamp: clock::Lamport::MAX,
offset: usize::MAX,
bias: Bias::Right,
buffer_id: None,
};
pub fn buffer_id(&self) -> BufferId {
match self {
Anchor::Start { buffer_id } => *buffer_id,
Anchor::End { buffer_id } => *buffer_id,
Anchor::Character { buffer_id, .. } => *buffer_id,
}
}
pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering {
let fragment_id_comparison = if self.timestamp == other.timestamp {
Ordering::Equal
} else {
buffer
.fragment_id_for_anchor(self)
.cmp(buffer.fragment_id_for_anchor(other))
};
debug_assert_eq!(
self.buffer_id(),
other.buffer_id(),
"anchors belong to different buffers"
);
fragment_id_comparison
.then_with(|| self.offset.cmp(&other.offset))
.then_with(|| self.bias.cmp(&other.bias))
match (self, other) {
(Anchor::Start { .. }, Anchor::Start { .. }) => Ordering::Equal,
(Anchor::End { .. }, Anchor::End { .. }) => Ordering::Equal,
(Anchor::Start { .. }, _) | (_, Anchor::End { .. }) => Ordering::Less,
(_, Anchor::Start { .. }) | (Anchor::End { .. }, _) => Ordering::Greater,
(
Anchor::Character {
insertion_id,
offset,
bias,
..
},
Anchor::Character {
insertion_id: other_insertion_id,
offset: other_offset,
bias: other_bias,
..
},
) => {
if insertion_id == other_insertion_id {
offset
.cmp(&other_offset)
.then_with(|| bias.cmp(&other_bias))
} else {
buffer
.fragment_id_for_anchor(self)
.cmp(buffer.fragment_id_for_anchor(other))
}
}
}
}
pub fn min(&self, other: &Self, buffer: &BufferSnapshot) -> Self {
@@ -70,18 +95,42 @@ impl Anchor {
}
pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
if self.bias == Bias::Left {
*self
} else {
buffer.anchor_before(self)
match self {
Anchor::Start { buffer_id } => Anchor::Start {
buffer_id: *buffer_id,
},
Anchor::End { .. } => buffer.anchor_before(buffer.len()),
Anchor::Character {
buffer_id,
insertion_id,
offset,
..
} => Anchor::Character {
buffer_id: *buffer_id,
insertion_id: *insertion_id,
offset: *offset,
bias: Bias::Left,
},
}
}
pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
if self.bias == Bias::Right {
*self
} else {
buffer.anchor_after(self)
match self {
Anchor::Start { .. } => buffer.anchor_after(0),
Anchor::End { buffer_id } => Anchor::End {
buffer_id: *buffer_id,
},
Anchor::Character {
buffer_id,
insertion_id,
offset,
..
} => Anchor::Character {
buffer_id: *buffer_id,
insertion_id: *insertion_id,
offset: *offset,
bias: Bias::Right,
},
}
}
@@ -94,17 +143,23 @@ impl Anchor {
/// Returns true when the [`Anchor`] is located inside a visible fragment.
pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
if *self == Anchor::MIN || *self == Anchor::MAX {
true
} else if self.buffer_id != Some(buffer.remote_id) {
false
} else {
let fragment_id = buffer.fragment_id_for_anchor(self);
let mut fragment_cursor = buffer.fragments.cursor::<(Option<&Locator>, usize)>(&None);
fragment_cursor.seek(&Some(fragment_id), Bias::Left, &None);
fragment_cursor
.item()
.map_or(false, |fragment| fragment.visible)
match self {
Anchor::Start { buffer_id } | Anchor::End { buffer_id } => {
*buffer_id == buffer.remote_id
}
Anchor::Character { buffer_id, .. } => {
if *buffer_id == buffer.remote_id {
let fragment_id = buffer.fragment_id_for_anchor(self);
let mut fragment_cursor =
buffer.fragments.cursor::<(Option<&Locator>, usize)>(&None);
fragment_cursor.seek(&Some(fragment_id), Bias::Left, &None);
fragment_cursor
.item()
.map_or(false, |fragment| fragment.visible)
} else {
false
}
}
}
}
}

View File

@@ -1542,16 +1542,15 @@ impl Buffer {
) -> impl 'static + Future<Output = Result<()>> {
let mut futures = Vec::new();
for anchor in anchors {
if !self.version.observed(anchor.timestamp)
&& anchor != Anchor::MAX
&& anchor != Anchor::MIN
{
let (tx, rx) = oneshot::channel();
self.edit_id_resolvers
.entry(anchor.timestamp)
.or_default()
.push(tx);
futures.push(rx);
if let Anchor::Character { insertion_id, .. } = anchor {
if !self.version.observed(insertion_id) {
let (tx, rx) = oneshot::channel();
self.edit_id_resolvers
.entry(insertion_id)
.or_default()
.push(tx);
futures.push(rx);
}
}
}
@@ -1928,6 +1927,18 @@ impl BufferSnapshot {
self.visible_text.summary()
}
pub fn min_anchor(&self) -> Anchor {
Anchor::Start {
buffer_id: self.remote_id,
}
}
pub fn max_anchor(&self) -> Anchor {
Anchor::End {
buffer_id: self.remote_id,
}
}
pub fn max_point(&self) -> Point {
self.visible_text.max_point()
}
@@ -2129,41 +2140,50 @@ impl BufferSnapshot {
let mut position = D::zero(&());
anchors.map(move |(anchor, payload)| {
if *anchor == Anchor::MIN {
return (D::zero(&()), payload);
} else if *anchor == Anchor::MAX {
return (D::from_text_summary(&self.visible_text.summary()), payload);
}
debug_assert_eq!(
anchor.buffer_id(),
self.remote_id,
"anchor belongs to a different buffer"
);
let anchor_key = InsertionFragmentKey {
timestamp: anchor.timestamp,
split_offset: anchor.offset,
};
insertion_cursor.seek(&anchor_key, anchor.bias, &());
if let Some(insertion) = insertion_cursor.item() {
let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key);
if comparison == Ordering::Greater
|| (anchor.bias == Bias::Left
&& comparison == Ordering::Equal
&& anchor.offset > 0)
{
insertion_cursor.prev(&());
match *anchor {
Anchor::Start { .. } => (D::zero(&()), payload),
Anchor::End { .. } => (D::from_text_summary(&self.visible_text.summary()), payload),
Anchor::Character {
insertion_id,
offset,
bias,
..
} => {
let anchor_key = InsertionFragmentKey {
timestamp: insertion_id,
split_offset: offset,
};
insertion_cursor.seek(&anchor_key, bias, &());
if let Some(insertion) = insertion_cursor.item() {
let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key);
if comparison == Ordering::Greater
|| (bias == Bias::Left && comparison == Ordering::Equal && offset > 0)
{
insertion_cursor.prev(&());
}
} else {
insertion_cursor.prev(&());
}
let insertion = insertion_cursor.item().expect("invalid insertion");
assert_eq!(insertion.timestamp, insertion_id, "invalid insertion");
fragment_cursor.seek_forward(&Some(&insertion.fragment_id), Bias::Left, &None);
let fragment = fragment_cursor.item().unwrap();
let mut fragment_offset = fragment_cursor.start().1;
if fragment.visible {
fragment_offset += offset - insertion.split_offset;
}
position.add_assign(&text_cursor.summary(fragment_offset));
(position.clone(), payload)
}
} else {
insertion_cursor.prev(&());
}
let insertion = insertion_cursor.item().expect("invalid insertion");
assert_eq!(insertion.timestamp, anchor.timestamp, "invalid insertion");
fragment_cursor.seek_forward(&Some(&insertion.fragment_id), Bias::Left, &None);
let fragment = fragment_cursor.item().unwrap();
let mut fragment_offset = fragment_cursor.start().1;
if fragment.visible {
fragment_offset += anchor.offset - insertion.split_offset;
}
position.add_assign(&text_cursor.summary(fragment_offset));
(position.clone(), payload)
})
}
@@ -2171,90 +2191,57 @@ impl BufferSnapshot {
where
D: TextDimension,
{
if *anchor == Anchor::MIN {
D::zero(&())
} else if *anchor == Anchor::MAX {
D::from_text_summary(&self.visible_text.summary())
} else {
let anchor_key = InsertionFragmentKey {
timestamp: anchor.timestamp,
split_offset: anchor.offset,
};
let mut insertion_cursor = self.insertions.cursor::<InsertionFragmentKey>(&());
insertion_cursor.seek(&anchor_key, anchor.bias, &());
if let Some(insertion) = insertion_cursor.item() {
let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key);
if comparison == Ordering::Greater
|| (anchor.bias == Bias::Left
&& comparison == Ordering::Equal
&& anchor.offset > 0)
{
insertion_cursor.prev(&());
}
} else {
insertion_cursor.prev(&());
}
let Some(insertion) = insertion_cursor
.item()
.filter(|insertion| insertion.timestamp == anchor.timestamp)
else {
panic!(
"invalid anchor {:?}. buffer id: {}, version: {:?}",
anchor, self.remote_id, self.version
);
};
let mut fragment_cursor = self.fragments.cursor::<(Option<&Locator>, usize)>(&None);
fragment_cursor.seek(&Some(&insertion.fragment_id), Bias::Left, &None);
let fragment = fragment_cursor.item().unwrap();
let mut fragment_offset = fragment_cursor.start().1;
if fragment.visible {
fragment_offset += anchor.offset - insertion.split_offset;
}
self.text_summary_for_range(0..fragment_offset)
}
self.summaries_for_anchors([anchor]).next().unwrap()
}
fn fragment_id_for_anchor(&self, anchor: &Anchor) -> &Locator {
if *anchor == Anchor::MIN {
Locator::min_ref()
} else if *anchor == Anchor::MAX {
Locator::max_ref()
} else {
let anchor_key = InsertionFragmentKey {
timestamp: anchor.timestamp,
split_offset: anchor.offset,
};
let mut insertion_cursor = self.insertions.cursor::<InsertionFragmentKey>(&());
insertion_cursor.seek(&anchor_key, anchor.bias, &());
if let Some(insertion) = insertion_cursor.item() {
let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key);
if comparison == Ordering::Greater
|| (anchor.bias == Bias::Left
&& comparison == Ordering::Equal
&& anchor.offset > 0)
{
debug_assert_eq!(
anchor.buffer_id(),
self.remote_id,
"anchor belongs to a different buffer"
);
match *anchor {
Anchor::Start { .. } => Locator::min_ref(),
Anchor::End { .. } => Locator::max_ref(),
Anchor::Character {
insertion_id,
offset,
bias,
..
} => {
let anchor_key = InsertionFragmentKey {
timestamp: insertion_id,
split_offset: offset,
};
let mut insertion_cursor = self.insertions.cursor::<InsertionFragmentKey>(&());
insertion_cursor.seek(&anchor_key, bias, &());
if let Some(insertion) = insertion_cursor.item() {
let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key);
if comparison == Ordering::Greater
|| (bias == Bias::Left && comparison == Ordering::Equal && offset > 0)
{
insertion_cursor.prev(&());
}
} else {
insertion_cursor.prev(&());
}
} else {
insertion_cursor.prev(&());
let Some(insertion) = insertion_cursor.item().filter(|insertion| {
if cfg!(debug_assertions) {
insertion.timestamp == insertion_id
} else {
true
}
}) else {
panic!(
"invalid anchor {:?}. buffer id: {}, version: {:?}",
anchor, self.remote_id, self.version
);
};
&insertion.fragment_id
}
let Some(insertion) = insertion_cursor.item().filter(|insertion| {
if cfg!(debug_assertions) {
insertion.timestamp == anchor.timestamp
} else {
true
}
}) else {
panic!(
"invalid anchor {:?}. buffer id: {}, version: {:?}",
anchor, self.remote_id, self.version
);
};
&insertion.fragment_id
}
}
@@ -2272,27 +2259,33 @@ impl BufferSnapshot {
fn anchor_at_offset(&self, offset: usize, bias: Bias) -> Anchor {
if bias == Bias::Left && offset == 0 {
Anchor::MIN
self.min_anchor()
} else if bias == Bias::Right && offset == self.len() {
Anchor::MAX
self.max_anchor()
} else {
let mut fragment_cursor = self.fragments.cursor::<usize>(&None);
fragment_cursor.seek(&offset, bias, &None);
let fragment = fragment_cursor.item().unwrap();
let overshoot = offset - *fragment_cursor.start();
Anchor {
timestamp: fragment.timestamp,
Anchor::Character {
insertion_id: fragment.timestamp,
offset: fragment.insertion_offset + overshoot,
bias,
buffer_id: Some(self.remote_id),
buffer_id: self.remote_id,
}
}
}
pub fn can_resolve(&self, anchor: &Anchor) -> bool {
*anchor == Anchor::MIN
|| *anchor == Anchor::MAX
|| (Some(self.remote_id) == anchor.buffer_id && self.version.observed(anchor.timestamp))
match *anchor {
Anchor::Start { buffer_id } => self.remote_id == buffer_id,
Anchor::End { buffer_id } => self.remote_id == buffer_id,
Anchor::Character {
buffer_id,
insertion_id,
..
} => self.remote_id == buffer_id && self.version.observed(insertion_id),
}
}
pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
@@ -2318,7 +2311,7 @@ impl BufferSnapshot {
where
D: TextDimension + Ord,
{
self.edits_since_in_range(since, Anchor::MIN..Anchor::MAX)
self.edits_since_in_range(since, self.min_anchor()..self.max_anchor())
}
pub fn anchored_edits_since<'a, D>(
@@ -2328,7 +2321,7 @@ impl BufferSnapshot {
where
D: TextDimension + Ord,
{
self.anchored_edits_since_in_range(since, Anchor::MIN..Anchor::MAX)
self.anchored_edits_since_in_range(since, self.min_anchor()..self.max_anchor())
}
pub fn edits_since_in_range<'a, D>(
@@ -2366,10 +2359,20 @@ impl BufferSnapshot {
let start_fragment_id = self.fragment_id_for_anchor(&range.start);
cursor.seek(&Some(start_fragment_id), Bias::Left, &None);
let start_fragment_offset = if let Anchor::Character { offset, .. } = range.start {
offset
} else {
0
};
let mut visible_start = cursor.start().1.visible;
let mut deleted_start = cursor.start().1.deleted;
if let Some(fragment) = cursor.item() {
let overshoot = range.start.offset - fragment.insertion_offset;
let overshoot = if let Anchor::Character { offset, .. } = range.start {
offset - fragment.insertion_offset
} else {
0
};
if fragment.visible {
visible_start += overshoot;
} else {
@@ -2377,6 +2380,11 @@ impl BufferSnapshot {
}
}
let end_fragment_id = self.fragment_id_for_anchor(&range.end);
let end_fragment_offset = if let Anchor::Character { offset, .. } = range.end {
offset
} else {
0
};
Edits {
visible_cursor: self.visible_text.cursor(visible_start),
@@ -2386,7 +2394,8 @@ impl BufferSnapshot {
since,
old_end: D::zero(&()),
new_end: D::zero(&()),
range: (start_fragment_id, range.start.offset)..(end_fragment_id, range.end.offset),
range: (start_fragment_id, start_fragment_offset)
..(end_fragment_id, end_fragment_offset),
buffer_id: self.remote_id,
}
}
@@ -2516,17 +2525,17 @@ impl<'a, D: TextDimension + Ord, F: FnMut(&FragmentSummary) -> bool> Iterator fo
break;
}
let start_anchor = Anchor {
timestamp: fragment.timestamp,
let start_anchor = Anchor::Character {
insertion_id: fragment.timestamp,
offset: fragment.insertion_offset,
bias: Bias::Right,
buffer_id: Some(self.buffer_id),
buffer_id: self.buffer_id,
};
let end_anchor = Anchor {
timestamp: fragment.timestamp,
let end_anchor = Anchor::Character {
insertion_id: fragment.timestamp,
offset: fragment.insertion_offset + fragment.len,
bias: Bias::Left,
buffer_id: Some(self.buffer_id),
buffer_id: self.buffer_id,
};
if !fragment.was_visible(self.since, self.undos) && fragment.visible {