Refactor `PathState` handling to be updated incrementally

finchie
Dec 15, 2025, 11:54 AM
LQJG2LGQMNJ35Y5H6XWCU3JWVIMOS5S7UQQQPIO5TCAXMLU3ORUQC

Dependencies

  • [2] WFWTKCJN Create initial Visual Studio Code extension
  • [3] TIPGJCUW Consistently use `EditorWorkingCopy` in `pijul-extension`
  • [4] DUZBRB3U Render correspoding hunk on inline credit hover
  • [5] 37JT3GCX Lazily load channel state instead of storing in memory
  • [6] GQ52YLWH Remove unnecessary debug logs
  • [7] 72K45XKD Refactor inline credit to improve hover messages

Change contents

  • file deletion: untracked.rs (----------)
    [2.3681][2.3683:3719](),[2.3719][2.3720:3720]()
    // TODO: rewrite to be generic over filesystem implementations
    use std::collections::HashSet;
    use camino::{Utf8Path, Utf8PathBuf};
    use canonical_path::CanonicalPathBuf;
    use libpijul::pristine::sanakirja::{MutTxn, SanakirjaError};
    use libpijul::{ArcTxn, TxnTExt};
    #[derive(Debug, thiserror::Error)]
    pub enum UntrackedItemError {
    #[error("unable to get untracked path from filesystem: {0:#?}")]
    IO(std::io::Error),
    #[error("unable to check if path is tracked: {0:#?}")]
    IsTracked(SanakirjaError),
    #[error("unable to convert `{invalid_path:#?}` to UTF-8: {conversion_error:#?}")]
    Utf8Path {
    invalid_path: std::path::PathBuf,
    conversion_error: camino::FromPathBufError,
    },
    }
    #[derive(Debug, thiserror::Error)]
    #[error(transparent)]
    pub enum UntrackedError {
    #[error("unable to get available parallelism: {0:#?}")]
    AvailableParallelism(std::io::Error),
    #[error("unable to canonicalize root path `{root_path}`: {io_error:#?}")]
    CanonicalRoot {
    root_path: Utf8PathBuf,
    io_error: std::io::Error,
    },
    #[error("unable to iterate untracked files: {0:#?}")]
    Iteration(std::io::Error),
    #[error(transparent)]
    IterationItem(#[from] UntrackedItemError),
    }
    pub fn file_system(
    root: &Utf8Path,
    file_system: &libpijul::working_copy::FileSystem,
    transaction: ArcTxn<MutTxn<()>>,
    ) -> Result<HashSet<Utf8PathBuf>, UntrackedError> {
    let untracked_items_transaction = transaction.clone();
    let canonical_path =
    CanonicalPathBuf::canonicalize(root).map_err(|io_error| UntrackedError::CanonicalRoot {
    root_path: root.to_path_buf(),
    io_error,
    })?;
    let untracked_files: HashSet<Utf8PathBuf> = file_system
    .iterate_prefix_rec(
    canonical_path.clone(),
    canonical_path,
    false,
    std::thread::available_parallelism()
    .map_err(UntrackedError::AvailableParallelism)?
    .get(),
    // Follow all paths
    |_path, _is_directory| true,
    )
    .map_err(UntrackedError::Iteration)?
    // Handle any errors in iteration
    .map(|filesystem_result| match filesystem_result {
    Ok((path, _is_directory)) => {
    Utf8PathBuf::try_from(path.clone()).map_err(|conversion_error| {
    UntrackedItemError::Utf8Path {
    invalid_path: path,
    conversion_error,
    }
    })
    }
    Err(io_error) => Err(UntrackedItemError::IO(io_error)),
    })
    // Filter out tracked paths
    .filter_map(|path_result| match path_result {
    Ok(path) => match untracked_items_transaction.read().is_tracked(path.as_str()) {
    Ok(true) => None,
    Ok(false) => Some(Ok(path)),
    Err(error) => Some(Err(UntrackedItemError::IsTracked(error))),
    },
    Err(error) => Some(Err(error)),
    })
    .collect::<Result<_, UntrackedItemError>>()?;
    Ok(untracked_files)
    }
  • file move: modified_paths.rs (----------)path_state.rs (----------)
    [2.3681]
    [2.6871]
  • edit in pijul-extension/src/path_state.rs at line 2
    [2.6903]
    [2.6903]
    use std::collections::hash_map::Entry;
    use std::num::NonZeroUsize;
  • replacement in pijul-extension/src/path_state.rs at line 5
    [2.6904][2.6904:6961]()
    use camino::Utf8PathBuf;
    use libpijul::change::BaseHunk;
    [2.6904]
    [2.6961]
    use camino::{Utf8Path, Utf8PathBuf};
    use canonical_path::CanonicalPathBuf;
    use libpijul::change::{BaseHunk, Hunk};
  • replacement in pijul-extension/src/path_state.rs at line 11
    [2.7103][2.7103:7154]()
    use libpijul::{ArcTxn, ChannelRef, RecordBuilder};
    [2.7103]
    [2.7154]
    use libpijul::{ArcTxn, ChannelRef, RecordBuilder, TxnTExt};
  • replacement in pijul-extension/src/path_state.rs at line 13
    [2.7155][2.7155:7180]()
    use crate::TrackedState;
    [2.7155]
    [2.7180]
    #[derive(Debug, thiserror::Error)]
    #[error(transparent)]
    pub enum CreatePathStatesError {
    #[error("Unable to canonicalize root path `{root_path}`: {io_error:#?}")]
    CanonicalRoot {
    root_path: Utf8PathBuf,
    io_error: std::io::Error,
    },
    #[error("Failed to iterate through workspace: {0}")]
    Iteration(std::io::Error),
    #[error("Failed to check if path is tracked: {0}")]
    IsTracked(SanakirjaError),
    }
  • replacement in pijul-extension/src/path_state.rs at line 28
    [2.7216][2.7216:7339]()
    pub enum ModifiedPathsError<C: std::error::Error + 'static, W: std::error::Error> {
    Globalize(#[from] SanakirjaError),
    [2.7216]
    [2.7339]
    pub enum PathStatesError<C: std::error::Error + 'static, W: std::error::Error> {
    Create(#[from] CreatePathStatesError),
    Sanakirja(#[from] SanakirjaError),
  • edit in pijul-extension/src/path_state.rs at line 32
    [2.7408]
    [2.7408]
    }
    #[derive(Clone, Copy, Debug)]
    pub enum TrackedState {
    Added,
    Removed,
    Modified,
    Moved,
    ModifiedAndMoved,
    }
    impl TrackedState {
    pub fn join_hunk(
    state: Option<Self>,
    hunk: &Hunk<Option<libpijul::Hash>, libpijul::change::Local>,
    ) -> Option<Self> {
    match hunk {
    BaseHunk::FileMove { .. } => match state {
    Some(TrackedState::Modified) => Some(TrackedState::ModifiedAndMoved),
    None => Some(TrackedState::Moved),
    Some(_) => unreachable!("{hunk:#?}"),
    },
    BaseHunk::FileDel { .. } => match state {
    None => Some(TrackedState::Removed),
    Some(_existing_state) => unreachable!("{hunk:#?}"),
    },
    BaseHunk::FileAdd { .. } => match state {
    None => Some(TrackedState::Added),
    Some(_existing_state) => unreachable!("{hunk:#?}"),
    },
    BaseHunk::Edit { .. } | BaseHunk::Replacement { .. } => match state {
    Some(TrackedState::Modified) => Some(TrackedState::Modified),
    Some(TrackedState::Moved) => Some(TrackedState::ModifiedAndMoved),
    None => Some(TrackedState::Modified),
    Some(_) => unreachable!("{hunk:#?}"),
    },
    // TODO: FileUndel
    // TODO: conflicts
    _ => state,
    }
    }
  • replacement in pijul-extension/src/path_state.rs at line 75
    [2.7411][2.7411:7808]()
    pub fn modified_paths<C, W>(
    transaction: &ArcTxn<MutTxn<()>>,
    channel: &ChannelRef<MutTxn<()>>,
    working_copy: &W,
    change_store: &C,
    ) -> Result<HashMap<Utf8PathBuf, TrackedState>, ModifiedPathsError<C::Error, W::Error>>
    where
    C: ChangeStore + Clone + Send + 'static,
    W: WorkingCopy + Clone + Send + Sync + 'static,
    {
    let mut unrecorded_changes = RecordBuilder::new();
    [2.7411]
    [2.7808]
    #[derive(Clone, Copy, Debug)]
    pub enum PathState {
    Untracked,
    Tracked(TrackedState),
    }
  • replacement in pijul-extension/src/path_state.rs at line 81
    [2.7809][2.7809:8128]()
    unrecorded_changes.record(
    transaction.clone(),
    libpijul::Algorithm::default(),
    false, // TODO: check and document
    &libpijul::DEFAULT_SEPARATOR,
    channel.clone(),
    working_copy,
    change_store,
    "",
    1, // TODO: figure out concurrency model
    )?;
    [2.7809]
    [2.8128]
    pub struct PathStates {
    // TODO: use a trie
    pub states: HashMap<Utf8PathBuf, PathState>,
    }
    impl PathStates {
    pub fn new<C>(
    root: &Utf8Path,
    transaction: &ArcTxn<MutTxn<()>>,
    channel: &ChannelRef<MutTxn<()>>,
    file_system: &libpijul::working_copy::FileSystem,
    change_store: &C,
    ) -> Result<Self, PathStatesError<C::Error, std::io::Error>>
    where
    C: ChangeStore + Clone + Send + 'static,
    {
    let mut path_states = Self::from_untracked_states(root, file_system, transaction)?;
    path_states.update_tracked_states("", transaction, channel, file_system, change_store)?;
  • replacement in pijul-extension/src/path_state.rs at line 100
    [2.8129][2.8129:8230]()
    let unrecorded_state = unrecorded_changes.finish();
    let mut modified_paths = HashMap::new();
    [2.8129]
    [2.8230]
    Ok(path_states)
    }
  • replacement in pijul-extension/src/path_state.rs at line 103
    [2.8231][2.8231:8725]()
    for hunk in unrecorded_state.actions {
    match hunk {
    BaseHunk::FileMove { path, .. } => {
    modified_paths
    .entry(Utf8PathBuf::from(path))
    .and_modify(|path_state| match path_state {
    TrackedState::Modified => *path_state = TrackedState::ModifiedAndMoved,
    _ => unreachable!("{path_state:#?}"),
    })
    .or_insert(TrackedState::Moved);
    [2.8231]
    [2.8725]
    fn from_untracked_states(
    root: &Utf8Path,
    file_system: &libpijul::working_copy::FileSystem,
    transaction: &ArcTxn<MutTxn<()>>,
    ) -> Result<Self, CreatePathStatesError> {
    let canonical_path = CanonicalPathBuf::canonicalize(root).map_err(|io_error| {
    CreatePathStatesError::CanonicalRoot {
    root_path: root.to_path_buf(),
    io_error,
  • replacement in pijul-extension/src/path_state.rs at line 113
    [2.8739][2.8739:8929]()
    BaseHunk::FileDel { path, .. } => {
    modified_paths
    .try_insert(Utf8PathBuf::from(path), TrackedState::Removed)
    .unwrap();
    [2.8739]
    [2.8929]
    })?;
    let file_system_iterator = file_system
    .iterate_prefix_rec(
    canonical_path.clone(),
    canonical_path,
    false,
    std::thread::available_parallelism()
    .unwrap_or(NonZeroUsize::MIN)
    .get(),
    // Follow all paths
    |_path, _is_directory| true,
    )
    .map_err(CreatePathStatesError::Iteration)?;
    let mut untracked_states = HashMap::new();
    let read_transaction = transaction.read();
    for entry in file_system_iterator {
    let (path, _is_directory) = match entry {
    Ok((path, is_directory)) => (path, is_directory),
    Err(error) => {
    tracing::error!(message = "Error traversing file system", %error);
    continue;
    }
    };
    let utf8_path = match Utf8PathBuf::from_path_buf(path) {
    Ok(utf8_path) => utf8_path,
    Err(path) => {
    tracing::error!(message = "Unable to convert PathBuf to Utf8PathBuf", ?path);
    continue;
    }
    };
    if !read_transaction
    .is_tracked(utf8_path.as_str())
    .map_err(CreatePathStatesError::IsTracked)?
    {
    untracked_states.insert(utf8_path, PathState::Untracked);
  • replacement in pijul-extension/src/path_state.rs at line 154
    [2.8943][2.8943:9131](),[2.9131][2.9131:9145](),[2.9145][2.9145:9670]()
    BaseHunk::FileAdd { path, .. } => {
    modified_paths
    .try_insert(Utf8PathBuf::from(path), TrackedState::Added)
    .unwrap();
    }
    BaseHunk::Edit { local, .. } | BaseHunk::Replacement { local, .. } => {
    modified_paths
    .entry(Utf8PathBuf::from(local.path))
    .and_modify(|path_state| match path_state {
    TrackedState::Modified => (),
    TrackedState::Moved => *path_state = TrackedState::ModifiedAndMoved,
    _ => unreachable!("{path_state:#?}"),
    })
    .or_insert(TrackedState::Modified);
    [2.8943]
    [2.9670]
    }
    Ok(Self {
    states: untracked_states,
    })
    }
    fn update_tracked_states<C, W>(
    &mut self,
    prefix: &str,
    transaction: &ArcTxn<MutTxn<()>>,
    channel: &ChannelRef<MutTxn<()>>,
    working_copy: &W,
    change_store: &C,
    ) -> Result<(), PathStatesError<C::Error, W::Error>>
    where
    C: ChangeStore + Clone + Send + 'static,
    W: WorkingCopy + Clone + Send + Sync + 'static,
    {
    let mut unrecorded_changes = RecordBuilder::new();
    unrecorded_changes.record(
    transaction.clone(),
    libpijul::Algorithm::default(),
    false, // TODO: check and document
    &libpijul::DEFAULT_SEPARATOR,
    channel.clone(),
    working_copy,
    change_store,
    prefix,
    1, // TODO: figure out concurrency model
    )?;
    let unrecorded_state = unrecorded_changes.finish();
    for hunk in unrecorded_state.actions {
    let globalized_hunk = hunk.globalize(&*transaction.read())?;
    let entry = self.states.entry(Utf8PathBuf::from(globalized_hunk.path()));
    let existing_tracked_state = match &entry {
    Entry::Occupied(occupied_entry) => match occupied_entry.get() {
    PathState::Untracked => None,
    PathState::Tracked(tracked_state) => Some(*tracked_state),
    },
    Entry::Vacant(_vacant_entry) => None,
    };
    if let Some(updated_state) =
    TrackedState::join_hunk(existing_tracked_state, &globalized_hunk)
    {
    entry.insert_entry(PathState::Tracked(updated_state));
    } else {
    tracing::info!(message = "Skipping unrecorded hunk", ?globalized_hunk);
  • edit in pijul-extension/src/path_state.rs at line 206
    [2.9684][2.9684:9773]()
    // TODO: FileUndel
    // TODO: conflicts
    _ => continue,
  • edit in pijul-extension/src/path_state.rs at line 207
    [2.9783]
    [2.9783]
    Ok(())
  • replacement in pijul-extension/src/path_state.rs at line 211
    [2.9790][2.9790:9813]()
    Ok(modified_paths)
    [2.9790]
    [2.9813]
    pub fn get_path_state(&self, path: &Utf8Path) -> Option<PathState> {
    self.states.get(path).copied()
    }
    // TODO: handle transitions:
    // - Added -> unrecorded
    // - Modified -> unmodified
    pub fn update_path_state<C, W>(
    &mut self,
    path: Utf8PathBuf,
    transaction: &ArcTxn<MutTxn<()>>,
    channel: &ChannelRef<MutTxn<()>>,
    working_copy: &W,
    change_store: &C,
    ) -> Result<(), PathStatesError<C::Error, W::Error>>
    where
    C: ChangeStore + Clone + Send + 'static,
    W: WorkingCopy + Clone + Send + Sync + 'static,
    {
    if transaction.read().is_tracked(path.as_str())? {
    self.update_tracked_states(
    path.as_str(),
    transaction,
    channel,
    working_copy,
    change_store,
    )?;
    } else {
    self.states.insert(path, PathState::Untracked);
    }
    Ok(())
    }
  • edit in pijul-extension/src/lib.rs at line 15
    [5.22][2.10812:10854](),[2.10812][2.10812:10854]()
    use std::collections::{HashMap, HashSet};
  • replacement in pijul-extension/src/lib.rs at line 23
    [2.11192][5.23:102]()
    use libpijul::{ArcTxn, Base32, ChangeId, ChannelRef, GraphTxnT, TxnT, Vertex};
    [2.11192]
    [3.0]
    use libpijul::{ArcTxn, ChangeId, ChannelRef, GraphTxnT, TxnT, Vertex};
  • replacement in pijul-extension/src/lib.rs at line 35
    [3.288][2.11538:11585](),[2.11538][2.11538:11585]()
    use crate::modified_paths::ModifiedPathsError;
    [3.288]
    [2.11585]
    use crate::path_state::{PathState, PathStates, PathStatesError};
  • replacement in pijul-extension/src/lib.rs at line 39
    [2.11646][2.11646:11689]()
    pub mod modified_paths;
    pub mod untracked;
    [2.11646]
    [2.11689]
    pub mod path_state;
  • edit in pijul-extension/src/lib.rs at line 48
    [2.12061][2.12061:12111](),[2.12111][5.165:217]()
    Untracked(#[from] untracked::UntrackedError),
    #[error("failed to get local authors: {0:#?}")]
  • replacement in pijul-extension/src/lib.rs at line 49
    [5.258][2.12200:12301](),[2.12200][2.12200:12301]()
    #[error("unable to get modified paths: {0:#?}")]
    Modified(#[from] ModifiedPathsError<C, W>),
    [5.258]
    [2.12301]
    #[error("unable to get path states: {0:#?}")]
    PathStates(#[from] PathStatesError<C, W>),
  • edit in pijul-extension/src/lib.rs at line 90
    [2.12850]
    [4.248]
    }
    #[derive(Debug, thiserror::Error)]
    pub enum UpdatePathStateError<C: std::error::Error + 'static, W: std::error::Error + 'static> {
    #[error("Unable to begin transaction: {0}")]
    BeginTransaction(#[from] BeginTransactionError),
    #[error(transparent)]
    PathStates(#[from] PathStatesError<C, W>),
  • edit in pijul-extension/src/lib.rs at line 143
    [3.751][2.13555:13766](),[2.13555][2.13555:13766]()
    }
    #[derive(Clone, Copy, Debug)]
    pub enum TrackedState {
    Added,
    Removed,
    Modified,
    Moved,
    ModifiedAndMoved,
    }
    #[derive(Debug)]
    pub enum PathState {
    Untracked,
    Tracked(TrackedState),
  • replacement in pijul-extension/src/lib.rs at line 155
    [2.14121][2.14121:14246]()
    // TODO: use a trie
    pub untracked: HashSet<Utf8PathBuf>,
    pub modified_paths: HashMap<Utf8PathBuf, TrackedState>,
    [2.14121]
    [4.1039]
    pub path_states: PathStates,
  • edit in pijul-extension/src/lib.rs at line 173
    [2.14891][2.14891:14955]()
    let working_copy = EditorWorkingCopy::new(file_system);
  • replacement in pijul-extension/src/lib.rs at line 175
    [2.15112][2.15112:15416]()
    let untracked =
    untracked::file_system(root, &working_copy.working_copy, transaction.clone())?;
    let modified_paths = modified_paths::modified_paths(
    &transaction,
    &channel,
    &working_copy.working_copy,
    &change_store,
    )?;
    [2.14956]
    [2.15416]
    let path_states =
    PathStates::new(root, &transaction, &channel, &file_system, &change_store)?;
    let working_copy = EditorWorkingCopy::new(file_system);
  • replacement in pijul-extension/src/lib.rs at line 183
    [2.15509][2.15509:15560]()
    untracked,
    modified_paths,
    [2.15509]
    [2.15560]
    path_states,
  • replacement in pijul-extension/src/lib.rs at line 373
    [2.15982][2.15982:16217]()
    if self.untracked.contains(path) {
    Some(PathState::Untracked)
    } else {
    self.modified_paths
    .get(path)
    .map(|path_state| PathState::Tracked(*path_state))
    }
    [2.15982]
    [2.16217]
    self.path_states.get_path_state(path)
  • replacement in pijul-extension/src/lib.rs at line 376
    [2.16224][2.16224:16320]()
    pub fn is_untracked(&self, path: &Utf8Path) -> bool {
    self.untracked.contains(path)
    [2.16224]
    [2.16320]
    pub fn iter_path_states(&self) -> impl Iterator<Item = (&Utf8Path, PathState)> {
    self.path_states
    .states
    .iter()
    .map(|(path, state)| (path.as_path(), *state))
  • replacement in pijul-extension/src/lib.rs at line 383
    [2.16327][2.16327:16440]()
    pub fn iter_modified(&self) -> impl Iterator<Item = &Utf8PathBuf> {
    self.modified_paths.keys()
    }
    [2.16327]
    [2.16440]
    pub fn update_path_state(
    &mut self,
    path: Utf8PathBuf,
    ) -> Result<(), UpdatePathStateError<C::Error, W::Error>> {
    let (transaction, channel) = begin_transaction(&self.pristine)?;
    self.path_states.update_path_state(
    path,
    &transaction,
    &channel,
    &self.working_copy.working_copy,
    &self.change_store,
    )?;
  • replacement in pijul-extension/src/lib.rs at line 397
    [2.16441][2.16441:16544]()
    pub fn iter_untracked(&self) -> impl Iterator<Item = &Utf8PathBuf> {
    self.untracked.iter()
    [2.16441]
    [2.16544]
    Ok(())
  • replacement in pijul-extension/src/lib.rs at line 435
    [3.1287][3.1287:1369]()
    // Since `FileCredits::new()` reads the file state from the working copy,
    [3.1287]
    [3.1369]
    // Since `FileCredits::new()` reads the file contents from the working copy,
  • replacement in pijul-extension/src/lib.rs at line 463
    [2.18038][3.1963:2049]()
    // Since the credits depend on file state already stored in the working copy,
    [2.18038]
    [3.2049]
    // Since the credits depend on file contents already stored in the working copy,
  • edit in pijul-extension/src/file_system/changes/unrecorded.rs at line 64
    [2.28477]
    [2.28477]
    debug_assert_eq!(globalized_hunk.path(), path.as_str());
  • edit in extensions/vscode/src/repository/open_repository.rs at line 5
    [2.99130]
    [2.99130]
    use pijul_extension::path_state::PathState;
  • replacement in extensions/vscode/src/repository/open_repository.rs at line 44
    [6.28][2.100529:100726](),[2.100529][2.100529:100726](),[2.100771][2.100771:100827](),[2.100869][2.100869:101335]()
    let mut unrecorded_changes = source_control.create_resource_group("changes", "Changes")?;
    let mut untracked_paths = source_control.create_resource_group("untracked", "Untracked")?;
    let mut modified_resource_states = Vec::new();
    for relative_modified_path in repository.iter_modified() {
    let absolute_modified_path = repository_path.join(relative_modified_path);
    let resource_uri = Uri::file(env, absolute_modified_path.as_str())?;
    let resource_state = SourceControlResourceState::new(env, &resource_uri)?;
    modified_resource_states.push(resource_state)
    }
    unrecorded_changes.set_resource_states(modified_resource_states)?;
    [6.28]
    [2.101335]
    let unrecorded_changes = source_control.create_resource_group("changes", "Changes")?;
    let untracked_paths = source_control.create_resource_group("untracked", "Untracked")?;
  • replacement in extensions/vscode/src/repository/open_repository.rs at line 47
    [2.101336][2.101336:101550](),[2.101620][2.101620:101790](),[2.101791][2.101791:101934](),[2.101979][2.101979:101997]()
    let mut untracked_resource_states = Vec::new();
    for relative_untracked_path in repository.iter_untracked() {
    let absolute_untracked_path = repository_path.join(relative_untracked_path);
    let resource_uri = Uri::file(env, absolute_untracked_path.as_str())?;
    let resource_state = SourceControlResourceState::new(env, &resource_uri)?;
    untracked_resource_states.push(resource_state)
    }
    untracked_paths.set_resource_states(untracked_resource_states)?;
    Ok(Self {
    [2.101336]
    [2.101997]
    let open_repository = Self {
  • replacement in extensions/vscode/src/repository/open_repository.rs at line 53
    [2.102247][2.102247:102258]()
    })
    [2.102247]
    [2.102258]
    };
    open_repository.update_resource_states(env, &repository_path)?;
    Ok(open_repository)
  • edit in extensions/vscode/src/repository/open_repository.rs at line 96
    [2.103520]
    [2.103520]
    #[tracing::instrument(skip_all)]
    pub fn update_resource_states(
    &self,
    env: &napi::Env,
    // TODO: ideally caller wouldn't have to provide repository path
    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(())
    }
  • replacement in extensions/vscode/src/lib.rs at line 21
    [2.109382][2.109382:109430]()
    use pijul_extension::{PathState, TrackedState};
    [2.109382]
    [2.109430]
    use pijul_extension::path_state::{PathState, TrackedState};