Detailed changes
@@ -310,6 +310,7 @@ dependencies = [
"language",
"log",
"menu",
+ "multi_buffer",
"ordered-float 2.10.0",
"parking_lot 0.11.2",
"project",
@@ -2410,6 +2411,7 @@ dependencies = [
"lazy_static",
"log",
"lsp",
+ "multi_buffer",
"ordered-float 2.10.0",
"parking_lot 0.11.2",
"postage",
@@ -4600,6 +4602,55 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389"
+[[package]]
+name = "multi_buffer"
+version = "0.1.0"
+dependencies = [
+ "aho-corasick",
+ "anyhow",
+ "client",
+ "clock",
+ "collections",
+ "context_menu",
+ "convert_case 0.6.0",
+ "copilot",
+ "ctor",
+ "env_logger 0.9.3",
+ "futures 0.3.28",
+ "git",
+ "gpui",
+ "indoc",
+ "itertools 0.10.5",
+ "language",
+ "lazy_static",
+ "log",
+ "lsp",
+ "ordered-float 2.10.0",
+ "parking_lot 0.11.2",
+ "postage",
+ "project",
+ "pulldown-cmark",
+ "rand 0.8.5",
+ "rich_text",
+ "schemars",
+ "serde",
+ "serde_derive",
+ "settings",
+ "smallvec",
+ "smol",
+ "snippet",
+ "sum_tree",
+ "text",
+ "theme",
+ "tree-sitter",
+ "tree-sitter-html",
+ "tree-sitter-rust",
+ "tree-sitter-typescript",
+ "unindent",
+ "util",
+ "workspace",
+]
+
[[package]]
name = "multimap"
version = "0.8.3"
@@ -46,6 +46,7 @@ members = [
"crates/lsp",
"crates/media",
"crates/menu",
+ "crates/multi_buffer",
"crates/node_runtime",
"crates/notifications",
"crates/outline",
@@ -17,6 +17,7 @@ fs = { path = "../fs" }
gpui = { path = "../gpui" }
language = { path = "../language" }
menu = { path = "../menu" }
+multi_buffer = { path = "../multi_buffer" }
search = { path = "../search" }
settings = { path = "../settings" }
theme = { path = "../theme" }
@@ -296,7 +296,7 @@ impl AssistantPanel {
};
let selection = editor.read(cx).selections.newest_anchor().clone();
- if selection.start.excerpt_id() != selection.end.excerpt_id() {
+ if selection.start.excerpt_id != selection.end.excerpt_id {
return;
}
let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx);
@@ -1,10 +1,11 @@
use crate::streaming_diff::{Hunk, StreamingDiff};
use ai::completion::{CompletionProvider, OpenAIRequest};
use anyhow::Result;
-use editor::{multi_buffer, Anchor, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
+use editor::{Anchor, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
use futures::{channel::mpsc, SinkExt, Stream, StreamExt};
use gpui::{Entity, ModelContext, ModelHandle, Task};
use language::{Rope, TransactionId};
+use multi_buffer;
use std::{cmp, future, ops::Range, sync::Arc};
pub enum Event {
@@ -14,6 +14,7 @@ test-support = [
"text/test-support",
"language/test-support",
"gpui/test-support",
+ "multi_buffer/test-support",
"project/test-support",
"util/test-support",
"workspace/test-support",
@@ -34,6 +35,7 @@ git = { path = "../git" }
gpui = { path = "../gpui" }
language = { path = "../language" }
lsp = { path = "../lsp" }
+multi_buffer = { path = "../multi_buffer" }
project = { path = "../project" }
rpc = { path = "../rpc" }
rich_text = { path = "../rich_text" }
@@ -993,8 +993,8 @@ mod tests {
use super::*;
use crate::display_map::inlay_map::InlayMap;
use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
- use crate::multi_buffer::MultiBuffer;
use gpui::{elements::Empty, Element};
+ use multi_buffer::MultiBuffer;
use rand::prelude::*;
use settings::SettingsStore;
use std::env;
@@ -91,7 +91,7 @@ impl<'a> FoldMapWriter<'a> {
// For now, ignore any ranges that span an excerpt boundary.
let fold = Fold(buffer.anchor_after(range.start)..buffer.anchor_before(range.end));
- if fold.0.start.excerpt_id() != fold.0.end.excerpt_id() {
+ if fold.0.start.excerpt_id != fold.0.end.excerpt_id {
continue;
}
@@ -1,10 +1,8 @@
-use crate::{
- multi_buffer::{MultiBufferChunks, MultiBufferRows},
- Anchor, InlayId, MultiBufferSnapshot, ToOffset,
-};
+use crate::{Anchor, InlayId, MultiBufferSnapshot, ToOffset};
use collections::{BTreeMap, BTreeSet};
use gpui::fonts::HighlightStyle;
use language::{Chunk, Edit, Point, TextSummary};
+use multi_buffer::{MultiBufferChunks, MultiBufferRows};
use std::{
any::TypeId,
cmp,
@@ -11,7 +11,6 @@ pub mod items;
mod link_go_to_definition;
mod mouse_context_menu;
pub mod movement;
-pub mod multi_buffer;
mod persistence;
pub mod scroll;
pub mod selections_collection;
@@ -7716,8 +7715,8 @@ impl Editor {
let mut buffer_highlights = this
.document_highlights_for_position(selection.head(), &buffer)
.filter(|highlight| {
- highlight.start.excerpt_id() == selection.head().excerpt_id()
- && highlight.end.excerpt_id() == selection.head().excerpt_id()
+ highlight.start.excerpt_id == selection.head().excerpt_id
+ && highlight.end.excerpt_id == selection.head().excerpt_id
});
buffer_highlights
.next()
@@ -87,3 +87,196 @@ pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) ->
}
}
}
+
+#[cfg(any(test, feature = "test_support"))]
+mod tests {
+ use crate::editor_tests::init_test;
+ use crate::Point;
+ use gpui::TestAppContext;
+ use multi_buffer::{ExcerptRange, MultiBuffer};
+ use project::{FakeFs, Project};
+ use unindent::Unindent;
+ #[gpui::test]
+ async fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
+ use git::diff::DiffHunkStatus;
+ init_test(cx, |_| {});
+
+ let fs = FakeFs::new(cx.background());
+ let project = Project::test(fs, [], cx).await;
+
+ // buffer has two modified hunks with two rows each
+ let buffer_1 = project
+ .update(cx, |project, cx| {
+ project.create_buffer(
+ "
+ 1.zero
+ 1.ONE
+ 1.TWO
+ 1.three
+ 1.FOUR
+ 1.FIVE
+ 1.six
+ "
+ .unindent()
+ .as_str(),
+ None,
+ cx,
+ )
+ })
+ .unwrap();
+ buffer_1.update(cx, |buffer, cx| {
+ buffer.set_diff_base(
+ Some(
+ "
+ 1.zero
+ 1.one
+ 1.two
+ 1.three
+ 1.four
+ 1.five
+ 1.six
+ "
+ .unindent(),
+ ),
+ cx,
+ );
+ });
+
+ // buffer has a deletion hunk and an insertion hunk
+ let buffer_2 = project
+ .update(cx, |project, cx| {
+ project.create_buffer(
+ "
+ 2.zero
+ 2.one
+ 2.two
+ 2.three
+ 2.four
+ 2.five
+ 2.six
+ "
+ .unindent()
+ .as_str(),
+ None,
+ cx,
+ )
+ })
+ .unwrap();
+ buffer_2.update(cx, |buffer, cx| {
+ buffer.set_diff_base(
+ Some(
+ "
+ 2.zero
+ 2.one
+ 2.one-and-a-half
+ 2.two
+ 2.three
+ 2.four
+ 2.six
+ "
+ .unindent(),
+ ),
+ cx,
+ );
+ });
+
+ cx.foreground().run_until_parked();
+
+ let multibuffer = cx.add_model(|cx| {
+ let mut multibuffer = MultiBuffer::new(0);
+ multibuffer.push_excerpts(
+ buffer_1.clone(),
+ [
+ // excerpt ends in the middle of a modified hunk
+ ExcerptRange {
+ context: Point::new(0, 0)..Point::new(1, 5),
+ primary: Default::default(),
+ },
+ // excerpt begins in the middle of a modified hunk
+ ExcerptRange {
+ context: Point::new(5, 0)..Point::new(6, 5),
+ primary: Default::default(),
+ },
+ ],
+ cx,
+ );
+ multibuffer.push_excerpts(
+ buffer_2.clone(),
+ [
+ // excerpt ends at a deletion
+ ExcerptRange {
+ context: Point::new(0, 0)..Point::new(1, 5),
+ primary: Default::default(),
+ },
+ // excerpt starts at a deletion
+ ExcerptRange {
+ context: Point::new(2, 0)..Point::new(2, 5),
+ primary: Default::default(),
+ },
+ // excerpt fully contains a deletion hunk
+ ExcerptRange {
+ context: Point::new(1, 0)..Point::new(2, 5),
+ primary: Default::default(),
+ },
+ // excerpt fully contains an insertion hunk
+ ExcerptRange {
+ context: Point::new(4, 0)..Point::new(6, 5),
+ primary: Default::default(),
+ },
+ ],
+ cx,
+ );
+ multibuffer
+ });
+
+ let snapshot = multibuffer.read_with(cx, |b, cx| b.snapshot(cx));
+
+ assert_eq!(
+ snapshot.text(),
+ "
+ 1.zero
+ 1.ONE
+ 1.FIVE
+ 1.six
+ 2.zero
+ 2.one
+ 2.two
+ 2.one
+ 2.two
+ 2.four
+ 2.five
+ 2.six"
+ .unindent()
+ );
+
+ let expected = [
+ (DiffHunkStatus::Modified, 1..2),
+ (DiffHunkStatus::Modified, 2..3),
+ //TODO: Define better when and where removed hunks show up at range extremities
+ (DiffHunkStatus::Removed, 6..6),
+ (DiffHunkStatus::Removed, 8..8),
+ (DiffHunkStatus::Added, 10..11),
+ ];
+
+ assert_eq!(
+ snapshot
+ .git_diff_hunks_in_range(0..12)
+ .map(|hunk| (hunk.status(), hunk.buffer_range))
+ .collect::<Vec<_>>(),
+ &expected,
+ );
+
+ assert_eq!(
+ snapshot
+ .git_diff_hunks_in_range_rev(0..12)
+ .map(|hunk| (hunk.status(), hunk.buffer_range))
+ .collect::<Vec<_>>(),
+ expected
+ .iter()
+ .rev()
+ .cloned()
+ .collect::<Vec<_>>()
+ .as_slice(),
+ );
+ }
+}
@@ -6,18 +6,18 @@ use std::{
use anyhow::Result;
+use crate::{Editor, ToPoint};
use collections::HashSet;
use futures::Future;
use gpui::{json, ViewContext, ViewHandle};
use indoc::indoc;
use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
use lsp::{notification, request};
+use multi_buffer::ToPointUtf16;
use project::Project;
use smol::stream::StreamExt;
use workspace::{AppState, Workspace, WorkspaceHandle};
-use crate::{multi_buffer::ToPointUtf16, Editor, ToPoint};
-
use super::editor_test_context::EditorTestContext;
pub struct EditorLspTestContext<'a> {
@@ -0,0 +1,80 @@
+[package]
+name = "multi_buffer"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/multi_buffer.rs"
+doctest = false
+
+[features]
+test-support = [
+ "copilot/test-support",
+ "text/test-support",
+ "language/test-support",
+ "gpui/test-support",
+ "util/test-support",
+ "tree-sitter-rust",
+ "tree-sitter-typescript"
+]
+
+[dependencies]
+client = { path = "../client" }
+clock = { path = "../clock" }
+collections = { path = "../collections" }
+context_menu = { path = "../context_menu" }
+git = { path = "../git" }
+gpui = { path = "../gpui" }
+language = { path = "../language" }
+lsp = { path = "../lsp" }
+rich_text = { path = "../rich_text" }
+settings = { path = "../settings" }
+snippet = { path = "../snippet" }
+sum_tree = { path = "../sum_tree" }
+text = { path = "../text" }
+theme = { path = "../theme" }
+util = { path = "../util" }
+
+aho-corasick = "1.1"
+anyhow.workspace = true
+convert_case = "0.6.0"
+futures.workspace = true
+indoc = "1.0.4"
+itertools = "0.10"
+lazy_static.workspace = true
+log.workspace = true
+ordered-float.workspace = true
+parking_lot.workspace = true
+postage.workspace = true
+pulldown-cmark = { version = "0.9.2", default-features = false }
+rand.workspace = true
+schemars.workspace = true
+serde.workspace = true
+serde_derive.workspace = true
+smallvec.workspace = true
+smol.workspace = true
+
+tree-sitter-rust = { workspace = true, optional = true }
+tree-sitter-html = { workspace = true, optional = true }
+tree-sitter-typescript = { workspace = true, optional = true }
+
+[dev-dependencies]
+copilot = { path = "../copilot", features = ["test-support"] }
+text = { path = "../text", features = ["test-support"] }
+language = { path = "../language", features = ["test-support"] }
+lsp = { path = "../lsp", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
+util = { path = "../util", features = ["test-support"] }
+project = { path = "../project", features = ["test-support"] }
+settings = { path = "../settings", features = ["test-support"] }
+workspace = { path = "../workspace", features = ["test-support"] }
+
+ctor.workspace = true
+env_logger.workspace = true
+rand.workspace = true
+unindent.workspace = true
+tree-sitter.workspace = true
+tree-sitter-rust.workspace = true
+tree-sitter-html.workspace = true
+tree-sitter-typescript.workspace = true
@@ -8,9 +8,9 @@ use sum_tree::Bias;
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
pub struct Anchor {
- pub(crate) buffer_id: Option<u64>,
- pub(crate) excerpt_id: ExcerptId,
- pub(crate) text_anchor: text::Anchor,
+ pub buffer_id: Option<u64>,
+ pub excerpt_id: ExcerptId,
+ pub text_anchor: text::Anchor,
}
impl Anchor {
@@ -30,10 +30,6 @@ impl Anchor {
}
}
- pub fn excerpt_id(&self) -> ExcerptId {
- self.excerpt_id
- }
-
pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering {
let excerpt_id_cmp = self.excerpt_id.cmp(&other.excerpt_id, snapshot);
if excerpt_id_cmp.is_eq() {
@@ -303,7 +303,7 @@ impl MultiBuffer {
self.snapshot.borrow().clone()
}
- pub(crate) fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> {
+ pub fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> {
self.sync(cx);
self.snapshot.borrow()
}
@@ -589,7 +589,7 @@ impl MultiBuffer {
self.start_transaction_at(Instant::now(), cx)
}
- pub(crate) fn start_transaction_at(
+ pub fn start_transaction_at(
&mut self,
now: Instant,
cx: &mut ModelContext<Self>,
@@ -608,7 +608,7 @@ impl MultiBuffer {
self.end_transaction_at(Instant::now(), cx)
}
- pub(crate) fn end_transaction_at(
+ pub fn end_transaction_at(
&mut self,
now: Instant,
cx: &mut ModelContext<Self>,
@@ -1508,7 +1508,7 @@ impl MultiBuffer {
"untitled".into()
}
- #[cfg(test)]
+ #[cfg(any(test, feature = "test-support"))]
pub fn is_parsing(&self, cx: &AppContext) -> bool {
self.as_singleton().unwrap().read(cx).is_parsing()
}
@@ -3198,7 +3198,7 @@ impl MultiBufferSnapshot {
theme: Option<&SyntaxTheme>,
) -> Option<(u64, Vec<OutlineItem<Anchor>>)> {
let anchor = self.anchor_before(offset);
- let excerpt_id = anchor.excerpt_id();
+ let excerpt_id = anchor.excerpt_id;
let excerpt = self.excerpt(excerpt_id)?;
Some((
excerpt.buffer_id,
@@ -4129,17 +4129,13 @@ where
#[cfg(test)]
mod tests {
- use crate::editor_tests::init_test;
-
use super::*;
use futures::StreamExt;
use gpui::{AppContext, TestAppContext};
use language::{Buffer, Rope};
- use project::{FakeFs, Project};
use rand::prelude::*;
use settings::SettingsStore;
use std::{env, rc::Rc};
- use unindent::Unindent;
use util::test::sample_text;
#[gpui::test]
@@ -4838,190 +4834,6 @@ mod tests {
);
}
- #[gpui::test]
- async fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
- use git::diff::DiffHunkStatus;
- init_test(cx, |_| {});
-
- let fs = FakeFs::new(cx.background());
- let project = Project::test(fs, [], cx).await;
-
- // buffer has two modified hunks with two rows each
- let buffer_1 = project
- .update(cx, |project, cx| {
- project.create_buffer(
- "
- 1.zero
- 1.ONE
- 1.TWO
- 1.three
- 1.FOUR
- 1.FIVE
- 1.six
- "
- .unindent()
- .as_str(),
- None,
- cx,
- )
- })
- .unwrap();
- buffer_1.update(cx, |buffer, cx| {
- buffer.set_diff_base(
- Some(
- "
- 1.zero
- 1.one
- 1.two
- 1.three
- 1.four
- 1.five
- 1.six
- "
- .unindent(),
- ),
- cx,
- );
- });
-
- // buffer has a deletion hunk and an insertion hunk
- let buffer_2 = project
- .update(cx, |project, cx| {
- project.create_buffer(
- "
- 2.zero
- 2.one
- 2.two
- 2.three
- 2.four
- 2.five
- 2.six
- "
- .unindent()
- .as_str(),
- None,
- cx,
- )
- })
- .unwrap();
- buffer_2.update(cx, |buffer, cx| {
- buffer.set_diff_base(
- Some(
- "
- 2.zero
- 2.one
- 2.one-and-a-half
- 2.two
- 2.three
- 2.four
- 2.six
- "
- .unindent(),
- ),
- cx,
- );
- });
-
- cx.foreground().run_until_parked();
-
- let multibuffer = cx.add_model(|cx| {
- let mut multibuffer = MultiBuffer::new(0);
- multibuffer.push_excerpts(
- buffer_1.clone(),
- [
- // excerpt ends in the middle of a modified hunk
- ExcerptRange {
- context: Point::new(0, 0)..Point::new(1, 5),
- primary: Default::default(),
- },
- // excerpt begins in the middle of a modified hunk
- ExcerptRange {
- context: Point::new(5, 0)..Point::new(6, 5),
- primary: Default::default(),
- },
- ],
- cx,
- );
- multibuffer.push_excerpts(
- buffer_2.clone(),
- [
- // excerpt ends at a deletion
- ExcerptRange {
- context: Point::new(0, 0)..Point::new(1, 5),
- primary: Default::default(),
- },
- // excerpt starts at a deletion
- ExcerptRange {
- context: Point::new(2, 0)..Point::new(2, 5),
- primary: Default::default(),
- },
- // excerpt fully contains a deletion hunk
- ExcerptRange {
- context: Point::new(1, 0)..Point::new(2, 5),
- primary: Default::default(),
- },
- // excerpt fully contains an insertion hunk
- ExcerptRange {
- context: Point::new(4, 0)..Point::new(6, 5),
- primary: Default::default(),
- },
- ],
- cx,
- );
- multibuffer
- });
-
- let snapshot = multibuffer.read_with(cx, |b, cx| b.snapshot(cx));
-
- assert_eq!(
- snapshot.text(),
- "
- 1.zero
- 1.ONE
- 1.FIVE
- 1.six
- 2.zero
- 2.one
- 2.two
- 2.one
- 2.two
- 2.four
- 2.five
- 2.six"
- .unindent()
- );
-
- let expected = [
- (DiffHunkStatus::Modified, 1..2),
- (DiffHunkStatus::Modified, 2..3),
- //TODO: Define better when and where removed hunks show up at range extremities
- (DiffHunkStatus::Removed, 6..6),
- (DiffHunkStatus::Removed, 8..8),
- (DiffHunkStatus::Added, 10..11),
- ];
-
- assert_eq!(
- snapshot
- .git_diff_hunks_in_range(0..12)
- .map(|hunk| (hunk.status(), hunk.buffer_range))
- .collect::<Vec<_>>(),
- &expected,
- );
-
- assert_eq!(
- snapshot
- .git_diff_hunks_in_range_rev(0..12)
- .map(|hunk| (hunk.status(), hunk.buffer_range))
- .collect::<Vec<_>>(),
- expected
- .iter()
- .rev()
- .cloned()
- .collect::<Vec<_>>()
- .as_slice(),
- );
- }
-
#[gpui::test(iterations = 100)]
fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) {
let operations = env::var("OPERATIONS")