parent
1936ba5e30
commit
7f6bb3d1eb
15 changed files with 347 additions and 213 deletions
51
Cargo.lock
generated
51
Cargo.lock
generated
|
@ -310,6 +310,7 @@ dependencies = [
|
||||||
"language",
|
"language",
|
||||||
"log",
|
"log",
|
||||||
"menu",
|
"menu",
|
||||||
|
"multi_buffer",
|
||||||
"ordered-float 2.10.0",
|
"ordered-float 2.10.0",
|
||||||
"parking_lot 0.11.2",
|
"parking_lot 0.11.2",
|
||||||
"project",
|
"project",
|
||||||
|
@ -2410,6 +2411,7 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"lsp",
|
"lsp",
|
||||||
|
"multi_buffer",
|
||||||
"ordered-float 2.10.0",
|
"ordered-float 2.10.0",
|
||||||
"parking_lot 0.11.2",
|
"parking_lot 0.11.2",
|
||||||
"postage",
|
"postage",
|
||||||
|
@ -4600,6 +4602,55 @@ version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389"
|
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]]
|
[[package]]
|
||||||
name = "multimap"
|
name = "multimap"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
|
|
|
@ -46,6 +46,7 @@ members = [
|
||||||
"crates/lsp",
|
"crates/lsp",
|
||||||
"crates/media",
|
"crates/media",
|
||||||
"crates/menu",
|
"crates/menu",
|
||||||
|
"crates/multi_buffer",
|
||||||
"crates/node_runtime",
|
"crates/node_runtime",
|
||||||
"crates/notifications",
|
"crates/notifications",
|
||||||
"crates/outline",
|
"crates/outline",
|
||||||
|
|
|
@ -17,6 +17,7 @@ fs = { path = "../fs" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
menu = { path = "../menu" }
|
menu = { path = "../menu" }
|
||||||
|
multi_buffer = { path = "../multi_buffer" }
|
||||||
search = { path = "../search" }
|
search = { path = "../search" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
|
|
|
@ -296,7 +296,7 @@ impl AssistantPanel {
|
||||||
};
|
};
|
||||||
|
|
||||||
let selection = editor.read(cx).selections.newest_anchor().clone();
|
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;
|
return;
|
||||||
}
|
}
|
||||||
let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx);
|
let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx);
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use crate::streaming_diff::{Hunk, StreamingDiff};
|
use crate::streaming_diff::{Hunk, StreamingDiff};
|
||||||
use ai::completion::{CompletionProvider, OpenAIRequest};
|
use ai::completion::{CompletionProvider, OpenAIRequest};
|
||||||
use anyhow::Result;
|
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 futures::{channel::mpsc, SinkExt, Stream, StreamExt};
|
||||||
use gpui::{Entity, ModelContext, ModelHandle, Task};
|
use gpui::{Entity, ModelContext, ModelHandle, Task};
|
||||||
use language::{Rope, TransactionId};
|
use language::{Rope, TransactionId};
|
||||||
|
use multi_buffer;
|
||||||
use std::{cmp, future, ops::Range, sync::Arc};
|
use std::{cmp, future, ops::Range, sync::Arc};
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
|
|
@ -14,6 +14,7 @@ test-support = [
|
||||||
"text/test-support",
|
"text/test-support",
|
||||||
"language/test-support",
|
"language/test-support",
|
||||||
"gpui/test-support",
|
"gpui/test-support",
|
||||||
|
"multi_buffer/test-support",
|
||||||
"project/test-support",
|
"project/test-support",
|
||||||
"util/test-support",
|
"util/test-support",
|
||||||
"workspace/test-support",
|
"workspace/test-support",
|
||||||
|
@ -34,6 +35,7 @@ git = { path = "../git" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
lsp = { path = "../lsp" }
|
lsp = { path = "../lsp" }
|
||||||
|
multi_buffer = { path = "../multi_buffer" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
rich_text = { path = "../rich_text" }
|
rich_text = { path = "../rich_text" }
|
||||||
|
|
|
@ -993,8 +993,8 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::display_map::inlay_map::InlayMap;
|
use crate::display_map::inlay_map::InlayMap;
|
||||||
use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
|
use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
|
||||||
use crate::multi_buffer::MultiBuffer;
|
|
||||||
use gpui::{elements::Empty, Element};
|
use gpui::{elements::Empty, Element};
|
||||||
|
use multi_buffer::MultiBuffer;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
|
@ -91,7 +91,7 @@ impl<'a> FoldMapWriter<'a> {
|
||||||
|
|
||||||
// For now, ignore any ranges that span an excerpt boundary.
|
// For now, ignore any ranges that span an excerpt boundary.
|
||||||
let fold = Fold(buffer.anchor_after(range.start)..buffer.anchor_before(range.end));
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
use crate::{
|
use crate::{Anchor, InlayId, MultiBufferSnapshot, ToOffset};
|
||||||
multi_buffer::{MultiBufferChunks, MultiBufferRows},
|
|
||||||
Anchor, InlayId, MultiBufferSnapshot, ToOffset,
|
|
||||||
};
|
|
||||||
use collections::{BTreeMap, BTreeSet};
|
use collections::{BTreeMap, BTreeSet};
|
||||||
use gpui::fonts::HighlightStyle;
|
use gpui::fonts::HighlightStyle;
|
||||||
use language::{Chunk, Edit, Point, TextSummary};
|
use language::{Chunk, Edit, Point, TextSummary};
|
||||||
|
use multi_buffer::{MultiBufferChunks, MultiBufferRows};
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
cmp,
|
cmp,
|
||||||
|
|
|
@ -11,7 +11,6 @@ pub mod items;
|
||||||
mod link_go_to_definition;
|
mod link_go_to_definition;
|
||||||
mod mouse_context_menu;
|
mod mouse_context_menu;
|
||||||
pub mod movement;
|
pub mod movement;
|
||||||
pub mod multi_buffer;
|
|
||||||
mod persistence;
|
mod persistence;
|
||||||
pub mod scroll;
|
pub mod scroll;
|
||||||
pub mod selections_collection;
|
pub mod selections_collection;
|
||||||
|
@ -7716,8 +7715,8 @@ impl Editor {
|
||||||
let mut buffer_highlights = this
|
let mut buffer_highlights = this
|
||||||
.document_highlights_for_position(selection.head(), &buffer)
|
.document_highlights_for_position(selection.head(), &buffer)
|
||||||
.filter(|highlight| {
|
.filter(|highlight| {
|
||||||
highlight.start.excerpt_id() == selection.head().excerpt_id()
|
highlight.start.excerpt_id == selection.head().excerpt_id
|
||||||
&& highlight.end.excerpt_id() == selection.head().excerpt_id()
|
&& highlight.end.excerpt_id == selection.head().excerpt_id
|
||||||
});
|
});
|
||||||
buffer_highlights
|
buffer_highlights
|
||||||
.next()
|
.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 anyhow::Result;
|
||||||
|
|
||||||
|
use crate::{Editor, ToPoint};
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use gpui::{json, ViewContext, ViewHandle};
|
use gpui::{json, ViewContext, ViewHandle};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
|
use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
|
||||||
use lsp::{notification, request};
|
use lsp::{notification, request};
|
||||||
|
use multi_buffer::ToPointUtf16;
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
use workspace::{AppState, Workspace, WorkspaceHandle};
|
use workspace::{AppState, Workspace, WorkspaceHandle};
|
||||||
|
|
||||||
use crate::{multi_buffer::ToPointUtf16, Editor, ToPoint};
|
|
||||||
|
|
||||||
use super::editor_test_context::EditorTestContext;
|
use super::editor_test_context::EditorTestContext;
|
||||||
|
|
||||||
pub struct EditorLspTestContext<'a> {
|
pub struct EditorLspTestContext<'a> {
|
||||||
|
|
80
crates/multi_buffer/Cargo.toml
Normal file
80
crates/multi_buffer/Cargo.toml
Normal file
|
@ -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)]
|
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
|
||||||
pub struct Anchor {
|
pub struct Anchor {
|
||||||
pub(crate) buffer_id: Option<u64>,
|
pub buffer_id: Option<u64>,
|
||||||
pub(crate) excerpt_id: ExcerptId,
|
pub excerpt_id: ExcerptId,
|
||||||
pub(crate) text_anchor: text::Anchor,
|
pub text_anchor: text::Anchor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering {
|
||||||
let excerpt_id_cmp = self.excerpt_id.cmp(&other.excerpt_id, snapshot);
|
let excerpt_id_cmp = self.excerpt_id.cmp(&other.excerpt_id, snapshot);
|
||||||
if excerpt_id_cmp.is_eq() {
|
if excerpt_id_cmp.is_eq() {
|
|
@ -303,7 +303,7 @@ impl MultiBuffer {
|
||||||
self.snapshot.borrow().clone()
|
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.sync(cx);
|
||||||
self.snapshot.borrow()
|
self.snapshot.borrow()
|
||||||
}
|
}
|
||||||
|
@ -589,7 +589,7 @@ impl MultiBuffer {
|
||||||
self.start_transaction_at(Instant::now(), cx)
|
self.start_transaction_at(Instant::now(), cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn start_transaction_at(
|
pub fn start_transaction_at(
|
||||||
&mut self,
|
&mut self,
|
||||||
now: Instant,
|
now: Instant,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
|
@ -608,7 +608,7 @@ impl MultiBuffer {
|
||||||
self.end_transaction_at(Instant::now(), cx)
|
self.end_transaction_at(Instant::now(), cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn end_transaction_at(
|
pub fn end_transaction_at(
|
||||||
&mut self,
|
&mut self,
|
||||||
now: Instant,
|
now: Instant,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
|
@ -1508,7 +1508,7 @@ impl MultiBuffer {
|
||||||
"untitled".into()
|
"untitled".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub fn is_parsing(&self, cx: &AppContext) -> bool {
|
pub fn is_parsing(&self, cx: &AppContext) -> bool {
|
||||||
self.as_singleton().unwrap().read(cx).is_parsing()
|
self.as_singleton().unwrap().read(cx).is_parsing()
|
||||||
}
|
}
|
||||||
|
@ -3198,7 +3198,7 @@ impl MultiBufferSnapshot {
|
||||||
theme: Option<&SyntaxTheme>,
|
theme: Option<&SyntaxTheme>,
|
||||||
) -> Option<(u64, Vec<OutlineItem<Anchor>>)> {
|
) -> Option<(u64, Vec<OutlineItem<Anchor>>)> {
|
||||||
let anchor = self.anchor_before(offset);
|
let anchor = self.anchor_before(offset);
|
||||||
let excerpt_id = anchor.excerpt_id();
|
let excerpt_id = anchor.excerpt_id;
|
||||||
let excerpt = self.excerpt(excerpt_id)?;
|
let excerpt = self.excerpt(excerpt_id)?;
|
||||||
Some((
|
Some((
|
||||||
excerpt.buffer_id,
|
excerpt.buffer_id,
|
||||||
|
@ -4129,17 +4129,13 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::editor_tests::init_test;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{AppContext, TestAppContext};
|
use gpui::{AppContext, TestAppContext};
|
||||||
use language::{Buffer, Rope};
|
use language::{Buffer, Rope};
|
||||||
use project::{FakeFs, Project};
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use std::{env, rc::Rc};
|
use std::{env, rc::Rc};
|
||||||
use unindent::Unindent;
|
|
||||||
use util::test::sample_text;
|
use util::test::sample_text;
|
||||||
|
|
||||||
#[gpui::test]
|
#[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)]
|
#[gpui::test(iterations = 100)]
|
||||||
fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) {
|
fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) {
|
||||||
let operations = env::var("OPERATIONS")
|
let operations = env::var("OPERATIONS")
|
Loading…
Add table
Add a link
Reference in a new issue