As promised, the refactored library allows for clean caching of decoration objects, so they are only re-computed when necessary.
OZ7NVILED6ZA6HUKY5IZHFU3Z7ASPLM62HFB2WZDQHWDKQHXQ6VAC
RR6FDU64N3Y3B5L6YEWWORXMJRKFH6NRIMTLORNKBPWSKUZIFCNAC
LQ3SUK3P3KXY5CQA552EJGFH2MESGNHKK4YZTIKOGFJAKVBMHHUQC
BS2SCVKJS7HVJDGBLKPURU75NGJGH5YQ2KDAT6QNEYGUJBVS6FUQC
47DXBIWXK2U3VJZW5V4XZEARZBV4H3WG375BN4OWW5XY5DN42GEQC
37UOV7PCILKVEPDUBJHLHBZEOTQ5NX5FCV6MQWTRY5GM5KXZNDGAC
7X2GGEHDEWGZCDFD2IAGUGHIPNFWY76TTVNKOIV7YL3D5PMAXX4AC
S34LGQX3XOVZUKWNE2YQNB3LJPXBBHP5GUYPYGTBFV63HYBLTQWAC
fn change_at<'a>(&'a self, target_line: u32) -> Result<&'a Change, anyhow::Error> {
let mut current_line: u64 = 0;
for (hash, line_count) in &self.hashes {
if (current_line..current_line + line_count).contains(&(target_line as u64)) {
if hash == &self.current_state.hash()? {
// Hash matches current state
return Ok(&self.current_state);
} else {
for change in &self.recorded_changes {
if &change.hash()? == hash {
return Ok(&change);
}
}
}
}
current_line += line_count;
}
bail!("Line outside of valid range");
}
pub fn authors(&self) -> String {
String::from("PLACEHOLDER AUTHOR")
pub fn authors_at(&self, target_line: u32) -> Result<String, anyhow::Error> {
let change = self.change_at(target_line)?;
if change == &self.current_state {
Ok(String::from("You"))
} else {
Ok(String::from("You"))
}
pub fn message(&self) -> String {
String::from("PLACEHOLDER MESSAGE")
pub fn message_at(&self, target_line: u32) -> Result<String, anyhow::Error> {
let change = self.change_at(target_line)?;
if change == &self.current_state {
Ok(String::from("Unrecorded changes"))
} else {
Ok(change.header.message.clone())
}
const change_for_line = file_states.get(path);
if (change_for_line === undefined) {
console.error(`No cached state found for ${path}`);
continue;
}
if (changes.length > 0) {
if (changes[-1].hash() !== change_for_line.hash()) {
changes.push(change_for_line);
const hash = file_state.hashAt(line);
if (credits.length > 0) {
if (credits[credits.length - 1].hash !== hash) {
credits.push({
hash,
line,
});
for (const change of changes) {
const decoration_options: DecorationRenderOptions = {
after: {
// TODO: find a way to align this text
contentText: ` ${change.authors()} • ${change.message()}`,
color: new ThemeColor("disabledForeground"),
},
isWholeLine: true,
};
const decoration_type =
window.createTextEditorDecorationType(decoration_options);
active_editor.setDecorations(decoration_type, active_editor.selections);
decorations.push(decoration_type);
const new_decorations: Map<number, TextEditorDecorationType> = new Map();
for (const change of credits) {
const last_decoration = last_decorations.get(change.line);
if (last_decoration !== undefined) {
new_decorations.set(change.line, last_decoration);
} else {
const decoration_options: DecorationRenderOptions = {
after: {
// TODO: find a way to align this text
contentText: ` ${file_state.authorsAt(
change.line,
)} • ${file_state.messageAt(change.line)}`,
color: new ThemeColor("disabledForeground"),
},
isWholeLine: true,
};
const decoration_type =
window.createTextEditorDecorationType(decoration_options);
const decoration_range: Range = new Range(change.line, 0, change.line, 0);
active_editor.setDecorations(decoration_type, [decoration_range]);
new_decorations.set(change.line, decoration_type);
}
}
for (const [line, decoration] of last_decorations.entries()) {
if (!new_decorations.has(line)) {
console.log(`Dispose line ${line + 1}`);
decoration.dispose();
}