Add staged status information to diff hunks (#24475)
Release Notes: - Render unstaged hunks in the project diff editor with a slashed background --------- Co-authored-by: maxbrunsfeld <max@zed.dev> Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
parent
a9de9e3cb4
commit
8f75fe25e5
28 changed files with 1132 additions and 753 deletions
48
Cargo.lock
generated
48
Cargo.lock
generated
|
@ -2024,6 +2024,24 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "buffer_diff"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"futures 0.3.31",
|
||||||
|
"git2",
|
||||||
|
"gpui",
|
||||||
|
"language",
|
||||||
|
"pretty_assertions",
|
||||||
|
"rope",
|
||||||
|
"serde_json",
|
||||||
|
"sum_tree",
|
||||||
|
"text",
|
||||||
|
"unindent",
|
||||||
|
"util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "built"
|
name = "built"
|
||||||
version = "0.7.5"
|
version = "0.7.5"
|
||||||
|
@ -2742,6 +2760,7 @@ dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"axum-extra",
|
"axum-extra",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
|
"buffer_diff",
|
||||||
"call",
|
"call",
|
||||||
"channel",
|
"channel",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
@ -2753,7 +2772,6 @@ dependencies = [
|
||||||
"ctor",
|
"ctor",
|
||||||
"dashmap 6.1.0",
|
"dashmap 6.1.0",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"diff 0.1.0",
|
|
||||||
"editor",
|
"editor",
|
||||||
"env_logger 0.11.6",
|
"env_logger 0.11.6",
|
||||||
"envy",
|
"envy",
|
||||||
|
@ -3860,24 +3878,6 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "diff"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"futures 0.3.31",
|
|
||||||
"git2",
|
|
||||||
"gpui",
|
|
||||||
"language",
|
|
||||||
"log",
|
|
||||||
"pretty_assertions",
|
|
||||||
"rope",
|
|
||||||
"serde_json",
|
|
||||||
"sum_tree",
|
|
||||||
"text",
|
|
||||||
"unindent",
|
|
||||||
"util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diff"
|
name = "diff"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
|
@ -4041,6 +4041,7 @@ dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assets",
|
"assets",
|
||||||
|
"buffer_diff",
|
||||||
"chrono",
|
"chrono",
|
||||||
"client",
|
"client",
|
||||||
"clock",
|
"clock",
|
||||||
|
@ -4048,7 +4049,6 @@ dependencies = [
|
||||||
"convert_case 0.7.1",
|
"convert_case 0.7.1",
|
||||||
"ctor",
|
"ctor",
|
||||||
"db",
|
"db",
|
||||||
"diff 0.1.0",
|
|
||||||
"emojis",
|
"emojis",
|
||||||
"env_logger 0.11.6",
|
"env_logger 0.11.6",
|
||||||
"file_icons",
|
"file_icons",
|
||||||
|
@ -5347,9 +5347,9 @@ name = "git_ui"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"buffer_diff",
|
||||||
"collections",
|
"collections",
|
||||||
"db",
|
"db",
|
||||||
"diff 0.1.0",
|
|
||||||
"editor",
|
"editor",
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
|
@ -7980,10 +7980,10 @@ name = "multi_buffer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"buffer_diff",
|
||||||
"clock",
|
"clock",
|
||||||
"collections",
|
"collections",
|
||||||
"ctor",
|
"ctor",
|
||||||
"diff 0.1.0",
|
|
||||||
"env_logger 0.11.6",
|
"env_logger 0.11.6",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
@ -9995,7 +9995,7 @@ version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
|
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"diff 0.1.13",
|
"diff",
|
||||||
"yansi",
|
"yansi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -10088,10 +10088,10 @@ dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"buffer_diff",
|
||||||
"client",
|
"client",
|
||||||
"clock",
|
"clock",
|
||||||
"collections",
|
"collections",
|
||||||
"diff 0.1.0",
|
|
||||||
"env_logger 0.11.6",
|
"env_logger 0.11.6",
|
||||||
"fancy-regex 0.14.0",
|
"fancy-regex 0.14.0",
|
||||||
"fs",
|
"fs",
|
||||||
|
|
|
@ -34,7 +34,7 @@ members = [
|
||||||
"crates/db",
|
"crates/db",
|
||||||
"crates/deepseek",
|
"crates/deepseek",
|
||||||
"crates/diagnostics",
|
"crates/diagnostics",
|
||||||
"crates/diff",
|
"crates/buffer_diff",
|
||||||
"crates/docs_preprocessor",
|
"crates/docs_preprocessor",
|
||||||
"crates/editor",
|
"crates/editor",
|
||||||
"crates/evals",
|
"crates/evals",
|
||||||
|
@ -235,7 +235,7 @@ copilot = { path = "crates/copilot" }
|
||||||
db = { path = "crates/db" }
|
db = { path = "crates/db" }
|
||||||
deepseek = { path = "crates/deepseek" }
|
deepseek = { path = "crates/deepseek" }
|
||||||
diagnostics = { path = "crates/diagnostics" }
|
diagnostics = { path = "crates/diagnostics" }
|
||||||
diff = { path = "crates/diff" }
|
buffer_diff = { path = "crates/buffer_diff" }
|
||||||
editor = { path = "crates/editor" }
|
editor = { path = "crates/editor" }
|
||||||
extension = { path = "crates/extension" }
|
extension = { path = "crates/extension" }
|
||||||
extension_host = { path = "crates/extension_host" }
|
extension_host = { path = "crates/extension_host" }
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "diff"
|
name = "buffer_diff"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
publish.workspace = true
|
publish.workspace = true
|
||||||
|
@ -9,17 +9,17 @@ license = "GPL-3.0-or-later"
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
path = "src/diff.rs"
|
path = "src/buffer_diff.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
test-support = []
|
test-support = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
git2.workspace = true
|
git2.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
language.workspace = true
|
language.workspace = true
|
||||||
log.workspace = true
|
|
||||||
rope.workspace = true
|
rope.workspace = true
|
||||||
sum_tree.workspace = true
|
sum_tree.workspace = true
|
||||||
text.workspace = true
|
text.workspace = true
|
||||||
|
@ -29,4 +29,5 @@ util.workspace = true
|
||||||
pretty_assertions.workspace = true
|
pretty_assertions.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
text = { workspace = true, features = ["test-support"] }
|
text = { workspace = true, features = ["test-support"] }
|
||||||
|
gpui = { workspace = true, features = ["test-support"] }
|
||||||
unindent.workspace = true
|
unindent.workspace = true
|
File diff suppressed because it is too large
Load diff
|
@ -33,7 +33,7 @@ clock.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
dashmap.workspace = true
|
dashmap.workspace = true
|
||||||
derive_more.workspace = true
|
derive_more.workspace = true
|
||||||
diff.workspace = true
|
buffer_diff.workspace = true
|
||||||
envy = "0.4.2"
|
envy = "0.4.2"
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
google_ai.workspace = true
|
google_ai.workspace = true
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use assistant_context_editor::ContextStore;
|
use assistant_context_editor::ContextStore;
|
||||||
use assistant_slash_command::SlashCommandWorkingSet;
|
use assistant_slash_command::SlashCommandWorkingSet;
|
||||||
|
use buffer_diff::{assert_hunks, DiffHunkSecondaryStatus, DiffHunkStatus};
|
||||||
use call::{room, ActiveCall, ParticipantLocation, Room};
|
use call::{room, ActiveCall, ParticipantLocation, Room};
|
||||||
use client::{User, RECEIVE_TIMEOUT};
|
use client::{User, RECEIVE_TIMEOUT};
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
|
@ -2613,11 +2614,11 @@ async fn test_git_diff_base_change(
|
||||||
diff.base_text_string().as_deref(),
|
diff.base_text_string().as_deref(),
|
||||||
Some(staged_text.as_str())
|
Some(staged_text.as_str())
|
||||||
);
|
);
|
||||||
diff::assert_hunks(
|
assert_hunks(
|
||||||
diff.snapshot.hunks_in_row_range(0..4, buffer),
|
diff.hunks_in_row_range(0..4, buffer, cx),
|
||||||
buffer,
|
buffer,
|
||||||
&diff.base_text_string().unwrap(),
|
&diff.base_text_string().unwrap(),
|
||||||
&[(1..2, "", "two\n")],
|
&[(1..2, "", "two\n", DiffHunkStatus::added())],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2641,11 +2642,11 @@ async fn test_git_diff_base_change(
|
||||||
diff.base_text_string().as_deref(),
|
diff.base_text_string().as_deref(),
|
||||||
Some(staged_text.as_str())
|
Some(staged_text.as_str())
|
||||||
);
|
);
|
||||||
diff::assert_hunks(
|
assert_hunks(
|
||||||
diff.snapshot.hunks_in_row_range(0..4, buffer),
|
diff.hunks_in_row_range(0..4, buffer, cx),
|
||||||
buffer,
|
buffer,
|
||||||
&diff.base_text_string().unwrap(),
|
&diff.base_text_string().unwrap(),
|
||||||
&[(1..2, "", "two\n")],
|
&[(1..2, "", "two\n", DiffHunkStatus::added())],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2663,11 +2664,16 @@ async fn test_git_diff_base_change(
|
||||||
diff.base_text_string().as_deref(),
|
diff.base_text_string().as_deref(),
|
||||||
Some(committed_text.as_str())
|
Some(committed_text.as_str())
|
||||||
);
|
);
|
||||||
diff::assert_hunks(
|
assert_hunks(
|
||||||
diff.snapshot.hunks_in_row_range(0..4, buffer),
|
diff.hunks_in_row_range(0..4, buffer, cx),
|
||||||
buffer,
|
buffer,
|
||||||
&diff.base_text_string().unwrap(),
|
&diff.base_text_string().unwrap(),
|
||||||
&[(1..2, "TWO\n", "two\n")],
|
&[(
|
||||||
|
1..2,
|
||||||
|
"TWO\n",
|
||||||
|
"two\n",
|
||||||
|
DiffHunkStatus::Modified(DiffHunkSecondaryStatus::HasSecondaryHunk),
|
||||||
|
)],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2689,11 +2695,11 @@ async fn test_git_diff_base_change(
|
||||||
diff.base_text_string().as_deref(),
|
diff.base_text_string().as_deref(),
|
||||||
Some(new_staged_text.as_str())
|
Some(new_staged_text.as_str())
|
||||||
);
|
);
|
||||||
diff::assert_hunks(
|
assert_hunks(
|
||||||
diff.snapshot.hunks_in_row_range(0..4, buffer),
|
diff.hunks_in_row_range(0..4, buffer, cx),
|
||||||
buffer,
|
buffer,
|
||||||
&diff.base_text_string().unwrap(),
|
&diff.base_text_string().unwrap(),
|
||||||
&[(2..3, "", "three\n")],
|
&[(2..3, "", "three\n", DiffHunkStatus::added())],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2703,11 +2709,11 @@ async fn test_git_diff_base_change(
|
||||||
diff.base_text_string().as_deref(),
|
diff.base_text_string().as_deref(),
|
||||||
Some(new_staged_text.as_str())
|
Some(new_staged_text.as_str())
|
||||||
);
|
);
|
||||||
diff::assert_hunks(
|
assert_hunks(
|
||||||
diff.snapshot.hunks_in_row_range(0..4, buffer),
|
diff.hunks_in_row_range(0..4, buffer, cx),
|
||||||
buffer,
|
buffer,
|
||||||
&diff.base_text_string().unwrap(),
|
&diff.base_text_string().unwrap(),
|
||||||
&[(2..3, "", "three\n")],
|
&[(2..3, "", "three\n", DiffHunkStatus::added())],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2717,11 +2723,16 @@ async fn test_git_diff_base_change(
|
||||||
diff.base_text_string().as_deref(),
|
diff.base_text_string().as_deref(),
|
||||||
Some(new_committed_text.as_str())
|
Some(new_committed_text.as_str())
|
||||||
);
|
);
|
||||||
diff::assert_hunks(
|
assert_hunks(
|
||||||
diff.snapshot.hunks_in_row_range(0..4, buffer),
|
diff.hunks_in_row_range(0..4, buffer, cx),
|
||||||
buffer,
|
buffer,
|
||||||
&diff.base_text_string().unwrap(),
|
&diff.base_text_string().unwrap(),
|
||||||
&[(1..2, "TWO_HUNDRED\n", "two\n")],
|
&[(
|
||||||
|
1..2,
|
||||||
|
"TWO_HUNDRED\n",
|
||||||
|
"two\n",
|
||||||
|
DiffHunkStatus::Modified(DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk),
|
||||||
|
)],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2763,11 +2774,11 @@ async fn test_git_diff_base_change(
|
||||||
diff.base_text_string().as_deref(),
|
diff.base_text_string().as_deref(),
|
||||||
Some(staged_text.as_str())
|
Some(staged_text.as_str())
|
||||||
);
|
);
|
||||||
diff::assert_hunks(
|
assert_hunks(
|
||||||
diff.snapshot.hunks_in_row_range(0..4, buffer),
|
diff.hunks_in_row_range(0..4, buffer, cx),
|
||||||
buffer,
|
buffer,
|
||||||
&diff.base_text_string().unwrap(),
|
&diff.base_text_string().unwrap(),
|
||||||
&[(1..2, "", "two\n")],
|
&[(1..2, "", "two\n", DiffHunkStatus::added())],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2790,11 +2801,11 @@ async fn test_git_diff_base_change(
|
||||||
diff.base_text_string().as_deref(),
|
diff.base_text_string().as_deref(),
|
||||||
Some(staged_text.as_str())
|
Some(staged_text.as_str())
|
||||||
);
|
);
|
||||||
diff::assert_hunks(
|
assert_hunks(
|
||||||
diff.snapshot.hunks_in_row_range(0..4, buffer),
|
diff.hunks_in_row_range(0..4, buffer, cx),
|
||||||
buffer,
|
buffer,
|
||||||
&staged_text,
|
&staged_text,
|
||||||
&[(1..2, "", "two\n")],
|
&[(1..2, "", "two\n", DiffHunkStatus::added())],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2812,11 +2823,11 @@ async fn test_git_diff_base_change(
|
||||||
diff.base_text_string().as_deref(),
|
diff.base_text_string().as_deref(),
|
||||||
Some(new_staged_text.as_str())
|
Some(new_staged_text.as_str())
|
||||||
);
|
);
|
||||||
diff::assert_hunks(
|
assert_hunks(
|
||||||
diff.snapshot.hunks_in_row_range(0..4, buffer),
|
diff.hunks_in_row_range(0..4, buffer, cx),
|
||||||
buffer,
|
buffer,
|
||||||
&new_staged_text,
|
&new_staged_text,
|
||||||
&[(2..3, "", "three\n")],
|
&[(2..3, "", "three\n", DiffHunkStatus::added())],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2826,11 +2837,11 @@ async fn test_git_diff_base_change(
|
||||||
diff.base_text_string().as_deref(),
|
diff.base_text_string().as_deref(),
|
||||||
Some(new_staged_text.as_str())
|
Some(new_staged_text.as_str())
|
||||||
);
|
);
|
||||||
diff::assert_hunks(
|
assert_hunks(
|
||||||
diff.snapshot.hunks_in_row_range(0..4, buffer),
|
diff.hunks_in_row_range(0..4, buffer, cx),
|
||||||
buffer,
|
buffer,
|
||||||
&new_staged_text,
|
&new_staged_text,
|
||||||
&[(2..3, "", "three\n")],
|
&[(2..3, "", "three\n", DiffHunkStatus::added())],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ clock.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
convert_case.workspace = true
|
convert_case.workspace = true
|
||||||
db.workspace = true
|
db.workspace = true
|
||||||
diff.workspace = true
|
buffer_diff.workspace = true
|
||||||
emojis.workspace = true
|
emojis.workspace = true
|
||||||
file_icons.workspace = true
|
file_icons.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
|
|
|
@ -73,17 +73,16 @@ use code_context_menus::{
|
||||||
AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
|
AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
|
||||||
CompletionsMenu, ContextMenuOrigin,
|
CompletionsMenu, ContextMenuOrigin,
|
||||||
};
|
};
|
||||||
use diff::DiffHunkStatus;
|
|
||||||
use git::blame::GitBlame;
|
use git::blame::GitBlame;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, impl_actions, linear_color_stop, linear_gradient, point, prelude::*, pulsating_between,
|
div, impl_actions, linear_color_stop, linear_gradient, point, prelude::*, pulsating_between,
|
||||||
px, relative, size, Action, Animation, AnimationExt, AnyElement, App, AsyncWindowContext,
|
px, relative, size, Action, Animation, AnimationExt, AnyElement, App, AsyncWindowContext,
|
||||||
AvailableSpace, Bounds, ClipboardEntry, ClipboardItem, Context, DispatchPhase, ElementId,
|
AvailableSpace, Background, Bounds, ClipboardEntry, ClipboardItem, Context, DispatchPhase,
|
||||||
Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId,
|
ElementId, Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent, Focusable,
|
||||||
FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext, Modifiers, MouseButton,
|
FontId, FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext, Modifiers,
|
||||||
MouseDownEvent, PaintQuad, ParentElement, Pixels, Render, SharedString, Size, Styled,
|
MouseButton, MouseDownEvent, PaintQuad, ParentElement, Pixels, Render, SharedString, Size,
|
||||||
StyledText, Subscription, Task, TextRun, TextStyle, TextStyleRefinement, UTF16Selection,
|
Styled, StyledText, Subscription, Task, TextRun, TextStyle, TextStyleRefinement,
|
||||||
UnderlineStyle, UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window,
|
UTF16Selection, UnderlineStyle, UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window,
|
||||||
};
|
};
|
||||||
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
||||||
use hover_popover::{hide_hover, HoverState};
|
use hover_popover::{hide_hover, HoverState};
|
||||||
|
@ -722,6 +721,7 @@ pub struct Editor {
|
||||||
show_git_blame_gutter: bool,
|
show_git_blame_gutter: bool,
|
||||||
show_git_blame_inline: bool,
|
show_git_blame_inline: bool,
|
||||||
show_git_blame_inline_delay_task: Option<Task<()>>,
|
show_git_blame_inline_delay_task: Option<Task<()>>,
|
||||||
|
distinguish_unstaged_diff_hunks: bool,
|
||||||
git_blame_inline_enabled: bool,
|
git_blame_inline_enabled: bool,
|
||||||
serialize_dirty_buffers: bool,
|
serialize_dirty_buffers: bool,
|
||||||
show_selection_menu: Option<bool>,
|
show_selection_menu: Option<bool>,
|
||||||
|
@ -1418,6 +1418,7 @@ impl Editor {
|
||||||
custom_context_menu: None,
|
custom_context_menu: None,
|
||||||
show_git_blame_gutter: false,
|
show_git_blame_gutter: false,
|
||||||
show_git_blame_inline: false,
|
show_git_blame_inline: false,
|
||||||
|
distinguish_unstaged_diff_hunks: false,
|
||||||
show_selection_menu: None,
|
show_selection_menu: None,
|
||||||
show_git_blame_inline_delay_task: None,
|
show_git_blame_inline_delay_task: None,
|
||||||
git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
|
git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
|
||||||
|
@ -6878,8 +6879,7 @@ impl Editor {
|
||||||
let buffer = buffer.read(cx);
|
let buffer = buffer.read(cx);
|
||||||
let original_text = diff
|
let original_text = diff
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.snapshot
|
.base_text()
|
||||||
.base_text
|
|
||||||
.as_ref()?
|
.as_ref()?
|
||||||
.as_rope()
|
.as_rope()
|
||||||
.slice(hunk.diff_base_byte_range.clone());
|
.slice(hunk.diff_base_byte_range.clone());
|
||||||
|
@ -12290,6 +12290,10 @@ impl Editor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_distinguish_unstaged_diff_hunks(&mut self) {
|
||||||
|
self.distinguish_unstaged_diff_hunks = true;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expand_all_diff_hunks(
|
pub fn expand_all_diff_hunks(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: &ExpandAllHunkDiffs,
|
_: &ExpandAllHunkDiffs,
|
||||||
|
@ -13332,14 +13336,14 @@ impl Editor {
|
||||||
&self,
|
&self,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> BTreeMap<DisplayRow, Hsla> {
|
) -> BTreeMap<DisplayRow, Background> {
|
||||||
let snapshot = self.snapshot(window, cx);
|
let snapshot = self.snapshot(window, cx);
|
||||||
let mut used_highlight_orders = HashMap::default();
|
let mut used_highlight_orders = HashMap::default();
|
||||||
self.highlighted_rows
|
self.highlighted_rows
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
|
.flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
|
||||||
.fold(
|
.fold(
|
||||||
BTreeMap::<DisplayRow, Hsla>::new(),
|
BTreeMap::<DisplayRow, Background>::new(),
|
||||||
|mut unique_rows, highlight| {
|
|mut unique_rows, highlight| {
|
||||||
let start = highlight.range.start.to_display_point(&snapshot);
|
let start = highlight.range.start.to_display_point(&snapshot);
|
||||||
let end = highlight.range.end.to_display_point(&snapshot);
|
let end = highlight.range.end.to_display_point(&snapshot);
|
||||||
|
@ -13356,7 +13360,7 @@ impl Editor {
|
||||||
used_highlight_orders.entry(row).or_insert(highlight.index);
|
used_highlight_orders.entry(row).or_insert(highlight.index);
|
||||||
if highlight.index >= *used_index {
|
if highlight.index >= *used_index {
|
||||||
*used_index = highlight.index;
|
*used_index = highlight.index;
|
||||||
unique_rows.insert(DisplayRow(row), highlight.color);
|
unique_rows.insert(DisplayRow(row), highlight.color.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unique_rows
|
unique_rows
|
||||||
|
@ -15518,7 +15522,7 @@ impl EditorSnapshot {
|
||||||
) {
|
) {
|
||||||
// Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
|
// Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
|
||||||
// when the caret is just above or just below the deleted hunk.
|
// when the caret is just above or just below the deleted hunk.
|
||||||
let allow_adjacent = hunk.status() == DiffHunkStatus::Removed;
|
let allow_adjacent = hunk.status().is_removed();
|
||||||
let related_to_selection = if allow_adjacent {
|
let related_to_selection = if allow_adjacent {
|
||||||
hunk.row_range.overlaps(&query_rows)
|
hunk.row_range.overlaps(&query_rows)
|
||||||
|| hunk.row_range.start == query_rows.end
|
|| hunk.row_range.start == query_rows.end
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
JoinLines,
|
JoinLines,
|
||||||
};
|
};
|
||||||
use diff::{BufferDiff, DiffHunkStatus};
|
use buffer_diff::{BufferDiff, DiffHunkStatus};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
|
div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
|
||||||
|
@ -11989,7 +11989,7 @@ async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
|
||||||
struct Row9.2;
|
struct Row9.2;
|
||||||
struct Row9.3;
|
struct Row9.3;
|
||||||
struct Row10;"#},
|
struct Row10;"#},
|
||||||
vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
|
vec![DiffHunkStatus::added(), DiffHunkStatus::added()],
|
||||||
indoc! {r#"struct Row;
|
indoc! {r#"struct Row;
|
||||||
struct Row1;
|
struct Row1;
|
||||||
struct Row1.1;
|
struct Row1.1;
|
||||||
|
@ -12027,7 +12027,7 @@ async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
|
||||||
struct Row8;
|
struct Row8;
|
||||||
struct Row9;
|
struct Row9;
|
||||||
struct Row10;"#},
|
struct Row10;"#},
|
||||||
vec![DiffHunkStatus::Added, DiffHunkStatus::Added],
|
vec![DiffHunkStatus::added(), DiffHunkStatus::added()],
|
||||||
indoc! {r#"struct Row;
|
indoc! {r#"struct Row;
|
||||||
struct Row1;
|
struct Row1;
|
||||||
struct Row2;
|
struct Row2;
|
||||||
|
@ -12074,11 +12074,11 @@ async fn test_addition_reverts(cx: &mut gpui::TestAppContext) {
|
||||||
«ˇ// something on bottom»
|
«ˇ// something on bottom»
|
||||||
struct Row10;"#},
|
struct Row10;"#},
|
||||||
vec![
|
vec![
|
||||||
DiffHunkStatus::Added,
|
DiffHunkStatus::added(),
|
||||||
DiffHunkStatus::Added,
|
DiffHunkStatus::added(),
|
||||||
DiffHunkStatus::Added,
|
DiffHunkStatus::added(),
|
||||||
DiffHunkStatus::Added,
|
DiffHunkStatus::added(),
|
||||||
DiffHunkStatus::Added,
|
DiffHunkStatus::added(),
|
||||||
],
|
],
|
||||||
indoc! {r#"struct Row;
|
indoc! {r#"struct Row;
|
||||||
ˇstruct Row1;
|
ˇstruct Row1;
|
||||||
|
@ -12126,7 +12126,7 @@ async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
|
||||||
struct Row99;
|
struct Row99;
|
||||||
struct Row9;
|
struct Row9;
|
||||||
struct Row10;"#},
|
struct Row10;"#},
|
||||||
vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
|
vec![DiffHunkStatus::modified(), DiffHunkStatus::modified()],
|
||||||
indoc! {r#"struct Row;
|
indoc! {r#"struct Row;
|
||||||
struct Row1;
|
struct Row1;
|
||||||
struct Row33;
|
struct Row33;
|
||||||
|
@ -12153,7 +12153,7 @@ async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
|
||||||
struct Row99;
|
struct Row99;
|
||||||
struct Row9;
|
struct Row9;
|
||||||
struct Row10;"#},
|
struct Row10;"#},
|
||||||
vec![DiffHunkStatus::Modified, DiffHunkStatus::Modified],
|
vec![DiffHunkStatus::modified(), DiffHunkStatus::modified()],
|
||||||
indoc! {r#"struct Row;
|
indoc! {r#"struct Row;
|
||||||
struct Row1;
|
struct Row1;
|
||||||
struct Row33;
|
struct Row33;
|
||||||
|
@ -12182,12 +12182,12 @@ async fn test_modification_reverts(cx: &mut gpui::TestAppContext) {
|
||||||
struct Row9;
|
struct Row9;
|
||||||
struct Row1011;ˇ"#},
|
struct Row1011;ˇ"#},
|
||||||
vec![
|
vec![
|
||||||
DiffHunkStatus::Modified,
|
DiffHunkStatus::modified(),
|
||||||
DiffHunkStatus::Modified,
|
DiffHunkStatus::modified(),
|
||||||
DiffHunkStatus::Modified,
|
DiffHunkStatus::modified(),
|
||||||
DiffHunkStatus::Modified,
|
DiffHunkStatus::modified(),
|
||||||
DiffHunkStatus::Modified,
|
DiffHunkStatus::modified(),
|
||||||
DiffHunkStatus::Modified,
|
DiffHunkStatus::modified(),
|
||||||
],
|
],
|
||||||
indoc! {r#"struct Row;
|
indoc! {r#"struct Row;
|
||||||
ˇstruct Row1;
|
ˇstruct Row1;
|
||||||
|
@ -12265,7 +12265,7 @@ struct Row10;"#};
|
||||||
ˇ
|
ˇ
|
||||||
struct Row8;
|
struct Row8;
|
||||||
struct Row10;"#},
|
struct Row10;"#},
|
||||||
vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
|
vec![DiffHunkStatus::removed(), DiffHunkStatus::removed()],
|
||||||
indoc! {r#"struct Row;
|
indoc! {r#"struct Row;
|
||||||
struct Row2;
|
struct Row2;
|
||||||
|
|
||||||
|
@ -12288,7 +12288,7 @@ struct Row10;"#};
|
||||||
ˇ»
|
ˇ»
|
||||||
struct Row8;
|
struct Row8;
|
||||||
struct Row10;"#},
|
struct Row10;"#},
|
||||||
vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
|
vec![DiffHunkStatus::removed(), DiffHunkStatus::removed()],
|
||||||
indoc! {r#"struct Row;
|
indoc! {r#"struct Row;
|
||||||
struct Row2;
|
struct Row2;
|
||||||
|
|
||||||
|
@ -12313,7 +12313,7 @@ struct Row10;"#};
|
||||||
|
|
||||||
struct Row8;ˇ
|
struct Row8;ˇ
|
||||||
struct Row10;"#},
|
struct Row10;"#},
|
||||||
vec![DiffHunkStatus::Removed, DiffHunkStatus::Removed],
|
vec![DiffHunkStatus::removed(), DiffHunkStatus::removed()],
|
||||||
indoc! {r#"struct Row;
|
indoc! {r#"struct Row;
|
||||||
struct Row1;
|
struct Row1;
|
||||||
ˇstruct Row2;
|
ˇstruct Row2;
|
||||||
|
@ -12338,9 +12338,9 @@ struct Row10;"#};
|
||||||
struct Row8;ˇ»
|
struct Row8;ˇ»
|
||||||
struct Row10;"#},
|
struct Row10;"#},
|
||||||
vec![
|
vec![
|
||||||
DiffHunkStatus::Removed,
|
DiffHunkStatus::removed(),
|
||||||
DiffHunkStatus::Removed,
|
DiffHunkStatus::removed(),
|
||||||
DiffHunkStatus::Removed,
|
DiffHunkStatus::removed(),
|
||||||
],
|
],
|
||||||
indoc! {r#"struct Row;
|
indoc! {r#"struct Row;
|
||||||
struct Row1;
|
struct Row1;
|
||||||
|
|
|
@ -25,21 +25,21 @@ use crate::{
|
||||||
EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT, FILE_HEADER_HEIGHT,
|
EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT, FILE_HEADER_HEIGHT,
|
||||||
GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
|
GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
|
||||||
};
|
};
|
||||||
|
use buffer_diff::{DiffHunkSecondaryStatus, DiffHunkStatus};
|
||||||
use client::ParticipantIndex;
|
use client::ParticipantIndex;
|
||||||
use collections::{BTreeMap, HashMap, HashSet};
|
use collections::{BTreeMap, HashMap, HashSet};
|
||||||
use diff::DiffHunkStatus;
|
|
||||||
use file_icons::FileIcons;
|
use file_icons::FileIcons;
|
||||||
use git::{blame::BlameEntry, Oid};
|
use git::{blame::BlameEntry, Oid};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
anchored, deferred, div, fill, linear_color_stop, linear_gradient, outline, point, px, quad,
|
anchored, deferred, div, fill, linear_color_stop, linear_gradient, outline, pattern_slash,
|
||||||
relative, size, svg, transparent_black, Action, AnyElement, App, AvailableSpace, Axis, Bounds,
|
point, px, quad, relative, size, svg, transparent_black, Action, AnyElement, App,
|
||||||
ClickEvent, ClipboardItem, ContentMask, Context, Corner, Corners, CursorStyle, DispatchPhase,
|
AvailableSpace, Axis, Bounds, ClickEvent, ClipboardItem, ContentMask, Context, Corner, Corners,
|
||||||
Edges, Element, ElementInputHandler, Entity, Focusable as _, FontId, GlobalElementId, Hitbox,
|
CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Focusable as _,
|
||||||
Hsla, InteractiveElement, IntoElement, KeyBindingContextPredicate, Keystroke, Length,
|
FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement,
|
||||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
|
KeyBindingContextPredicate, Keystroke, Length, ModifiersChangedEvent, MouseButton,
|
||||||
ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size,
|
MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta,
|
||||||
StatefulInteractiveElement, Style, Styled, Subscription, TextRun, TextStyleRefinement,
|
ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled,
|
||||||
WeakEntity, Window,
|
Subscription, TextRun, TextStyleRefinement, WeakEntity, Window,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::{
|
use language::{
|
||||||
|
@ -85,7 +85,6 @@ enum DisplayDiffHunk {
|
||||||
Folded {
|
Folded {
|
||||||
display_row: DisplayRow,
|
display_row: DisplayRow,
|
||||||
},
|
},
|
||||||
|
|
||||||
Unfolded {
|
Unfolded {
|
||||||
diff_base_byte_range: Range<usize>,
|
diff_base_byte_range: Range<usize>,
|
||||||
display_row_range: Range<DisplayRow>,
|
display_row_range: Range<DisplayRow>,
|
||||||
|
@ -2116,7 +2115,7 @@ impl EditorElement {
|
||||||
.get(&display_row)
|
.get(&display_row)
|
||||||
.unwrap_or(&non_relative_number);
|
.unwrap_or(&non_relative_number);
|
||||||
write!(&mut line_number, "{number}").unwrap();
|
write!(&mut line_number, "{number}").unwrap();
|
||||||
if row_info.diff_status == Some(DiffHunkStatus::Removed) {
|
if matches!(row_info.diff_status, Some(DiffHunkStatus::Removed(_))) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4007,8 +4006,10 @@ impl EditorElement {
|
||||||
if row_infos[row_ix].diff_status.is_none() {
|
if row_infos[row_ix].diff_status.is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if row_infos[row_ix].diff_status == Some(DiffHunkStatus::Added)
|
if matches!(
|
||||||
&& *status != DiffHunkStatus::Added
|
row_infos[row_ix].diff_status,
|
||||||
|
Some(DiffHunkStatus::Added(_))
|
||||||
|
) && !matches!(*status, DiffHunkStatus::Added(_))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -4191,26 +4192,26 @@ impl EditorElement {
|
||||||
window.paint_quad(fill(Bounds { origin, size }, color));
|
window.paint_quad(fill(Bounds { origin, size }, color));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut current_paint: Option<(Hsla, Range<DisplayRow>)> = None;
|
let mut current_paint: Option<(gpui::Background, Range<DisplayRow>)> = None;
|
||||||
for (&new_row, &new_color) in &layout.highlighted_rows {
|
for (&new_row, &new_background) in &layout.highlighted_rows {
|
||||||
match &mut current_paint {
|
match &mut current_paint {
|
||||||
Some((current_color, current_range)) => {
|
Some((current_background, current_range)) => {
|
||||||
let current_color = *current_color;
|
let current_background = *current_background;
|
||||||
let new_range_started = current_color != new_color
|
let new_range_started = current_background != new_background
|
||||||
|| current_range.end.next_row() != new_row;
|
|| current_range.end.next_row() != new_row;
|
||||||
if new_range_started {
|
if new_range_started {
|
||||||
paint_highlight(
|
paint_highlight(
|
||||||
current_range.start,
|
current_range.start,
|
||||||
current_range.end,
|
current_range.end,
|
||||||
current_color,
|
current_background,
|
||||||
);
|
);
|
||||||
current_paint = Some((new_color, new_row..new_row));
|
current_paint = Some((new_background, new_row..new_row));
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
current_range.end = current_range.end.next_row();
|
current_range.end = current_range.end.next_row();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => current_paint = Some((new_color, new_row..new_row)),
|
None => current_paint = Some((new_background, new_row..new_row)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if let Some((color, range)) = current_paint {
|
if let Some((color, range)) = current_paint {
|
||||||
|
@ -4409,6 +4410,7 @@ impl EditorElement {
|
||||||
hunk_bounds,
|
hunk_bounds,
|
||||||
cx.theme().status().modified,
|
cx.theme().status().modified,
|
||||||
Corners::all(px(0.)),
|
Corners::all(px(0.)),
|
||||||
|
&DiffHunkSecondaryStatus::None,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
DisplayDiffHunk::Unfolded {
|
DisplayDiffHunk::Unfolded {
|
||||||
|
@ -4416,22 +4418,29 @@ impl EditorElement {
|
||||||
display_row_range,
|
display_row_range,
|
||||||
..
|
..
|
||||||
} => hitbox.as_ref().map(|hunk_hitbox| match status {
|
} => hitbox.as_ref().map(|hunk_hitbox| match status {
|
||||||
DiffHunkStatus::Added => (
|
DiffHunkStatus::Added(secondary_status) => (
|
||||||
hunk_hitbox.bounds,
|
hunk_hitbox.bounds,
|
||||||
cx.theme().status().created,
|
cx.theme().status().created,
|
||||||
Corners::all(px(0.)),
|
Corners::all(px(0.)),
|
||||||
|
secondary_status,
|
||||||
),
|
),
|
||||||
DiffHunkStatus::Modified => (
|
DiffHunkStatus::Modified(secondary_status) => (
|
||||||
hunk_hitbox.bounds,
|
hunk_hitbox.bounds,
|
||||||
cx.theme().status().modified,
|
cx.theme().status().modified,
|
||||||
Corners::all(px(0.)),
|
Corners::all(px(0.)),
|
||||||
|
secondary_status,
|
||||||
),
|
),
|
||||||
DiffHunkStatus::Removed if !display_row_range.is_empty() => (
|
DiffHunkStatus::Removed(secondary_status)
|
||||||
hunk_hitbox.bounds,
|
if !display_row_range.is_empty() =>
|
||||||
cx.theme().status().deleted,
|
{
|
||||||
Corners::all(px(0.)),
|
(
|
||||||
),
|
hunk_hitbox.bounds,
|
||||||
DiffHunkStatus::Removed => (
|
cx.theme().status().deleted,
|
||||||
|
Corners::all(px(0.)),
|
||||||
|
secondary_status,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DiffHunkStatus::Removed(secondary_status) => (
|
||||||
Bounds::new(
|
Bounds::new(
|
||||||
point(
|
point(
|
||||||
hunk_hitbox.origin.x - hunk_hitbox.size.width,
|
hunk_hitbox.origin.x - hunk_hitbox.size.width,
|
||||||
|
@ -4441,11 +4450,17 @@ impl EditorElement {
|
||||||
),
|
),
|
||||||
cx.theme().status().deleted,
|
cx.theme().status().deleted,
|
||||||
Corners::all(1. * line_height),
|
Corners::all(1. * line_height),
|
||||||
|
secondary_status,
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((hunk_bounds, background_color, corner_radii)) = hunk_to_paint {
|
if let Some((hunk_bounds, mut background_color, corner_radii, secondary_status)) =
|
||||||
|
hunk_to_paint
|
||||||
|
{
|
||||||
|
if *secondary_status != DiffHunkSecondaryStatus::None {
|
||||||
|
background_color.a *= 0.6;
|
||||||
|
}
|
||||||
window.paint_quad(quad(
|
window.paint_quad(quad(
|
||||||
hunk_bounds,
|
hunk_bounds,
|
||||||
corner_radii,
|
corner_radii,
|
||||||
|
@ -4481,7 +4496,7 @@ impl EditorElement {
|
||||||
status,
|
status,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if *status == DiffHunkStatus::Removed && display_row_range.is_empty() {
|
if status.is_removed() && display_row_range.is_empty() {
|
||||||
let row = display_row_range.start;
|
let row = display_row_range.start;
|
||||||
|
|
||||||
let offset = line_height / 2.;
|
let offset = line_height / 2.;
|
||||||
|
@ -5128,9 +5143,9 @@ impl EditorElement {
|
||||||
end_display_row.0 -= 1;
|
end_display_row.0 -= 1;
|
||||||
}
|
}
|
||||||
let color = match &hunk.status() {
|
let color = match &hunk.status() {
|
||||||
DiffHunkStatus::Added => theme.status().created,
|
DiffHunkStatus::Added(_) => theme.status().created,
|
||||||
DiffHunkStatus::Modified => theme.status().modified,
|
DiffHunkStatus::Modified(_) => theme.status().modified,
|
||||||
DiffHunkStatus::Removed => theme.status().deleted,
|
DiffHunkStatus::Removed(_) => theme.status().deleted,
|
||||||
};
|
};
|
||||||
ColoredRange {
|
ColoredRange {
|
||||||
start: start_display_row,
|
start: start_display_row,
|
||||||
|
@ -6798,19 +6813,46 @@ impl Element for EditorElement {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut highlighted_rows = self
|
let (mut highlighted_rows, distinguish_unstaged_hunks) =
|
||||||
.editor
|
self.editor.update(cx, |editor, cx| {
|
||||||
.update(cx, |editor, cx| editor.highlighted_display_rows(window, cx));
|
(
|
||||||
|
editor.highlighted_display_rows(window, cx),
|
||||||
|
editor.distinguish_unstaged_diff_hunks,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
for (ix, row_info) in row_infos.iter().enumerate() {
|
for (ix, row_info) in row_infos.iter().enumerate() {
|
||||||
let color = match row_info.diff_status {
|
let background = match row_info.diff_status {
|
||||||
Some(DiffHunkStatus::Added) => style.status.created_background,
|
Some(DiffHunkStatus::Added(secondary_status)) => {
|
||||||
Some(DiffHunkStatus::Removed) => style.status.deleted_background,
|
let color = style.status.created_background;
|
||||||
|
match secondary_status {
|
||||||
|
DiffHunkSecondaryStatus::HasSecondaryHunk
|
||||||
|
| DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
|
||||||
|
if distinguish_unstaged_hunks =>
|
||||||
|
{
|
||||||
|
pattern_slash(color, line_height.0 / 4.0)
|
||||||
|
}
|
||||||
|
_ => color.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(DiffHunkStatus::Removed(secondary_status)) => {
|
||||||
|
let color = style.status.deleted_background;
|
||||||
|
match secondary_status {
|
||||||
|
DiffHunkSecondaryStatus::HasSecondaryHunk
|
||||||
|
| DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
|
||||||
|
if distinguish_unstaged_hunks =>
|
||||||
|
{
|
||||||
|
pattern_slash(color, line_height.0 / 4.0)
|
||||||
|
}
|
||||||
|
_ => color.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
highlighted_rows
|
highlighted_rows
|
||||||
.entry(start_row + DisplayRow(ix as u32))
|
.entry(start_row + DisplayRow(ix as u32))
|
||||||
.or_insert(color);
|
.or_insert(background);
|
||||||
}
|
}
|
||||||
|
|
||||||
let highlighted_ranges = self.editor.read(cx).background_highlights_in_range(
|
let highlighted_ranges = self.editor.read(cx).background_highlights_in_range(
|
||||||
|
@ -7643,7 +7685,7 @@ pub struct EditorLayout {
|
||||||
indent_guides: Option<Vec<IndentGuideLayout>>,
|
indent_guides: Option<Vec<IndentGuideLayout>>,
|
||||||
visible_display_row_range: Range<DisplayRow>,
|
visible_display_row_range: Range<DisplayRow>,
|
||||||
active_rows: BTreeMap<DisplayRow, bool>,
|
active_rows: BTreeMap<DisplayRow, bool>,
|
||||||
highlighted_rows: BTreeMap<DisplayRow, Hsla>,
|
highlighted_rows: BTreeMap<DisplayRow, gpui::Background>,
|
||||||
line_elements: SmallVec<[AnyElement; 1]>,
|
line_elements: SmallVec<[AnyElement; 1]>,
|
||||||
line_numbers: Arc<HashMap<MultiBufferRow, LineNumberLayout>>,
|
line_numbers: Arc<HashMap<MultiBufferRow, LineNumberLayout>>,
|
||||||
display_hunks: Vec<(DisplayDiffHunk, Option<Hitbox>)>,
|
display_hunks: Vec<(DisplayDiffHunk, Option<Hitbox>)>,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{ApplyAllDiffHunks, Editor, EditorEvent, SemanticsProvider};
|
use crate::{ApplyAllDiffHunks, Editor, EditorEvent, SemanticsProvider};
|
||||||
|
use buffer_diff::BufferDiff;
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use diff::BufferDiff;
|
|
||||||
use futures::{channel::mpsc, future::join_all};
|
use futures::{channel::mpsc, future::join_all};
|
||||||
use gpui::{App, Entity, EventEmitter, Focusable, Render, Subscription, Task};
|
use gpui::{App, Entity, EventEmitter, Focusable, Render, Subscription, Task};
|
||||||
use language::{Buffer, BufferEvent, Capability};
|
use language::{Buffer, BufferEvent, Capability};
|
||||||
|
@ -185,7 +185,7 @@ impl ProposedChangesEditor {
|
||||||
} else {
|
} else {
|
||||||
branch_buffer = location.buffer.update(cx, |buffer, cx| buffer.branch(cx));
|
branch_buffer = location.buffer.update(cx, |buffer, cx| buffer.branch(cx));
|
||||||
new_diffs.push(cx.new(|cx| {
|
new_diffs.push(cx.new(|cx| {
|
||||||
let mut diff = BufferDiff::new(&branch_buffer, cx);
|
let mut diff = BufferDiff::new(branch_buffer.read(cx));
|
||||||
let _ = diff.set_base_text(
|
let _ = diff.set_base_text(
|
||||||
location.buffer.clone(),
|
location.buffer.clone(),
|
||||||
branch_buffer.read(cx).text_snapshot(),
|
branch_buffer.read(cx).text_snapshot(),
|
||||||
|
|
|
@ -2,8 +2,8 @@ use crate::{
|
||||||
display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
|
display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
|
||||||
RowExt,
|
RowExt,
|
||||||
};
|
};
|
||||||
|
use buffer_diff::DiffHunkStatus;
|
||||||
use collections::BTreeMap;
|
use collections::BTreeMap;
|
||||||
use diff::DiffHunkStatus;
|
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -459,9 +459,9 @@ pub fn assert_state_with_diff(
|
||||||
.zip(line_infos)
|
.zip(line_infos)
|
||||||
.map(|(line, info)| {
|
.map(|(line, info)| {
|
||||||
let mut marker = match info.diff_status {
|
let mut marker = match info.diff_status {
|
||||||
Some(DiffHunkStatus::Added) => "+ ",
|
Some(DiffHunkStatus::Added(_)) => "+ ",
|
||||||
Some(DiffHunkStatus::Removed) => "- ",
|
Some(DiffHunkStatus::Removed(_)) => "- ",
|
||||||
Some(DiffHunkStatus::Modified) => unreachable!(),
|
Some(DiffHunkStatus::Modified(_)) => unreachable!(),
|
||||||
None => {
|
None => {
|
||||||
if has_diff {
|
if has_diff {
|
||||||
" "
|
" "
|
||||||
|
|
|
@ -16,7 +16,7 @@ path = "src/git_ui.rs"
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
db.workspace = true
|
db.workspace = true
|
||||||
diff.workspace = true
|
buffer_diff.workspace = true
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
feature_flags.workspace = true
|
feature_flags.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use buffer_diff::BufferDiff;
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use diff::BufferDiff;
|
|
||||||
use editor::{scroll::Autoscroll, Editor, EditorEvent};
|
use editor::{scroll::Autoscroll, Editor, EditorEvent};
|
||||||
use feature_flags::FeatureFlagViewExt;
|
use feature_flags::FeatureFlagViewExt;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
@ -126,6 +126,7 @@ impl ProjectDiff {
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
diff_display_editor.set_distinguish_unstaged_diff_hunks();
|
||||||
diff_display_editor.set_expand_all_diff_hunks(cx);
|
diff_display_editor.set_expand_all_diff_hunks(cx);
|
||||||
diff_display_editor.register_addon(GitPanelAddon {
|
diff_display_editor.register_addon(GitPanelAddon {
|
||||||
git_panel: git_panel.clone(),
|
git_panel: git_panel.clone(),
|
||||||
|
@ -317,10 +318,10 @@ impl ProjectDiff {
|
||||||
|
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
let diff = diff.read(cx);
|
let diff = diff.read(cx);
|
||||||
let diff_hunk_ranges = if diff.snapshot.base_text.is_none() {
|
let diff_hunk_ranges = if diff.base_text().is_none() {
|
||||||
vec![Point::zero()..snapshot.max_point()]
|
vec![Point::zero()..snapshot.max_point()]
|
||||||
} else {
|
} else {
|
||||||
diff.diff_hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot)
|
diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot, cx)
|
||||||
.map(|diff_hunk| diff_hunk.buffer_range.to_point(&snapshot))
|
.map(|diff_hunk| diff_hunk.buffer_range.to_point(&snapshot))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,10 +25,30 @@ impl Render for PatternExample {
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(gpui::blue())
|
.border_color(gpui::blue())
|
||||||
.child(div().w(px(54.0)).h(px(18.0)).bg(pattern_slash(gpui::red())))
|
.child(
|
||||||
.child(div().w(px(54.0)).h(px(18.0)).bg(pattern_slash(gpui::red())))
|
div()
|
||||||
.child(div().w(px(54.0)).h(px(18.0)).bg(pattern_slash(gpui::red())))
|
.w(px(54.0))
|
||||||
.child(div().w(px(54.0)).h(px(18.0)).bg(pattern_slash(gpui::red()))),
|
.h(px(18.0))
|
||||||
|
.bg(pattern_slash(gpui::red(), 18.0 / 2.0)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.w(px(54.0))
|
||||||
|
.h(px(18.0))
|
||||||
|
.bg(pattern_slash(gpui::red(), 18.0 / 2.0)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.w(px(54.0))
|
||||||
|
.h(px(18.0))
|
||||||
|
.bg(pattern_slash(gpui::red(), 18.0 / 2.0)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.w(px(54.0))
|
||||||
|
.h(px(18.0))
|
||||||
|
.bg(pattern_slash(gpui::red(), 18.0 / 2.0)),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
@ -42,25 +62,25 @@ impl Render for PatternExample {
|
||||||
div()
|
div()
|
||||||
.w(px(256.0))
|
.w(px(256.0))
|
||||||
.h(px(56.0))
|
.h(px(56.0))
|
||||||
.bg(pattern_slash(gpui::red())),
|
.bg(pattern_slash(gpui::red(), 56.0 / 3.0)),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.w(px(256.0))
|
.w(px(256.0))
|
||||||
.h(px(56.0))
|
.h(px(56.0))
|
||||||
.bg(pattern_slash(gpui::green())),
|
.bg(pattern_slash(gpui::green(), 56.0 / 3.0)),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.w(px(256.0))
|
.w(px(256.0))
|
||||||
.h(px(56.0))
|
.h(px(56.0))
|
||||||
.bg(pattern_slash(gpui::blue())),
|
.bg(pattern_slash(gpui::blue(), 56.0 / 3.0)),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.w(px(256.0))
|
.w(px(256.0))
|
||||||
.h(px(26.0))
|
.h(px(26.0))
|
||||||
.bg(pattern_slash(gpui::yellow())),
|
.bg(pattern_slash(gpui::yellow(), 56.0 / 3.0)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
|
|
|
@ -587,7 +587,7 @@ pub struct Background {
|
||||||
pub(crate) tag: BackgroundTag,
|
pub(crate) tag: BackgroundTag,
|
||||||
pub(crate) color_space: ColorSpace,
|
pub(crate) color_space: ColorSpace,
|
||||||
pub(crate) solid: Hsla,
|
pub(crate) solid: Hsla,
|
||||||
pub(crate) angle: f32,
|
pub(crate) gradient_angle_or_pattern_height: f32,
|
||||||
pub(crate) colors: [LinearColorStop; 2],
|
pub(crate) colors: [LinearColorStop; 2],
|
||||||
/// Padding for alignment for repr(C) layout.
|
/// Padding for alignment for repr(C) layout.
|
||||||
pad: u32,
|
pad: u32,
|
||||||
|
@ -600,7 +600,7 @@ impl Default for Background {
|
||||||
tag: BackgroundTag::Solid,
|
tag: BackgroundTag::Solid,
|
||||||
solid: Hsla::default(),
|
solid: Hsla::default(),
|
||||||
color_space: ColorSpace::default(),
|
color_space: ColorSpace::default(),
|
||||||
angle: 0.0,
|
gradient_angle_or_pattern_height: 0.0,
|
||||||
colors: [LinearColorStop::default(), LinearColorStop::default()],
|
colors: [LinearColorStop::default(), LinearColorStop::default()],
|
||||||
pad: 0,
|
pad: 0,
|
||||||
}
|
}
|
||||||
|
@ -608,10 +608,11 @@ impl Default for Background {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a hash pattern background
|
/// Creates a hash pattern background
|
||||||
pub fn pattern_slash(color: Hsla) -> Background {
|
pub fn pattern_slash(color: Hsla, thickness: f32) -> Background {
|
||||||
Background {
|
Background {
|
||||||
tag: BackgroundTag::PatternSlash,
|
tag: BackgroundTag::PatternSlash,
|
||||||
solid: color,
|
solid: color,
|
||||||
|
gradient_angle_or_pattern_height: thickness,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -630,7 +631,7 @@ pub fn linear_gradient(
|
||||||
) -> Background {
|
) -> Background {
|
||||||
Background {
|
Background {
|
||||||
tag: BackgroundTag::LinearGradient,
|
tag: BackgroundTag::LinearGradient,
|
||||||
angle,
|
gradient_angle_or_pattern_height: angle,
|
||||||
colors: [from.into(), to.into()],
|
colors: [from.into(), to.into()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ struct Background {
|
||||||
// 1u is Oklab color
|
// 1u is Oklab color
|
||||||
color_space: u32,
|
color_space: u32,
|
||||||
solid: Hsla,
|
solid: Hsla,
|
||||||
angle: f32,
|
gradient_angle_or_pattern_height: f32,
|
||||||
colors: array<LinearColorStop, 2>,
|
colors: array<LinearColorStop, 2>,
|
||||||
pad: u32,
|
pad: u32,
|
||||||
}
|
}
|
||||||
|
@ -310,17 +310,18 @@ fn prepare_gradient_color(tag: u32, color_space: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gradient_color(background: Background, position: vec2<f32>, bounds: Bounds,
|
fn gradient_color(background: Background, position: vec2<f32>, bounds: Bounds,
|
||||||
sold_color: vec4<f32>, color0: vec4<f32>, color1: vec4<f32>) -> vec4<f32> {
|
solid_color: vec4<f32>, color0: vec4<f32>, color1: vec4<f32>) -> vec4<f32> {
|
||||||
var background_color = vec4<f32>(0.0);
|
var background_color = vec4<f32>(0.0);
|
||||||
|
|
||||||
switch (background.tag) {
|
switch (background.tag) {
|
||||||
default: {
|
default: {
|
||||||
return sold_color;
|
return solid_color;
|
||||||
}
|
}
|
||||||
case 1u: {
|
case 1u: {
|
||||||
// Linear gradient background.
|
// Linear gradient background.
|
||||||
// -90 degrees to match the CSS gradient angle.
|
// -90 degrees to match the CSS gradient angle.
|
||||||
let radians = (background.angle % 360.0 - 90.0) * M_PI_F / 180.0;
|
let angle = background.gradient_angle_or_pattern_height;
|
||||||
|
let radians = (angle % 360.0 - 90.0) * M_PI_F / 180.0;
|
||||||
var direction = vec2<f32>(cos(radians), sin(radians));
|
var direction = vec2<f32>(cos(radians), sin(radians));
|
||||||
let stop0_percentage = background.colors[0].percentage;
|
let stop0_percentage = background.colors[0].percentage;
|
||||||
let stop1_percentage = background.colors[1].percentage;
|
let stop1_percentage = background.colors[1].percentage;
|
||||||
|
@ -359,19 +360,18 @@ fn gradient_color(background: Background, position: vec2<f32>, bounds: Bounds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 2u: {
|
case 2u: {
|
||||||
let base_pattern_size = bounds.size.y / 5.0;
|
let pattern_height = background.gradient_angle_or_pattern_height;
|
||||||
let width = base_pattern_size * 0.5;
|
let stripe_angle = M_PI_F / 4.0;
|
||||||
let slash_spacing = 0.89;
|
let pattern_period = pattern_height * sin(stripe_angle);
|
||||||
let radians = M_PI_F / 4.0;
|
|
||||||
let rotation = mat2x2<f32>(
|
let rotation = mat2x2<f32>(
|
||||||
cos(radians), -sin(radians),
|
cos(stripe_angle), -sin(stripe_angle),
|
||||||
sin(radians), cos(radians)
|
sin(stripe_angle), cos(stripe_angle)
|
||||||
);
|
);
|
||||||
let relative_position = position - bounds.origin;
|
let relative_position = position - bounds.origin;
|
||||||
let rotated_point = rotation * relative_position;
|
let rotated_point = rotation * relative_position;
|
||||||
let pattern = (rotated_point.x / slash_spacing) % (base_pattern_size * 2.0);
|
let pattern = rotated_point.x % pattern_period;
|
||||||
let distance = min(pattern, base_pattern_size * 2.0 - pattern) - width;
|
let distance = min(pattern, pattern_period - pattern) - pattern_period / 4;
|
||||||
background_color = sold_color;
|
background_color = solid_color;
|
||||||
background_color.a *= saturate(0.5 - distance);
|
background_color.a *= saturate(0.5 - distance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -833,7 +833,8 @@ float4 fill_color(Background background,
|
||||||
break;
|
break;
|
||||||
case 1: {
|
case 1: {
|
||||||
// -90 degrees to match the CSS gradient angle.
|
// -90 degrees to match the CSS gradient angle.
|
||||||
float radians = (fmod(background.angle, 360.0) - 90.0) * (M_PI_F / 180.0);
|
float gradient_angle = background.gradient_angle_or_pattern_height;
|
||||||
|
float radians = (fmod(gradient_angle, 360.0) - 90.0) * (M_PI_F / 180.0);
|
||||||
float2 direction = float2(cos(radians), sin(radians));
|
float2 direction = float2(cos(radians), sin(radians));
|
||||||
|
|
||||||
// Expand the short side to be the same as the long side
|
// Expand the short side to be the same as the long side
|
||||||
|
@ -874,19 +875,14 @@ float4 fill_color(Background background,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
// This pattern is full of magic numbers to make it line up perfectly
|
float pattern_height = background.gradient_angle_or_pattern_height;
|
||||||
// when vertically stacked. Make sure you know what you are doing
|
float stripe_angle = M_PI_F / 4.0;
|
||||||
// if you change this!
|
float pattern_period = pattern_height * sin(stripe_angle);
|
||||||
|
float2x2 rotation = rotate2d(stripe_angle);
|
||||||
float base_pattern_size = bounds.size.height / 5;
|
|
||||||
float width = base_pattern_size * 0.5;
|
|
||||||
float slash_spacing = .89;
|
|
||||||
float radians = M_PI_F / 4.0;
|
|
||||||
float2x2 rotation = rotate2d(radians);
|
|
||||||
float2 relative_position = position - float2(bounds.origin.x, bounds.origin.y);
|
float2 relative_position = position - float2(bounds.origin.x, bounds.origin.y);
|
||||||
float2 rotated_point = rotation * relative_position;
|
float2 rotated_point = rotation * relative_position;
|
||||||
float pattern = fmod(rotated_point.x / slash_spacing, base_pattern_size * 2.0);
|
float pattern = fmod(rotated_point.x, pattern_period);
|
||||||
float distance = min(pattern, base_pattern_size * 2.0 - pattern) - width;
|
float distance = min(pattern, pattern_period - pattern) - pattern_period / 4.0;
|
||||||
color = solid_color;
|
color = solid_color;
|
||||||
color.a *= saturate(0.5 - distance);
|
color.a *= saturate(0.5 - distance);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -14,7 +14,7 @@ doctest = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
test-support = [
|
test-support = [
|
||||||
"diff/test-support",
|
"buffer_diff/test-support",
|
||||||
"gpui/test-support",
|
"gpui/test-support",
|
||||||
"language/test-support",
|
"language/test-support",
|
||||||
"text/test-support",
|
"text/test-support",
|
||||||
|
@ -26,7 +26,7 @@ anyhow.workspace = true
|
||||||
clock.workspace = true
|
clock.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
ctor.workspace = true
|
ctor.workspace = true
|
||||||
diff.workspace = true
|
buffer_diff.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
|
@ -47,7 +47,7 @@ tree-sitter.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
diff = { workspace = true, features = ["test-support"] }
|
buffer_diff = { workspace = true, features = ["test-support"] }
|
||||||
gpui = { workspace = true, features = ["test-support"] }
|
gpui = { workspace = true, features = ["test-support"] }
|
||||||
indoc.workspace = true
|
indoc.workspace = true
|
||||||
language = { workspace = true, features = ["test-support"] }
|
language = { workspace = true, features = ["test-support"] }
|
||||||
|
|
|
@ -73,7 +73,7 @@ impl Anchor {
|
||||||
if let Some(base_text) = snapshot
|
if let Some(base_text) = snapshot
|
||||||
.diffs
|
.diffs
|
||||||
.get(&excerpt.buffer_id)
|
.get(&excerpt.buffer_id)
|
||||||
.and_then(|diff| diff.base_text.as_ref())
|
.and_then(|diff| diff.base_text())
|
||||||
{
|
{
|
||||||
let self_anchor = self.diff_base_anchor.filter(|a| base_text.can_resolve(a));
|
let self_anchor = self.diff_base_anchor.filter(|a| base_text.can_resolve(a));
|
||||||
let other_anchor = other.diff_base_anchor.filter(|a| base_text.can_resolve(a));
|
let other_anchor = other.diff_base_anchor.filter(|a| base_text.can_resolve(a));
|
||||||
|
@ -110,7 +110,7 @@ impl Anchor {
|
||||||
if let Some(base_text) = snapshot
|
if let Some(base_text) = snapshot
|
||||||
.diffs
|
.diffs
|
||||||
.get(&excerpt.buffer_id)
|
.get(&excerpt.buffer_id)
|
||||||
.and_then(|diff| diff.base_text.as_ref())
|
.and_then(|diff| diff.base_text())
|
||||||
{
|
{
|
||||||
if a.buffer_id == Some(base_text.remote_id()) {
|
if a.buffer_id == Some(base_text.remote_id()) {
|
||||||
return a.bias_left(base_text);
|
return a.bias_left(base_text);
|
||||||
|
@ -135,7 +135,7 @@ impl Anchor {
|
||||||
if let Some(base_text) = snapshot
|
if let Some(base_text) = snapshot
|
||||||
.diffs
|
.diffs
|
||||||
.get(&excerpt.buffer_id)
|
.get(&excerpt.buffer_id)
|
||||||
.and_then(|diff| diff.base_text.as_ref())
|
.and_then(|diff| diff.base_text())
|
||||||
{
|
{
|
||||||
if a.buffer_id == Some(base_text.remote_id()) {
|
if a.buffer_id == Some(base_text.remote_id()) {
|
||||||
return a.bias_right(&base_text);
|
return a.bias_right(&base_text);
|
||||||
|
|
|
@ -7,9 +7,11 @@ pub use anchor::{Anchor, AnchorRangeExt, Offset};
|
||||||
pub use position::{TypedOffset, TypedPoint, TypedRow};
|
pub use position::{TypedOffset, TypedPoint, TypedRow};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use buffer_diff::{
|
||||||
|
BufferDiff, BufferDiffEvent, BufferDiffSnapshot, DiffHunkSecondaryStatus, DiffHunkStatus,
|
||||||
|
};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use collections::{BTreeMap, Bound, HashMap, HashSet};
|
use collections::{BTreeMap, Bound, HashMap, HashSet};
|
||||||
use diff::{BufferDiff, BufferDiffEvent, BufferDiffSnapshot, DiffHunkStatus};
|
|
||||||
use futures::{channel::mpsc, SinkExt};
|
use futures::{channel::mpsc, SinkExt};
|
||||||
use gpui::{App, Context, Entity, EntityId, EventEmitter, Task};
|
use gpui::{App, Context, Entity, EntityId, EventEmitter, Task};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -129,16 +131,18 @@ pub struct MultiBufferDiffHunk {
|
||||||
pub excerpt_id: ExcerptId,
|
pub excerpt_id: ExcerptId,
|
||||||
/// The range within the buffer's diff base that this hunk corresponds to.
|
/// The range within the buffer's diff base that this hunk corresponds to.
|
||||||
pub diff_base_byte_range: Range<usize>,
|
pub diff_base_byte_range: Range<usize>,
|
||||||
|
/// Whether or not this hunk also appears in the 'secondary diff'.
|
||||||
|
pub secondary_status: DiffHunkSecondaryStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MultiBufferDiffHunk {
|
impl MultiBufferDiffHunk {
|
||||||
pub fn status(&self) -> DiffHunkStatus {
|
pub fn status(&self) -> DiffHunkStatus {
|
||||||
if self.buffer_range.start == self.buffer_range.end {
|
if self.buffer_range.start == self.buffer_range.end {
|
||||||
DiffHunkStatus::Removed
|
DiffHunkStatus::Removed(self.secondary_status)
|
||||||
} else if self.diff_base_byte_range.is_empty() {
|
} else if self.diff_base_byte_range.is_empty() {
|
||||||
DiffHunkStatus::Added
|
DiffHunkStatus::Added(self.secondary_status)
|
||||||
} else {
|
} else {
|
||||||
DiffHunkStatus::Modified
|
DiffHunkStatus::Modified(self.secondary_status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,7 +229,14 @@ impl DiffState {
|
||||||
DiffState {
|
DiffState {
|
||||||
_subscription: cx.subscribe(&diff, |this, diff, event, cx| match event {
|
_subscription: cx.subscribe(&diff, |this, diff, event, cx| match event {
|
||||||
BufferDiffEvent::DiffChanged { changed_range } => {
|
BufferDiffEvent::DiffChanged { changed_range } => {
|
||||||
this.buffer_diff_changed(diff, changed_range.clone(), cx)
|
let changed_range = if let Some(changed_range) = changed_range {
|
||||||
|
changed_range.clone()
|
||||||
|
} else if diff.read(cx).base_text().is_none() && this.all_diff_hunks_expanded {
|
||||||
|
text::Anchor::MIN..text::Anchor::MAX
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
this.buffer_diff_changed(diff, changed_range, cx)
|
||||||
}
|
}
|
||||||
BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx),
|
BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx),
|
||||||
}),
|
}),
|
||||||
|
@ -241,7 +252,7 @@ pub struct MultiBufferSnapshot {
|
||||||
excerpts: SumTree<Excerpt>,
|
excerpts: SumTree<Excerpt>,
|
||||||
excerpt_ids: SumTree<ExcerptIdMapping>,
|
excerpt_ids: SumTree<ExcerptIdMapping>,
|
||||||
diffs: TreeMap<BufferId, BufferDiffSnapshot>,
|
diffs: TreeMap<BufferId, BufferDiffSnapshot>,
|
||||||
pub diff_transforms: SumTree<DiffTransform>,
|
diff_transforms: SumTree<DiffTransform>,
|
||||||
trailing_excerpt_update_count: usize,
|
trailing_excerpt_update_count: usize,
|
||||||
non_text_state_update_count: usize,
|
non_text_state_update_count: usize,
|
||||||
edit_count: usize,
|
edit_count: usize,
|
||||||
|
@ -252,20 +263,27 @@ pub struct MultiBufferSnapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum DiffTransform {
|
enum DiffTransform {
|
||||||
BufferContent {
|
BufferContent {
|
||||||
summary: TextSummary,
|
summary: TextSummary,
|
||||||
inserted_hunk_anchor: Option<(ExcerptId, text::Anchor)>,
|
inserted_hunk_info: Option<DiffTransformHunkInfo>,
|
||||||
},
|
},
|
||||||
DeletedHunk {
|
DeletedHunk {
|
||||||
summary: TextSummary,
|
summary: TextSummary,
|
||||||
buffer_id: BufferId,
|
buffer_id: BufferId,
|
||||||
hunk_anchor: (ExcerptId, text::Anchor),
|
hunk_info: DiffTransformHunkInfo,
|
||||||
base_text_byte_range: Range<usize>,
|
base_text_byte_range: Range<usize>,
|
||||||
has_trailing_newline: bool,
|
has_trailing_newline: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
struct DiffTransformHunkInfo {
|
||||||
|
excerpt_id: ExcerptId,
|
||||||
|
hunk_start_anchor: text::Anchor,
|
||||||
|
hunk_secondary_status: DiffHunkSecondaryStatus,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ExcerptInfo {
|
pub struct ExcerptInfo {
|
||||||
pub id: ExcerptId,
|
pub id: ExcerptId,
|
||||||
|
@ -310,7 +328,7 @@ pub struct RowInfo {
|
||||||
pub buffer_id: Option<BufferId>,
|
pub buffer_id: Option<BufferId>,
|
||||||
pub buffer_row: Option<u32>,
|
pub buffer_row: Option<u32>,
|
||||||
pub multibuffer_row: Option<MultiBufferRow>,
|
pub multibuffer_row: Option<MultiBufferRow>,
|
||||||
pub diff_status: Option<diff::DiffHunkStatus>,
|
pub diff_status: Option<buffer_diff::DiffHunkStatus>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`].
|
/// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`].
|
||||||
|
@ -431,7 +449,7 @@ struct MultiBufferCursor<'a, D: TextDimension> {
|
||||||
struct MultiBufferRegion<'a, D: TextDimension> {
|
struct MultiBufferRegion<'a, D: TextDimension> {
|
||||||
buffer: &'a BufferSnapshot,
|
buffer: &'a BufferSnapshot,
|
||||||
is_main_buffer: bool,
|
is_main_buffer: bool,
|
||||||
is_inserted_hunk: bool,
|
diff_hunk_status: Option<DiffHunkStatus>,
|
||||||
excerpt: &'a Excerpt,
|
excerpt: &'a Excerpt,
|
||||||
buffer_range: Range<D>,
|
buffer_range: Range<D>,
|
||||||
range: Range<D>,
|
range: Range<D>,
|
||||||
|
@ -2146,7 +2164,7 @@ impl MultiBuffer {
|
||||||
let mut snapshot = self.snapshot.borrow_mut();
|
let mut snapshot = self.snapshot.borrow_mut();
|
||||||
let diff = diff.read(cx);
|
let diff = diff.read(cx);
|
||||||
let buffer_id = diff.buffer_id;
|
let buffer_id = diff.buffer_id;
|
||||||
let diff = diff.snapshot.clone();
|
let diff = diff.snapshot(cx);
|
||||||
snapshot.diffs.insert(buffer_id, diff);
|
snapshot.diffs.insert(buffer_id, diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2160,36 +2178,29 @@ impl MultiBuffer {
|
||||||
|
|
||||||
let diff = diff.read(cx);
|
let diff = diff.read(cx);
|
||||||
let buffer_id = diff.buffer_id;
|
let buffer_id = diff.buffer_id;
|
||||||
let mut diff = diff.snapshot.clone();
|
|
||||||
if diff.base_text.is_none() && self.all_diff_hunks_expanded {
|
|
||||||
diff = BufferDiffSnapshot::new_with_single_insertion(cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut snapshot = self.snapshot.borrow_mut();
|
|
||||||
let base_text_changed =
|
|
||||||
snapshot
|
|
||||||
.diffs
|
|
||||||
.get(&buffer_id)
|
|
||||||
.map_or(true, |diff_snapshot| {
|
|
||||||
match (&diff_snapshot.base_text, &diff.base_text) {
|
|
||||||
(None, None) => false,
|
|
||||||
(None, Some(_)) => true,
|
|
||||||
(Some(_), None) => true,
|
|
||||||
(Some(old), Some(new)) => {
|
|
||||||
let (old_id, old_empty) = (old.remote_id(), old.is_empty());
|
|
||||||
let (new_id, new_empty) = (new.remote_id(), new.is_empty());
|
|
||||||
new_id != old_id && (!new_empty || !old_empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
snapshot.diffs.insert(buffer_id, diff);
|
|
||||||
|
|
||||||
let buffers = self.buffers.borrow();
|
let buffers = self.buffers.borrow();
|
||||||
let Some(buffer_state) = buffers.get(&buffer_id) else {
|
let Some(buffer_state) = buffers.get(&buffer_id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let diff_change_range = range.to_offset(buffer_state.buffer.read(cx));
|
let buffer = buffer_state.buffer.read(cx);
|
||||||
|
let diff_change_range = range.to_offset(buffer);
|
||||||
|
|
||||||
|
let mut new_diff = diff.snapshot(cx);
|
||||||
|
if new_diff.base_text().is_none() && self.all_diff_hunks_expanded {
|
||||||
|
let secondary_diff_insertion = new_diff
|
||||||
|
.secondary_diff()
|
||||||
|
.map_or(true, |secondary_diff| secondary_diff.base_text().is_none());
|
||||||
|
new_diff = BufferDiff::build_with_single_insertion(secondary_diff_insertion, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut snapshot = self.snapshot.borrow_mut();
|
||||||
|
let base_text_changed = snapshot
|
||||||
|
.diffs
|
||||||
|
.get(&buffer_id)
|
||||||
|
.map_or(true, |old_diff| !new_diff.base_texts_eq(old_diff));
|
||||||
|
|
||||||
|
snapshot.diffs.insert(buffer_id, new_diff);
|
||||||
|
|
||||||
let mut excerpt_edits = Vec::new();
|
let mut excerpt_edits = Vec::new();
|
||||||
for locator in &buffer_state.excerpts {
|
for locator in &buffer_state.excerpts {
|
||||||
|
@ -2367,7 +2378,7 @@ impl MultiBuffer {
|
||||||
if *cursor.start() >= end {
|
if *cursor.start() >= end {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if item.hunk_anchor().is_some() {
|
if item.hunk_info().is_some() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
cursor.next(&());
|
cursor.next(&());
|
||||||
|
@ -2820,11 +2831,11 @@ impl MultiBuffer {
|
||||||
let keep_next_old_transform = (old_diff_transforms.start().0 >= edit.old.end)
|
let keep_next_old_transform = (old_diff_transforms.start().0 >= edit.old.end)
|
||||||
&& match old_diff_transforms.item() {
|
&& match old_diff_transforms.item() {
|
||||||
Some(DiffTransform::BufferContent {
|
Some(DiffTransform::BufferContent {
|
||||||
inserted_hunk_anchor: Some(hunk_anchor),
|
inserted_hunk_info: Some(hunk),
|
||||||
..
|
..
|
||||||
}) => excerpts
|
}) => excerpts.item().is_some_and(|excerpt| {
|
||||||
.item()
|
hunk.hunk_start_anchor.is_valid(&excerpt.buffer)
|
||||||
.is_some_and(|excerpt| hunk_anchor.1.is_valid(&excerpt.buffer)),
|
}),
|
||||||
_ => true,
|
_ => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2853,7 +2864,7 @@ impl MultiBuffer {
|
||||||
new_diff_transforms.push(
|
new_diff_transforms.push(
|
||||||
DiffTransform::BufferContent {
|
DiffTransform::BufferContent {
|
||||||
summary: Default::default(),
|
summary: Default::default(),
|
||||||
inserted_hunk_anchor: None,
|
inserted_hunk_info: None,
|
||||||
},
|
},
|
||||||
&(),
|
&(),
|
||||||
);
|
);
|
||||||
|
@ -2876,8 +2887,8 @@ impl MultiBuffer {
|
||||||
excerpts: &mut Cursor<Excerpt, TypedOffset<Excerpt>>,
|
excerpts: &mut Cursor<Excerpt, TypedOffset<Excerpt>>,
|
||||||
old_diff_transforms: &mut Cursor<DiffTransform, (TypedOffset<Excerpt>, usize)>,
|
old_diff_transforms: &mut Cursor<DiffTransform, (TypedOffset<Excerpt>, usize)>,
|
||||||
new_diff_transforms: &mut SumTree<DiffTransform>,
|
new_diff_transforms: &mut SumTree<DiffTransform>,
|
||||||
end_of_current_insert: &mut Option<(TypedOffset<Excerpt>, ExcerptId, text::Anchor)>,
|
end_of_current_insert: &mut Option<(TypedOffset<Excerpt>, DiffTransformHunkInfo)>,
|
||||||
old_expanded_hunks: &mut HashSet<(ExcerptId, text::Anchor)>,
|
old_expanded_hunks: &mut HashSet<DiffTransformHunkInfo>,
|
||||||
snapshot: &MultiBufferSnapshot,
|
snapshot: &MultiBufferSnapshot,
|
||||||
change_kind: DiffChangeKind,
|
change_kind: DiffChangeKind,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -2889,12 +2900,12 @@ impl MultiBuffer {
|
||||||
|
|
||||||
// Record which hunks were previously expanded.
|
// Record which hunks were previously expanded.
|
||||||
while let Some(item) = old_diff_transforms.item() {
|
while let Some(item) = old_diff_transforms.item() {
|
||||||
if let Some(hunk_anchor) = item.hunk_anchor() {
|
if let Some(hunk_info) = item.hunk_info() {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
"previously expanded hunk at {}",
|
"previously expanded hunk at {}",
|
||||||
old_diff_transforms.start().0
|
old_diff_transforms.start().0
|
||||||
);
|
);
|
||||||
old_expanded_hunks.insert(hunk_anchor);
|
old_expanded_hunks.insert(hunk_info);
|
||||||
}
|
}
|
||||||
if old_diff_transforms.end(&()).0 > edit.old.end {
|
if old_diff_transforms.end(&()).0 > edit.old.end {
|
||||||
break;
|
break;
|
||||||
|
@ -2918,7 +2929,7 @@ impl MultiBuffer {
|
||||||
if let Some((diff, base_text)) = snapshot
|
if let Some((diff, base_text)) = snapshot
|
||||||
.diffs
|
.diffs
|
||||||
.get(&excerpt.buffer_id)
|
.get(&excerpt.buffer_id)
|
||||||
.and_then(|diff| Some((diff, diff.base_text.as_ref()?)))
|
.and_then(|diff| Some((diff, diff.base_text()?)))
|
||||||
{
|
{
|
||||||
let buffer = &excerpt.buffer;
|
let buffer = &excerpt.buffer;
|
||||||
let excerpt_start = *excerpts.start();
|
let excerpt_start = *excerpts.start();
|
||||||
|
@ -2936,7 +2947,11 @@ impl MultiBuffer {
|
||||||
for hunk in diff.hunks_intersecting_range(edit_anchor_range, buffer) {
|
for hunk in diff.hunks_intersecting_range(edit_anchor_range, buffer) {
|
||||||
let hunk_buffer_range = hunk.buffer_range.to_offset(buffer);
|
let hunk_buffer_range = hunk.buffer_range.to_offset(buffer);
|
||||||
|
|
||||||
let hunk_anchor = (excerpt.id, hunk.buffer_range.start);
|
let hunk_info = DiffTransformHunkInfo {
|
||||||
|
excerpt_id: excerpt.id,
|
||||||
|
hunk_start_anchor: hunk.buffer_range.start,
|
||||||
|
hunk_secondary_status: hunk.secondary_status,
|
||||||
|
};
|
||||||
if hunk_buffer_range.start < excerpt_buffer_start {
|
if hunk_buffer_range.start < excerpt_buffer_start {
|
||||||
log::trace!("skipping hunk that starts before excerpt");
|
log::trace!("skipping hunk that starts before excerpt");
|
||||||
continue;
|
continue;
|
||||||
|
@ -2960,7 +2975,7 @@ impl MultiBuffer {
|
||||||
|
|
||||||
// For every existing hunk, determine if it was previously expanded
|
// For every existing hunk, determine if it was previously expanded
|
||||||
// and if it should currently be expanded.
|
// and if it should currently be expanded.
|
||||||
let was_previously_expanded = old_expanded_hunks.contains(&hunk_anchor);
|
let was_previously_expanded = old_expanded_hunks.contains(&hunk_info);
|
||||||
let should_expand_hunk = match &change_kind {
|
let should_expand_hunk = match &change_kind {
|
||||||
DiffChangeKind::DiffUpdated { base_changed: true } => {
|
DiffChangeKind::DiffUpdated { base_changed: true } => {
|
||||||
self.all_diff_hunks_expanded
|
self.all_diff_hunks_expanded
|
||||||
|
@ -3008,7 +3023,7 @@ impl MultiBuffer {
|
||||||
base_text_byte_range: hunk.diff_base_byte_range.clone(),
|
base_text_byte_range: hunk.diff_base_byte_range.clone(),
|
||||||
summary: base_text_summary,
|
summary: base_text_summary,
|
||||||
buffer_id: excerpt.buffer_id,
|
buffer_id: excerpt.buffer_id,
|
||||||
hunk_anchor,
|
hunk_info,
|
||||||
has_trailing_newline,
|
has_trailing_newline,
|
||||||
},
|
},
|
||||||
&(),
|
&(),
|
||||||
|
@ -3016,11 +3031,8 @@ impl MultiBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hunk_buffer_range.is_empty() {
|
if !hunk_buffer_range.is_empty() {
|
||||||
*end_of_current_insert = Some((
|
*end_of_current_insert =
|
||||||
hunk_excerpt_end.min(excerpt_end),
|
Some((hunk_excerpt_end.min(excerpt_end), hunk_info));
|
||||||
hunk_anchor.0,
|
|
||||||
hunk_anchor.1,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3042,13 +3054,13 @@ impl MultiBuffer {
|
||||||
subtree: SumTree<DiffTransform>,
|
subtree: SumTree<DiffTransform>,
|
||||||
) {
|
) {
|
||||||
if let Some(DiffTransform::BufferContent {
|
if let Some(DiffTransform::BufferContent {
|
||||||
inserted_hunk_anchor,
|
inserted_hunk_info,
|
||||||
summary,
|
summary,
|
||||||
}) = subtree.first()
|
}) = subtree.first()
|
||||||
{
|
{
|
||||||
if self.extend_last_buffer_content_transform(
|
if self.extend_last_buffer_content_transform(
|
||||||
new_transforms,
|
new_transforms,
|
||||||
*inserted_hunk_anchor,
|
*inserted_hunk_info,
|
||||||
*summary,
|
*summary,
|
||||||
) {
|
) {
|
||||||
let mut cursor = subtree.cursor::<()>(&());
|
let mut cursor = subtree.cursor::<()>(&());
|
||||||
|
@ -3067,7 +3079,7 @@ impl MultiBuffer {
|
||||||
transform: DiffTransform,
|
transform: DiffTransform,
|
||||||
) {
|
) {
|
||||||
if let DiffTransform::BufferContent {
|
if let DiffTransform::BufferContent {
|
||||||
inserted_hunk_anchor,
|
inserted_hunk_info: inserted_hunk_anchor,
|
||||||
summary,
|
summary,
|
||||||
} = transform
|
} = transform
|
||||||
{
|
{
|
||||||
|
@ -3087,19 +3099,14 @@ impl MultiBuffer {
|
||||||
old_snapshot: &MultiBufferSnapshot,
|
old_snapshot: &MultiBufferSnapshot,
|
||||||
new_transforms: &mut SumTree<DiffTransform>,
|
new_transforms: &mut SumTree<DiffTransform>,
|
||||||
end_offset: ExcerptOffset,
|
end_offset: ExcerptOffset,
|
||||||
current_inserted_hunk: Option<(ExcerptOffset, ExcerptId, text::Anchor)>,
|
current_inserted_hunk: Option<(ExcerptOffset, DiffTransformHunkInfo)>,
|
||||||
) {
|
) {
|
||||||
let inserted_region =
|
let inserted_region = current_inserted_hunk.map(|(insertion_end_offset, hunk_info)| {
|
||||||
current_inserted_hunk.map(|(insertion_end_offset, excerpt_id, anchor)| {
|
(end_offset.min(insertion_end_offset), Some(hunk_info))
|
||||||
(
|
});
|
||||||
end_offset.min(insertion_end_offset),
|
|
||||||
Some((excerpt_id, anchor)),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let unchanged_region = [(end_offset, None)];
|
let unchanged_region = [(end_offset, None)];
|
||||||
|
|
||||||
for (end_offset, inserted_hunk_anchor) in
|
for (end_offset, inserted_hunk_info) in inserted_region.into_iter().chain(unchanged_region)
|
||||||
inserted_region.into_iter().chain(unchanged_region)
|
|
||||||
{
|
{
|
||||||
let start_offset = new_transforms.summary().excerpt_len();
|
let start_offset = new_transforms.summary().excerpt_len();
|
||||||
if end_offset <= start_offset {
|
if end_offset <= start_offset {
|
||||||
|
@ -3110,13 +3117,13 @@ impl MultiBuffer {
|
||||||
|
|
||||||
if !self.extend_last_buffer_content_transform(
|
if !self.extend_last_buffer_content_transform(
|
||||||
new_transforms,
|
new_transforms,
|
||||||
inserted_hunk_anchor,
|
inserted_hunk_info,
|
||||||
summary_to_add,
|
summary_to_add,
|
||||||
) {
|
) {
|
||||||
new_transforms.push(
|
new_transforms.push(
|
||||||
DiffTransform::BufferContent {
|
DiffTransform::BufferContent {
|
||||||
summary: summary_to_add,
|
summary: summary_to_add,
|
||||||
inserted_hunk_anchor,
|
inserted_hunk_info,
|
||||||
},
|
},
|
||||||
&(),
|
&(),
|
||||||
)
|
)
|
||||||
|
@ -3127,7 +3134,7 @@ impl MultiBuffer {
|
||||||
fn extend_last_buffer_content_transform(
|
fn extend_last_buffer_content_transform(
|
||||||
&self,
|
&self,
|
||||||
new_transforms: &mut SumTree<DiffTransform>,
|
new_transforms: &mut SumTree<DiffTransform>,
|
||||||
new_inserted_hunk_anchor: Option<(ExcerptId, text::Anchor)>,
|
new_inserted_hunk_info: Option<DiffTransformHunkInfo>,
|
||||||
summary_to_add: TextSummary,
|
summary_to_add: TextSummary,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut did_extend = false;
|
let mut did_extend = false;
|
||||||
|
@ -3135,10 +3142,10 @@ impl MultiBuffer {
|
||||||
|last_transform| {
|
|last_transform| {
|
||||||
if let DiffTransform::BufferContent {
|
if let DiffTransform::BufferContent {
|
||||||
summary,
|
summary,
|
||||||
inserted_hunk_anchor,
|
inserted_hunk_info: inserted_hunk_anchor,
|
||||||
} = last_transform
|
} = last_transform
|
||||||
{
|
{
|
||||||
if *inserted_hunk_anchor == new_inserted_hunk_anchor {
|
if *inserted_hunk_anchor == new_inserted_hunk_info {
|
||||||
*summary += summary_to_add;
|
*summary += summary_to_add;
|
||||||
did_extend = true;
|
did_extend = true;
|
||||||
}
|
}
|
||||||
|
@ -3469,6 +3476,7 @@ impl MultiBufferSnapshot {
|
||||||
excerpt_id: excerpt.id,
|
excerpt_id: excerpt.id,
|
||||||
buffer_range: hunk.buffer_range.clone(),
|
buffer_range: hunk.buffer_range.clone(),
|
||||||
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
|
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
|
||||||
|
secondary_status: hunk.secondary_status,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -3837,6 +3845,7 @@ impl MultiBufferSnapshot {
|
||||||
excerpt_id: excerpt.id,
|
excerpt_id: excerpt.id,
|
||||||
buffer_range: hunk.buffer_range.clone(),
|
buffer_range: hunk.buffer_range.clone(),
|
||||||
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
|
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
|
||||||
|
secondary_status: hunk.secondary_status,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4309,10 +4318,7 @@ impl MultiBufferSnapshot {
|
||||||
} => {
|
} => {
|
||||||
let buffer_start = base_text_byte_range.start + start_overshoot;
|
let buffer_start = base_text_byte_range.start + start_overshoot;
|
||||||
let mut buffer_end = base_text_byte_range.start + end_overshoot;
|
let mut buffer_end = base_text_byte_range.start + end_overshoot;
|
||||||
let Some(base_text) = self
|
let Some(base_text) = self.diffs.get(buffer_id).and_then(|diff| diff.base_text())
|
||||||
.diffs
|
|
||||||
.get(buffer_id)
|
|
||||||
.and_then(|diff| diff.base_text.as_ref())
|
|
||||||
else {
|
else {
|
||||||
panic!("{:?} is in non-existent deleted hunk", range.start)
|
panic!("{:?} is in non-existent deleted hunk", range.start)
|
||||||
};
|
};
|
||||||
|
@ -4361,10 +4367,7 @@ impl MultiBufferSnapshot {
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let buffer_end = base_text_byte_range.start + overshoot;
|
let buffer_end = base_text_byte_range.start + overshoot;
|
||||||
let Some(base_text) = self
|
let Some(base_text) = self.diffs.get(buffer_id).and_then(|diff| diff.base_text())
|
||||||
.diffs
|
|
||||||
.get(buffer_id)
|
|
||||||
.and_then(|diff| diff.base_text.as_ref())
|
|
||||||
else {
|
else {
|
||||||
panic!("{:?} is in non-existent deleted hunk", range.end)
|
panic!("{:?} is in non-existent deleted hunk", range.end)
|
||||||
};
|
};
|
||||||
|
@ -4469,10 +4472,8 @@ impl MultiBufferSnapshot {
|
||||||
}) => {
|
}) => {
|
||||||
let mut in_deleted_hunk = false;
|
let mut in_deleted_hunk = false;
|
||||||
if let Some(diff_base_anchor) = &anchor.diff_base_anchor {
|
if let Some(diff_base_anchor) = &anchor.diff_base_anchor {
|
||||||
if let Some(base_text) = self
|
if let Some(base_text) =
|
||||||
.diffs
|
self.diffs.get(buffer_id).and_then(|diff| diff.base_text())
|
||||||
.get(buffer_id)
|
|
||||||
.and_then(|diff| diff.base_text.as_ref())
|
|
||||||
{
|
{
|
||||||
if base_text.can_resolve(&diff_base_anchor) {
|
if base_text.can_resolve(&diff_base_anchor) {
|
||||||
let base_text_offset = diff_base_anchor.to_offset(&base_text);
|
let base_text_offset = diff_base_anchor.to_offset(&base_text);
|
||||||
|
@ -4809,7 +4810,7 @@ impl MultiBufferSnapshot {
|
||||||
let base_text = self
|
let base_text = self
|
||||||
.diffs
|
.diffs
|
||||||
.get(buffer_id)
|
.get(buffer_id)
|
||||||
.and_then(|diff| diff.base_text.as_ref())
|
.and_then(|diff| diff.base_text())
|
||||||
.expect("missing diff base");
|
.expect("missing diff base");
|
||||||
if offset_in_transform > base_text_byte_range.len() {
|
if offset_in_transform > base_text_byte_range.len() {
|
||||||
debug_assert!(*has_trailing_newline);
|
debug_assert!(*has_trailing_newline);
|
||||||
|
@ -5969,17 +5970,17 @@ impl MultiBufferSnapshot {
|
||||||
for item in self.diff_transforms.iter() {
|
for item in self.diff_transforms.iter() {
|
||||||
if let DiffTransform::BufferContent {
|
if let DiffTransform::BufferContent {
|
||||||
summary,
|
summary,
|
||||||
inserted_hunk_anchor,
|
inserted_hunk_info,
|
||||||
} = item
|
} = item
|
||||||
{
|
{
|
||||||
if let Some(DiffTransform::BufferContent {
|
if let Some(DiffTransform::BufferContent {
|
||||||
inserted_hunk_anchor: prev_inserted_hunk_anchor,
|
inserted_hunk_info: prev_inserted_hunk_info,
|
||||||
..
|
..
|
||||||
}) = prev_transform
|
}) = prev_transform
|
||||||
{
|
{
|
||||||
if *inserted_hunk_anchor == *prev_inserted_hunk_anchor {
|
if *inserted_hunk_info == *prev_inserted_hunk_info {
|
||||||
panic!(
|
panic!(
|
||||||
"multiple adjacent buffer content transforms with is_inserted_hunk = {inserted_hunk_anchor:?}. transforms: {:+?}",
|
"multiple adjacent buffer content transforms with is_inserted_hunk = {inserted_hunk_info:?}. transforms: {:+?}",
|
||||||
self.diff_transforms.items(&()));
|
self.diff_transforms.items(&()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6149,10 +6150,11 @@ where
|
||||||
buffer_id,
|
buffer_id,
|
||||||
base_text_byte_range,
|
base_text_byte_range,
|
||||||
has_trailing_newline,
|
has_trailing_newline,
|
||||||
|
hunk_info,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let diff = self.diffs.get(&buffer_id)?;
|
let diff = self.diffs.get(&buffer_id)?;
|
||||||
let buffer = diff.base_text.as_ref()?;
|
let buffer = diff.base_text()?;
|
||||||
let mut rope_cursor = buffer.as_rope().cursor(0);
|
let mut rope_cursor = buffer.as_rope().cursor(0);
|
||||||
let buffer_start = rope_cursor.summary::<D>(base_text_byte_range.start);
|
let buffer_start = rope_cursor.summary::<D>(base_text_byte_range.start);
|
||||||
let buffer_range_len = rope_cursor.summary::<D>(base_text_byte_range.end);
|
let buffer_range_len = rope_cursor.summary::<D>(base_text_byte_range.end);
|
||||||
|
@ -6165,14 +6167,15 @@ where
|
||||||
excerpt,
|
excerpt,
|
||||||
has_trailing_newline: *has_trailing_newline,
|
has_trailing_newline: *has_trailing_newline,
|
||||||
is_main_buffer: false,
|
is_main_buffer: false,
|
||||||
is_inserted_hunk: false,
|
diff_hunk_status: Some(DiffHunkStatus::Removed(
|
||||||
|
hunk_info.hunk_secondary_status,
|
||||||
|
)),
|
||||||
buffer_range: buffer_start..buffer_end,
|
buffer_range: buffer_start..buffer_end,
|
||||||
range: start..end,
|
range: start..end,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
DiffTransform::BufferContent {
|
DiffTransform::BufferContent {
|
||||||
inserted_hunk_anchor,
|
inserted_hunk_info, ..
|
||||||
..
|
|
||||||
} => {
|
} => {
|
||||||
let buffer = &excerpt.buffer;
|
let buffer = &excerpt.buffer;
|
||||||
let buffer_context_start = excerpt.range.context.start.summary::<D>(buffer);
|
let buffer_context_start = excerpt.range.context.start.summary::<D>(buffer);
|
||||||
|
@ -6209,7 +6212,8 @@ where
|
||||||
excerpt,
|
excerpt,
|
||||||
has_trailing_newline,
|
has_trailing_newline,
|
||||||
is_main_buffer: true,
|
is_main_buffer: true,
|
||||||
is_inserted_hunk: inserted_hunk_anchor.is_some(),
|
diff_hunk_status: inserted_hunk_info
|
||||||
|
.map(|info| DiffHunkStatus::Added(info.hunk_secondary_status)),
|
||||||
buffer_range: buffer_start..buffer_end,
|
buffer_range: buffer_start..buffer_end,
|
||||||
range: start..end,
|
range: start..end,
|
||||||
})
|
})
|
||||||
|
@ -6717,13 +6721,12 @@ impl sum_tree::KeyedItem for ExcerptIdMapping {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiffTransform {
|
impl DiffTransform {
|
||||||
fn hunk_anchor(&self) -> Option<(ExcerptId, text::Anchor)> {
|
fn hunk_info(&self) -> Option<DiffTransformHunkInfo> {
|
||||||
match self {
|
match self {
|
||||||
DiffTransform::DeletedHunk { hunk_anchor, .. } => Some(*hunk_anchor),
|
DiffTransform::DeletedHunk { hunk_info, .. } => Some(*hunk_info),
|
||||||
DiffTransform::BufferContent {
|
DiffTransform::BufferContent {
|
||||||
inserted_hunk_anchor,
|
inserted_hunk_info, ..
|
||||||
..
|
} => *inserted_hunk_info,
|
||||||
} => *inserted_hunk_anchor,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7020,13 +7023,9 @@ impl<'a> Iterator for MultiBufferRows<'a> {
|
||||||
buffer_id: Some(region.buffer.remote_id()),
|
buffer_id: Some(region.buffer.remote_id()),
|
||||||
buffer_row: Some(buffer_point.row),
|
buffer_row: Some(buffer_point.row),
|
||||||
multibuffer_row: Some(MultiBufferRow(self.point.row)),
|
multibuffer_row: Some(MultiBufferRow(self.point.row)),
|
||||||
diff_status: if region.is_inserted_hunk && self.point < region.range.end {
|
diff_status: region
|
||||||
Some(DiffHunkStatus::Added)
|
.diff_hunk_status
|
||||||
} else if !region.is_main_buffer {
|
.filter(|_| self.point < region.range.end),
|
||||||
Some(DiffHunkStatus::Removed)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
self.point += Point::new(1, 0);
|
self.point += Point::new(1, 0);
|
||||||
result
|
result
|
||||||
|
@ -7194,7 +7193,7 @@ impl<'a> Iterator for MultiBufferChunks<'a> {
|
||||||
}
|
}
|
||||||
chunks
|
chunks
|
||||||
} else {
|
} else {
|
||||||
let base_buffer = &self.diffs.get(&buffer_id)?.base_text.as_ref()?;
|
let base_buffer = &self.diffs.get(&buffer_id)?.base_text()?;
|
||||||
base_buffer.chunks(base_text_start..base_text_end, self.language_aware)
|
base_buffer.chunks(base_text_start..base_text_end, self.language_aware)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use diff::DiffHunkStatus;
|
use buffer_diff::DiffHunkStatus;
|
||||||
use gpui::{App, TestAppContext};
|
use gpui::{App, TestAppContext};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use language::{Buffer, Rope};
|
use language::{Buffer, Rope};
|
||||||
|
@ -979,8 +979,6 @@ fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
|
||||||
|
|
||||||
let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
|
let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
|
||||||
multibuffer.update(cx, |multibuffer, cx| {
|
multibuffer.update(cx, |multibuffer, cx| {
|
||||||
multibuffer.set_all_diff_hunks_expanded(cx);
|
|
||||||
multibuffer.add_diff(diff.clone(), cx);
|
|
||||||
multibuffer.push_excerpts(
|
multibuffer.push_excerpts(
|
||||||
buffer.clone(),
|
buffer.clone(),
|
||||||
[ExcerptRange {
|
[ExcerptRange {
|
||||||
|
@ -989,6 +987,8 @@ fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
|
||||||
}],
|
}],
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
multibuffer.set_all_diff_hunks_expanded(cx);
|
||||||
|
multibuffer.add_diff(diff.clone(), cx);
|
||||||
});
|
});
|
||||||
cx.run_until_parked();
|
cx.run_until_parked();
|
||||||
|
|
||||||
|
@ -1325,13 +1325,13 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) {
|
||||||
.map(|info| (info.buffer_row, info.diff_status))
|
.map(|info| (info.buffer_row, info.diff_status))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
vec![
|
vec![
|
||||||
(Some(0), Some(DiffHunkStatus::Added)),
|
(Some(0), Some(DiffHunkStatus::added())),
|
||||||
(Some(1), None),
|
(Some(1), None),
|
||||||
(Some(1), Some(DiffHunkStatus::Removed)),
|
(Some(1), Some(DiffHunkStatus::removed())),
|
||||||
(Some(2), Some(DiffHunkStatus::Added)),
|
(Some(2), Some(DiffHunkStatus::added())),
|
||||||
(Some(3), None),
|
(Some(3), None),
|
||||||
(Some(3), Some(DiffHunkStatus::Removed)),
|
(Some(3), Some(DiffHunkStatus::removed())),
|
||||||
(Some(4), Some(DiffHunkStatus::Removed)),
|
(Some(4), Some(DiffHunkStatus::removed())),
|
||||||
(Some(4), None),
|
(Some(4), None),
|
||||||
(Some(5), None)
|
(Some(5), None)
|
||||||
]
|
]
|
||||||
|
@ -1999,12 +1999,8 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
|
||||||
|
|
||||||
let id_1 = buffer_1.read_with(cx, |buffer, _| buffer.remote_id());
|
let id_1 = buffer_1.read_with(cx, |buffer, _| buffer.remote_id());
|
||||||
let id_2 = buffer_2.read_with(cx, |buffer, _| buffer.remote_id());
|
let id_2 = buffer_2.read_with(cx, |buffer, _| buffer.remote_id());
|
||||||
let base_id_1 = diff_1.read_with(cx, |diff, _| {
|
let base_id_1 = diff_1.read_with(cx, |diff, _| diff.base_text().as_ref().unwrap().remote_id());
|
||||||
diff.snapshot.base_text.as_ref().unwrap().remote_id()
|
let base_id_2 = diff_2.read_with(cx, |diff, _| diff.base_text().as_ref().unwrap().remote_id());
|
||||||
});
|
|
||||||
let base_id_2 = diff_2.read_with(cx, |diff, _| {
|
|
||||||
diff.snapshot.base_text.as_ref().unwrap().remote_id()
|
|
||||||
});
|
|
||||||
|
|
||||||
let buffer_lines = (0..=snapshot.max_row().0)
|
let buffer_lines = (0..=snapshot.max_row().0)
|
||||||
.map(|row| {
|
.map(|row| {
|
||||||
|
@ -2191,9 +2187,8 @@ impl ReferenceMultibuffer {
|
||||||
let Some(diff) = self.diffs.get(&buffer_id) else {
|
let Some(diff) = self.diffs.get(&buffer_id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let diff = diff.read(cx).snapshot.clone();
|
|
||||||
let excerpt_range = excerpt.range.to_offset(&buffer);
|
let excerpt_range = excerpt.range.to_offset(&buffer);
|
||||||
for hunk in diff.hunks_intersecting_range(range, &buffer) {
|
for hunk in diff.read(cx).hunks_intersecting_range(range, &buffer, cx) {
|
||||||
let hunk_range = hunk.buffer_range.to_offset(&buffer);
|
let hunk_range = hunk.buffer_range.to_offset(&buffer);
|
||||||
if hunk_range.start < excerpt_range.start || hunk_range.start > excerpt_range.end {
|
if hunk_range.start < excerpt_range.start || hunk_range.start > excerpt_range.end {
|
||||||
continue;
|
continue;
|
||||||
|
@ -2226,12 +2221,12 @@ impl ReferenceMultibuffer {
|
||||||
let buffer = excerpt.buffer.read(cx);
|
let buffer = excerpt.buffer.read(cx);
|
||||||
let buffer_range = excerpt.range.to_offset(buffer);
|
let buffer_range = excerpt.range.to_offset(buffer);
|
||||||
let diff = self.diffs.get(&buffer.remote_id()).unwrap().read(cx);
|
let diff = self.diffs.get(&buffer.remote_id()).unwrap().read(cx);
|
||||||
let diff = diff.snapshot.clone();
|
// let diff = diff.snapshot.clone();
|
||||||
let base_buffer = diff.base_text.as_ref().unwrap();
|
let base_buffer = diff.base_text().unwrap();
|
||||||
|
|
||||||
let mut offset = buffer_range.start;
|
let mut offset = buffer_range.start;
|
||||||
let mut hunks = diff
|
let mut hunks = diff
|
||||||
.hunks_intersecting_range(excerpt.range.clone(), buffer)
|
.hunks_intersecting_range(excerpt.range.clone(), buffer, cx)
|
||||||
.peekable();
|
.peekable();
|
||||||
|
|
||||||
while let Some(hunk) = hunks.next() {
|
while let Some(hunk) = hunks.next() {
|
||||||
|
@ -2284,7 +2279,7 @@ impl ReferenceMultibuffer {
|
||||||
buffer_start: Some(
|
buffer_start: Some(
|
||||||
base_buffer.offset_to_point(hunk.diff_base_byte_range.start),
|
base_buffer.offset_to_point(hunk.diff_base_byte_range.start),
|
||||||
),
|
),
|
||||||
status: Some(DiffHunkStatus::Removed),
|
status: Some(DiffHunkStatus::Removed(hunk.secondary_status)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2299,7 +2294,7 @@ impl ReferenceMultibuffer {
|
||||||
buffer_id: Some(buffer.remote_id()),
|
buffer_id: Some(buffer.remote_id()),
|
||||||
range: len..text.len(),
|
range: len..text.len(),
|
||||||
buffer_start: Some(buffer.offset_to_point(offset)),
|
buffer_start: Some(buffer.offset_to_point(offset)),
|
||||||
status: Some(DiffHunkStatus::Added),
|
status: Some(DiffHunkStatus::Added(hunk.secondary_status)),
|
||||||
});
|
});
|
||||||
offset = hunk_range.end;
|
offset = hunk_range.end;
|
||||||
}
|
}
|
||||||
|
@ -2365,8 +2360,8 @@ impl ReferenceMultibuffer {
|
||||||
let buffer = excerpt.buffer.read(cx).snapshot();
|
let buffer = excerpt.buffer.read(cx).snapshot();
|
||||||
let excerpt_range = excerpt.range.to_offset(&buffer);
|
let excerpt_range = excerpt.range.to_offset(&buffer);
|
||||||
let buffer_id = buffer.remote_id();
|
let buffer_id = buffer.remote_id();
|
||||||
let diff = &self.diffs.get(&buffer_id).unwrap().read(cx).snapshot;
|
let diff = self.diffs.get(&buffer_id).unwrap().read(cx);
|
||||||
let mut hunks = diff.hunks_in_row_range(0..u32::MAX, &buffer).peekable();
|
let mut hunks = diff.hunks_in_row_range(0..u32::MAX, &buffer, cx).peekable();
|
||||||
excerpt.expanded_diff_hunks.retain(|hunk_anchor| {
|
excerpt.expanded_diff_hunks.retain(|hunk_anchor| {
|
||||||
if !hunk_anchor.is_valid(&buffer) {
|
if !hunk_anchor.is_valid(&buffer) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -2670,7 +2665,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
|
||||||
expected_row_infos
|
expected_row_infos
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(
|
.filter_map(
|
||||||
|info| if info.diff_status == Some(DiffHunkStatus::Removed) {
|
|info| if matches!(info.diff_status, Some(DiffHunkStatus::Removed(_))) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
info.buffer_row
|
info.buffer_row
|
||||||
|
@ -3027,9 +3022,9 @@ fn format_diff(
|
||||||
.zip(row_infos)
|
.zip(row_infos)
|
||||||
.map(|((ix, line), info)| {
|
.map(|((ix, line), info)| {
|
||||||
let marker = match info.diff_status {
|
let marker = match info.diff_status {
|
||||||
Some(DiffHunkStatus::Added) => "+ ",
|
Some(DiffHunkStatus::Added(_)) => "+ ",
|
||||||
Some(DiffHunkStatus::Removed) => "- ",
|
Some(DiffHunkStatus::Removed(_)) => "- ",
|
||||||
Some(DiffHunkStatus::Modified) => unreachable!(),
|
Some(DiffHunkStatus::Modified(_)) => unreachable!(),
|
||||||
None => {
|
None => {
|
||||||
if has_diff && !line.is_empty() {
|
if has_diff && !line.is_empty() {
|
||||||
" "
|
" "
|
||||||
|
|
|
@ -30,7 +30,7 @@ async-trait.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
clock.workspace = true
|
clock.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
diff.workspace = true
|
buffer_diff.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
fuzzy.workspace = true
|
||||||
|
@ -78,7 +78,7 @@ fancy-regex.workspace = true
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
client = { workspace = true, features = ["test-support"] }
|
client = { workspace = true, features = ["test-support"] }
|
||||||
collections = { workspace = true, features = ["test-support"] }
|
collections = { workspace = true, features = ["test-support"] }
|
||||||
diff = { workspace = true, features = ["test-support"] }
|
buffer_diff = { workspace = true, features = ["test-support"] }
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
fs = { workspace = true, features = ["test-support"] }
|
fs = { workspace = true, features = ["test-support"] }
|
||||||
git2.workspace = true
|
git2.workspace = true
|
||||||
|
|
|
@ -6,9 +6,9 @@ use crate::{
|
||||||
};
|
};
|
||||||
use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};
|
use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};
|
||||||
use anyhow::{anyhow, bail, Context as _, Result};
|
use anyhow::{anyhow, bail, Context as _, Result};
|
||||||
|
use buffer_diff::{BufferDiff, BufferDiffEvent};
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use collections::{hash_map, HashMap, HashSet};
|
use collections::{hash_map, HashMap, HashSet};
|
||||||
use diff::{BufferDiff, BufferDiffEvent, BufferDiffSnapshot};
|
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use futures::{channel::oneshot, future::Shared, Future, FutureExt as _, StreamExt};
|
use futures::{channel::oneshot, future::Shared, Future, FutureExt as _, StreamExt};
|
||||||
use git::{blame::Blame, repository::RepoPath};
|
use git::{blame::Blame, repository::RepoPath};
|
||||||
|
@ -207,72 +207,74 @@ impl BufferDiffState {
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
self.recalculate_diff_task = Some(cx.spawn(|this, mut cx| async move {
|
self.recalculate_diff_task = Some(cx.spawn(|this, mut cx| async move {
|
||||||
|
let mut unstaged_changed_range = None;
|
||||||
if let Some(unstaged_diff) = &unstaged_diff {
|
if let Some(unstaged_diff) = &unstaged_diff {
|
||||||
let snapshot = if index_changed || language_changed {
|
unstaged_changed_range = BufferDiff::update_diff(
|
||||||
cx.update(|cx| {
|
unstaged_diff.clone(),
|
||||||
BufferDiffSnapshot::build(
|
buffer.clone(),
|
||||||
buffer.clone(),
|
index,
|
||||||
index,
|
index_changed,
|
||||||
language.clone(),
|
language_changed,
|
||||||
language_registry.clone(),
|
language.clone(),
|
||||||
cx,
|
language_registry.clone(),
|
||||||
)
|
&mut cx,
|
||||||
})?
|
)
|
||||||
.await
|
.await?;
|
||||||
} else {
|
|
||||||
unstaged_diff
|
|
||||||
.read_with(&cx, |changes, cx| {
|
|
||||||
BufferDiffSnapshot::build_with_base_buffer(
|
|
||||||
buffer.clone(),
|
|
||||||
index,
|
|
||||||
changes.snapshot.base_text.clone(),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})?
|
|
||||||
.await
|
|
||||||
};
|
|
||||||
|
|
||||||
unstaged_diff.update(&mut cx, |unstaged_diff, cx| {
|
unstaged_diff.update(&mut cx, |_, cx| {
|
||||||
unstaged_diff.set_state(snapshot, &buffer, cx);
|
|
||||||
if language_changed {
|
if language_changed {
|
||||||
cx.emit(BufferDiffEvent::LanguageChanged);
|
cx.emit(BufferDiffEvent::LanguageChanged);
|
||||||
}
|
}
|
||||||
|
if let Some(changed_range) = unstaged_changed_range.clone() {
|
||||||
|
cx.emit(BufferDiffEvent::DiffChanged {
|
||||||
|
changed_range: Some(changed_range),
|
||||||
|
})
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(uncommitted_diff) = &uncommitted_diff {
|
if let Some(uncommitted_diff) = &uncommitted_diff {
|
||||||
let snapshot =
|
let uncommitted_changed_range =
|
||||||
if let (Some(unstaged_diff), true) = (&unstaged_diff, index_matches_head) {
|
if let (Some(unstaged_diff), true) = (&unstaged_diff, index_matches_head) {
|
||||||
unstaged_diff.read_with(&cx, |diff, _| diff.snapshot.clone())?
|
uncommitted_diff.update(&mut cx, |uncommitted_diff, cx| {
|
||||||
} else if head_changed || language_changed {
|
uncommitted_diff.update_diff_from(&buffer, unstaged_diff, cx)
|
||||||
cx.update(|cx| {
|
|
||||||
BufferDiffSnapshot::build(
|
|
||||||
buffer.clone(),
|
|
||||||
head,
|
|
||||||
language.clone(),
|
|
||||||
language_registry.clone(),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})?
|
})?
|
||||||
.await
|
|
||||||
} else {
|
} else {
|
||||||
uncommitted_diff
|
BufferDiff::update_diff(
|
||||||
.read_with(&cx, |changes, cx| {
|
uncommitted_diff.clone(),
|
||||||
BufferDiffSnapshot::build_with_base_buffer(
|
buffer.clone(),
|
||||||
buffer.clone(),
|
head,
|
||||||
head,
|
head_changed,
|
||||||
changes.snapshot.base_text.clone(),
|
language_changed,
|
||||||
cx,
|
language.clone(),
|
||||||
)
|
language_registry.clone(),
|
||||||
})?
|
&mut cx,
|
||||||
.await
|
)
|
||||||
|
.await?
|
||||||
};
|
};
|
||||||
|
|
||||||
uncommitted_diff.update(&mut cx, |diff, cx| {
|
uncommitted_diff.update(&mut cx, |uncommitted_diff, cx| {
|
||||||
diff.set_state(snapshot, &buffer, cx);
|
|
||||||
if language_changed {
|
if language_changed {
|
||||||
cx.emit(BufferDiffEvent::LanguageChanged);
|
cx.emit(BufferDiffEvent::LanguageChanged);
|
||||||
}
|
}
|
||||||
|
let changed_range = match (unstaged_changed_range, uncommitted_changed_range) {
|
||||||
|
(None, None) => None,
|
||||||
|
(Some(unstaged_range), None) => {
|
||||||
|
uncommitted_diff.range_to_hunk_range(unstaged_range, &buffer, cx)
|
||||||
|
}
|
||||||
|
(None, Some(uncommitted_range)) => Some(uncommitted_range),
|
||||||
|
(Some(unstaged_range), Some(uncommitted_range)) => maybe!({
|
||||||
|
let expanded_range = uncommitted_diff.range_to_hunk_range(
|
||||||
|
unstaged_range,
|
||||||
|
&buffer,
|
||||||
|
cx,
|
||||||
|
)?;
|
||||||
|
let start = expanded_range.start.min(&uncommitted_range.start, &buffer);
|
||||||
|
let end = expanded_range.end.max(&uncommitted_range.end, &buffer);
|
||||||
|
Some(start..end)
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
cx.emit(BufferDiffEvent::DiffChanged { changed_range });
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,6 +282,7 @@ impl BufferDiffState {
|
||||||
this.update(&mut cx, |this, _| {
|
this.update(&mut cx, |this, _| {
|
||||||
this.index_changed = false;
|
this.index_changed = false;
|
||||||
this.head_changed = false;
|
this.head_changed = false;
|
||||||
|
this.language_changed = false;
|
||||||
for tx in this.diff_updated_futures.drain(..) {
|
for tx in this.diff_updated_futures.drain(..) {
|
||||||
tx.send(()).ok();
|
tx.send(()).ok();
|
||||||
}
|
}
|
||||||
|
@ -1478,29 +1481,19 @@ impl BufferStore {
|
||||||
diff_state.language = language;
|
diff_state.language = language;
|
||||||
diff_state.language_registry = language_registry;
|
diff_state.language_registry = language_registry;
|
||||||
|
|
||||||
let diff = cx.new(|_| BufferDiff {
|
let diff = cx.new(|_| BufferDiff::new(&text_snapshot));
|
||||||
buffer_id,
|
|
||||||
snapshot: BufferDiffSnapshot::new(&text_snapshot),
|
|
||||||
unstaged_diff: None,
|
|
||||||
});
|
|
||||||
match kind {
|
match kind {
|
||||||
DiffKind::Unstaged => diff_state.unstaged_diff = Some(diff.downgrade()),
|
DiffKind::Unstaged => diff_state.unstaged_diff = Some(diff.downgrade()),
|
||||||
DiffKind::Uncommitted => {
|
DiffKind::Uncommitted => {
|
||||||
let unstaged_diff = if let Some(diff) = diff_state.unstaged_diff() {
|
let unstaged_diff = if let Some(diff) = diff_state.unstaged_diff() {
|
||||||
diff
|
diff
|
||||||
} else {
|
} else {
|
||||||
let unstaged_diff = cx.new(|_| BufferDiff {
|
let unstaged_diff = cx.new(|_| BufferDiff::new(&text_snapshot));
|
||||||
buffer_id,
|
|
||||||
snapshot: BufferDiffSnapshot::new(&text_snapshot),
|
|
||||||
unstaged_diff: None,
|
|
||||||
});
|
|
||||||
diff_state.unstaged_diff = Some(unstaged_diff.downgrade());
|
diff_state.unstaged_diff = Some(unstaged_diff.downgrade());
|
||||||
unstaged_diff
|
unstaged_diff
|
||||||
};
|
};
|
||||||
|
|
||||||
diff.update(cx, |diff, _| {
|
diff.update(cx, |diff, _| diff.set_secondary_diff(unstaged_diff));
|
||||||
diff.unstaged_diff = Some(unstaged_diff);
|
|
||||||
});
|
|
||||||
diff_state.uncommitted_diff = Some(diff.downgrade())
|
diff_state.uncommitted_diff = Some(diff.downgrade())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2397,9 +2390,8 @@ impl BufferStore {
|
||||||
shared.diff = Some(diff.clone());
|
shared.diff = Some(diff.clone());
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
let staged_text = diff.read_with(&cx, |diff, _| {
|
let staged_text =
|
||||||
diff.snapshot.base_text.as_ref().map(|buffer| buffer.text())
|
diff.read_with(&cx, |diff, _| diff.base_text().map(|buffer| buffer.text()))?;
|
||||||
})?;
|
|
||||||
Ok(proto::OpenUnstagedDiffResponse { staged_text })
|
Ok(proto::OpenUnstagedDiffResponse { staged_text })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2430,14 +2422,13 @@ impl BufferStore {
|
||||||
use proto::open_uncommitted_diff_response::Mode;
|
use proto::open_uncommitted_diff_response::Mode;
|
||||||
|
|
||||||
let staged_buffer = diff
|
let staged_buffer = diff
|
||||||
.unstaged_diff
|
.secondary_diff()
|
||||||
.as_ref()
|
.and_then(|diff| diff.read(cx).base_text());
|
||||||
.and_then(|diff| diff.read(cx).snapshot.base_text.as_ref());
|
|
||||||
|
|
||||||
let mode;
|
let mode;
|
||||||
let staged_text;
|
let staged_text;
|
||||||
let committed_text;
|
let committed_text;
|
||||||
if let Some(committed_buffer) = &diff.snapshot.base_text {
|
if let Some(committed_buffer) = diff.base_text() {
|
||||||
committed_text = Some(committed_buffer.text());
|
committed_text = Some(committed_buffer.text());
|
||||||
if let Some(staged_buffer) = staged_buffer {
|
if let Some(staged_buffer) = staged_buffer {
|
||||||
if staged_buffer.remote_id() == committed_buffer.remote_id() {
|
if staged_buffer.remote_id() == committed_buffer.remote_id() {
|
||||||
|
|
|
@ -21,7 +21,7 @@ mod project_tests;
|
||||||
|
|
||||||
mod direnv;
|
mod direnv;
|
||||||
mod environment;
|
mod environment;
|
||||||
use diff::BufferDiff;
|
use buffer_diff::BufferDiff;
|
||||||
pub use environment::EnvironmentErrorMessage;
|
pub use environment::EnvironmentErrorMessage;
|
||||||
use git::Repository;
|
use git::Repository;
|
||||||
pub mod search_history;
|
pub mod search_history;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{Event, *};
|
use crate::{Event, *};
|
||||||
use diff::assert_hunks;
|
use buffer_diff::{assert_hunks, DiffHunkSecondaryStatus, DiffHunkStatus};
|
||||||
use fs::FakeFs;
|
use fs::FakeFs;
|
||||||
use futures::{future, StreamExt};
|
use futures::{future, StreamExt};
|
||||||
use gpui::{App, SemanticVersion, UpdateGlobal};
|
use gpui::{App, SemanticVersion, UpdateGlobal};
|
||||||
|
@ -5692,15 +5692,16 @@ async fn test_unstaged_diff_for_buffer(cx: &mut gpui::TestAppContext) {
|
||||||
unstaged_diff.update(cx, |unstaged_diff, cx| {
|
unstaged_diff.update(cx, |unstaged_diff, cx| {
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
assert_hunks(
|
assert_hunks(
|
||||||
unstaged_diff.diff_hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot),
|
unstaged_diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot, cx),
|
||||||
&snapshot,
|
&snapshot,
|
||||||
&unstaged_diff.base_text_string().unwrap(),
|
&unstaged_diff.base_text_string().unwrap(),
|
||||||
&[
|
&[
|
||||||
(0..1, "", "// print goodbye\n"),
|
(0..1, "", "// print goodbye\n", DiffHunkStatus::added()),
|
||||||
(
|
(
|
||||||
2..3,
|
2..3,
|
||||||
" println!(\"hello world\");\n",
|
" println!(\"hello world\");\n",
|
||||||
" println!(\"goodbye world\");\n",
|
" println!(\"goodbye world\");\n",
|
||||||
|
DiffHunkStatus::modified(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -5722,10 +5723,15 @@ async fn test_unstaged_diff_for_buffer(cx: &mut gpui::TestAppContext) {
|
||||||
unstaged_diff.update(cx, |unstaged_diff, cx| {
|
unstaged_diff.update(cx, |unstaged_diff, cx| {
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
assert_hunks(
|
assert_hunks(
|
||||||
unstaged_diff.diff_hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot),
|
unstaged_diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot, cx),
|
||||||
&snapshot,
|
&snapshot,
|
||||||
&unstaged_diff.snapshot.base_text.as_ref().unwrap().text(),
|
&unstaged_diff.base_text().unwrap().text(),
|
||||||
&[(2..3, "", " println!(\"goodbye world\");\n")],
|
&[(
|
||||||
|
2..3,
|
||||||
|
"",
|
||||||
|
" println!(\"goodbye world\");\n",
|
||||||
|
DiffHunkStatus::added(),
|
||||||
|
)],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5795,10 +5801,7 @@ async fn test_uncommitted_diff_for_buffer(cx: &mut gpui::TestAppContext) {
|
||||||
|
|
||||||
uncommitted_diff.read_with(cx, |diff, _| {
|
uncommitted_diff.read_with(cx, |diff, _| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
diff.snapshot
|
diff.base_text().and_then(|base| base.language().cloned()),
|
||||||
.base_text
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|base| base.language().cloned()),
|
|
||||||
Some(language)
|
Some(language)
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -5807,15 +5810,21 @@ async fn test_uncommitted_diff_for_buffer(cx: &mut gpui::TestAppContext) {
|
||||||
uncommitted_diff.update(cx, |uncommitted_diff, cx| {
|
uncommitted_diff.update(cx, |uncommitted_diff, cx| {
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
assert_hunks(
|
assert_hunks(
|
||||||
uncommitted_diff.diff_hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot),
|
uncommitted_diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot, cx),
|
||||||
&snapshot,
|
&snapshot,
|
||||||
&uncommitted_diff.base_text_string().unwrap(),
|
&uncommitted_diff.base_text_string().unwrap(),
|
||||||
&[
|
&[
|
||||||
(0..1, "", "// print goodbye\n"),
|
(
|
||||||
|
0..1,
|
||||||
|
"",
|
||||||
|
"// print goodbye\n",
|
||||||
|
DiffHunkStatus::Added(DiffHunkSecondaryStatus::HasSecondaryHunk),
|
||||||
|
),
|
||||||
(
|
(
|
||||||
2..3,
|
2..3,
|
||||||
" println!(\"hello world\");\n",
|
" println!(\"hello world\");\n",
|
||||||
" println!(\"goodbye world\");\n",
|
" println!(\"goodbye world\");\n",
|
||||||
|
DiffHunkStatus::modified(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -5837,10 +5846,15 @@ async fn test_uncommitted_diff_for_buffer(cx: &mut gpui::TestAppContext) {
|
||||||
uncommitted_diff.update(cx, |uncommitted_diff, cx| {
|
uncommitted_diff.update(cx, |uncommitted_diff, cx| {
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
assert_hunks(
|
assert_hunks(
|
||||||
uncommitted_diff.diff_hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot),
|
uncommitted_diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot, cx),
|
||||||
&snapshot,
|
&snapshot,
|
||||||
&uncommitted_diff.snapshot.base_text.as_ref().unwrap().text(),
|
&uncommitted_diff.base_text().unwrap().text(),
|
||||||
&[(2..3, "", " println!(\"goodbye world\");\n")],
|
&[(
|
||||||
|
2..3,
|
||||||
|
"",
|
||||||
|
" println!(\"goodbye world\");\n",
|
||||||
|
DiffHunkStatus::added(),
|
||||||
|
)],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5898,13 +5912,14 @@ async fn test_single_file_diffs(cx: &mut gpui::TestAppContext) {
|
||||||
uncommitted_diff.update(cx, |uncommitted_diff, cx| {
|
uncommitted_diff.update(cx, |uncommitted_diff, cx| {
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
assert_hunks(
|
assert_hunks(
|
||||||
uncommitted_diff.diff_hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot),
|
uncommitted_diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot, cx),
|
||||||
&snapshot,
|
&snapshot,
|
||||||
&uncommitted_diff.snapshot.base_text.as_ref().unwrap().text(),
|
&uncommitted_diff.base_text_string().unwrap(),
|
||||||
&[(
|
&[(
|
||||||
1..2,
|
1..2,
|
||||||
" println!(\"hello from HEAD\");\n",
|
" println!(\"hello from HEAD\");\n",
|
||||||
" println!(\"hello from the working copy\");\n",
|
" println!(\"hello from the working copy\");\n",
|
||||||
|
DiffHunkStatus::modified(),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1246,8 +1246,7 @@ async fn test_remote_git_diffs(cx: &mut TestAppContext, server_cx: &mut TestAppC
|
||||||
diff.read_with(cx, |diff, cx| {
|
diff.read_with(cx, |diff, cx| {
|
||||||
assert_eq!(diff.base_text_string().unwrap(), text_1);
|
assert_eq!(diff.base_text_string().unwrap(), text_1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
diff.unstaged_diff
|
diff.secondary_diff()
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.base_text_string()
|
.base_text_string()
|
||||||
|
@ -1266,8 +1265,7 @@ async fn test_remote_git_diffs(cx: &mut TestAppContext, server_cx: &mut TestAppC
|
||||||
diff.read_with(cx, |diff, cx| {
|
diff.read_with(cx, |diff, cx| {
|
||||||
assert_eq!(diff.base_text_string().unwrap(), text_1);
|
assert_eq!(diff.base_text_string().unwrap(), text_1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
diff.unstaged_diff
|
diff.secondary_diff()
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.base_text_string()
|
.base_text_string()
|
||||||
|
@ -1286,8 +1284,7 @@ async fn test_remote_git_diffs(cx: &mut TestAppContext, server_cx: &mut TestAppC
|
||||||
diff.read_with(cx, |diff, cx| {
|
diff.read_with(cx, |diff, cx| {
|
||||||
assert_eq!(diff.base_text_string().unwrap(), text_2);
|
assert_eq!(diff.base_text_string().unwrap(), text_2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
diff.unstaged_diff
|
diff.secondary_diff()
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.base_text_string()
|
.base_text_string()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue