Compare commits

...

43 Commits

Author SHA1 Message Date
Antonio Scandurra
15abdc0e61 WIP 2024-10-01 12:01:50 -06:00
Antonio Scandurra
45b806f236 Cleanup
Co-Authored-By: Max <max@zed.dev>
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-30 19:26:28 -06:00
Antonio Scandurra
7c988d0e66 Checkpoint: randomized test passing
Co-Authored-By: Nathan <nathan@zed.dev>
Co-Authored-By: Max <max@zed.dev>
2024-09-30 19:20:39 -06:00
Nathan Sobo
3d61cee893 WIP 2024-09-30 18:55:25 -06:00
Nathan Sobo
0bb7477aa1 Rework insert_excerpts in anticipation of tracking edits
Co-Authored-By: Antonio <antonio@zed.dev>
Co-Authored-By: Max <max@zed.dev>
2024-09-30 17:48:33 -06:00
Antonio Scandurra
5e1395cdc5 WIP 2024-09-30 14:35:53 -06:00
Antonio Scandurra
9b069903a7 WIP: Start on publishing edits
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-30 12:19:59 -06:00
Nathan Sobo
b53c812da4 WIP 2024-09-30 11:26:33 -06:00
Nathan Sobo
03ece03628 Fix multibyte chars in randomized test
Co-Authored-By: Antonio <antonio@zed.dev>
2024-09-30 09:30:29 -06:00
Nathan Sobo
784b5ea8bb More edits
Co-Authored-By: Antonio <antonio@zed.dev>
2024-09-30 09:29:08 -06:00
Nathan Sobo
429f1b7f72 💄
Co-Authored-By: Antonio <antonio@zed.dev>
2024-09-30 09:27:05 -06:00
Nathan Sobo
088380b9b2 Fix randomized tests
Co-Authored-By: Antonio <antonio@zed.dev>
2024-09-30 09:08:20 -06:00
Antonio Scandurra
c12b1ff3e6 WIP 2024-09-29 21:24:44 -06:00
Antonio Scandurra
2de9a2fc1d Checkpoint: passing with 3 operations 2024-09-29 19:25:30 -06:00
Antonio Scandurra
51c1710f80 WIP: start on edits 2024-09-29 17:01:35 -06:00
Antonio Scandurra
c49a8df756 Checkpoint: renames 2024-09-29 16:57:54 -06:00
Antonio Scandurra
da325c70c2 Checkpoint 2024-09-29 16:21:47 -06:00
Antonio Scandurra
55cc715637 Checkpoint
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-29 15:53:27 -06:00
Antonio Scandurra
f5db7a4ee5 Checkpoint
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-29 15:14:10 -06:00
Antonio Scandurra
4b31d1b47e Checkpoint
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-29 12:59:42 -06:00
Antonio Scandurra
cc4ba8d035 Merge branch 'main' into overhaul-multibuffer
# Conflicts:
#	crates/language/src/buffer_tests.rs
2024-09-29 11:04:06 -06:00
Antonio Scandurra
6da51e2443 WIP 2024-09-29 10:12:28 -06:00
Antonio Scandurra
957010d068 WIP: Start on edits 2024-09-28 18:18:12 -06:00
Antonio Scandurra
24829054c5 Checkpoint
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-28 15:15:21 -06:00
Antonio Scandurra
b90f23b10c Checkpoint
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-28 14:38:51 -06:00
Antonio Scandurra
9dedaba067 Checkpoint
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-28 14:26:16 -06:00
Antonio Scandurra
e6e2f45b21 WIP 2024-09-26 16:51:14 -06:00
Antonio Scandurra
12744f84f9 WIP 2024-09-25 11:30:47 -06:00
Antonio Scandurra
a787fedac0 Implement renames 2024-09-24 11:57:27 -06:00
Antonio Scandurra
1e1b36537a Avoid storing BufferSnapshot on the Excerpt 2024-09-24 08:16:06 -06:00
Antonio Scandurra
e0e9656b2b WIP: start on MultiBuffer::sync 2024-09-24 08:08:47 -06:00
Antonio Scandurra
14815d0e80 Avoid unwrapping when merging excerpts 2024-09-23 21:23:56 -06:00
Antonio Scandurra
cb5720f252 Checkpoint 2024-09-23 21:21:22 -06:00
Antonio Scandurra
014d792be2 WIP 2024-09-23 19:36:33 -06:00
Antonio Scandurra
a67789d6d7 Checkpoint
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-23 18:57:06 -06:00
Antonio Scandurra
fb0cebd3d1 Checkpoint 2024-09-23 16:31:23 -06:00
Antonio Scandurra
e0e39bf2c7 Merge remote-tracking branch 'origin/main' into overhaul-multibuffer 2024-09-23 16:21:46 -06:00
Antonio Scandurra
32d3074bbb WIP 2024-09-23 16:21:44 -06:00
Antonio Scandurra
d55b61a10c WIP 2024-09-23 15:56:31 -06:00
Antonio Scandurra
e192aefed3 Checkpoint
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-23 14:57:13 -06:00
Antonio Scandurra
479ff87909 Introduce a new multi_buffer2 crate
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-23 14:33:20 -06:00
Antonio Scandurra
10b452fc25 Checkpoint
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-23 14:30:29 -06:00
Antonio Scandurra
bcce7b7f62 Require buffer_id in text::Anchor
Co-Authored-By: Nathan <nathan@zed.dev>
2024-09-23 14:14:33 -06:00
26 changed files with 2032 additions and 563 deletions

