Lots of code cleanup needed here
S34LGQX3XOVZUKWNE2YQNB3LJPXBBHP5GUYPYGTBFV63HYBLTQWAC pub enum ResourceState {Modified,}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");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 = 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 mut record_builder = RecordBuilder::new();record_builder.record(txn.clone(),libpijul::Algorithm::default(),stop_early,&libpijul::DEFAULT_SEPARATOR,channel.clone(),&repo.working_copy,&repo.changes,&prefix,std::thread::available_parallelism()?.into(),)?;let recorded = record_builder.finish();let write_txn = txn.write();let actions: Vec<_> = recorded.actions.into_iter().map(|rec| rec.globalize(&*write_txn).unwrap()).collect();let contents = if let Ok(cont) = std::sync::Arc::try_unwrap(recorded.contents) {cont.into_inner()} else {unreachable!()};let change = LocalChange::make_change(&*write_txn,&channel,actions,contents,ChangeHeader::default(),Vec::new(),)?;let (dependencies, extra_known) =libpijul::change::dependencies(&*write_txn, &*channel.read(), change.changes.iter())?;let mut i = 0;let mut hashes = HashMap::default();for hash in dependencies.iter() {hashes.insert(*hash, i);i += 1;}for hash in extra_known.iter() {if let Entry::Vacant(entry) = hashes.entry(*hash) {entry.insert(i);i += 1;}}change.write_all_deps(|hash| {if let Entry::Vacant(entry) = hashes.entry(hash) {entry.insert(i);i += 1;}Ok(())})?;
Ok(change.changes.iter().map(|hunk| {hunk.to_owned().to_printable(&repo.changes, &hashes, &change.contents).unwrap()}).collect())}#[derive(Eq, PartialEq)]pub enum Annotation {Added,Modified,}impl Annotation {fn display(&self) -> String {match self {Annotation::Added => "A",Annotation::Modified => "M",}.to_string()}fn tooltip(&self) -> String {match self {Annotation::Added => "Added",Annotation::Modified => "Modified",}.to_string()}}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");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)}PrintableHunk::Replace { path, .. } => (PathBuf::from(path), Annotation::Modified),PrintableHunk::Edit { path, .. } => (PathBuf::from(path), Annotation::Modified),_ => continue,};let entry = annotations.entry(path.to_string_lossy().to_string()).or_insert(vec![]);if !entry.contains(&annotation) {entry.push(annotation);}}let annotations_array = cx.empty_array();for (i, (path, file_annotations)) in annotations.iter().enumerate() {let annotations = cx.empty_object();let path = cx.string(path);let display_annotations = cx.string(file_annotations.iter().map(|i| i.display()).collect::<Vec<String>>().join(", "),);let tooltip_annotations = cx.string(file_annotations.iter().map(|i| i.tooltip()).collect::<Vec<String>>().join(", "),);annotations.set(&mut cx, "path", path)?;annotations.set(&mut cx, "display", display_annotations)?;annotations.set(&mut cx, "tooltip", tooltip_annotations)?;annotations_array.set(&mut cx, i as u32, annotations)?;}Ok(annotations_array)}
// TODO: ignored should include untracked filesfn ignored(mut cx: FunctionContext) -> JsResult<JsBoolean> {let root: Handle<JsString> = cx.argument(0)?;let root: String = root.value(&mut cx);let root = CanonicalPath::new(&root).unwrap();let path: Handle<JsString> = cx.argument(1)?;let path: String = path.value(&mut cx);let path = CanonicalPath::new(Path::new(&path)).unwrap();Ok(cx.boolean(!libpijul::working_copy::filesystem::filter_ignore(root,path,root.join(path).unwrap().is_dir(),)),)}// TODO: fn tracked()fn find_closest_root(mut cx: FunctionContext) -> JsResult<JsString> {let repositories: Handle<JsArray> = cx.argument(0)?;let repositories = repositories.to_vec(&mut cx)?;assert!(!repositories.is_empty());let path: Handle<JsString> = cx.argument(1)?;let path = PathBuf::from(path.value(&mut cx));let mut best_path = PathBuf::new();for repository in repositories {let repository_path: Handle<JsString> = repository.downcast(&mut cx).unwrap();let repository_path = PathBuf::from(repository_path.value(&mut cx));if path.strip_prefix(&repository_path).is_ok() {if repository_path.ancestors().count() > best_path.ancestors().count() {best_path = repository_path;}}}Ok(cx.string(&best_path.to_string_lossy()))}
import { commands, ExtensionContext, Uri, window, workspace } from 'vscode';
import { commands, ExtensionContext, Uri, window, workspace, scm, SourceControl, RelativePattern, SourceControlResourceGroup } from 'vscode';import path from 'path';const { resolveRepositories, shortDiff, ignored, findClosestRoot } = require('../index.node');
}function update_resource(repository: string, resource: { path: string, display: string, tooltip: string, untracked: boolean }) {const repo = source_control_views.get(repository)!;let resource_states = resource.untracked ? repo.resource_groups[1].resourceStates : repo.resource_groups[0].resourceStates;let to_replace = resource_states.find((value, _index) => {value.resourceUri === Uri.file(resource.path)});const new_resource = {resourceUri: Uri.file(path.join(repository, resource.path)),decorations: {tooltip: resource.tooltip,}}if (to_replace === undefined) {// Does not exst as resource in groupsource_control_views.get(repository)!.resource_groups[0].resourceStates = [new_resource];} else {to_replace = new_resource;}}interface LoadedRepository {source_control: SourceControl,resource_groups: SourceControlResourceGroup[],
const repositories = resolveRepositories(paths_to_resolve());for (let repo_path of repositories) {const repo_scm = scm.createSourceControl(scm_id, scm_label, Uri.file(repo_path));if (!source_control_views.has(repo_path)) {console.log(`Activating source control for repository: ${repo_path}`);// Each 'resource group' appears as its own headingconst groups = [repo_scm.createResourceGroup('unrecorded', 'Unrecorded changes'),repo_scm.createResourceGroup('untracked', 'Untracked files'),]// TODO: might need to fix https://github.com/microsoft/vscode/issues/162433 to make dynamicrepo_scm.inputBox.placeholder = "Message (Ctrl+Enter to record)";const watcher = workspace.createFileSystemWatcher(new RelativePattern(repo_scm.rootUri!, '**/*'));// When a file is created, check to see if pijul is tracking it// watcher.onDidCreate((event) => {// })watcher.onDidChange((event) => {try {const repository = findClosestRoot(Array.from(source_control_views.keys()), event.fsPath);if (!ignored(repository, event.fsPath)) {console.log(`Responding to change event on file ${event.fsPath} with parent ${repository}`);const diffs = shortDiff(event.fsPath);console.assert(diffs.length <= 1);// // TODO: check if file is trackedif (diffs.length === 1) {// File has changed, update diff in explorer treeupdate_resource(repository, diffs[0]);} else {// File has no diff, clear any diff in explorer tree}} else {console.log(`Ignored change event from ${event.fsPath}`);}} catch (e) {console.error(e);}})let loaded_repository: LoadedRepository = {source_control: repo_scm,resource_groups: groups,}source_control_views.set(repo_path, loaded_repository);}}