editor: Do not reserve inline blame scroll width when blame is in the status bar (#61422)
## Problem When `git.inline_blame.location` is `status_bar` (anything other than `inline`), nothing is painted inline at the end of the line — yet the horizontal scroll range still reserves the longest row's inline blame width (introduced in #23374), letting the editor scroll into blank space when a line is long enough to scroll. Reported in #24752 (e.g. @Nemesis19's comment: still reproduces with `location: status_bar`). ## Fix Skip the blame width reservation in the scroll range when the blame location isn't `Inline` — one guard in the `longest_line_blame_width` closure in `element.rs`. This is independent of the soft-wrap fix for #24752 (#61319) and can land on its own. ## Test `test_status_bar_blame_location_reserves_no_scroll_width` asserts that, with a long line and no soft wrap, moving blame from inline to the status bar shrinks the horizontal scroll range (the reservation is dropped). It uses a fixed-width test `BlameRenderer`, since the default test renderer returns `None` and never exercised the reservation. Release Notes: - Fixed inline git blame reserving horizontal scroll space even when blame is displayed in the status bar
This commit is contained in:
1 parent
f555af0236
commit
9cbae806f2
1 file changed
+198
-1
@@ -64,7 +64,7 @@ use multi_buffer::{
|
||||
|
||||
use project::{
|
||||
debugger::breakpoint_store::{Breakpoint, BreakpointSessionState},
|
||||
project_settings::ProjectSettings,
|
||||
project_settings::{InlineBlameLocation, ProjectSettings},
|
||||
};
|
||||
use settings::{
|
||||
GitGutterSetting, GitHunkStyleSetting, IndentGuideBackgroundColoring, IndentGuideColoring,
|
||||
@@ -8599,6 +8599,14 @@ impl Element for EditorElement {
|
||||
if !editor.show_git_blame_inline {
|
||||
return None;
|
||||
}
|
||||
// Blame is only painted inline for the Inline location, so
|
||||
// reserving scroll room for it in other locations would let
|
||||
// the editor scroll into blank space.
|
||||
if ProjectSettings::get_global(cx).git.inline_blame.location
|
||||
!= InlineBlameLocation::Inline
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let blame = editor.blame.as_ref()?;
|
||||
let (_, blame_entry) = blame
|
||||
.update(cx, |blame, cx| {
|
||||
@@ -10999,6 +11007,195 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_status_bar_blame_location_reserves_no_scroll_width(cx: &mut TestAppContext) {
|
||||
struct FixedWidthBlameRenderer;
|
||||
|
||||
impl BlameRenderer for FixedWidthBlameRenderer {
|
||||
fn max_author_length(&self) -> usize {
|
||||
20
|
||||
}
|
||||
|
||||
fn render_blame_entry(
|
||||
&self,
|
||||
_: &gpui::TextStyle,
|
||||
_: BlameEntry,
|
||||
_: Option<ParsedCommitMessage>,
|
||||
_: Vec<SharedString>,
|
||||
_: Entity<project::git_store::Repository>,
|
||||
_: WeakEntity<Workspace>,
|
||||
_: Entity<Editor>,
|
||||
_: usize,
|
||||
_: Hsla,
|
||||
_: &mut Window,
|
||||
_: &mut App,
|
||||
) -> Option<AnyElement> {
|
||||
None
|
||||
}
|
||||
|
||||
fn render_inline_blame_entry(
|
||||
&self,
|
||||
_: &gpui::TextStyle,
|
||||
_: BlameEntry,
|
||||
_: &mut App,
|
||||
) -> Option<AnyElement> {
|
||||
Some(div().w(px(160.)).into_any_element())
|
||||
}
|
||||
|
||||
fn render_blame_entry_popover(
|
||||
&self,
|
||||
_: BlameEntry,
|
||||
_: ScrollHandle,
|
||||
_: Option<ParsedCommitMessage>,
|
||||
_: Vec<SharedString>,
|
||||
_: Entity<Markdown>,
|
||||
_: Entity<project::git_store::Repository>,
|
||||
_: WeakEntity<Workspace>,
|
||||
_: &mut Window,
|
||||
_: &mut App,
|
||||
) -> Option<AnyElement> {
|
||||
None
|
||||
}
|
||||
|
||||
fn open_blame_commit(
|
||||
&self,
|
||||
_: BlameEntry,
|
||||
_: Entity<project::git_store::Repository>,
|
||||
_: WeakEntity<Workspace>,
|
||||
_: &mut Window,
|
||||
_: &mut App,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
init_test(cx, |_| {});
|
||||
cx.update(|cx| crate::git::set_blame_renderer(FixedWidthBlameRenderer, cx));
|
||||
|
||||
let fs = project::FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
util::path!("/my-repo"),
|
||||
serde_json::json!({
|
||||
".git": {},
|
||||
"file.txt": "a ".repeat(100),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
fs.set_blame_for_repo(
|
||||
std::path::Path::new(util::path!("/my-repo/.git")),
|
||||
vec![(
|
||||
git::repository::repo_path("file.txt"),
|
||||
git::blame::Blame {
|
||||
entries: vec![BlameEntry {
|
||||
sha: "1b1b1b".parse().unwrap(),
|
||||
range: 0..1,
|
||||
original_line_number: 0,
|
||||
author: None,
|
||||
author_mail: None,
|
||||
author_time: None,
|
||||
author_tz: None,
|
||||
committer_name: None,
|
||||
committer_email: None,
|
||||
committer_time: None,
|
||||
committer_tz: None,
|
||||
summary: None,
|
||||
previous: None,
|
||||
filename: String::new(),
|
||||
}],
|
||||
..Default::default()
|
||||
},
|
||||
)],
|
||||
);
|
||||
|
||||
let project = project::Project::test(fs, [util::path!("/my-repo").as_ref()], cx).await;
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer(util::path!("/my-repo/file.txt"), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let buffer_id = buffer.read_with(cx, |buffer, _| buffer.remote_id());
|
||||
let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
|
||||
|
||||
let window = cx.add_window(|window, cx| {
|
||||
// No soft wrap: a long line legitimately scrolls horizontally, so the
|
||||
// inline blame width reservation is meaningful and observable here.
|
||||
let mut editor = Editor::new(EditorMode::full(), buffer, Some(project), window, cx);
|
||||
editor.set_soft_wrap_mode(language_settings::SoftWrap::None, cx);
|
||||
editor
|
||||
});
|
||||
let cx = &mut VisualTestContext::from_window(*window, cx);
|
||||
let editor = window.root(cx).unwrap();
|
||||
cx.update(|window, cx| window.focus(&editor.read(cx).focus_handle(cx), cx));
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor
|
||||
.blame()
|
||||
.expect("inline blame should be running")
|
||||
.clone()
|
||||
.update(cx, |blame, cx| blame.focus(cx))
|
||||
});
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
// Ensure the blame entry actually loaded, so a broken setup can't let this
|
||||
// test pass vacuously with a zero-width blame reservation on both draws.
|
||||
editor.update(cx, |editor, cx| {
|
||||
assert!(editor.show_git_blame_inline);
|
||||
let blame = editor
|
||||
.blame()
|
||||
.expect("inline blame should be running")
|
||||
.clone();
|
||||
let entry = blame.update(cx, |blame, cx| {
|
||||
blame
|
||||
.blame_for_rows(
|
||||
&[RowInfo {
|
||||
buffer_row: Some(0),
|
||||
buffer_id: Some(buffer_id),
|
||||
..Default::default()
|
||||
}],
|
||||
cx,
|
||||
)
|
||||
.next()
|
||||
.flatten()
|
||||
});
|
||||
assert!(entry.is_some(), "blame entry should be available");
|
||||
});
|
||||
|
||||
let style = cx.update(|_, cx| editor.update(cx, |editor, cx| editor.style(cx).clone()));
|
||||
|
||||
// Default `Inline` location: the long line plus the reserved inline blame
|
||||
// width push the horizontal scroll range past the viewport.
|
||||
let (_, state) = cx.draw(Default::default(), size(px(226.), px(500.)), |_, _| {
|
||||
EditorElement::new(&editor, style.clone())
|
||||
});
|
||||
let scroll_max_with_inline_blame = state.position_map.scroll_max.x;
|
||||
assert!(
|
||||
scroll_max_with_inline_blame > 0.,
|
||||
"inline blame on a long line should reserve horizontal scroll room"
|
||||
);
|
||||
|
||||
// Moving blame to the status bar paints nothing inline, so the reservation
|
||||
// must be dropped and the scroll range shrink accordingly.
|
||||
cx.update(|_, cx| {
|
||||
cx.update_global::<settings::SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings(cx, |settings| {
|
||||
settings
|
||||
.git
|
||||
.get_or_insert_default()
|
||||
.inline_blame
|
||||
.get_or_insert_default()
|
||||
.location = Some(settings::InlineBlameLocation::StatusBar);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let (_, state) = cx.draw(Default::default(), size(px(226.), px(500.)), |_, _| {
|
||||
EditorElement::new(&editor, style.clone())
|
||||
});
|
||||
assert!(
|
||||
state.position_map.scroll_max.x < scroll_max_with_inline_blame,
|
||||
"Blame in the status bar should not reserve horizontal scroll room"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_point_for_position_clipped_rows(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
Reference in new issue
Block a user