Consistently use `EditorWorkingCopy` in `pijul-extension`

finchie
Dec 9, 2025, 7:07 AM
TIPGJCUW2P2DNOVVQ6RETSFDEGLC5C75CNRRB42WCUA7HHQT5C3QC

Dependencies

  • [2] WFWTKCJN Create initial Visual Studio Code extension
  • [3] M2NAH3DC Store modified time of `FileContents` as `jiff::Timestamp`

Change contents

  • edit in pijul-extension/src/lib.rs at line 18
    [2.10928][2.10928:10955]()
    use libpijul::changestore;
  • edit in pijul-extension/src/lib.rs at line 24
    [2.11244]
    [2.11244]
    use libpijul::{TxnTExt, changestore};
  • replacement in pijul-extension/src/lib.rs at line 28
    [2.11350][2.11350:11419]()
    use crate::file_system::changes::unrecorded::UnrecordedChangesError;
    [2.11350]
    [2.11419]
    use crate::file_system::changes::{FileCredits, FileCreditsError};
    use crate::file_system::open_file::contents::FileContents;
  • replacement in pijul-extension/src/lib.rs at line 31
    [2.11481][2.11481:11538]()
    use crate::file_system::working_copy::EditorWorkingCopy;
    [2.11481]
    [2.11538]
    use crate::file_system::working_copy::{
    EditorWorkingCopy, UpdateOpenFileContentsError, UpdateOpenFileCreditsError,
    };
  • replacement in pijul-extension/src/lib.rs at line 80
    [2.13085][2.13085:13125]()
    OpenFile(#[from] OpenFileError<C>),
    [2.13085]
    [2.13125]
    OpenFile(#[from] OpenFileError<C, W>),
  • edit in pijul-extension/src/lib.rs at line 83
    [2.13196]
    [2.13196]
    #[error(transparent)]
    FileCredits(#[from] FileCreditsError<C, W>),
  • replacement in pijul-extension/src/lib.rs at line 88
    [2.13234][2.13234:13297]()
    pub enum UpdateOpenFileError<C: std::error::Error + 'static> {
    [2.13234]
    [2.13297]
    pub enum UpdateOpenFileError<C: std::error::Error + 'static, W: std::error::Error + 'static> {
    #[error("Unable to begin transaction: {0}")]
    BeginTransaction(#[from] BeginTransactionError),
  • replacement in pijul-extension/src/lib.rs at line 92
    [2.13323][2.13323:13502](),[2.13502][2.13502:13555]()
    UnrecordedChanges(#[from] UnrecordedChangesError<C>),
    #[error("No file open at {0}")]
    NoMatchingFile(Utf8PathBuf),
    #[error("Unable to begin transaction: {0:#?}")]
    BeginTransaction(#[from] BeginTransactionError),
    [2.13323]
    [2.13555]
    UpdateContents(#[from] UpdateOpenFileContentsError),
    #[error(transparent)]
    UpdateCredits(#[from] UpdateOpenFileCreditsError<C, W>),
  • replacement in pijul-extension/src/lib.rs at line 216
    [2.17110][2.17110:17349]()
    let open_file = OpenFile::open(
    path.clone(),
    file_contents,
    metadata,
    &transaction,
    &channel,
    &self.change_store,
    &self.working_copy,
    )?;
    [2.17110]
    [2.17349]
    let open_file = OpenFile {
    contents: FileContents::new(path.clone(), metadata, file_contents, &self.working_copy),
    credits: None,
    };
  • replacement in pijul-extension/src/lib.rs at line 224
    [2.17449][2.17449:17521]()
    .map_err(|_error| CreateOpenFileError::AlreadyOpen(path))?;
    [2.17449]
    [2.17521]
    .map_err(|_error| CreateOpenFileError::AlreadyOpen(path.clone()))?;
    let is_tracked = transaction
    .read()
    .is_tracked(path.as_str())
    .map_err(|error| OpenFileError::CheckIfTracked {
    path: path.clone(),
    error,
    })?;
  • edit in pijul-extension/src/lib.rs at line 234
    [2.17522]
    [2.17522]
    // Generate the credits if applicable
    // Since `FileCredits::new()` reads the file state from the working copy,
    // it must be done _after_ inserting the `FileContents` first.
    let file_credits = match is_tracked {
    true => Some(FileCredits::new(
    &path,
    &self.working_copy.open_files.get(&path).unwrap().contents,
    &transaction,
    &channel,
    &self.change_store,
    &self.working_copy,
    )?),
    false => None,
    };
    self.working_copy.open_files.get_mut(&path).unwrap().credits = file_credits;
  • replacement in pijul-extension/src/lib.rs at line 260
    [2.17718][2.17718:17771](),[2.17771][2.17771:17964]()
    ) -> Result<(), UpdateOpenFileError<C::Error>> {
    let open_file = self
    .working_copy
    .open_files
    .get_mut(path)
    .ok_or_else(|| UpdateOpenFileError::NoMatchingFile(path.to_path_buf()))?;
    [2.17718]
    [2.17964]
    ) -> Result<(), UpdateOpenFileError<C::Error, W::Error>> {
  • replacement in pijul-extension/src/lib.rs at line 263
    [2.18038][2.18038:18064]()
    open_file.update(
    [2.18038]
    [2.18064]
    // Since the credits depend on file state already stored in the working copy,
    // update its contents _before_ updating the credits.
    self.working_copy.update_open_file_contents(
    path,
  • edit in pijul-extension/src/lib.rs at line 270
    [2.18157]
    [2.18157]
    )?;
    self.working_copy.update_open_file_credits(
    path,
  • edit in pijul-extension/src/file_system/working_copy.rs at line 4
    [2.18986]
    [2.18986]
    use libpijul::ArcTxn;
    use libpijul::ChannelRef;
    use libpijul::changestore::ChangeStore;
    use libpijul::pristine::sanakirja::MutTxn;
  • edit in pijul-extension/src/file_system/working_copy.rs at line 11
    [2.19073]
    [2.19073]
    use crate::file_system::changes::unrecorded::UnrecordedChangesError;
    use crate::file_system::changes::unrecorded::UnrecordedState;
  • edit in pijul-extension/src/file_system/working_copy.rs at line 14
    [2.19118]
    [2.19118]
    #[derive(Debug, thiserror::Error)]
    pub enum UpdateOpenFileContentsError {
    #[error("No file open at {0}")]
    NoMatchingFile(Utf8PathBuf),
    }
  • edit in pijul-extension/src/file_system/working_copy.rs at line 21
    [2.19119]
    [2.19119]
    #[derive(Debug, thiserror::Error)]
    pub enum UpdateOpenFileCreditsError<C: std::error::Error + 'static, W: std::error::Error + 'static>
    {
    #[error("No file open at {0}")]
    NoMatchingFile(Utf8PathBuf),
    #[error(transparent)]
    UnrecordedChanges(#[from] UnrecordedChangesError<C, W>),
    }
  • edit in pijul-extension/src/file_system/working_copy.rs at line 43
    [2.19510]
    [2.19510]
    pub fn update_open_file_contents(
    &mut self,
    path: &Utf8Path,
    character_offset: usize,
    characters_replaced: usize,
    replacement_text: &str,
    ) -> Result<(), UpdateOpenFileContentsError> {
    let open_file = self
    .open_files
    .get_mut(path)
    .ok_or_else(|| UpdateOpenFileContentsError::NoMatchingFile(path.to_path_buf()))?;
    if characters_replaced > 0 {
    open_file
    .contents
    .text
    .remove(character_offset..(character_offset + characters_replaced));
    }
    if !replacement_text.is_empty() {
    open_file
    .contents
    .text
    .insert(character_offset, replacement_text);
    }
    Ok(())
    }
    pub fn update_open_file_credits<C>(
    &mut self,
    path: &Utf8Path,
    transaction: &ArcTxn<MutTxn<()>>,
    channel: &ChannelRef<MutTxn<()>>,
    change_store: &C,
    ) -> Result<(), UpdateOpenFileCreditsError<C::Error, W::Error>>
    where
    C: ChangeStore + Clone + Send + 'static,
    {
    if !self.open_files.contains_key(path) {
    return Err(UpdateOpenFileCreditsError::NoMatchingFile(
    path.to_path_buf(),
    ));
    }
    let unrecorded_state = match self.open_files.get(path).unwrap().credits.is_some() {
    true => Some(UnrecordedState::new(
    path,
    transaction,
    channel,
    change_store,
    self,
    )?),
    false => None,
    };
    if let Some(file_credits) = &mut self.open_files.get_mut(path).unwrap().credits {
    file_credits.unrecorded_state = unrecorded_state.unwrap();
    }
    let open_file = self.open_files.get_mut(path).unwrap();
    if let Some(file_credits) = &mut open_file.credits {
    file_credits.spans = file_credits.recompute_spans(&open_file.contents);
    }
    Ok(())
    }
  • edit in pijul-extension/src/file_system/open_file/mod.rs at line 4
    [2.20513][2.20513:20706]()
    use libpijul::ArcTxn;
    use libpijul::ChannelRef;
    use libpijul::TxnTExt;
    use libpijul::changestore::ChangeStore;
    use libpijul::pristine::InodeMetadata;
    use libpijul::pristine::sanakirja::MutTxn;
  • edit in pijul-extension/src/file_system/open_file/mod.rs at line 5
    [2.20757][2.20757:20802]()
    use libpijul::working_copy::WorkingCopyRead;
  • edit in pijul-extension/src/file_system/open_file/mod.rs at line 6
    [2.20803][2.20803:20986]()
    use crate::file_system::changes::CreditSource;
    use crate::file_system::changes::FileCredits;
    use crate::file_system::changes::FileCreditsError;
    use crate::file_system::changes::Span;
  • edit in pijul-extension/src/file_system/open_file/mod.rs at line 7
    [2.21055]
    [2.21055]
    use crate::file_system::changes::{CreditSource, FileCredits, FileCreditsError, Span};
  • replacement in pijul-extension/src/file_system/open_file/mod.rs at line 13
    [2.21169][2.21169:21226]()
    pub enum OpenFileError<C: std::error::Error + 'static> {
    [2.21169]
    [2.21226]
    pub enum OpenFileError<C: std::error::Error + 'static, W: std::error::Error + 'static> {
  • replacement in pijul-extension/src/file_system/open_file/mod.rs at line 20
    [2.21403][2.21403:21455]()
    FileChanges(#[from] UnrecordedChangesError<C>),
    [2.21403]
    [2.21455]
    FileChanges(#[from] UnrecordedChangesError<C, W>),
  • replacement in pijul-extension/src/file_system/open_file/mod.rs at line 22
    [2.21481][2.21481:21527]()
    TrackedFile(#[from] FileCreditsError<C>),
    [2.21481]
    [2.21527]
    TrackedFile(#[from] FileCreditsError<C, W>),
  • edit in pijul-extension/src/file_system/open_file/mod.rs at line 28
    [2.21608]
    [2.21608]
    // TODO: use more structured representation for tracked vs untracked files
  • edit in pijul-extension/src/file_system/open_file/mod.rs at line 33
    [2.21665][2.21665:22586](),[2.22586][2.22586:22587](),[2.22587][2.22587:23138](),[2.23138][2.23138:23139](),[2.23139][2.23139:23300](),[2.23300][2.23300:23301](),[2.23301][2.23301:23514](),[2.23514][2.23514:23515](),[2.23515][2.23515:23536](),[2.23536][2.23536:23537]()
    pub fn open<C, W>(
    path: Utf8PathBuf,
    file_contents: String,
    metadata: InodeMetadata,
    transaction: &ArcTxn<MutTxn<()>>,
    channel: &ChannelRef<MutTxn<()>>,
    change_store: &C,
    working_copy: &W,
    ) -> Result<Self, OpenFileError<C::Error>>
    where
    C: ChangeStore + Clone + Send + 'static,
    W: WorkingCopyRead,
    {
    let contents = FileContents::new(path.clone(), metadata, file_contents, working_copy);
    let credits = if transaction
    .read()
    .is_tracked(path.as_str())
    .map_err(|error| OpenFileError::CheckIfTracked { path, error })?
    {
    Some(FileCredits::new(
    &contents,
    transaction,
    channel,
    change_store,
    )?)
    } else {
    None
    };
    Ok(Self { contents, credits })
    }
    pub fn update<C>(
    &mut self,
    character_offset: usize,
    characters_replaced: usize,
    replacement_text: &str,
    transaction: &ArcTxn<MutTxn<()>>,
    channel: &ChannelRef<MutTxn<()>>,
    change_store: &C,
    ) -> Result<(), UnrecordedChangesError<C::Error>>
    where
    C: ChangeStore + Clone + Send + 'static,
    {
    if characters_replaced > 0 {
    self.contents
    .text
    .remove(character_offset..(character_offset + characters_replaced));
    }
    if !replacement_text.is_empty() {
    self.contents
    .text
    .insert(character_offset, replacement_text);
    }
    if let Some(credits) = &mut self.credits {
    // TODO: only re-compute spans that were affected
    credits.recompute_spans(&self.contents, transaction, channel, change_store)?;
    }
    Ok(())
    }
  • edit in pijul-extension/src/file_system/open_file/contents.rs at line 7
    [2.24155][2.24155:24401]()
    #[derive(Debug, thiserror::Error)]
    pub enum FileContentsError {
    #[error("Invalid path: expected {expected_path:?}, got {incorrect_path:?}")]
    InvalidPath {
    expected_path: Utf8PathBuf,
    incorrect_path: Utf8PathBuf,
    },
    }
  • edit in pijul-extension/src/file_system/open_file/contents.rs at line 9
    [2.24451][2.24451:24521]()
    // TODO: path shouldn't be stored here
    pub path: Utf8PathBuf,
  • edit in pijul-extension/src/file_system/open_file/contents.rs at line 44
    [2.24943][2.24943:24961]()
    path,
  • edit in pijul-extension/src/file_system/open_file/contents.rs at line 50
    [2.25088][2.25088:25960](),[2.25960][3.889:977](),[3.977][2.26037:26267](),[2.26037][2.26037:26267](),[2.26267][3.978:1016](),[3.1016][2.26298:26306](),[2.26298][2.26298:26306]()
    // TODO: implement `WorkingCopyRead` for `EditorWorkingCopy` instead?
    impl WorkingCopyRead for FileContents {
    type Error = FileContentsError;
    fn file_metadata(&self, file: &str) -> Result<InodeMetadata, Self::Error> {
    if file != self.path.as_str() {
    return Err(FileContentsError::InvalidPath {
    expected_path: self.path.clone(),
    incorrect_path: Utf8PathBuf::from(file),
    });
    }
    Ok(self.metadata)
    }
    fn read_file(&self, file: &str, buffer: &mut Vec<u8>) -> Result<(), Self::Error> {
    if file != self.path.as_str() {
    return Err(FileContentsError::InvalidPath {
    expected_path: self.path.clone(),
    incorrect_path: Utf8PathBuf::from(file),
    });
    }
    buffer.extend(self.text.bytes());
    Ok(())
    }
    fn modified_time(&self, file: &str) -> Result<std::time::SystemTime, Self::Error> {
    if file != self.path.as_str() {
    return Err(FileContentsError::InvalidPath {
    expected_path: self.path.clone(),
    incorrect_path: Utf8PathBuf::from(file),
    });
    }
    Ok(self.modified_time.into())
    }
    }
  • edit in pijul-extension/src/file_system/changes/unrecorded.rs at line 1
    [2.26457]
    [2.26458]
    use camino::Utf8Path;
  • edit in pijul-extension/src/file_system/changes/unrecorded.rs at line 5
    [2.26613]
    [2.26613]
    use libpijul::working_copy::WorkingCopyRead;
  • edit in pijul-extension/src/file_system/changes/unrecorded.rs at line 9
    [2.26704][2.26704:26784]()
    use crate::file_system::open_file::contents::{FileContents, FileContentsError};
  • replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 12
    [2.26842][2.26842:26908]()
    pub enum UnrecordedChangesError<C: std::error::Error + 'static> {
    [2.26842]
    [2.26908]
    pub enum UnrecordedChangesError<C: std::error::Error + 'static, W: std::error::Error + 'static> {
  • replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 15
    [2.26992][2.26992:27077]()
    Record(#[from] libpijul::record::RecordError<C, FileContentsError, MutTxn<()>>),
    [2.26992]
    [2.27077]
    Record(#[from] libpijul::record::RecordError<C, W, MutTxn<()>>),
  • replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 33
    [2.27346][2.27346:27377]()
    pub struct UnrecordedChanges {
    [2.27346]
    [2.27377]
    pub struct UnrecordedState {
  • replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 37
    [2.27416][2.27416:27441](),[2.27441][2.27441:27489]()
    impl UnrecordedChanges {
    pub fn new<C>(
    file: &FileContents,
    [2.27416]
    [2.27489]
    impl UnrecordedState {
    pub fn new<C, W>(
    path: &Utf8Path,
  • replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 43
    [2.27599][2.27599:27655]()
    ) -> Result<Self, UnrecordedChangesError<C::Error>>
    [2.27599]
    [2.27655]
    // TODO: don't require a full working copy, use a `WorkingCopyRead` implementation that
    // only gives information on the current file. This is currently blocked on
    // `RecordBuilder::record()` seeming to ignore the full-file prefix and generating
    // invalid deletion hunks when the shim returns an error on unrelated files.
    working_copy: &W,
    ) -> Result<Self, UnrecordedChangesError<C::Error, W::Error>>
  • edit in pijul-extension/src/file_system/changes/unrecorded.rs at line 51
    [2.27714]
    [2.27714]
    W: WorkingCopyRead + Clone + Send + Sync + 'static,
  • replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 61
    [2.28010][2.28010:28028]()
    file,
    [2.28010]
    [2.28028]
    working_copy,
  • replacement in pijul-extension/src/file_system/changes/unrecorded.rs at line 63
    [2.28054][2.28054:28086]()
    file.path.as_str(),
    [2.28054]
    [2.28086]
    path.as_str(),
  • edit in pijul-extension/src/file_system/changes/mod.rs at line 3
    [2.36801]
    [2.36801]
    use camino::Utf8Path;
  • edit in pijul-extension/src/file_system/changes/mod.rs at line 6
    [2.36884]
    [2.36884]
    use libpijul::working_copy::WorkingCopyRead;
  • replacement in pijul-extension/src/file_system/changes/mod.rs at line 67
    [2.38625][2.38625:38685]()
    pub enum FileCreditsError<C: std::error::Error + 'static> {
    [2.38625]
    [2.38685]
    pub enum FileCreditsError<C: std::error::Error + 'static, W: std::error::Error + 'static> {
  • replacement in pijul-extension/src/file_system/changes/mod.rs at line 71
    [2.38845][2.38845:38902]()
    UntrackedChanges(#[from] UnrecordedChangesError<C>),
    [2.38845]
    [2.38902]
    UntrackedChanges(#[from] UnrecordedChangesError<C, W>),
  • edit in pijul-extension/src/file_system/changes/mod.rs at line 77
    [2.39003]
    [2.39003]
    pub unrecorded_state: unrecorded::UnrecordedState,
  • replacement in pijul-extension/src/file_system/changes/mod.rs at line 82
    [2.39065][2.39065:39113]()
    pub fn new<C>(
    file: &FileContents,
    [2.39065]
    [2.39113]
    pub fn new<C, W>(
    path: &Utf8Path,
    file_contents: &FileContents,
  • replacement in pijul-extension/src/file_system/changes/mod.rs at line 88
    [2.39223][2.39223:39273]()
    ) -> Result<Self, FileCreditsError<C::Error>>
    [2.39223]
    [2.39273]
    working_copy: &W,
    ) -> Result<Self, FileCreditsError<C::Error, W::Error>>
  • edit in pijul-extension/src/file_system/changes/mod.rs at line 92
    [2.39332]
    [2.39332]
    W: WorkingCopyRead + Clone + Send + Sync + 'static,
  • replacement in pijul-extension/src/file_system/changes/mod.rs at line 94
    [2.39338][2.39338:39437]()
    let state = recorded::RecordedState::new(&file.path, transaction, channel, change_store)?;
    [2.39338]
    [2.39437]
    let recorded_state =
    recorded::RecordedState::new(path, transaction, channel, change_store)?;
    let unrecorded_state = unrecorded::UnrecordedState::new(
    path,
    transaction,
    channel,
    change_store,
    working_copy,
    )?;
  • replacement in pijul-extension/src/file_system/changes/mod.rs at line 105
    [2.39476][2.39476:39511]()
    recorded_state: state,
    [2.39476]
    [2.39511]
    recorded_state,
    unrecorded_state,
  • replacement in pijul-extension/src/file_system/changes/mod.rs at line 109
    [2.39553][2.39553:39634]()
    tracked_file.recompute_spans(file, transaction, channel, change_store)?;
    [2.39553]
    [2.39634]
    tracked_file.spans = tracked_file.recompute_spans(file_contents);
  • replacement in pijul-extension/src/file_system/changes/mod.rs at line 114
    [2.39667][2.39667:40098](),[2.40098][2.40098:40099]()
    pub fn recompute_spans<C>(
    &mut self,
    file: &FileContents,
    transaction: &ArcTxn<MutTxn<()>>,
    channel: &ChannelRef<MutTxn<()>>,
    change_store: &C,
    ) -> Result<(), UnrecordedChangesError<C::Error>>
    where
    C: ChangeStore + Clone + Send + 'static,
    {
    let untracked_changes =
    unrecorded::UnrecordedChanges::new(file, transaction, channel, change_store)?;
    [2.39667]
    [2.40099]
    pub fn recompute_spans(&self, file: &FileContents) -> Vec<Span<CreditSource>> {
  • replacement in pijul-extension/src/file_system/changes/mod.rs at line 116
    [2.40142][2.40142:40219]()
    let mut untracked_spans = untracked_changes.spans.iter().peekable();
    [2.40142]
    [2.40219]
    let mut untracked_spans = self.unrecorded_state.spans.iter().peekable();
  • replacement in pijul-extension/src/file_system/changes/mod.rs at line 250
    [2.45570][2.45570:45620]()
    self.spans = merged_spans;
    Ok(())
    [2.45570]
    [2.45620]
    merged_spans