feat: add Git and Jujutsu gutter indicators

This commit is contained in:
2026-10-13 17:44:29 +02:00
parent e84d70463f
commit 55cdee1a13
14 changed files with 1392 additions and 32 deletions
+73 -1
View File
@@ -1511,6 +1511,12 @@ impl FileStamp {
}
}
struct GutterChanges {
revision: u64,
path: PathBuf,
lines: Vec<Option<crate::gutter::Change>>,
}
struct Buffer {
id: usize,
diff_snapshot: Option<(u64, usize, Focus)>,
@@ -1524,6 +1530,7 @@ struct Buffer {
save_revision: u64,
diagnostics: BTreeMap<String, AnnotationSet>,
semantic_tokens: Option<Arc<[SemanticToken]>>,
gutter_changes: Option<GutterChanges>,
view: ViewState,
path: Option<PathBuf>,
identity: Option<PathBuf>,
@@ -1580,6 +1587,7 @@ impl Buffer {
save_revision: 0,
diagnostics: BTreeMap::new(),
semantic_tokens: None,
gutter_changes: None,
view: ViewState {
targets: vec![Target::caret(Cursor::default())],
..ViewState::empty()
@@ -1755,6 +1763,7 @@ pub struct Editor {
battery_charge: Option<u8>,
battery_label: String,
git_blame: bool,
gutter_style: crate::gutter::GutterStyle,
line_blame: Option<(BlameSource, Arc<crate::blame::LineBlame>, String)>,
lsp_names: Vec<String>,
lsp_label: String,
@@ -1855,6 +1864,7 @@ impl Editor {
battery_charge: None,
battery_label: String::new(),
git_blame: true,
gutter_style: crate::gutter::GutterStyle::default(),
line_blame: None,
lsp_names: Vec::new(),
lsp_label: String::new(),
@@ -4884,6 +4894,68 @@ impl Editor {
pub fn battery_label(&self) -> &str {
&self.battery_label
}
pub(crate) fn gutter_style(&self) -> crate::gutter::GutterStyle {
self.gutter_style
}
pub(crate) fn set_gutter_style(&mut self, style: crate::gutter::GutterStyle) {
self.gutter_style = style;
}
pub(crate) fn gutter_change(&self, row: usize) -> Option<crate::gutter::Change> {
if self.active.diff_snapshot.is_some() || self.active.reload_error.is_some() {
return None;
}
let changes = self.active.gutter_changes.as_ref()?;
if changes.revision != self.active.text_revision
|| Some(changes.path.as_path()) != self.path()
{
return None;
}
changes.lines.get(row).copied().flatten()
}
pub(crate) fn set_gutter_changes(
&mut self,
buffer: usize,
revision: u64,
path: &Path,
changes: Vec<Option<crate::gutter::Change>>,
) -> bool {
let buffer = if buffer == self.active.id {
&mut self.active
} else if let Some(buffer) = self.parked.get_mut(&buffer) {
buffer
} else {
return false;
};
if buffer.diff_snapshot.is_some()
|| buffer.text_revision != revision
|| buffer.path.as_deref() != Some(path)
|| changes.len() > buffer.lines.len()
{
return false;
}
if buffer
.gutter_changes
.as_ref()
.is_some_and(|old| old.revision == revision && old.path == path && old.lines == changes)
{
return false;
}
let visible = changes.iter().any(Option::is_some)
|| buffer
.gutter_changes
.as_ref()
.is_some_and(|old| old.lines.iter().any(Option::is_some));
buffer.gutter_changes = Some(GutterChanges {
revision,
path: path.to_path_buf(),
lines: changes,
});
visible
}
pub fn git_blame(&self) -> bool {
self.git_blame
}
@@ -10305,7 +10377,7 @@ fn validate_text(text: &str) -> Result<(), String> {
}
}
fn normalize_newlines(text: &str) -> Cow<'_, str> {
pub(crate) fn normalize_newlines(text: &str) -> Cow<'_, str> {
if !text.contains('\r') {
return Cow::Borrowed(text);
}