The refactoring from this change makes things a lot cleaner, so it should also come with a few less bugs
B7YKJDKD7ZQ2JV4FLKZUHMIH6BQXH57ALGQOGQ4YTFGITBYMWY4QC
fn get_hunks(
cx: &mut FunctionContext,
path: Option<PathBuf>,
stop_early: bool,
) -> Result<Vec<PrintableHunk>, anyhow::Error> {
let repo = Repository::find_root(path.clone()).expect("Could not find root");
fn get_hunks(path: &Path, stop_early: bool) -> Result<Vec<PrintableHunk>, anyhow::Error> {
let repo = Repository::find_root(Some(path.to_owned())).expect("Could not find root");
let prefix = if let Some(path) = path {
path.strip_prefix(&repo.path)?.to_string_lossy().to_string()
} else {
String::new()
};
console_log(cx, format!("Prefix: {prefix} root: {:?}", repo.path)).unwrap();
let prefix = path.strip_prefix(&repo.path)?.to_string_lossy().to_string();
Annotation::Added => "A",
Annotation::Modified => "M",
Self::Tracked(annotations) => annotations
.iter()
.map(|annotation| {
match annotation {
Annotation::Modified => "M",
}
.to_string()
})
.collect::<Vec<String>>()
.join(", "),
Self::Added => String::from("A"),
Self::Untracked => String::from("U"),
Self::Ignored => String::new(),
Annotation::Added => "Added",
Annotation::Modified => "Modified",
Self::Tracked(annotations) => annotations
.iter()
.map(|annotation| {
match annotation {
Annotation::Modified => "Modified",
}
.to_string()
})
.collect::<Vec<String>>()
.join(", "),
Self::Added => String::from("Added"),
Self::Untracked => String::from("Untracked"),
Self::Ignored => String::new(),
fn short_diff(mut cx: FunctionContext) -> JsResult<JsArray> {
let path: Handle<JsString> = cx.argument(0)?;
let path = PathBuf::from(path.value(&mut cx));
let hunks = get_hunks(&mut cx, Some(path), true).expect("Unable to load hunks");
pub fn untracked(path: &Path) -> Result<Vec<PathBuf>, anyhow::Error> {
let repo = Repository::find_root(Some(path.to_owned())).expect("Could not find root");
let txn = repo.pristine.arc_txn_begin()?;
let channel_name = txn
.read()
.current_channel()
.unwrap_or(libpijul::DEFAULT_CHANNEL)
.to_string();
let channel = txn.write().open_or_create_channel(&channel_name)?;
let prefix = path.strip_prefix(&repo.path)?.to_string_lossy().to_string();
let mut record_builder = RecordBuilder::new();
if let Err(libpijul::record::RecordError::PathNotInRepo(_)) = record_builder.record(
txn.clone(),
libpijul::Algorithm::default(),
true,
&libpijul::DEFAULT_SEPARATOR,
channel.clone(),
&repo.working_copy,
&repo.changes,
&prefix,
std::thread::available_parallelism()?.into(),
) {
// `path` is a single file, no more work needed
assert!(path.is_file());
return Ok(vec![path.to_owned()]);
}
let mut annotations: HashMap<String, Vec<Annotation>> = HashMap::new();
console_log(&mut cx, format!("Hunk length: {}", hunks.len()))?;
for hunk in hunks.iter() {
let (path, annotation) = match hunk {
PrintableHunk::FileAddition { name, parent, .. } => {
(PathBuf::from(parent).join(name), Annotation::Added)
let repo_path = CanonicalPathBuf::canonicalize(&repo.path)?;
let given_path = CanonicalPathBuf::canonicalize(&path)?;
let threads = std::thread::available_parallelism()?.into();
Ok(repo
.working_copy
.iterate_prefix_rec(repo_path.clone(), given_path.clone(), false, threads)?
.filter_map(move |x| {
let (path, _) = x.unwrap();
use path_slash::PathExt;
let path_str = path.to_slash_lossy();
let read_txn = &*txn.read();
if !read_txn.is_tracked(&path_str).unwrap_or(false) {
Some(path)
} else {
None
PrintableHunk::Replace { path, .. } => (PathBuf::from(path), Annotation::Modified),
PrintableHunk::Edit { path, .. } => (PathBuf::from(path), Annotation::Modified),
_ => continue,
};
})
.collect())
}
pub fn annotate_path(path: PathBuf) -> Result<HashMap<String, State>, anyhow::Error> {
let untracked = untracked(&path).unwrap();
let mut annotations: HashMap<String, State> = HashMap::new();
if path.is_file() && untracked == vec![path.clone()] {
annotations.insert(path.to_string_lossy().to_string(), State::Untracked);
} else {
let hunks = get_hunks(&path, true).expect("Unable to load hunks");
for hunk in hunks.iter() {
match hunk {
PrintableHunk::FileAddition { name, parent, .. } => {
let path = PathBuf::from(parent).join(name);
annotations
.try_insert(path.to_string_lossy().to_string(), State::Added)
.expect("File additions should be a unique hunk");
}
PrintableHunk::Replace { path, .. } | PrintableHunk::Edit { path, .. } => {
if let State::Tracked(tracked) = annotations
.entry(path.to_owned())
.or_insert(State::Tracked(vec![]))
{
tracked.push(Annotation::Modified);
} else {
unreachable!();
}
}
_ => continue,
};
}
let entry = annotations
.entry(path.to_string_lossy().to_string())
.or_insert(vec![]);
if !entry.contains(&annotation) {
entry.push(annotation);
for untracked_file in untracked {
let key = untracked_file.to_string_lossy().to_string();
debug_assert!(annotations.get(&key).is_none());
annotations.insert(key, State::Untracked);
let badge_annotations = cx.string(
file_annotations
.iter()
.map(|i| i.badge())
.collect::<Vec<String>>()
.join(", "),
);
let tooltip_annotations = cx.string(
file_annotations
.iter()
.map(|i| i.tooltip())
.collect::<Vec<String>>()
.join(", "),
);
let badge_annotations = cx.string(file_annotations.badge());
let tooltip_annotations = cx.string(file_annotations.tooltip());