Render correspoding hunk on inline credit hover
Dependencies
- [2]
WFWTKCJNCreate initial Visual Studio Code extension - [3]
72K45XKDRefactor inline credit to improve hover messages - [4]
M2NAH3DCStore modified time of `FileContents` as `jiff::Timestamp` - [5]
TIPGJCUWConsistently use `EditorWorkingCopy` in `pijul-extension`
Change contents
- replacement in pijul-extension/src/lib.rs at line 17
use libpijul::change::ChangeHeader;use libpijul::change::{Atom, BaseHunk, Change, ChangeHeader, TextSerError}; - replacement in pijul-extension/src/lib.rs at line 23
use libpijul::{ArcTxn, ChangeId, ChannelRef, TxnT};use libpijul::{ArcTxn, ChangeId, ChannelRef, TxnT, Vertex}; - replacement in pijul-extension/src/lib.rs at line 28
use crate::file_system::changes::{FileCredits, FileCreditsError};use crate::file_system::changes::{ActiveHunk, CreditSource, FileCredits, FileCreditsError, HunkDiff,}; - edit in pijul-extension/src/lib.rs at line 75
}#[derive(Debug, thiserror::Error)]pub enum ActiveHunkError<C: std::error::Error + 'static> {#[error("File is not tracked by Pijul")]Untracked,#[error("Missing file contents for hunk: {hunk:#?}")]MissingFileContents {hunk: Box<BaseHunk<Atom<Option<libpijul::Hash>>, libpijul::change::Local>>,},#[error("Change not found: {change_id:?}")]ChangeNotFound { change_id: ChangeId },#[error("No hunk found for vertex: {vertex:?}")]NoHunkForVertex { vertex: Vertex<ChangeId> },#[error("Failed to get change contents: {0}")]ChangeContents(#[from] TextSerError<C>),#[error("Hunk not yet implemented: {hunk:#?}")]UnimplementedHunk {hunk: Box<BaseHunk<Atom<Option<libpijul::Hash>>, libpijul::change::Local>>,}, - edit in pijul-extension/src/lib.rs at line 148
// TODO: LRU cache for recent changes - replacement in pijul-extension/src/lib.rs at line 197
pub fn get_change_header(&self, change_id: &ChangeId) -> Option<&ChangeHeader> {self.channel_state.get_change_header(change_id)pub fn get_change(&self, change_id: &ChangeId) -> Option<&Change> {self.channel_state.get_change(change_id)}pub fn get_active_hunk(&self,open_file: &OpenFile,credit_source: CreditSource,) -> Result<ActiveHunk, ActiveHunkError<C::Error>> {let Some(credits) = open_file.credits.as_ref() else {return Err(ActiveHunkError::Untracked);};let change_contents = match credit_source {CreditSource::Tracked { vertex } => {let Some(change) = self.channel_state.get_change(&vertex.change) else {return Err(ActiveHunkError::ChangeNotFound {change_id: vertex.change,});};&change.contents}CreditSource::Untracked { hunk_index: _ } => &credits.unrecorded_state.change_contents,};let (hunk_index, hunk) = match credit_source {CreditSource::Tracked { vertex } => self.channel_state.find_vertex_hunk(vertex).ok_or_else(|| ActiveHunkError::NoHunkForVertex { vertex })?,CreditSource::Untracked { hunk_index } => {// Return 1-indexed hunks(hunk_index + 1, &credits.unrecorded_state.hunks[hunk_index])}};// TODO: properly handle encodinglet hunk_diff = match hunk {BaseHunk::FileAdd {contents,add_name: _,add_inode: _,path: _,encoding,} => {let Some(contents) = contents else {return Err(ActiveHunkError::MissingFileContents {hunk: Box::new(hunk.clone()),});};let file_contents = libpijul::change::get_change_contents(&self.change_store,contents,change_contents,)?;let file_text = String::from_utf8(file_contents).unwrap();HunkDiff::TextChange {lines_added: file_text.lines().map(str::to_string).collect(),lines_removed: Vec::new(),}}BaseHunk::Replacement {change,replacement,local: _,encoding,} => {let old_contents = libpijul::change::get_change_contents(&self.change_store,change,change_contents,)?;let new_contents = libpijul::change::get_change_contents(&self.change_store,replacement,change_contents,)?;let old_text = String::from_utf8(old_contents).unwrap();let new_text = String::from_utf8(new_contents).unwrap();HunkDiff::TextChange {lines_added: new_text.lines().map(str::to_string).collect(),lines_removed: old_text.lines().map(str::to_string).collect(),}}BaseHunk::Edit {change,local: _,encoding,} => {let new_contents = libpijul::change::get_change_contents(&self.change_store,change,change_contents,)?;let text = String::from_utf8(new_contents).unwrap();if let Atom::EdgeMap(edge) = change&& (edge.edges.is_empty() || edge.edges[0].flag.is_deleted()){HunkDiff::TextChange {lines_added: Vec::new(),lines_removed: text.lines().map(str::to_string).collect(),}} else {HunkDiff::TextChange {lines_added: text.lines().map(str::to_string).collect(),lines_removed: Vec::new(),}}}_ => {return Err(ActiveHunkError::UnimplementedHunk {hunk: Box::new(hunk.clone()),});}};Ok(ActiveHunk {index: hunk_index,diff: hunk_diff,}) - replacement in pijul-extension/src/file_system/open_file/mod.rs at line 36
None => Vec::from([Span {value: CreditSource::Untracked,lines,}]),None => Vec::new(), - replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 8
use crate::file_system::changes::Span;use crate::file_system::changes::{HunkOffset, Span}; - edit in pijul-extension/src/file_system/changes/unrecorded.rs at line 18
#[derive(Clone, Copy, Debug)]pub enum SpanKind {Replacement {old_line_count: usize,new_line_count: usize,},Addition {lines_added: usize,},Deletion {lines_removed: usize,},} - replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 20
pub spans: Vec<Span<SpanKind>>,pub change_contents: Vec<u8>,pub hunks: Vec<BaseHunk<Atom<Option<libpijul::Hash>>, libpijul::change::Local>>,pub spans: Vec<Span<HunkOffset>>, - edit in pijul-extension/src/file_system/changes/unrecorded.rs at line 57
- edit in pijul-extension/src/file_system/changes/unrecorded.rs at line 59
let mut hunks = Vec::with_capacity(unrecorded_state.actions.len()); - replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 65
match globalized_hunk {match &globalized_hunk { - replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 74
&change,change, - replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 79
&replacement,replacement, - replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 92
value: SpanKind::Replacement {value: HunkOffset::Replacement { - replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 106
&change,change, - edit in pijul-extension/src/file_system/changes/unrecorded.rs at line 115
// Skip deletions as they don't contribute any spans - replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 119
value: SpanKind::Deletion {value: HunkOffset::Deletion { - replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 126
value: SpanKind::Addition {value: HunkOffset::Addition { - edit in pijul-extension/src/file_system/changes/unrecorded.rs at line 141
hunks.push(globalized_hunk); - edit in pijul-extension/src/file_system/changes/unrecorded.rs at line 147
change_contents: change_contents.clone(),hunks, - replacement in pijul-extension/src/file_system/changes/recorded.rs at line 33
spans: Vec<Span<ChangeId>>,spans: Vec<Span<Vertex<ChangeId>>>, - replacement in pijul-extension/src/file_system/changes/recorded.rs at line 41
pub spans: Vec<Span<ChangeId>>,pub spans: Vec<Span<Vertex<ChangeId>>>, - replacement in pijul-extension/src/file_system/changes/recorded.rs at line 89
value: CreditSource::Tracked(span.value),value: CreditSource::Tracked { vertex: span.value }, - replacement in pijul-extension/src/file_system/changes/recorded.rs at line 134
value: vertex.change,value: vertex, - replacement in pijul-extension/src/file_system/changes/mod.rs at line 7
use libpijul::{ArcTxn, ChangeId, ChannelRef};use libpijul::{ArcTxn, ChangeId, ChannelRef, Vertex}; - edit in pijul-extension/src/file_system/changes/mod.rs at line 15
// TODO: conflicts etc#[derive(Clone, Debug)]pub enum HunkDiff {TextChange {lines_added: Vec<String>,lines_removed: Vec<String>,},} - edit in pijul-extension/src/file_system/changes/mod.rs at line 26
pub struct ActiveHunk {pub index: usize,pub diff: HunkDiff,}#[derive(Clone, Copy, Debug)]pub enum HunkOffset {Replacement {old_line_count: usize,new_line_count: usize,},Addition {lines_added: usize,},Deletion {lines_removed: usize,},}#[derive(Clone, Debug)] - replacement in pijul-extension/src/file_system/changes/mod.rs at line 89
#[derive(Clone, Debug, PartialEq, Eq)]#[derive(Clone, Copy, Debug, PartialEq, Eq)] - replacement in pijul-extension/src/file_system/changes/mod.rs at line 91
Tracked(ChangeId),Untracked,Tracked { vertex: Vertex<ChangeId> },Untracked { hunk_index: usize }, - replacement in pijul-extension/src/file_system/changes/mod.rs at line 145
let mut untracked_spans = self.unrecorded_state.spans.iter().peekable();let mut untracked_spans = self.unrecorded_state.spans.iter().enumerate().peekable(); - replacement in pijul-extension/src/file_system/changes/mod.rs at line 149
while let Some(untracked_span) = untracked_spans.next() {while let Some((hunk_index, untracked_span)) = untracked_spans.next() { - replacement in pijul-extension/src/file_system/changes/mod.rs at line 160
unrecorded::SpanKind::Replacement {HunkOffset::Replacement { - replacement in pijul-extension/src/file_system/changes/mod.rs at line 165
value: CreditSource::Untracked,value: CreditSource::Untracked { hunk_index }, - replacement in pijul-extension/src/file_system/changes/mod.rs at line 173
unrecorded::SpanKind::Addition { lines_added } => {HunkOffset::Addition { lines_added } => { - replacement in pijul-extension/src/file_system/changes/mod.rs at line 175
value: CreditSource::Untracked,value: CreditSource::Untracked { hunk_index }, - replacement in pijul-extension/src/file_system/changes/mod.rs at line 181
unrecorded::SpanKind::Deletion { lines_removed } => {HunkOffset::Deletion { lines_removed } => { - replacement in pijul-extension/src/file_system/changes/mod.rs at line 190
if let Some(next_untracked_span) = untracked_spans.peek()if let Some((_next_hunk_index, next_untracked_span)) = untracked_spans.peek() - replacement in pijul-extension/src/channel_state.rs at line 4
use libpijul::change::ChangeHeader;use libpijul::change::{Atom, BaseHunk, Change, ChangeHeader, Local}; - replacement in pijul-extension/src/channel_state.rs at line 8
use libpijul::{ArcTxn, ChangeId, ChannelRef, GraphTxnT, TxnTExt};use libpijul::{ArcTxn, ChangeId, ChannelRef, GraphTxnT, Hash, TxnTExt, Vertex}; - replacement in pijul-extension/src/channel_state.rs at line 24
changes: HashMap<ChangeId, ChangeHeader>,// TODO: lazy access from ChangeStore?changes: HashMap<ChangeId, Change>, - replacement in pijul-extension/src/channel_state.rs at line 62
.get_header(&hash.into()).get_change(&hash.into()) - edit in pijul-extension/src/channel_state.rs at line 65
dbg!(&change_header.authors); - replacement in pijul-extension/src/channel_state.rs at line 75
pub fn get_change_header(&self, change_id: &ChangeId) -> Option<&ChangeHeader> {pub fn find_vertex_hunk(&self,vertex: Vertex<ChangeId>,) -> Option<(usize, &BaseHunk<Atom<Option<Hash>>, Local>)> {let change = self.changes.get(&vertex.change)?;change.changes.iter().enumerate().find_map(|(index, hunk)| {if hunk.iter().any(|atom| match atom {Atom::NewVertex(new_vertex) => {// Changes that depend on this one may later fragment the original contents,// so the current vertex may be a subset of the original contents.new_vertex.start <= vertex.start && new_vertex.end >= vertex.end}Atom::EdgeMap(_edge_map) => false,}) {// Hunks are 1-indexedSome((index + 1, hunk))} else {None}})}pub fn get_change(&self, change_id: &ChangeId) -> Option<&Change> { - replacement in pijul-extension/src/channel_state.rs at line 103
self.changes.values()self.changes.values().map(|change| &change.header) - edit in extensions/vscode/templates/hover/untracked.html at line 1[3.81]
{{ hunk_template }} - replacement in extensions/vscode/templates/hover/tracked.html at line 20
{% if let Some(description) = description %}<hr />{%- if let Some(description) = description %} - replacement in extensions/vscode/templates/hover/tracked.html at line 24
{% endif %}[3.560]<hr />{% endif %}{{ hunk_template }} - file addition: diff.md[3.40]
**{{ heading }}**```diff{%- for removed_line in lines_removed %}- {{removed_line}}{%- endfor %}{%- for added_line in lines_added %}+ {{added_line}}{%- endfor %}``` - replacement in extensions/vscode/src/inline_credit/mod.rs at line 91
&credit_span.value,open_file,credit_span.value, - replacement in extensions/vscode/src/inline_credit/line_annotation.rs at line 61
CreditSource::Tracked(change_id) => {let change_header = repository.get_change_header(change_id).ok_or_else(|| {napi::Error::from_reason(format!("unable to get change {change_id:#?}"))CreditSource::Tracked { vertex } => {let change = repository.get_change(&vertex.change).ok_or_else(|| {napi::Error::from_reason(format!("Unable to get change for verte {vertex:#?}")) - edit in extensions/vscode/src/inline_credit/line_annotation.rs at line 65
- replacement in extensions/vscode/src/inline_credit/line_annotation.rs at line 67
.authors_for_change(change_header).authors_for_change(&change.header) - replacement in extensions/vscode/src/inline_credit/line_annotation.rs at line 84
timestamp: change_header.timestamp,message: &change_header.message,timestamp: change.header.timestamp,message: &change.header.message, - replacement in extensions/vscode/src/inline_credit/line_annotation.rs at line 88
CreditSource::Untracked => {CreditSource::Untracked { .. } => { - replacement in extensions/vscode/src/inline_credit/hover.rs at line 12
use pijul_extension::file_system::changes::CreditSource;use pijul_extension::file_system::changes::{CreditSource, HunkDiff};use pijul_extension::file_system::open_file::OpenFile; - edit in extensions/vscode/src/inline_credit/hover.rs at line 21
}#[localize("l10n/**/inline_credit/hover.ftl")]struct HunkHeading {index: usize,}#[derive(askama::Template)]#[template(path = "hover/diff.md", escape = "none")]struct HunkHoverTemplate {heading: String,lines_added: Vec<String>,lines_removed: Vec<String>, - edit in extensions/vscode/src/inline_credit/hover.rs at line 48
hunk_template: HunkHoverTemplate, - replacement in extensions/vscode/src/inline_credit/hover.rs at line 53
struct UntrackedHoverTemplate;struct UntrackedHoverTemplate {hunk_template: HunkHoverTemplate,} - replacement in extensions/vscode/src/inline_credit/hover.rs at line 61
credit_source: &CreditSource,open_file: &OpenFile,credit_source: CreditSource, - edit in extensions/vscode/src/inline_credit/hover.rs at line 64
let active_hunk = repository.get_active_hunk(open_file, credit_source).map_err(|error| napi::Error::from_reason(format!("Unable to get active hunk: {error}")))?;let mut hunk_heading_buffer = String::new();let hunk_heading = HunkHeading {index: active_hunk.index,};hunk_heading.localize(localization_context, &mut hunk_heading_buffer);let hunk_template = match active_hunk.diff {HunkDiff::TextChange {lines_added,lines_removed,} => HunkHoverTemplate {heading: hunk_heading_buffer,lines_added,lines_removed,},}; - replacement in extensions/vscode/src/inline_credit/hover.rs at line 86
CreditSource::Tracked(change_id) => {let change_header = repository.get_change_header(change_id).ok_or_else(|| {napi::Error::from_reason(format!("unable to get change {change_id:#?}"))CreditSource::Tracked { vertex } => {let change = repository.get_change(&vertex.change).ok_or_else(|| {napi::Error::from_reason(format!("Unable to get change for vertex: {vertex:#?}")) - replacement in extensions/vscode/src/inline_credit/hover.rs at line 94
.authors_for_change(change_header).authors_for_change(&change.header) - replacement in extensions/vscode/src/inline_credit/hover.rs at line 111
change_headerchange.header - replacement in extensions/vscode/src/inline_credit/hover.rs at line 123
let zoned_timestamp = change_header.timestamp.to_zoned(TimeZone::system());let zoned_timestamp = change.header.timestamp.to_zoned(TimeZone::system()); - replacement in extensions/vscode/src/inline_credit/hover.rs at line 141
title: &change_header.message,title: &change.header.message, - replacement in extensions/vscode/src/inline_credit/hover.rs at line 147
change_id: change_id.to_base32(),description: change_header.description.as_deref(),change_id: vertex.change.to_base32(),description: change.header.description.as_deref(),hunk_template, - replacement in extensions/vscode/src/inline_credit/hover.rs at line 154
CreditSource::Untracked => {let hover_template = UntrackedHoverTemplate;CreditSource::Untracked { .. } => {let hover_template = UntrackedHoverTemplate { hunk_template }; - replacement in extensions/vscode/l10n/en-US/inline_credit/hover.ftl at line 2
change-id = Change ID[3.12081]change-id = Change IDhunk-heading = Hunk { $index }