use std::collections::HashMap;
use camino::{Utf8Path, Utf8PathBuf};
use pijul_extension::FileSystemRepository;
use pijul_extension::path_state::PathState;
use crate::vscode_sys::reference::{
SourceControlRef, SourceControlResourceGroupRef, TextEditorRef,
};
use crate::vscode_sys::{self, SourceControlResourceState};
use crate::vscode_sys::{TextEditor, Uri};
pub struct OpenRepository {
pub repository: FileSystemRepository,
pub source_control: SourceControlRef,
open_editors: HashMap<Utf8PathBuf, TextEditorRef>,
unrecorded_changes: SourceControlResourceGroupRef,
untracked_paths: SourceControlResourceGroupRef,
}
impl OpenRepository {
#[tracing::instrument(skip_all)]
pub fn new(
env: &napi::Env,
repository_uri: &Uri,
) -> Result<Self, napi::Error> {
tracing::debug!(message = "Opening repository", uri = ?repository_uri.to_string()?);
let repository_path = Utf8PathBuf::from(repository_uri.get_fs_path()?);
let source_control =
vscode_sys::scm::create_source_control(env, "pijul", "Pijul", repository_uri)?;
let repository = FileSystemRepository::new(&repository_path).map_err(|error| {
napi::Error::from_reason(format!(
"cannot open workspace at {repository_path}: {error}"
))
})?;
let unrecorded_changes = source_control.create_resource_group("changes", "Changes")?;
let untracked_paths = source_control.create_resource_group("untracked", "Untracked")?;
let open_repository = Self {
source_control: source_control.create_ref()?,
repository,
unrecorded_changes: unrecorded_changes.create_ref()?,
untracked_paths: untracked_paths.create_ref()?,
open_editors: HashMap::new(),
};
open_repository.update_resource_states(env, &repository_path)?;
Ok(open_repository)
}
#[tracing::instrument(skip_all)]
pub fn register_text_editor(
&mut self,
path: Utf8PathBuf,
text_editor: TextEditor,
) -> Result<(), napi::Error> {
if self.repository.get_open_file(&path).is_none() {
let document = text_editor.get_document()?;
let file_contents = document.get_text(None)?;
self.repository
.create_open_file(path.clone(), file_contents)
.map_err(|error| {
napi::Error::from_reason(format!("Unable to create open file: {error}"))
})?;
let text_editor_reference = text_editor.create_ref()?;
self.open_editors
.try_insert(path, text_editor_reference)
.map_err(|_error| napi::Error::from_reason("Text editor already exists"))?;
}
Ok(())
}
#[tracing::instrument(skip_all)]
pub fn get_text_editor<'env>(
&self,
env: &'env napi::Env,
path: &Utf8Path,
) -> Result<Option<TextEditor<'env>>, napi::Error> {
self.open_editors
.get(path)
.map(|reference| reference.get_inner(env))
.transpose()
}
#[tracing::instrument(skip_all)]
pub fn update_resource_states(
&self,
env: &napi::Env,
repository_path: &Utf8Path,
) -> Result<(), napi::Error> {
let mut modified_resource_states = Vec::new();
let mut untracked_resource_states = Vec::new();
for (relative_path, path_state) in self.repository.iter_path_states() {
let absolute_path = repository_path.join(relative_path);
let resource_uri = Uri::file(env, absolute_path.as_str())?;
let resource_state = SourceControlResourceState::new(env, &resource_uri)?;
match path_state {
PathState::Untracked => untracked_resource_states.push(resource_state),
PathState::Tracked(_tracked_state) => modified_resource_states.push(resource_state),
}
}
self.unrecorded_changes
.get_inner(env)?
.set_resource_states(modified_resource_states)?;
self.untracked_paths
.get_inner(env)?
.set_resource_states(untracked_resource_states)?;
Ok(())
}
}