Due to what seems like a bug/quirk in Pijul's implementation, recording an exact file prefix seems to still record changes to other files. Consequently, the single-file WorkingCopyRead shim would generate invalid file deletion hunks as it only gave information about itself. This unfortunately complicates the code a bit further, and can hopefully be reverted if the behaviour is changed upstream.
TIPGJCUW2P2DNOVVQ6RETSFDEGLC5C75CNRRB42WCUA7HHQT5C3QC UnrecordedChanges(#[from] UnrecordedChangesError<C>),#[error("No file open at {0}")]NoMatchingFile(Utf8PathBuf),#[error("Unable to begin transaction: {0:#?}")]BeginTransaction(#[from] BeginTransactionError),
UpdateContents(#[from] UpdateOpenFileContentsError),#[error(transparent)]UpdateCredits(#[from] UpdateOpenFileCreditsError<C, W>),
let open_file = OpenFile::open(path.clone(),file_contents,metadata,&transaction,&channel,&self.change_store,&self.working_copy,)?;
let open_file = OpenFile {contents: FileContents::new(path.clone(), metadata, file_contents, &self.working_copy),credits: None,};
.map_err(|_error| CreateOpenFileError::AlreadyOpen(path))?;
.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,})?;
// 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;
) -> Result<(), UpdateOpenFileError<C::Error>> {let open_file = self.working_copy.open_files.get_mut(path).ok_or_else(|| UpdateOpenFileError::NoMatchingFile(path.to_path_buf()))?;
) -> Result<(), UpdateOpenFileError<C::Error, W::Error>> {
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>>whereC: 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(())}
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>>whereC: 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>>whereC: 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 affectedcredits.recompute_spans(&self.contents, transaction, channel, change_store)?;}Ok(())}
// 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())}}
) -> Result<Self, UnrecordedChangesError<C::Error>>
// 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>>
let state = recorded::RecordedState::new(&file.path, transaction, channel, change_store)?;
let recorded_state =recorded::RecordedState::new(path, transaction, channel, change_store)?;let unrecorded_state = unrecorded::UnrecordedState::new(path,transaction,channel,change_store,working_copy,)?;
pub fn recompute_spans<C>(&mut self,file: &FileContents,transaction: &ArcTxn<MutTxn<()>>,channel: &ChannelRef<MutTxn<()>>,change_store: &C,) -> Result<(), UnrecordedChangesError<C::Error>>whereC: ChangeStore + Clone + Send + 'static,{let untracked_changes =unrecorded::UnrecordedChanges::new(file, transaction, channel, change_store)?;
pub fn recompute_spans(&self, file: &FileContents) -> Vec<Span<CreditSource>> {