perf: cache file attribution for instant blame navigation

This commit is contained in:
Valdemar Lorenzen
2026-10-13 12:33:15 +02:00
parent 7a3c27c30e
commit e36c28c981
6 changed files with 461 additions and 148 deletions
+51 -11
View File
@@ -1561,13 +1561,19 @@ impl Buffer {
}
fn refresh_file(&mut self) -> io::Result<bool> {
if self.diff_snapshot.is_some() || self.dirty() {
if self.diff_snapshot.is_some() {
return Ok(false);
}
let Some(path) = &self.path else {
return Ok(false);
};
let stamp = FileStamp::read(path)?;
if self.dirty() {
if stamp.is_none() && self.disk_stamp.is_some() {
return Err(io::Error::new(io::ErrorKind::NotFound, "File was removed"));
}
return Ok(false);
}
if stamp == self.disk_stamp {
return Ok(false);
}
@@ -1670,8 +1676,10 @@ struct PendingLspNavigation {
pub(crate) struct BlameSource {
buffer: usize,
revision: u64,
pub(crate) path: PathBuf,
pub(crate) path: Arc<Path>,
pub(crate) row: usize,
save_revision: u64,
disk_stamp: Option<FileStamp>,
pane: usize,
tab: usize,
}
@@ -1712,7 +1720,7 @@ pub struct Editor {
battery_charge: Option<u8>,
battery_label: String,
git_blame: bool,
line_blame: Option<(BlameSource, String)>,
line_blame: Option<(BlameSource, Arc<crate::blame::LineBlame>, String)>,
lsp_names: Vec<String>,
lsp_label: String,
quit: bool,
@@ -4682,27 +4690,37 @@ impl Editor {
|| self.should_quit()
|| self.diff.is_some()
|| self.active.diff_snapshot.is_some()
|| self.active.reload_error.is_some()
{
return None;
}
Some(BlameSource {
buffer: self.buffer_id(),
revision: self.text_revision(),
path: self.path()?.to_owned(),
path: Arc::from(self.path()?),
row: self.cursor().row,
save_revision: self.active.save_revision,
disk_stamp: self.active.disk_stamp,
pane: self.pane_id(),
tab: self.tab_id(),
})
}
pub(crate) fn blame_source_is_current(&self, source: &BlameSource) -> bool {
pub(crate) fn blame_file_source_is_current(&self, source: &BlameSource) -> bool {
self.git_blame
&& !self.should_quit()
&& self.diff.is_none()
&& self.active.diff_snapshot.is_none()
&& self.active.reload_error.is_none()
&& source.buffer == self.buffer_id()
&& source.revision == self.text_revision()
&& Some(source.path.as_path()) == self.path()
&& source.save_revision == self.active.save_revision
&& source.disk_stamp == self.active.disk_stamp
&& Some(source.path.as_ref()) == self.path()
}
pub(crate) fn blame_source_is_current(&self, source: &BlameSource) -> bool {
self.blame_file_source_is_current(source)
&& source.row == self.cursor().row
&& source.pane == self.pane_id()
&& source.tab == self.tab_id()
@@ -4712,14 +4730,32 @@ impl Editor {
self.line_blame.take().is_some()
}
pub(crate) fn set_cached_line_blame(
&mut self,
source: &BlameSource,
blame: Arc<crate::blame::LineBlame>,
) -> bool {
let mut source = source.clone();
source.row = self.cursor().row;
source.pane = self.pane_id();
source.tab = self.tab_id();
self.set_line_blame(source, blame)
}
pub(crate) fn set_line_blame(
&mut self,
source: BlameSource,
blame: crate::blame::LineBlame,
blame: Arc<crate::blame::LineBlame>,
) -> bool {
if !self.blame_source_is_current(&source) {
return false;
}
if let Some((previous, metadata, _)) = &mut self.line_blame
&& Arc::ptr_eq(metadata, &blame)
{
*previous = source;
return false;
}
let label = if blame.uncommitted {
"Uncommitted".to_owned()
} else {
@@ -4728,15 +4764,19 @@ impl Editor {
blame.author, blame.summary, blame.commit
))
};
self.line_blame = Some((source, label));
true
let changed = self
.line_blame
.as_ref()
.is_none_or(|(_, _, previous)| previous != &label);
self.line_blame = Some((source, blame, label));
changed
}
pub(crate) fn line_blame(&self) -> Option<&str> {
self.line_blame
.as_ref()
.filter(|(source, _)| self.blame_source_is_current(source))
.map(|(_, label)| label.as_str())
.filter(|(source, _, _)| self.blame_source_is_current(source))
.map(|(_, _, label)| label.as_str())
}
pub fn lsp_names(&self) -> &[String] {
&self.lsp_names