PoC
This commit is contained in:
parent
34a9c3f864
commit
551e5a7851
4 changed files with 163 additions and 33 deletions
|
|
@ -48,6 +48,8 @@ pub struct SourcePath {
|
|||
pub struct SourceFile {
|
||||
path: Arc<Path>,
|
||||
file: Arc<Mutex<File>>,
|
||||
/// References to `SourceFile` do not prevent mutating `lines`.
|
||||
/// Also `lines` is lazily initialized.
|
||||
lines: Arc<OnceLock<RefCell<Vec<SourceLine>>>>,
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +121,75 @@ impl SourceFile {
|
|||
Ref::map(self.lines.get().unwrap().borrow(), |lines| lines.as_slice())
|
||||
}
|
||||
|
||||
/// With debug assertions, panics if `lines` are not contiguous.
|
||||
pub fn insert_lines(&mut self, new_lines: &[SourceLine]) -> Result<(), IoError> {
|
||||
if new_lines.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
debug_assert!(new_lines.is_sorted_by(|lhs, rhs| lhs.line.next() == rhs.line));
|
||||
|
||||
let path = self.path();
|
||||
let cur_lines = self.lines()?;
|
||||
let first_half = cur_lines
|
||||
.iter()
|
||||
.take(new_lines.last().unwrap().line.prev().index() as usize)
|
||||
.map(SourceLine::text);
|
||||
let middle = new_lines.iter().map(SourceLine::text);
|
||||
let second_half = cur_lines
|
||||
.iter()
|
||||
.skip(new_lines.last().unwrap().line.prev().index() as usize)
|
||||
.map(SourceLine::text);
|
||||
|
||||
let final_lines: Vec<SourceLine> = first_half
|
||||
.chain(middle)
|
||||
.chain(second_half)
|
||||
.enumerate()
|
||||
.map(|(idx, text)| SourceLine {
|
||||
line: Line::from_index(idx as u64),
|
||||
text,
|
||||
path: self.path(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Assert lines are continuous.
|
||||
debug_assert!(final_lines.is_sorted_by(|lhs, rhs| lhs.line.next() == rhs.line));
|
||||
debug_assert_eq!(cur_lines.len() + new_lines.len(), final_lines.len());
|
||||
|
||||
drop(cur_lines);
|
||||
|
||||
// Write it to a file in the same directory.
|
||||
let new_name: OsString = [
|
||||
// foo
|
||||
path.file_name().unwrap(),
|
||||
OsStr::new(".tmp"),
|
||||
]
|
||||
.into_iter()
|
||||
.collect::<OsString>();
|
||||
let tmp_path = path.with_file_name(&new_name);
|
||||
let tmp_file = File::options()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.custom_flags(libc::O_EXCL | libc::O_CLOEXEC)
|
||||
.open(&tmp_path)?;
|
||||
|
||||
let mut writer = BufWriter::new(tmp_file);
|
||||
for line in final_lines.iter() {
|
||||
writer.write_all(line.text().as_bytes())?;
|
||||
writer.write_all(b"\n")?;
|
||||
}
|
||||
writer.flush()?;
|
||||
drop(writer);
|
||||
// Rename the temporary file to the new file, which is atomic (TODO: I think).
|
||||
fs_err::rename(&tmp_path, &path)?;
|
||||
|
||||
// Finally, update state.
|
||||
self.lines.get().unwrap().replace(final_lines);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_line(&mut self, at: Line, text: Arc<str>) -> Result<(), IoError> {
|
||||
self.lines()?;
|
||||
let path = self.path();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue