diff --git a/Cargo.lock b/Cargo.lock index 7a530a20109cb..cfdc709d40921 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1140,6 +1140,7 @@ dependencies = [ "unicode-general-category", "unicode-segmentation", "unicode-width", + "walkdir", ] [[package]] diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index ca6cd51e354e7..6ff8700b2a2f3 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -32,6 +32,7 @@ regex = "1" bitflags = "1.3" ahash = "0.8.2" hashbrown = { version = "0.13.1", features = ["raw"] } +walkdir = "2.3" log = "0.4" serde = { version = "1.0", features = ["derive"] } diff --git a/helix-core/src/history.rs b/helix-core/src/history.rs index 939c385505134..bab69b6318f0d 100644 --- a/helix-core/src/history.rs +++ b/helix-core/src/history.rs @@ -68,12 +68,14 @@ struct Revision { timestamp: Instant, } -const HEADER_TAG: &str = "Helix Undofile\n\n"; +const HEADER_TAG: &str = "Helix Undofile"; +const CURRENT_VERSION: u8 = 1; pub fn serialize_history(file: File, history: &History) -> std::io::Result<()> { let mut writer = std::io::BufWriter::new(file); write_string(&mut writer, HEADER_TAG)?; + write_byte(&mut writer, CURRENT_VERSION)?; write_usize(&mut writer, history.current)?; write_vec(&mut writer, &history.revisions, serialize_revision)?; Ok(()) @@ -87,6 +89,7 @@ pub fn deserialize_history(file: File) -> std::io::Result { "missing undofile header", )) } else { + let _version = read_byte(&mut reader)?; let timestamp = Instant::now(); let current = read_usize(&mut reader)?; let revisions = read_vec(&mut reader, |reader| { diff --git a/helix-core/src/transaction.rs b/helix-core/src/transaction.rs index 489de26241f0c..9e831aed2884d 100644 --- a/helix-core/src/transaction.rs +++ b/helix-core/src/transaction.rs @@ -431,10 +431,9 @@ pub fn serialize_transaction( write_vec(writer, selection.ranges(), |writer, range| { write_usize(writer, range.anchor)?; write_usize(writer, range.head)?; - write_bool(writer, range.horiz.is_some())?; - if let Some(horiz) = range.horiz.as_ref() { - write_u32(writer, *horiz)?; - } + write_option(writer, range.horiz.as_ref(), |writer, horiz| { + write_u32(writer, *horiz) + })?; Ok(()) })?; diff --git a/helix-view/src/session/mod.rs b/helix-view/src/session/mod.rs index 89fc641137bb4..c25aca958ec5e 100644 --- a/helix-view/src/session/mod.rs +++ b/helix-view/src/session/mod.rs @@ -8,7 +8,7 @@ use std::{ use anyhow::{Context, Result}; use sha1_smol::Sha1; -pub struct Session { +pub struct Workspace { path: PathBuf, lock: Option, } @@ -21,7 +21,7 @@ pub fn path_as_bytes(path: PathBuf) -> Vec { return std::os::unix::ffi::OsStrExt::as_bytes(path.as_os_str()).into(); } -impl Session { +impl Workspace { // TODO: Allow custom session names to be passed. pub fn new(path: PathBuf) -> Result { let bytes = path_as_bytes(path); @@ -30,6 +30,10 @@ impl Session { Ok(Self { path, lock: None }) } + pub fn path(&self) -> PathBuf { + self.path.clone() + } + pub fn get(&mut self, filename: &str) -> Result { if self.lock.is_none() { let lock = FileLock::shared(self.path.join(".helix.lock"))?; @@ -118,7 +122,6 @@ mod sys { flock(file, flag) } - #[cfg(not(target_os = "solaris"))] fn flock(file: &File, flag: libc::c_int) -> Result<()> { let ret = unsafe { libc::flock(file.as_raw_fd(), flag) }; if ret < 0 { @@ -127,42 +130,6 @@ mod sys { Ok(()) } } - - #[cfg(target_os = "solaris")] - fn flock(file: &File, flag: libc::c_int) -> Result<()> { - // Solaris lacks flock(), so try to emulate using fcntl() - let mut flock = libc::flock { - l_type: 0, - l_whence: 0, - l_start: 0, - l_len: 0, - l_sysid: 0, - l_pid: 0, - l_pad: [0, 0, 0, 0], - }; - flock.l_type = if flag & libc::LOCK_UN != 0 { - libc::F_UNLCK - } else if flag & libc::LOCK_EX != 0 { - libc::F_WRLCK - } else if flag & libc::LOCK_SH != 0 { - libc::F_RDLCK - } else { - panic!("unexpected flock() operation") - }; - - let mut cmd = libc::F_SETLKW; - if (flag & libc::LOCK_NB) != 0 { - cmd = libc::F_SETLK; - } - - let ret = unsafe { libc::fcntl(file.as_raw_fd(), cmd, &flock) }; - - if ret < 0 { - anyhow::bail!(Error::last_os_error()) - } else { - Ok(()) - } - } } #[cfg(windows)] diff --git a/helix-view/src/session/undo.rs b/helix-view/src/session/undo.rs index 5be173e3d8b7d..55d9f8bef3326 100644 --- a/helix-view/src/session/undo.rs +++ b/helix-view/src/session/undo.rs @@ -1,103 +1,70 @@ -use std::fs::File; -use std::io::BufReader; -use std::io::BufWriter; -use std::path::PathBuf; - -#[cfg(unix)] -use std::os::unix::prelude::OsStrExt; - -use anyhow::Context; -use anyhow::Result; -use helix_core::history::deserialize_history; -use helix_core::history::serialize_history; -use helix_core::parse::*; - -use crate::Editor; - -use super::Session; - -// TODO: Check if serialized files already exist, and use them. -// TODO: Maybe have a way to verify that the histories match, and overwrite if they don't. -pub fn serialize(session: &mut Session, editor: &mut Editor) -> Result<()> { - // Handle existing index file to merge. - let mut index_file = session.get_mut("undo/index")?; - let mut index = deserialize_index(&index_file).context("failed to parse undo index")?; - for path in editor.documents().filter_map(|doc| doc.path().cloned()) { - if !index.iter().any(|(_, value)| *value == path) { - let key = index.last().map(|(key, _)| key + 1).unwrap_or(0); - index.push((key, path)); - } - } - serialize_index(&mut index_file, &index)?; - - for (filename, doc_path) in index { - let doc = match editor - .documents_mut() - .find(|doc| doc.path() == Some(&doc_path)) - { - Some(doc) => doc, - None => continue, - }; - let filename = format!("undo/{filename}"); - let file = session.get_mut(&filename)?; - let history = doc.history.take(); - serialize_history(file, &history)?; - doc.history.set(history); - } - - Ok(()) -} - -pub fn deserialize(session: &mut Session, editor: &mut Editor) -> Result<()> { - let index = session - .get("undo/index") - .and_then(|file| deserialize_index(&file)) - .context("failed to parse index file")?; - - for (filename, doc_path) in index { - let id = editor.open(&doc_path, crate::editor::Action::Load)?; - let doc = editor.document_mut(id).unwrap(); - let filename = format!("undo/{filename}"); - let file = session.get(&filename)?; - doc.history = std::cell::Cell::new(deserialize_history(file)?); - } - - Ok(()) -} - -fn serialize_index(file: &mut File, documents: &[(usize, PathBuf)]) -> Result<()> { - let mut writer = BufWriter::new(file); - write_vec(&mut writer, documents, |writer, (id, path)| { - write_usize(writer, *id)?; - - #[cfg(windows)] - write_string(writer, path.to_str().unwrap())?; - - #[cfg(unix)] - { - let bytes = path.as_os_str().as_bytes(); - write_vec(writer, bytes, |writer, byte| write_byte(writer, *byte))?; - } - Ok(()) - })?; - - Ok(()) -} - -fn deserialize_index(file: &File) -> Result> { - let mut reader = BufReader::new(file); - let res = read_vec(&mut reader, |reader| { - let id = read_usize(reader)?; - - #[cfg(windows)] - let path = PathBuf::from(read_string(reader)?); - - #[cfg(unix)] - let path = { - let bytes = read_vec(reader, read_byte)?; - PathBuf::from(std::ffi::OsStr::from_bytes(&bytes)) - }; - Ok((id, path)) - })?; - Ok(res) -} +// use std::fs::File; +// use std::io::BufReader; +// use std::io::BufWriter; +// use std::path::PathBuf; + +// #[cfg(unix)] +// use std::os::unix::prelude::OsStrExt; + +// use anyhow::Context; +// use anyhow::Result; +// use helix_core::history::deserialize_history; +// use helix_core::history::serialize_history; +// use helix_core::parse::*; + +// use crate::Editor; + +// use super::Session; + +// // TODO: Check if serialized files already exist, and use them. +// // TODO: Maybe have a way to verify that the histories match, and overwrite if they don't. +// pub fn serialize(session: &mut Session, editor: &mut Editor) -> Result<()> { +// let cwd = std::env::current_dir()?; +// for doc in editor.documents_mut().filter(|doc| doc.path().is_some()) { + +// } +// // Handle existing index file to merge. +// let mut index_file = session.get_mut("undo/index")?; +// let mut index = deserialize_index(&index_file).context("failed to parse undo index")?; +// for path in editor.documents().filter_map(|doc| doc.path().cloned()) { +// if !index.iter().any(|(_, value)| *value == path) { +// let key = index.last().map(|(key, _)| key + 1).unwrap_or(0); +// index.push((key, path)); +// } +// } +// serialize_index(&mut index_file, &index)?; + +// for (filename, doc_path) in index { +// let doc = match editor +// .documents_mut() +// .find(|doc| doc.path() == Some(&doc_path)) +// { +// Some(doc) => doc, +// None => continue, +// }; +// let filename = format!("undo/{filename}"); +// let file = session.get_mut(&filename)?; +// let history = doc.history.take(); +// serialize_history(file, &history)?; +// doc.history.set(history); +// } + +// Ok(()) +// } + +// pub fn deserialize(session: &mut Session, editor: &mut Editor) -> Result<()> { +// let index = session +// .get("undo/index") +// .and_then(|file| deserialize_index(&file)) +// .context("failed to parse index file")?; + +// for (filename, doc_path) in index { +// let id = editor.open(&doc_path, crate::editor::Action::Load)?; +// let doc = editor.document_mut(id).unwrap(); +// let filename = format!("undo/{filename}"); +// let file = session.get(&filename)?; +// doc.history = std::cell::Cell::new(deserialize_history(file)?); +// } + +// Ok(()) +// }