Compare commits
7 Commits
fix-git-ht
...
anchor-enu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ec78700b6 | ||
|
|
250a27945c | ||
|
|
7a971e301c | ||
|
|
4cfe6469dc | ||
|
|
adcdc9cd89 | ||
|
|
fcd27b0019 | ||
|
|
013e12d3bc |
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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`].
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")?;
|
||||
|
||||
@@ -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(),
|
||||
),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user