580 lines · 22.2 KB · Rust
RawDownload
use std::{mem, ops::Range, sync::Arc}; use collections::HashSet;use gpui::{App, AppContext, Context, Entity};use itertools::Itertools;use language::{Buffer, BufferSnapshot};use rope::Point;use text::{Bias, BufferId, OffsetRangeExt, locator::Locator};use util::{post_inc, rel_path::RelPath};use ztracing::instrument; use crate::{    Anchor, ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer, build_excerpt_ranges,}; #[derive(Debug, Clone)]pub struct PathExcerptInsertResult {    pub inserted_ranges: Vec<Range<Anchor>>,    pub excerpt_ids: Vec<ExcerptId>,    pub added_new_excerpt: bool,} #[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 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::unix(&buffer.entity_id().to_string())                    .unwrap()                    .into_arc(),            }        }    }} impl MultiBuffer {    pub fn paths(&self) -> impl Iterator<Item = &PathKey> + '_ {        self.excerpts_by_path.keys()    }     pub fn excerpts_for_path(&self, path: &PathKey) -> impl '_ + Iterator<Item = ExcerptId> {        self.excerpts_by_path            .get(path)            .map(|excerpts| excerpts.as_slice())            .unwrap_or_default()            .iter()            .copied()    }     pub fn path_for_excerpt(&self, excerpt: ExcerptId) -> Option<PathKey> {        self.paths_by_excerpt.get(&excerpt).cloned()    }     pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {        if let Some(to_remove) = self.excerpts_by_path.remove(&path) {            self.remove_excerpts(to_remove, cx)        }    }     pub fn buffer_for_path(&self, path: &PathKey, cx: &App) -> Option<Entity<Buffer>> {        let excerpt_id = self.excerpts_by_path.get(path)?.first()?;        let snapshot = self.read(cx);        let excerpt = snapshot.excerpt(*excerpt_id)?;        self.buffer(excerpt.buffer_id)    }     pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {        let excerpt_id = self.excerpts_by_path.get(path)?.first()?;        let snapshot = self.read(cx);        let excerpt = snapshot.excerpt(*excerpt_id)?;        Some(Anchor::in_buffer(excerpt.id, excerpt.range.context.start))    }     /// Sets excerpts, returns `true` if at least one new excerpt was added.    #[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>,    ) -> (Vec<Range<Anchor>>, bool) {        let buffer_snapshot = buffer.read(cx).snapshot();        let excerpt_ranges = build_excerpt_ranges(ranges, context_line_count, &buffer_snapshot);         let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);        let excerpt_insertion_result = self.set_merged_excerpt_ranges_for_path(            path,            buffer,            excerpt_ranges,            &buffer_snapshot,            new,            counts,            cx,        );        (            excerpt_insertion_result.inserted_ranges,            excerpt_insertion_result.added_new_excerpt,        )    }     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>,    ) -> (Vec<Range<Anchor>>, bool) {        let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);        let excerpt_insertion_result = self.set_merged_excerpt_ranges_for_path(            path,            buffer,            excerpt_ranges,            buffer_snapshot,            new,            counts,            cx,        );        (            excerpt_insertion_result.inserted_ranges,            excerpt_insertion_result.added_new_excerpt,        )    }     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 (excerpt_ranges, new, counts) = app                .background_spawn(async move {                    let ranges = ranges.into_iter().map(|range| range.to_point(&snapshot));                    let excerpt_ranges =                        build_excerpt_ranges(ranges, context_line_count, &snapshot);                    let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);                    (excerpt_ranges, new, counts)                })                .await;             multi_buffer                .update(&mut app, move |multi_buffer, cx| {                    let excerpt_insertion_result = multi_buffer.set_merged_excerpt_ranges_for_path(                        path_key,                        buffer,                        excerpt_ranges,                        &buffer_snapshot,                        new,                        counts,                        cx,                    );                    excerpt_insertion_result.inserted_ranges                })                .ok()                .unwrap_or_default()        }    }     pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context<Self>) {        self.remove_excerpts(            self.excerpts_for_buffer(buffer, cx)                .into_iter()                .map(|(excerpt, _)| excerpt),            cx,        );    }     pub(super) fn expand_excerpts_with_paths(        &mut self,        ids: impl IntoIterator<Item = ExcerptId>,        line_count: u32,        direction: ExpandExcerptDirection,        cx: &mut Context<Self>,    ) {        let grouped = ids            .into_iter()            .chunk_by(|id| self.paths_by_excerpt.get(id).cloned())            .into_iter()            .filter_map(|(k, v)| Some((k?, v.into_iter().collect::<Vec<_>>())))            .collect::<Vec<_>>();        let snapshot = self.snapshot(cx);         for (path, ids) in grouped.into_iter() {            let Some(excerpt_ids) = self.excerpts_by_path.get(&path) else {                continue;            };             let ids_to_expand = HashSet::from_iter(ids);            let mut excerpt_id_ = None;            let expanded_ranges = excerpt_ids.iter().filter_map(|excerpt_id| {                let excerpt = snapshot.excerpt(*excerpt_id)?;                let excerpt_id = excerpt.id;                if excerpt_id_.is_none() {                    excerpt_id_ = Some(excerpt_id);                }                 let mut context = excerpt.range.context.to_point(&excerpt.buffer);                if ids_to_expand.contains(&excerpt_id) {                    match direction {                        ExpandExcerptDirection::Up => {                            context.start.row = context.start.row.saturating_sub(line_count);                            context.start.column = 0;                        }                        ExpandExcerptDirection::Down => {                            context.end.row =                                (context.end.row + line_count).min(excerpt.buffer.max_point().row);                            context.end.column = excerpt.buffer.line_len(context.end.row);                        }                        ExpandExcerptDirection::UpAndDown => {                            context.start.row = context.start.row.saturating_sub(line_count);                            context.start.column = 0;                            context.end.row =                                (context.end.row + line_count).min(excerpt.buffer.max_point().row);                            context.end.column = excerpt.buffer.line_len(context.end.row);                        }                    }                }                 Some(ExcerptRange {                    context,                    primary: excerpt.range.primary.to_point(&excerpt.buffer),                })            });            let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();            for range in expanded_ranges {                if let Some(last_range) = merged_ranges.last_mut()                    && last_range.context.end >= range.context.start                {                    last_range.context.end = range.context.end;                    continue;                }                merged_ranges.push(range)            }            let Some(excerpt_id) = excerpt_id_ else {                continue;            };            let Some(buffer_id) = &snapshot.buffer_id_for_excerpt(excerpt_id) else {                continue;            };             let Some(buffer) = self.buffers.get(buffer_id).map(|b| b.buffer.clone()) else {                continue;            };             let buffer_snapshot = buffer.read(cx).snapshot();            self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, merged_ranges, cx);        }    }     /// Sets excerpts, returns `true` if at least one new excerpt was added.    pub fn set_merged_excerpt_ranges_for_path(        &mut self,        path: PathKey,        buffer: Entity<Buffer>,        ranges: Vec<ExcerptRange<Point>>,        buffer_snapshot: &BufferSnapshot,        new: Vec<ExcerptRange<Point>>,        counts: Vec<usize>,        cx: &mut Context<Self>,    ) -> PathExcerptInsertResult {        let (new, counts) =            self.expand_new_ranges_to_existing(&path, buffer_snapshot, new, counts, cx);        let (excerpt_ids, added_new_excerpt) =            self.update_path_excerpts(path, buffer, buffer_snapshot, new, cx);         let mut inserted_ranges = Vec::new();        let mut ranges = ranges.into_iter();        for (&excerpt_id, range_count) in excerpt_ids.iter().zip(counts.into_iter()) {            for range in ranges.by_ref().take(range_count) {                let range = Anchor::range_in_buffer(                    excerpt_id,                    buffer_snapshot.anchor_before(&range.primary.start)                        ..buffer_snapshot.anchor_after(&range.primary.end),                );                inserted_ranges.push(range)            }        }         PathExcerptInsertResult {            inserted_ranges,            excerpt_ids,            added_new_excerpt,        }    }     /// Expand each new merged range to encompass any overlapping existing    /// excerpt, then re-merge. This turns "partial overlap where the union    /// equals the existing range" into an exact match, avoiding unnecessary    /// remove+insert churn that floods the wrap map with edits.    fn expand_new_ranges_to_existing(        &self,        path: &PathKey,        buffer_snapshot: &BufferSnapshot,        mut new: Vec<ExcerptRange<Point>>,        counts: Vec<usize>,        cx: &App,    ) -> (Vec<ExcerptRange<Point>>, Vec<usize>) {        let existing = self.excerpts_by_path.get(path).cloned().unwrap_or_default();        if existing.is_empty() || new.is_empty() {            return (new, counts);        }         let snapshot = self.snapshot(cx);        let buffer_id = buffer_snapshot.remote_id();        let existing_ranges: Vec<Range<Point>> = existing            .iter()            .filter_map(|&id| {                let excerpt = snapshot.excerpt(id)?;                (excerpt.buffer_id == buffer_id)                    .then(|| excerpt.range.context.to_point(buffer_snapshot))            })            .collect();         let mut changed = false;        for new_range in &mut new {            for existing_range in &existing_ranges {                if new_range.context.start <= existing_range.end                    && new_range.context.end >= existing_range.start                {                    let expanded_start = new_range.context.start.min(existing_range.start);                    let expanded_end = new_range.context.end.max(existing_range.end);                    if expanded_start != new_range.context.start                        || expanded_end != new_range.context.end                    {                        new_range.context.start = expanded_start;                        new_range.context.end = expanded_end;                        changed = true;                    }                }            }        }         if !changed {            return (new, counts);        }         let mut result_ranges: Vec<ExcerptRange<Point>> = Vec::new();        let mut result_counts: Vec<usize> = Vec::new();        for (range, count) in new.into_iter().zip(counts) {            if let Some(last) = result_ranges.last_mut() {                if last.context.end >= range.context.start                    || last.context.end.row + 1 == range.context.start.row                {                    last.context.end = last.context.end.max(range.context.end);                    *result_counts.last_mut().unwrap() += count;                    continue;                }            }            result_ranges.push(range);            result_counts.push(count);        }         (result_ranges, result_counts)    }     fn update_path_excerpts(        &mut self,        path: PathKey,        buffer: Entity<Buffer>,        buffer_snapshot: &BufferSnapshot,        new: Vec<ExcerptRange<Point>>,        cx: &mut Context<Self>,    ) -> (Vec<ExcerptId>, bool) {        let mut insert_after = self            .excerpts_by_path            .range(..path.clone())            .next_back()            .and_then(|(_, value)| value.last().copied())            .unwrap_or(ExcerptId::min());         let existing = self            .excerpts_by_path            .get(&path)            .cloned()            .unwrap_or_default();        let mut new_iter = new.into_iter().peekable();        let mut existing_iter = existing.into_iter().peekable();         let mut excerpt_ids = Vec::new();        let mut to_remove = Vec::new();        let mut to_insert: Vec<(ExcerptId, ExcerptRange<Point>)> = Vec::new();        let mut added_a_new_excerpt = false;        let snapshot = self.snapshot(cx);         let mut next_excerpt_id =            if let Some(last_entry) = self.snapshot.get_mut().excerpt_ids.last() {                last_entry.id.0 + 1            } else {                1            };         let mut next_excerpt_id = move || ExcerptId(post_inc(&mut next_excerpt_id));         let mut excerpts_cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());        excerpts_cursor.next();         loop {            let existing = if let Some(&existing_id) = existing_iter.peek() {                let locator = snapshot.excerpt_locator_for_id(existing_id);                excerpts_cursor.seek_forward(&Some(locator), Bias::Left);                if let Some(excerpt) = excerpts_cursor.item() {                    if excerpt.buffer_id != buffer_snapshot.remote_id() {                        to_remove.push(existing_id);                        existing_iter.next();                        continue;                    }                    Some((existing_id, excerpt.range.context.to_point(buffer_snapshot)))                } else {                    None                }            } else {                None            };             let new = new_iter.peek();            // Try to merge the next new range or existing excerpt into the last            // queued insert.            if let Some((last_id, last)) = to_insert.last_mut() {                // Next new range overlaps the last queued insert: absorb it by                // extending the insert's end.                if let Some(new) = new                    && last.context.end >= new.context.start                {                    last.context.end = last.context.end.max(new.context.end);                    excerpt_ids.push(*last_id);                    new_iter.next();                    continue;                }                // Next existing excerpt overlaps the last queued insert: absorb                // it by extending the insert's end, and record the existing                // excerpt as replaced so anchors in it resolve to the new one.                if let Some((existing_id, existing_range)) = &existing                    && last.context.end >= existing_range.start                {                    last.context.end = last.context.end.max(existing_range.end);                    to_remove.push(*existing_id);                    self.snapshot                        .get_mut()                        .replaced_excerpts                        .insert(*existing_id, *last_id);                    existing_iter.next();                    continue;                }            }             match (new, existing) {                (None, None) => break,                 // No more new ranges; remove the remaining existing excerpt.                (None, Some((existing_id, _))) => {                    existing_iter.next();                    to_remove.push(existing_id);                }                 // No more existing excerpts; queue the new range for insertion.                (Some(_), None) => {                    added_a_new_excerpt = true;                    let new_id = next_excerpt_id();                    excerpt_ids.push(new_id);                    to_insert.push((new_id, new_iter.next().unwrap()));                }                 // Existing excerpt ends before the new range starts, so it                // has no corresponding new range and must be removed. Flush                // pending inserts and advance `insert_after` past it so that                // future inserts receive locators *after* this excerpt's                // locator, preserving forward ordering.                (Some(new), Some((_, existing_range)))                    if existing_range.end < new.context.start =>                {                    self.insert_excerpts_with_ids_after(                        insert_after,                        buffer.clone(),                        mem::take(&mut to_insert),                        cx,

Lines 1–500 of 580.