mirror of
https://github.com/zed-industries/zed.git
synced 2026-08-28 20:55:02 +00:00
This is necessary to remove some `util` dependencies from crates, as well as better sharing for our projects. This also includes the WIP AbsPath abstraction as well as some bug fixes from internal tooling. Release Notes: - N/A or Added/Fixed/Improved ...
693 lines
25 KiB
Rust
693 lines
25 KiB
Rust
use std::{ops::Range, rc::Rc, sync::Arc};
|
|
|
|
use gpui::{App, AppContext, Context, Entity};
|
|
use itertools::Itertools;
|
|
use language::{Buffer, BufferEditSource, BufferSnapshot};
|
|
use rope::Point;
|
|
use sum_tree::{Dimensions, SumTree};
|
|
use text::{Bias, BufferId, Edit, OffsetRangeExt, Patch};
|
|
use util::rel_path::RelPath;
|
|
use ztracing::instrument;
|
|
|
|
use crate::{
|
|
Anchor, BufferState, BufferStateSnapshot, DiffChangeKind, Event, Excerpt, ExcerptOffset,
|
|
ExcerptRange, ExcerptSummary, ExpandExcerptDirection, MultiBuffer, MultiBufferOffset,
|
|
PathKeyIndex, build_excerpt_ranges, remove_diff_state,
|
|
};
|
|
|
|
#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
|
|
pub struct PathKey {
|
|
// Used by the derived PartialOrd & Ord
|
|
pub sort_prefix: Option<u64>,
|
|
pub path: Arc<RelPath>,
|
|
}
|
|
|
|
impl PathKey {
|
|
pub fn min() -> Self {
|
|
Self {
|
|
sort_prefix: None,
|
|
path: RelPath::empty_arc(),
|
|
}
|
|
}
|
|
|
|
pub fn sorted(sort_prefix: u64) -> Self {
|
|
Self {
|
|
sort_prefix: Some(sort_prefix),
|
|
path: RelPath::empty_arc(),
|
|
}
|
|
}
|
|
pub fn with_sort_prefix(sort_prefix: u64, path: Arc<RelPath>) -> Self {
|
|
Self {
|
|
sort_prefix: Some(sort_prefix),
|
|
path,
|
|
}
|
|
}
|
|
|
|
pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {
|
|
if let Some(file) = buffer.read(cx).file() {
|
|
Self::with_sort_prefix(file.worktree_id(cx).to_proto(), file.path().clone())
|
|
} else {
|
|
Self {
|
|
sort_prefix: None,
|
|
path: RelPath::from_unix_str(&buffer.entity_id().to_string())
|
|
.unwrap()
|
|
.into_arc(),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl MultiBuffer {
|
|
pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
|
|
let snapshot = self.snapshot(cx);
|
|
let excerpt = snapshot.excerpts_for_path(path).next()?;
|
|
let path_key_index = snapshot.path_key_index_for_buffer(excerpt.context.start.buffer_id)?;
|
|
Some(Anchor::in_buffer(path_key_index, excerpt.context.start))
|
|
}
|
|
|
|
pub fn set_excerpts_for_buffer(
|
|
&mut self,
|
|
buffer: Entity<Buffer>,
|
|
ranges: impl IntoIterator<Item = Range<Point>>,
|
|
context_line_count: u32,
|
|
cx: &mut Context<Self>,
|
|
) -> bool {
|
|
let path = PathKey::for_buffer(&buffer, cx);
|
|
self.set_excerpts_for_path(path, buffer, ranges, context_line_count, cx)
|
|
}
|
|
|
|
/// Sets excerpts, returns `true` if at least one new excerpt was added.
|
|
///
|
|
/// Any existing excerpts for this buffer or this path will be replaced by the provided ranges.
|
|
#[instrument(skip_all)]
|
|
pub fn set_excerpts_for_path(
|
|
&mut self,
|
|
path: PathKey,
|
|
buffer: Entity<Buffer>,
|
|
ranges: impl IntoIterator<Item = Range<Point>>,
|
|
context_line_count: u32,
|
|
cx: &mut Context<Self>,
|
|
) -> bool {
|
|
let buffer_snapshot = buffer.read(cx).snapshot();
|
|
let ranges: Vec<_> = ranges.into_iter().collect();
|
|
let excerpt_ranges = build_excerpt_ranges(ranges, context_line_count, &buffer_snapshot);
|
|
|
|
let merged = Self::merge_excerpt_ranges(&excerpt_ranges);
|
|
let (inserted, _path_key_index) =
|
|
self.set_merged_excerpt_ranges_for_path(path, buffer, &buffer_snapshot, merged, cx);
|
|
inserted
|
|
}
|
|
|
|
/// Like [`Self::set_excerpts_for_path`], but expands the provided ranges to cover any overlapping existing excerpts
|
|
/// for the same buffer and path.
|
|
///
|
|
/// Existing excerpts that do not overlap any of the provided ranges are discarded.
|
|
pub fn update_excerpts_for_path(
|
|
&mut self,
|
|
path: PathKey,
|
|
buffer: Entity<Buffer>,
|
|
ranges: impl IntoIterator<Item = Range<Point>>,
|
|
context_line_count: u32,
|
|
cx: &mut Context<Self>,
|
|
) -> bool {
|
|
let buffer_snapshot = buffer.read(cx).snapshot();
|
|
let ranges: Vec<_> = ranges.into_iter().collect();
|
|
let excerpt_ranges = build_excerpt_ranges(ranges, context_line_count, &buffer_snapshot);
|
|
let merged = self.merge_new_with_existing_excerpt_ranges(
|
|
&path,
|
|
&buffer_snapshot,
|
|
excerpt_ranges,
|
|
cx,
|
|
);
|
|
|
|
let (inserted, _path_key_index) =
|
|
self.set_merged_excerpt_ranges_for_path(path, buffer, &buffer_snapshot, merged, cx);
|
|
inserted
|
|
}
|
|
|
|
pub fn merge_new_with_existing_excerpt_ranges(
|
|
&self,
|
|
path: &PathKey,
|
|
buffer_snapshot: &BufferSnapshot,
|
|
mut excerpt_ranges: Vec<ExcerptRange<Point>>,
|
|
cx: &App,
|
|
) -> Vec<ExcerptRange<Point>> {
|
|
let multibuffer_snapshot = self.snapshot(cx);
|
|
|
|
if multibuffer_snapshot.path_for_buffer(buffer_snapshot.remote_id()) == Some(path) {
|
|
excerpt_ranges.sort_by_key(|range| range.context.start);
|
|
let mut combined_ranges = Vec::new();
|
|
let mut new_ranges = excerpt_ranges.into_iter().peekable();
|
|
for existing_range in
|
|
multibuffer_snapshot.excerpts_for_buffer(buffer_snapshot.remote_id())
|
|
{
|
|
let existing_range = ExcerptRange {
|
|
context: existing_range.context.to_point(buffer_snapshot),
|
|
primary: existing_range.primary.to_point(buffer_snapshot),
|
|
};
|
|
while let Some(new_range) = new_ranges.peek()
|
|
&& new_range.context.end < existing_range.context.start
|
|
{
|
|
combined_ranges.push(new_range.clone());
|
|
new_ranges.next();
|
|
}
|
|
|
|
if let Some(new_range) = new_ranges.peek()
|
|
&& new_range.context.start <= existing_range.context.end
|
|
{
|
|
combined_ranges.push(existing_range)
|
|
}
|
|
}
|
|
combined_ranges.extend(new_ranges);
|
|
excerpt_ranges = combined_ranges;
|
|
}
|
|
|
|
excerpt_ranges.sort_by_key(|range| range.context.start);
|
|
Self::merge_excerpt_ranges(&excerpt_ranges)
|
|
}
|
|
|
|
pub fn set_excerpt_ranges_for_path(
|
|
&mut self,
|
|
path: PathKey,
|
|
buffer: Entity<Buffer>,
|
|
buffer_snapshot: &BufferSnapshot,
|
|
excerpt_ranges: Vec<ExcerptRange<Point>>,
|
|
cx: &mut Context<Self>,
|
|
) -> bool {
|
|
let merged = Self::merge_excerpt_ranges(&excerpt_ranges);
|
|
let (inserted, _path_key_index) =
|
|
self.set_merged_excerpt_ranges_for_path(path, buffer, buffer_snapshot, merged, cx);
|
|
inserted
|
|
}
|
|
|
|
pub fn set_anchored_excerpts_for_path(
|
|
&self,
|
|
path_key: PathKey,
|
|
buffer: Entity<Buffer>,
|
|
ranges: Vec<Range<text::Anchor>>,
|
|
context_line_count: u32,
|
|
cx: &Context<Self>,
|
|
) -> impl Future<Output = Vec<Range<Anchor>>> + use<> {
|
|
let buffer_snapshot = buffer.read(cx).snapshot();
|
|
let multi_buffer = cx.weak_entity();
|
|
let mut app = cx.to_async();
|
|
async move {
|
|
let snapshot = buffer_snapshot.clone();
|
|
let (ranges, merged_excerpt_ranges) = app
|
|
.background_spawn(async move {
|
|
let point_ranges = ranges.iter().map(|range| range.to_point(&snapshot));
|
|
let excerpt_ranges =
|
|
build_excerpt_ranges(point_ranges, context_line_count, &snapshot);
|
|
let merged = Self::merge_excerpt_ranges(&excerpt_ranges);
|
|
(ranges, merged)
|
|
})
|
|
.await;
|
|
|
|
multi_buffer
|
|
.update(&mut app, move |multi_buffer, cx| {
|
|
let (_, path_key_index) = multi_buffer.set_merged_excerpt_ranges_for_path(
|
|
path_key,
|
|
buffer,
|
|
&buffer_snapshot,
|
|
merged_excerpt_ranges,
|
|
cx,
|
|
);
|
|
ranges
|
|
.into_iter()
|
|
.map(|range| Anchor::range_in_buffer(path_key_index, range))
|
|
.collect()
|
|
})
|
|
.ok()
|
|
.unwrap_or_default()
|
|
}
|
|
}
|
|
|
|
pub fn expand_excerpts(
|
|
&mut self,
|
|
anchors: impl IntoIterator<Item = Anchor>,
|
|
line_count: u32,
|
|
direction: ExpandExcerptDirection,
|
|
cx: &mut Context<Self>,
|
|
) {
|
|
if line_count == 0 {
|
|
return;
|
|
}
|
|
|
|
let snapshot = self.snapshot(cx);
|
|
let mut sorted_anchors = anchors
|
|
.into_iter()
|
|
.filter_map(|anchor| anchor.excerpt_anchor())
|
|
.collect::<Vec<_>>();
|
|
if sorted_anchors.is_empty() {
|
|
return;
|
|
}
|
|
sorted_anchors.sort_by(|a, b| a.cmp(b, &snapshot));
|
|
let buffers = sorted_anchors.into_iter().chunk_by(|anchor| anchor.path);
|
|
let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(());
|
|
|
|
for (path_index, excerpt_anchors) in &buffers {
|
|
let path = snapshot
|
|
.path_keys
|
|
.get_index(path_index.0 as usize)
|
|
.expect("anchor from wrong multibuffer");
|
|
|
|
let mut excerpt_anchors = excerpt_anchors.peekable();
|
|
let mut ranges = Vec::new();
|
|
|
|
cursor.seek_forward(path, Bias::Left);
|
|
let Some((buffer, buffer_snapshot)) = cursor
|
|
.item()
|
|
.map(|excerpt| (excerpt.buffer(&self), excerpt.buffer_snapshot(&snapshot)))
|
|
else {
|
|
continue;
|
|
};
|
|
|
|
while let Some(excerpt) = cursor.item()
|
|
&& &excerpt.path_key == path
|
|
{
|
|
let mut range = ExcerptRange {
|
|
context: excerpt.range.context.to_point(buffer_snapshot),
|
|
primary: excerpt.range.primary.to_point(buffer_snapshot),
|
|
};
|
|
|
|
let mut needs_expand = false;
|
|
while excerpt_anchors.peek().is_some_and(|anchor| {
|
|
excerpt
|
|
.range
|
|
.contains(&anchor.text_anchor(), buffer_snapshot)
|
|
}) {
|
|
needs_expand = true;
|
|
excerpt_anchors.next();
|
|
}
|
|
|
|
if needs_expand {
|
|
match direction {
|
|
ExpandExcerptDirection::Up => {
|
|
range.context.start.row =
|
|
range.context.start.row.saturating_sub(line_count);
|
|
range.context.start.column = 0;
|
|
}
|
|
ExpandExcerptDirection::Down => {
|
|
range.context.end.row = (range.context.end.row + line_count)
|
|
.min(excerpt.buffer_snapshot(&snapshot).max_point().row);
|
|
range.context.end.column = excerpt
|
|
.buffer_snapshot(&snapshot)
|
|
.line_len(range.context.end.row);
|
|
}
|
|
ExpandExcerptDirection::UpAndDown => {
|
|
range.context.start.row =
|
|
range.context.start.row.saturating_sub(line_count);
|
|
range.context.start.column = 0;
|
|
range.context.end.row = (range.context.end.row + line_count)
|
|
.min(excerpt.buffer_snapshot(&snapshot).max_point().row);
|
|
range.context.end.column = excerpt
|
|
.buffer_snapshot(&snapshot)
|
|
.line_len(range.context.end.row);
|
|
}
|
|
}
|
|
}
|
|
|
|
ranges.push(range);
|
|
cursor.next();
|
|
}
|
|
|
|
ranges.sort_by_key(|r| r.context.start);
|
|
|
|
self.set_excerpt_ranges_for_path(path.clone(), buffer, buffer_snapshot, ranges, cx);
|
|
}
|
|
}
|
|
|
|
/// Sets excerpts, returns `true` if at least one new excerpt was added.
|
|
pub(crate) fn set_merged_excerpt_ranges_for_path<T>(
|
|
&mut self,
|
|
path: PathKey,
|
|
buffer: Entity<Buffer>,
|
|
buffer_snapshot: &BufferSnapshot,
|
|
new: Vec<ExcerptRange<T>>,
|
|
cx: &mut Context<Self>,
|
|
) -> (bool, PathKeyIndex)
|
|
where
|
|
T: language::ToOffset,
|
|
{
|
|
let anchor_ranges = new
|
|
.into_iter()
|
|
.map(|r| ExcerptRange {
|
|
context: buffer_snapshot.anchor_before(r.context.start)
|
|
..buffer_snapshot.anchor_after(r.context.end),
|
|
primary: buffer_snapshot.anchor_before(r.primary.start)
|
|
..buffer_snapshot.anchor_after(r.primary.end),
|
|
})
|
|
.collect::<Vec<_>>();
|
|
let inserted =
|
|
self.update_path_excerpts(path.clone(), buffer, buffer_snapshot, &anchor_ranges, cx);
|
|
let path_key_index = self.get_or_create_path_key_index(&path);
|
|
(inserted, path_key_index)
|
|
}
|
|
|
|
pub(crate) fn get_or_create_path_key_index(&mut self, path_key: &PathKey) -> PathKeyIndex {
|
|
let mut snapshot = self.snapshot.borrow_mut();
|
|
|
|
if let Some(existing) = snapshot.path_keys.get_index_of(path_key) {
|
|
return PathKeyIndex(existing as u64);
|
|
}
|
|
|
|
PathKeyIndex(
|
|
Arc::make_mut(&mut snapshot.path_keys)
|
|
.insert_full(path_key.clone())
|
|
.0 as u64,
|
|
)
|
|
}
|
|
|
|
pub fn update_path_excerpts(
|
|
&mut self,
|
|
path_key: PathKey,
|
|
buffer: Entity<Buffer>,
|
|
buffer_snapshot: &BufferSnapshot,
|
|
to_insert: &Vec<ExcerptRange<text::Anchor>>,
|
|
cx: &mut Context<Self>,
|
|
) -> bool {
|
|
let path_key_index = self.get_or_create_path_key_index(&path_key);
|
|
if let Some(old_path_key) = self
|
|
.snapshot(cx)
|
|
.path_for_buffer(buffer_snapshot.remote_id())
|
|
&& old_path_key != &path_key
|
|
{
|
|
self.remove_excerpts(old_path_key.clone(), cx);
|
|
}
|
|
|
|
if to_insert.len() == 0 {
|
|
self.remove_excerpts(path_key.clone(), cx);
|
|
|
|
return false;
|
|
}
|
|
assert_eq!(self.history.transaction_depth(), 0);
|
|
self.sync_mut(cx);
|
|
|
|
let buffer_id = buffer_snapshot.remote_id();
|
|
|
|
let mut snapshot = self.snapshot.get_mut();
|
|
let mut cursor = snapshot
|
|
.excerpts
|
|
.cursor::<Dimensions<PathKey, ExcerptOffset>>(());
|
|
let mut new_excerpts = SumTree::new(());
|
|
|
|
let new_ranges = to_insert.clone();
|
|
let mut to_insert = to_insert.iter().peekable();
|
|
let mut patch = Patch::empty();
|
|
let mut added_new_excerpt = false;
|
|
|
|
new_excerpts.append(cursor.slice(&path_key, Bias::Left), ());
|
|
|
|
// handle the case where the path key used to be associated
|
|
// with a different buffer by removing its excerpts.
|
|
if let Some(excerpt) = cursor.item()
|
|
&& &excerpt.path_key == &path_key
|
|
&& excerpt.buffer_id != buffer_id
|
|
{
|
|
let old_buffer_id = excerpt.buffer_id;
|
|
self.buffers.remove(&old_buffer_id);
|
|
snapshot.buffers.remove(&old_buffer_id);
|
|
remove_diff_state(&mut snapshot.diffs, old_buffer_id);
|
|
self.diffs.remove(&old_buffer_id);
|
|
let before = cursor.position.1;
|
|
cursor.seek_forward(&path_key, Bias::Right);
|
|
let after = cursor.position.1;
|
|
patch.push(Edit {
|
|
old: before..after,
|
|
new: new_excerpts.summary().len()..new_excerpts.summary().len(),
|
|
});
|
|
cx.emit(Event::BuffersRemoved {
|
|
removed_buffer_ids: vec![old_buffer_id],
|
|
});
|
|
}
|
|
|
|
while let Some(excerpt) = cursor.item()
|
|
&& excerpt.path_key == path_key
|
|
{
|
|
assert_eq!(excerpt.buffer_id, buffer_id);
|
|
let Some(next_excerpt) = to_insert.peek() else {
|
|
break;
|
|
};
|
|
if &excerpt.range == *next_excerpt {
|
|
let before = new_excerpts.summary().len();
|
|
new_excerpts.update_last(
|
|
|prev_excerpt| {
|
|
if !prev_excerpt.has_trailing_newline {
|
|
prev_excerpt.has_trailing_newline = true;
|
|
patch.push(Edit {
|
|
old: cursor.position.1..cursor.position.1,
|
|
new: before..before + MultiBufferOffset(1),
|
|
});
|
|
}
|
|
},
|
|
(),
|
|
);
|
|
new_excerpts.push(excerpt.clone(), ());
|
|
to_insert.next();
|
|
cursor.next();
|
|
continue;
|
|
}
|
|
|
|
if excerpt
|
|
.range
|
|
.context
|
|
.start
|
|
.cmp(&next_excerpt.context.start, &buffer_snapshot)
|
|
.is_le()
|
|
{
|
|
// remove old excerpt
|
|
let before = cursor.position.1;
|
|
cursor.next();
|
|
let after = cursor.position.1;
|
|
patch.push(Edit {
|
|
old: before..after,
|
|
new: new_excerpts.summary().len()..new_excerpts.summary().len(),
|
|
});
|
|
} else {
|
|
// insert new excerpt
|
|
let next_excerpt = to_insert.next().unwrap();
|
|
added_new_excerpt = true;
|
|
let before = new_excerpts.summary().len();
|
|
new_excerpts.update_last(
|
|
|prev_excerpt| {
|
|
prev_excerpt.has_trailing_newline = true;
|
|
},
|
|
(),
|
|
);
|
|
new_excerpts.push(
|
|
Excerpt::new(
|
|
path_key.clone(),
|
|
path_key_index,
|
|
&buffer_snapshot,
|
|
next_excerpt.clone(),
|
|
false,
|
|
),
|
|
(),
|
|
);
|
|
let after = new_excerpts.summary().len();
|
|
patch.push_maybe_empty(Edit {
|
|
old: cursor.position.1..cursor.position.1,
|
|
new: before..after,
|
|
});
|
|
}
|
|
}
|
|
|
|
// remove any further trailing excerpts
|
|
let mut before = cursor.position.1;
|
|
cursor.seek_forward(&path_key, Bias::Right);
|
|
let after = cursor.position.1;
|
|
// if we removed the previous last excerpt, remove the trailing newline from the new last excerpt
|
|
if cursor.item().is_none() && to_insert.peek().is_none() {
|
|
new_excerpts.update_last(
|
|
|excerpt| {
|
|
if excerpt.has_trailing_newline {
|
|
before.0.0 = before
|
|
.0
|
|
.0
|
|
.checked_sub(1)
|
|
.expect("should have preceding excerpt");
|
|
excerpt.has_trailing_newline = false;
|
|
}
|
|
},
|
|
(),
|
|
);
|
|
}
|
|
patch.push(Edit {
|
|
old: before..after,
|
|
new: new_excerpts.summary().len()..new_excerpts.summary().len(),
|
|
});
|
|
|
|
while let Some(next_excerpt) = to_insert.next() {
|
|
added_new_excerpt = true;
|
|
let before = new_excerpts.summary().len();
|
|
new_excerpts.update_last(
|
|
|prev_excerpt| {
|
|
prev_excerpt.has_trailing_newline = true;
|
|
},
|
|
(),
|
|
);
|
|
new_excerpts.push(
|
|
Excerpt::new(
|
|
path_key.clone(),
|
|
path_key_index,
|
|
&buffer_snapshot,
|
|
next_excerpt.clone(),
|
|
false,
|
|
),
|
|
(),
|
|
);
|
|
let after = new_excerpts.summary().len();
|
|
patch.push_maybe_empty(Edit {
|
|
old: cursor.position.1..cursor.position.1,
|
|
new: before..after,
|
|
});
|
|
}
|
|
|
|
let suffix_start = cursor.position.1;
|
|
let suffix = cursor.suffix();
|
|
let changed_trailing_excerpt = suffix.is_empty();
|
|
if !suffix.is_empty() {
|
|
let before = new_excerpts.summary().len();
|
|
new_excerpts.update_last(
|
|
|prev_excerpt| {
|
|
if !prev_excerpt.has_trailing_newline {
|
|
prev_excerpt.has_trailing_newline = true;
|
|
patch.push(Edit {
|
|
old: suffix_start..suffix_start,
|
|
new: before..before + MultiBufferOffset(1),
|
|
});
|
|
}
|
|
},
|
|
(),
|
|
);
|
|
}
|
|
new_excerpts.append(suffix, ());
|
|
drop(cursor);
|
|
|
|
snapshot.excerpts = new_excerpts;
|
|
snapshot.buffers.insert(
|
|
buffer_id,
|
|
BufferStateSnapshot {
|
|
path_key: path_key.clone(),
|
|
path_key_index,
|
|
buffer_snapshot: buffer_snapshot.clone(),
|
|
},
|
|
);
|
|
|
|
self.buffers.entry(buffer_id).or_insert_with(|| {
|
|
self.buffer_changed_since_sync.replace(true);
|
|
buffer.update(cx, |buffer, _| {
|
|
buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync));
|
|
});
|
|
BufferState {
|
|
_subscriptions: [
|
|
cx.observe(&buffer, |_, _, cx| cx.notify()),
|
|
cx.subscribe(&buffer, Self::on_buffer_event),
|
|
],
|
|
buffer: buffer.clone(),
|
|
}
|
|
});
|
|
|
|
if changed_trailing_excerpt {
|
|
snapshot.trailing_excerpt_update_count += 1;
|
|
}
|
|
|
|
let edits = Self::sync_diff_transforms(
|
|
&mut snapshot,
|
|
patch.into_inner(),
|
|
DiffChangeKind::BufferEdited,
|
|
);
|
|
if !edits.is_empty() {
|
|
self.subscriptions.publish(edits);
|
|
cx.emit(Event::Edited {
|
|
edited_buffer: None,
|
|
source: BufferEditSource::User,
|
|
});
|
|
cx.emit(Event::BufferRangesUpdated {
|
|
buffer,
|
|
path_key: path_key.clone(),
|
|
ranges: new_ranges,
|
|
});
|
|
cx.notify();
|
|
}
|
|
|
|
added_new_excerpt
|
|
}
|
|
|
|
pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context<Self>) {
|
|
let snapshot = self.sync_mut(cx);
|
|
let Some(path) = snapshot.path_for_buffer(buffer).cloned() else {
|
|
return;
|
|
};
|
|
self.remove_excerpts(path, cx);
|
|
}
|
|
|
|
pub fn remove_excerpts(&mut self, path: PathKey, cx: &mut Context<Self>) {
|
|
assert_eq!(self.history.transaction_depth(), 0);
|
|
self.sync_mut(cx);
|
|
|
|
let mut snapshot = self.snapshot.get_mut();
|
|
let mut cursor = snapshot
|
|
.excerpts
|
|
.cursor::<Dimensions<PathKey, ExcerptOffset>>(());
|
|
let mut new_excerpts = SumTree::new(());
|
|
new_excerpts.append(cursor.slice(&path, Bias::Left), ());
|
|
let mut edit_start = cursor.position.1;
|
|
let mut buffer_id = None;
|
|
if let Some(excerpt) = cursor.item()
|
|
&& excerpt.path_key == path
|
|
{
|
|
buffer_id = Some(excerpt.buffer_id);
|
|
}
|
|
cursor.seek(&path, Bias::Right);
|
|
let edit_end = cursor.position.1;
|
|
let suffix = cursor.suffix();
|
|
let changed_trailing_excerpt = suffix.is_empty();
|
|
new_excerpts.append(suffix, ());
|
|
|
|
if let Some(buffer_id) = buffer_id {
|
|
snapshot.buffers.remove(&buffer_id);
|
|
remove_diff_state(&mut snapshot.diffs, buffer_id);
|
|
self.buffers.remove(&buffer_id);
|
|
self.diffs.remove(&buffer_id);
|
|
cx.emit(Event::BuffersRemoved {
|
|
removed_buffer_ids: vec![buffer_id],
|
|
})
|
|
}
|
|
drop(cursor);
|
|
if changed_trailing_excerpt {
|
|
snapshot.trailing_excerpt_update_count += 1;
|
|
new_excerpts.update_last(
|
|
|excerpt| {
|
|
if excerpt.has_trailing_newline {
|
|
excerpt.has_trailing_newline = false;
|
|
edit_start.0.0 = edit_start
|
|
.0
|
|
.0
|
|
.checked_sub(1)
|
|
.expect("should have at least one excerpt");
|
|
}
|
|
},
|
|
(),
|
|
)
|
|
}
|
|
|
|
let edit = Edit {
|
|
old: edit_start..edit_end,
|
|
new: edit_start..edit_start,
|
|
};
|
|
snapshot.excerpts = new_excerpts;
|
|
|
|
let edits =
|
|
Self::sync_diff_transforms(&mut snapshot, vec![edit], DiffChangeKind::BufferEdited);
|
|
if !edits.is_empty() {
|
|
self.subscriptions.publish(edits);
|
|
}
|
|
|
|
cx.emit(Event::Edited {
|
|
edited_buffer: None,
|
|
source: BufferEditSource::User,
|
|
});
|
|
cx.notify();
|
|
}
|
|
}
|