26
Cargo.lock generated
View File

@@ -7065,6 +7065,32 @@ dependencies = [
"util",
]
[[package]]
name = "multi_buffer2"
version = "0.1.0"
dependencies = [
"anyhow",
"clock",
"collections",
"ctor",
"env_logger",
"futures 0.3.30",
"gpui",
"itertools 0.13.0",
"language",
"log",
"parking_lot",
"rand 0.8.5",
"rpc",
"serde",
"settings",
"smallvec",
"sum_tree",
"text",
"theme",
"util",
]
[[package]]
name = "multimap"
version = "0.8.3"

View File

@@ -67,6 +67,7 @@ members = [
"crates/media",
"crates/menu",
"crates/multi_buffer",
"crates/multi_buffer2",
"crates/node_runtime",
"crates/notifications",
"crates/ollama",

View File

@@ -599,7 +599,7 @@ impl Context {
});
let message = MessageAnchor {
id: first_message_id,
start: language::Anchor::MIN,
start: this.buffer.read(cx).min_anchor(),
};
this.messages_metadata.insert(
first_message_id,
@@ -862,11 +862,9 @@ impl Context {
}
match op {
ContextOperation::InsertMessage { anchor, .. } => self
.buffer
.read(cx)
.version
.observed(anchor.start.timestamp),
ContextOperation::InsertMessage { anchor, .. } => {
self.buffer.read(cx).can_resolve(&anchor.start)
}
ContextOperation::UpdateMessage { message_id, .. } => {
self.messages_metadata.contains_key(message_id)
}
@@ -876,20 +874,12 @@ impl Context {
sections,
..
} => {
let version = &self.buffer.read(cx).version;
let buffer = self.buffer.read(cx);
sections
.iter()
.map(|section| &section.range)
.chain([output_range])
.all(|range| {
let observed_start = range.start == language::Anchor::MIN
|| range.start == language::Anchor::MAX
|| version.observed(range.start.timestamp);
let observed_end = range.end == language::Anchor::MIN
|| range.end == language::Anchor::MAX
|| version.observed(range.end.timestamp);
observed_start && observed_end
})
.all(|range| buffer.can_resolve(&range.start) && buffer.can_resolve(&range.end))
}
ContextOperation::BufferOperation(_) => {
panic!("buffer operations should always be applied")
@@ -1563,7 +1553,7 @@ impl Context {
}
if let Some(mut pending_step) = pending_step {
pending_step.range.end = text::Anchor::MAX;
pending_step.range.end = buffer.max_anchor();
new_steps.push(pending_step);
}
@@ -2707,7 +2697,7 @@ impl Context {
messages.next();
}
}
let message_end_anchor = message_end.unwrap_or(language::Anchor::MAX);
let message_end_anchor = message_end.unwrap_or(buffer.max_anchor());
let message_end = message_end_anchor.to_offset(buffer);
return Some(Message {
@@ -2932,6 +2922,7 @@ impl SavedContext {
buffer: &Model<Buffer>,
cx: &mut ModelContext<Context>,
) -> Vec<ContextOperation> {
let buffer = buffer.read(cx);
let mut operations = Vec::new();
let mut version = clock::Global::new();
let mut next_timestamp = clock::Lamport::new(ReplicaId::default());
@@ -2944,7 +2935,7 @@ impl SavedContext {
operations.push(ContextOperation::InsertMessage {
anchor: MessageAnchor {
id: message.id,
start: buffer.read(cx).anchor_before(message.start),
start: buffer.anchor_before(message.start),
},
metadata: MessageMetadata {
role: message.metadata.role,
@@ -2977,19 +2968,16 @@ impl SavedContext {
let timestamp = next_timestamp.tick();
operations.push(ContextOperation::SlashCommandFinished {
id: SlashCommandId(timestamp),
output_range: language::Anchor::MIN..language::Anchor::MAX,
output_range: buffer.min_anchor()..buffer.max_anchor(),
sections: self
.slash_command_output_sections
.into_iter()
.map(|section| {
let buffer = buffer.read(cx);
SlashCommandOutputSection {
range: buffer.anchor_after(section.range.start)
..buffer.anchor_before(section.range.end),
icon: section.icon,
label: section.label,
metadata: section.metadata,
}
.map(|section| SlashCommandOutputSection {
range: buffer.anchor_after(section.range.start)
..buffer.anchor_before(section.range.end),
icon: section.icon,
label: section.label,
metadata: section.metadata,
})
.collect(),
version: version.clone(),

View File

@@ -64,14 +64,15 @@ pub enum WorkflowSuggestion {
impl WorkflowSuggestion {
pub fn range(&self) -> Range<language::Anchor> {
match self {
Self::Update { range, .. } => range.clone(),
Self::CreateFile { .. } => language::Anchor::MIN..language::Anchor::MAX,
Self::InsertBefore { position, .. } | Self::InsertAfter { position, .. } => {
*position..*position
}
Self::Delete { range, .. } => range.clone(),
}
todo!()
// match self {
// Self::Update { range, .. } => range.clone(),
// Self::CreateFile { .. } => language::Anchor::MIN..language::Anchor::MAX,
// Self::InsertBefore { position, .. } | Self::InsertAfter { position, .. } => {
// *position..*position
// }
// Self::Delete { range, .. } => range.clone(),
// }
}
pub fn description(&self) -> Option<&str> {

View File

@@ -1198,7 +1198,7 @@ async fn test_share_project(
buffer_a.read_with(cx_a, |buffer, _| {
buffer
.snapshot()
.selections_in_range(text::Anchor::MIN..text::Anchor::MAX, false)
.selections_in_range(buffer.min_anchor()..buffer.max_anchor(), false)
.count()
== 1
});
@@ -1237,7 +1237,7 @@ async fn test_share_project(
buffer_a.read_with(cx_a, |buffer, _| {
buffer
.snapshot()
.selections_in_range(text::Anchor::MIN..text::Anchor::MAX, false)
.selections_in_range(buffer.min_anchor()..buffer.max_anchor(), false)
.count()
== 0
});

View File

@@ -429,7 +429,7 @@ impl ProjectDiagnosticsEditor {
prev_excerpt_id = excerpt_id;
first_excerpt_id.get_or_insert(prev_excerpt_id);
group_state.excerpts.push(excerpt_id);
let header_position = (excerpt_id, language::Anchor::MIN);
let header_position = (excerpt_id, snapshot.min_anchor());
if is_first_excerpt_for_group {
is_first_excerpt_for_group = false;

View File

@@ -1194,7 +1194,7 @@ mod tests {
use rand::prelude::*;
use settings::SettingsStore;
use std::{cmp::Reverse, env, sync::Arc};
use text::Patch;
use text::{BufferId, Patch};
use util::post_inc;
#[test]
@@ -1205,7 +1205,9 @@ mod tests {
Anchor::min(),
&InlayHint {
label: InlayHintLabel::String("a".to_string()),
position: text::Anchor::default(),
position: text::Anchor::Start {
buffer_id: BufferId::new(1).unwrap()
},
padding_left: false,
padding_right: false,
tooltip: None,
@@ -1225,7 +1227,9 @@ mod tests {
Anchor::min(),
&InlayHint {
label: InlayHintLabel::String("a".to_string()),
position: text::Anchor::default(),
position: text::Anchor::Start {
buffer_id: BufferId::new(1).unwrap()
},
padding_left: true,
padding_right: true,
tooltip: None,
@@ -1245,7 +1249,9 @@ mod tests {
Anchor::min(),
&InlayHint {
label: InlayHintLabel::String(" a ".to_string()),
position: text::Anchor::default(),
position: text::Anchor::Start {
buffer_id: BufferId::new(1).unwrap()
},
padding_left: false,
padding_right: false,
tooltip: None,
@@ -1265,7 +1271,9 @@ mod tests {
Anchor::min(),
&InlayHint {
label: InlayHintLabel::String(" a ".to_string()),
position: text::Anchor::default(),
position: text::Anchor::Start {
buffer_id: BufferId::new(1).unwrap()
},
padding_left: true,
padding_right: true,
tooltip: None,

View File

@@ -3119,59 +3119,60 @@ impl Editor {
fn linked_editing_ranges_for(
&self,
selection: Range<text::Anchor>,
cx: &AppContext,
_selection: Range<text::Anchor>,
_cx: &AppContext,
) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
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) {
return None;
}
let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
let snapshot = buffer.read(cx).snapshot();
self.linked_edit_ranges
.get(end_buffer_id, selection.start..selection.end, &snapshot)
.map(|ranges| (ranges, snapshot, buffer))
})?;
use text::ToOffset as TO;
// find offset from the start of current range to current cursor position
let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
todo!()
// 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) {
// return None;
// }
// let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
// let snapshot = buffer.read(cx).snapshot();
// self.linked_edit_ranges
// .get(end_buffer_id, selection.start..selection.end, &snapshot)
// .map(|ranges| (ranges, snapshot, buffer))
// })?;
// use text::ToOffset as TO;
// // find offset from the start of current range to current cursor position
// let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
let start_difference = start_offset - start_byte_offset;
let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
let end_difference = end_offset - start_byte_offset;
// Current range has associated linked ranges.
let mut linked_edits = HashMap::<_, Vec<_>>::default();
for range in linked_ranges.iter() {
let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
let end_offset = start_offset + end_difference;
let start_offset = start_offset + start_difference;
if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
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
{
return false;
}
TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
&& TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
}) {
continue;
}
let start = buffer_snapshot.anchor_after(start_offset);
let end = buffer_snapshot.anchor_after(end_offset);
linked_edits
.entry(buffer.clone())
.or_default()
.push(start..end);
}
Some(linked_edits)
// let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
// let start_difference = start_offset - start_byte_offset;
// let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
// let end_difference = end_offset - start_byte_offset;
// // Current range has associated linked ranges.
// let mut linked_edits = HashMap::<_, Vec<_>>::default();
// for range in linked_ranges.iter() {
// let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
// let end_offset = start_offset + end_difference;
// let start_offset = start_offset + start_difference;
// if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
// 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
// {
// return false;
// }
// TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
// && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
// }) {
// continue;
// }
// let start = buffer_snapshot.anchor_after(start_offset);
// let end = buffer_snapshot.anchor_after(end_offset);
// linked_edits
// .entry(buffer.clone())
// .or_default()
// .push(start..end);
// }
// Some(linked_edits)
}
pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
@@ -5640,7 +5641,7 @@ 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 {
if selection_start.buffer_id() != selection_end.buffer_id() {
continue;
}
if let Some(ranges) =
@@ -11657,13 +11658,14 @@ impl Editor {
let start = highlight.range.start.to_display_point(&snapshot);
let end = highlight.range.end.to_display_point(&snapshot);
let start_row = start.row().0;
let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
&& end.column() == 0
{
end.row().0.saturating_sub(1)
} else {
end.row().0
};
let end_row =
if !matches!(highlight.range.end.text_anchor, text::Anchor::End { .. })
&& end.column() == 0
{
end.row().0.saturating_sub(1)
} else {
end.row().0
};
for row in start_row..=end_row {
let used_index =
used_highlight_orders.entry(row).or_insert(highlight.index);
@@ -12967,7 +12969,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(snapshot.min_anchor()..buffer_position);
let mut lines = chunks.lines();
let Some(line_at) = lines.next().filter(|line| !line.is_empty()) else {

View File

@@ -738,7 +738,12 @@ 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::Character {
buffer_id: invalid_anchor.text_anchor.buffer_id(),
insertion_id: clock::Lamport::MAX,
offset: 42,
bias: Bias::Left,
};
let invalid_point = Point::new(9999, 0);
editor.navigate(
Box::new(NavigationData {

View File

@@ -169,65 +169,66 @@ impl TasksForRanges {
fn remove_cached_ranges_from_query(
&mut self,
buffer_snapshot: &BufferSnapshot,
query_range: Range<language::Anchor>,
_buffer_snapshot: &BufferSnapshot,
_query_range: Range<language::Anchor>,
) -> Vec<Range<language::Anchor>> {
let mut ranges_to_query = Vec::new();
let mut latest_cached_range = None::<&mut Range<language::Anchor>>;
for cached_range in self
.sorted_ranges
.iter_mut()
.skip_while(|cached_range| {
cached_range
.end
.cmp(&query_range.start, buffer_snapshot)
.is_lt()
})
.take_while(|cached_range| {
cached_range
.start
.cmp(&query_range.end, buffer_snapshot)
.is_le()
})
{
match latest_cached_range {
Some(latest_cached_range) => {
if latest_cached_range.end.offset.saturating_add(1) < cached_range.start.offset
{
ranges_to_query.push(latest_cached_range.end..cached_range.start);
cached_range.start = latest_cached_range.end;
}
}
None => {
if query_range
.start
.cmp(&cached_range.start, buffer_snapshot)
.is_lt()
{
ranges_to_query.push(query_range.start..cached_range.start);
cached_range.start = query_range.start;
}
}
}
latest_cached_range = Some(cached_range);
}
todo!()
// let mut ranges_to_query = Vec::new();
// let mut latest_cached_range = None::<&mut Range<language::Anchor>>;
// for cached_range in self
// .sorted_ranges
// .iter_mut()
// .skip_while(|cached_range| {
// cached_range
// .end
// .cmp(&query_range.start, buffer_snapshot)
// .is_lt()
// })
// .take_while(|cached_range| {
// cached_range
// .start
// .cmp(&query_range.end, buffer_snapshot)
// .is_le()
// })
// {
// match latest_cached_range {
// Some(latest_cached_range) => {
// if latest_cached_range.end.offset.saturating_add(1) < cached_range.start.offset
// {
// ranges_to_query.push(latest_cached_range.end..cached_range.start);
// cached_range.start = latest_cached_range.end;
// }
// }
// None => {
// if query_range
// .start
// .cmp(&cached_range.start, buffer_snapshot)
// .is_lt()
// {
// ranges_to_query.push(query_range.start..cached_range.start);
// cached_range.start = query_range.start;
// }
// }
// }
// latest_cached_range = Some(cached_range);
// }
match latest_cached_range {
Some(latest_cached_range) => {
if latest_cached_range.end.offset.saturating_add(1) < query_range.end.offset {
ranges_to_query.push(latest_cached_range.end..query_range.end);
latest_cached_range.end = query_range.end;
}
}
None => {
ranges_to_query.push(query_range.clone());
self.sorted_ranges.push(query_range);
self.sorted_ranges
.sort_by(|range_a, range_b| range_a.start.cmp(&range_b.start, buffer_snapshot));
}
}
// match latest_cached_range {
// Some(latest_cached_range) => {
// if latest_cached_range.end.offset.saturating_add(1) < query_range.end.offset {
// ranges_to_query.push(latest_cached_range.end..query_range.end);
// latest_cached_range.end = query_range.end;
// }
// }
// None => {
// ranges_to_query.push(query_range.clone());
// self.sorted_ranges.push(query_range);
// self.sorted_ranges
// .sort_by(|range_a, range_b| range_a.start.cmp(&range_b.start, buffer_snapshot));
// }
// }
ranges_to_query
// ranges_to_query
}
fn invalidate_range(&mut self, buffer: &BufferSnapshot, range: &Range<language::Anchor>) {

View File

@@ -15,6 +15,7 @@ pub(super) struct LinkedEditingRanges(
);
impl LinkedEditingRanges {
#[allow(dead_code)] // todo!()
pub(super) fn get(
&self,
id: BufferId,

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

@@ -835,7 +835,7 @@ impl Buffer {
branch
.edits_since_in_range::<usize>(
&self.version,
range.unwrap_or(Anchor::MIN..Anchor::MAX),
range.unwrap_or(self.min_anchor()..self.max_anchor()),
)
.map(|edit| {
(

View File

@@ -2496,15 +2496,17 @@ fn assert_diff_hunks(
cx: &mut TestAppContext,
expected_hunks: &[(Range<u32>, &str, &str)],
) {
let (snapshot, diff_base) = buffer.read_with(cx, |buffer, _| {
(buffer.snapshot(), buffer.diff_base().unwrap().to_string())
buffer.read_with(cx, |buffer, _| {
let snapshot = buffer.snapshot();
let diff_base = buffer.diff_base().unwrap().to_string();
assert_hunks(
snapshot
.git_diff_hunks_intersecting_range(snapshot.min_anchor()..snapshot.max_anchor()),
&snapshot,
&diff_base,
expected_hunks,
);
});
assert_hunks(
snapshot.git_diff_hunks_intersecting_range(Anchor::MIN..Anchor::MAX),
&snapshot,
&diff_base,
expected_hunks,
);
}
#[gpui::test(iterations = 100)]
@@ -2786,7 +2788,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

@@ -16,19 +16,21 @@ pub struct Anchor {
impl Anchor {
pub fn min() -> Self {
Self {
buffer_id: None,
excerpt_id: ExcerptId::min(),
text_anchor: text::Anchor::MIN,
}
todo!()
// 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,
}
todo!()
// Self {
// buffer_id: None,
// excerpt_id: ExcerptId::max(),
// text_anchor: text::Anchor::MAX,
// }
}
pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering {
@@ -47,33 +49,36 @@ impl Anchor {
}
pub fn bias(&self) -> Bias {
self.text_anchor.bias
todo!()
// self.text_anchor.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),
};
}
}
*self
pub fn bias_left(&self, _snapshot: &MultiBufferSnapshot) -> Anchor {
todo!()
// 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),
// };
// }
// }
// *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),
};
}
}
*self
pub fn bias_right(&self, _snapshot: &MultiBufferSnapshot) -> Anchor {
todo!()
// 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),
// };
// }
// }
// *self
}
pub fn summary<D>(&self, snapshot: &MultiBufferSnapshot) -> D

View File

@@ -437,19 +437,20 @@ 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());
this.singleton = true;
this.push_excerpts(
buffer,
[ExcerptRange {
context: text::Anchor::MIN..text::Anchor::MAX,
primary: None,
}],
cx,
);
this.snapshot.borrow_mut().singleton = true;
this
pub fn singleton(_buffer: Model<Buffer>, _cx: &mut ModelContext<Self>) -> Self {
todo!()
// let mut this = Self::new(buffer.read(cx).capability());
// this.singleton = true;
// this.push_excerpts(
// buffer,
// [ExcerptRange {
// context: text::Anchor::MIN..text::Anchor::MAX,
// primary: None,
// }],
// cx,
// );
// this.snapshot.borrow_mut().singleton = true;
// this
}
/// Returns an up-to-date snapshot of the MultiBuffer.
@@ -3056,106 +3057,107 @@ impl MultiBufferSnapshot {
summaries
}
pub fn refresh_anchors<'a, I>(&'a self, anchors: I) -> Vec<(usize, Anchor, bool)>
pub fn refresh_anchors<'a, I>(&'a self, _anchors: I) -> Vec<(usize, Anchor, bool)>
where
I: 'a + IntoIterator<Item = &'a Anchor>,
{
let mut anchors = anchors.into_iter().enumerate().peekable();
let mut cursor = self.excerpts.cursor::<Option<&Locator>>(&());
cursor.next(&());
todo!()
// let mut anchors = anchors.into_iter().enumerate().peekable();
// let mut cursor = self.excerpts.cursor::<Option<&Locator>>(&());
// cursor.next(&());
let mut result = Vec::new();
// let mut result = Vec::new();
while let Some((_, anchor)) = anchors.peek() {
let old_excerpt_id = anchor.excerpt_id;
// while let Some((_, anchor)) = anchors.peek() {
// let old_excerpt_id = anchor.excerpt_id;
// Find the location where this anchor's excerpt should be.
let old_locator = self.excerpt_locator_for_id(old_excerpt_id);
cursor.seek_forward(&Some(old_locator), Bias::Left, &());
// // Find the location where this anchor's excerpt should be.
// let old_locator = self.excerpt_locator_for_id(old_excerpt_id);
// cursor.seek_forward(&Some(old_locator), Bias::Left, &());
if cursor.item().is_none() {
cursor.next(&());
}
// if cursor.item().is_none() {
// cursor.next(&());
// }
let next_excerpt = cursor.item();
let prev_excerpt = cursor.prev_item();
// let next_excerpt = cursor.item();
// let prev_excerpt = cursor.prev_item();
// Process all of the anchors for this excerpt.
while let Some((_, anchor)) = anchors.peek() {
if anchor.excerpt_id != old_excerpt_id {
break;
}
let (anchor_ix, anchor) = anchors.next().unwrap();
let mut anchor = *anchor;
// // Process all of the anchors for this excerpt.
// while let Some((_, anchor)) = anchors.peek() {
// if anchor.excerpt_id != old_excerpt_id {
// break;
// }
// let (anchor_ix, anchor) = anchors.next().unwrap();
// let mut anchor = *anchor;
// Leave min and max anchors unchanged if invalid or
// if the old excerpt still exists at this location
let mut kept_position = next_excerpt
.map_or(false, |e| e.id == old_excerpt_id && e.contains(&anchor))
|| old_excerpt_id == ExcerptId::max()
|| old_excerpt_id == ExcerptId::min();
// // Leave min and max anchors unchanged if invalid or
// // if the old excerpt still exists at this location
// let mut kept_position = next_excerpt
// .map_or(false, |e| e.id == old_excerpt_id && e.contains(&anchor))
// || old_excerpt_id == ExcerptId::max()
// || old_excerpt_id == ExcerptId::min();
// If the old excerpt no longer exists at this location, then attempt to
// find an equivalent position for this anchor in an adjacent excerpt.
if !kept_position {
for excerpt in [next_excerpt, prev_excerpt].iter().filter_map(|e| *e) {
if excerpt.contains(&anchor) {
anchor.excerpt_id = excerpt.id;
kept_position = true;
break;
}
}
}
// // If the old excerpt no longer exists at this location, then attempt to
// // find an equivalent position for this anchor in an adjacent excerpt.
// if !kept_position {
// for excerpt in [next_excerpt, prev_excerpt].iter().filter_map(|e| *e) {
// if excerpt.contains(&anchor) {
// anchor.excerpt_id = excerpt.id;
// kept_position = true;
// break;
// }
// }
// }
// If there's no adjacent excerpt that contains the anchor's position,
// then report that the anchor has lost its position.
if !kept_position {
anchor = if let Some(excerpt) = next_excerpt {
let mut text_anchor = excerpt
.range
.context
.start
.bias(anchor.text_anchor.bias, &excerpt.buffer);
if text_anchor
.cmp(&excerpt.range.context.end, &excerpt.buffer)
.is_gt()
{
text_anchor = excerpt.range.context.end;
}
Anchor {
buffer_id: Some(excerpt.buffer_id),
excerpt_id: excerpt.id,
text_anchor,
}
} else if let Some(excerpt) = prev_excerpt {
let mut text_anchor = excerpt
.range
.context
.end
.bias(anchor.text_anchor.bias, &excerpt.buffer);
if text_anchor
.cmp(&excerpt.range.context.start, &excerpt.buffer)
.is_lt()
{
text_anchor = excerpt.range.context.start;
}
Anchor {
buffer_id: Some(excerpt.buffer_id),
excerpt_id: excerpt.id,
text_anchor,
}
} else if anchor.text_anchor.bias == Bias::Left {
Anchor::min()
} else {
Anchor::max()
};
}
// // If there's no adjacent excerpt that contains the anchor's position,
// // then report that the anchor has lost its position.
// if !kept_position {
// anchor = if let Some(excerpt) = next_excerpt {
// let mut text_anchor = excerpt
// .range
// .context
// .start
// .bias(anchor.text_anchor.bias, &excerpt.buffer);
// if text_anchor
// .cmp(&excerpt.range.context.end, &excerpt.buffer)
// .is_gt()
// {
// text_anchor = excerpt.range.context.end;
// }
// Anchor {
// buffer_id: Some(excerpt.buffer_id),
// excerpt_id: excerpt.id,
// text_anchor,
// }
// } else if let Some(excerpt) = prev_excerpt {
// let mut text_anchor = excerpt
// .range
// .context
// .end
// .bias(anchor.text_anchor.bias, &excerpt.buffer);
// if text_anchor
// .cmp(&excerpt.range.context.start, &excerpt.buffer)
// .is_lt()
// {
// text_anchor = excerpt.range.context.start;
// }
// Anchor {
// buffer_id: Some(excerpt.buffer_id),
// excerpt_id: excerpt.id,
// text_anchor,
// }
// } else if anchor.text_anchor.bias == Bias::Left {
// Anchor::min()
// } else {
// Anchor::max()
// };
// }
result.push((anchor_ix, anchor, kept_position));
}
}
result.sort_unstable_by(|a, b| a.1.cmp(&b.1, self));
result
// result.push((anchor_ix, anchor, kept_position));
// }
// }
// result.sort_unstable_by(|a, b| a.1.cmp(&b.1, self));
// result
}
pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {

View File

@@ -0,0 +1,51 @@
[package]
name = "multi_buffer2"
version = "0.1.0"
edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/multi_buffer2.rs"
doctest = false
[features]
test-support = [
"text/test-support",
"language/test-support",
"gpui/test-support",
"util/test-support",
]
[dependencies]
anyhow.workspace = true
clock.workspace = true
collections.workspace = true
ctor.workspace = true
env_logger.workspace = true
futures.workspace = true
gpui.workspace = true
itertools.workspace = true
language.workspace = true
log.workspace = true
parking_lot.workspace = true
rand.workspace = true
settings.workspace = true
serde.workspace = true
smallvec.workspace = true
sum_tree.workspace = true
text.workspace = true
theme.workspace = true
util.workspace = true
[dev-dependencies]
gpui = { workspace = true, features = ["test-support"] }
language = { workspace = true, features = ["test-support"] }
rand.workspace = true
rpc = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
text = { workspace = true, features = ["test-support"] }
util = { workspace = true, features = ["test-support"] }

View File

@@ -0,0 +1 @@
../../LICENSE-GPL

File diff suppressed because it is too large Load Diff

View File

@@ -1451,8 +1451,9 @@ impl LspStore {
let actions = this
.update(cx, move |this, cx| {
let range = buffer.read(cx).min_anchor()..buffer.read(cx).max_anchor();
let request = GetCodeActions {
range: text::Anchor::MIN..text::Anchor::MAX,
range,
kinds: Some(code_actions),
};
let server = LanguageServerToQuery::Other(language_server.server_id());
@@ -1769,9 +1770,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));
@@ -2417,7 +2416,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(vec![range_start, range_end])
})?
.await
.context("waiting for inlay hint request range edits")?;

View File

@@ -576,7 +576,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 {