diff --git a/Cargo.lock b/Cargo.lock index d2f906698..24a1226fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,6 +122,12 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.4.0" @@ -1445,6 +1451,7 @@ dependencies = [ "open", "pulldown-cmark", "same-file", + "rmp-serde", "serde", "serde_json", "signal-hook", @@ -1876,6 +1883,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "pathdiff" version = "0.2.1" @@ -2050,6 +2063,28 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rmp" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "ropey" version = "1.6.1" diff --git a/helix-loader/src/lib.rs b/helix-loader/src/lib.rs index f36c76c4f..ee34f7520 100644 --- a/helix-loader/src/lib.rs +++ b/helix-loader/src/lib.rs @@ -15,6 +15,8 @@ static CONFIG_FILE: once_cell::sync::OnceCell = once_cell::sync::OnceCe static LOG_FILE: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); +static SHADA_FILE: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); + pub fn initialize_config_file(specified_file: Option) { let config_file = specified_file.unwrap_or_else(default_config_file); ensure_parent_dir(&config_file); @@ -27,6 +29,12 @@ pub fn initialize_log_file(specified_file: Option) { LOG_FILE.set(log_file).ok(); } +pub fn initialize_shada_file(specified_file: Option) { + let shada_file = specified_file.unwrap_or_else(default_shada_file); + ensure_parent_dir(&shada_file); + SHADA_FILE.set(shada_file).ok(); +} + /// A list of runtime directories from highest to lowest priority /// /// The priority is: @@ -132,6 +140,14 @@ pub fn cache_dir() -> PathBuf { path } +pub fn state_dir() -> PathBuf { + // TODO: allow env var override + let strategy = choose_base_strategy().expect("Unable to find the state directory!"); + let mut path = strategy.state_dir().unwrap(); + path.push("helix"); + path +} + pub fn config_file() -> PathBuf { CONFIG_FILE.get().map(|path| path.to_path_buf()).unwrap() } @@ -140,6 +156,10 @@ pub fn log_file() -> PathBuf { LOG_FILE.get().map(|path| path.to_path_buf()).unwrap() } +pub fn shada_file() -> PathBuf { + SHADA_FILE.get().map(|path| path.to_path_buf()).unwrap() +} + pub fn workspace_config_file() -> PathBuf { find_workspace().0.join(".helix").join("config.toml") } @@ -152,6 +172,10 @@ pub fn default_log_file() -> PathBuf { cache_dir().join("helix.log") } +pub fn default_shada_file() -> PathBuf { + state_dir().join("helix.shada") +} + /// Merge two TOML documents, merging values from `right` onto `left` /// /// When an array exists in both `left` and `right`, `right`'s array is diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 5f691d44a..a488e45bd 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -68,6 +68,9 @@ toml = "0.8" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } +# shada +rmp-serde = "1.1.2" + # ripgrep for global search grep-regex = "0.1.12" grep-searcher = "0.1.13" diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index bd6b5a870..50519072e 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -27,6 +27,7 @@ use crate::{ handlers, job::Jobs, keymap::Keymaps, + shada, ui::{self, overlay::overlaid}, }; @@ -1254,6 +1255,8 @@ impl Application { )); } + shada::write_shada_file(); + errs } } diff --git a/helix-term/src/args.rs b/helix-term/src/args.rs index 0b1c9cde0..ee74504de 100644 --- a/helix-term/src/args.rs +++ b/helix-term/src/args.rs @@ -16,6 +16,7 @@ pub struct Args { pub verbosity: u64, pub log_file: Option, pub config_file: Option, + pub shada_file: Option, pub files: Vec<(PathBuf, Position)>, pub working_directory: Option, } @@ -61,6 +62,10 @@ impl Args { Some(path) => args.log_file = Some(path.into()), None => anyhow::bail!("--log must specify a path to write"), }, + "--shada" => match argv.next().as_deref() { + Some(path) => args.shada_file = Some(path.into()), + None => anyhow::bail!("--shada must specify a path to write"), + }, "-w" | "--working-dir" => match argv.next().as_deref() { Some(path) => { args.working_directory = if Path::new(path).is_dir() { diff --git a/helix-term/src/lib.rs b/helix-term/src/lib.rs index cf4fbd9fa..fad07668f 100644 --- a/helix-term/src/lib.rs +++ b/helix-term/src/lib.rs @@ -10,6 +10,7 @@ pub mod events; pub mod health; pub mod job; pub mod keymap; +pub mod shada; pub mod ui; use std::path::Path; diff --git a/helix-term/src/main.rs b/helix-term/src/main.rs index a3a27a076..0c8f6c473 100644 --- a/helix-term/src/main.rs +++ b/helix-term/src/main.rs @@ -63,6 +63,7 @@ FLAGS: -v Increases logging verbosity each use for up to 3 times --log Specifies a file to use for logging (default file: {}) + --shada Specifies a file to use for shared data -V, --version Prints version information --vsplit Splits all given files vertically into different windows --hsplit Splits all given files horizontally into different windows @@ -80,6 +81,7 @@ FLAGS: helix_loader::initialize_config_file(args.config_file.clone()); helix_loader::initialize_log_file(args.log_file.clone()); + helix_loader::initialize_shada_file(args.shada_file.clone()); // Help has a higher priority and should be handled separately. if args.display_help { diff --git a/helix-term/src/shada.rs b/helix-term/src/shada.rs new file mode 100644 index 000000000..108ce036a --- /dev/null +++ b/helix-term/src/shada.rs @@ -0,0 +1,75 @@ +use helix_loader::{shada_file, VERSION_AND_GIT_HASH}; +use helix_view::view::ViewPosition; +use rmp_serde::Serializer; +use serde::{Deserialize, Serialize}; +use std::{ + fs::File, + time::{SystemTime, UNIX_EPOCH}, +}; + +// TODO: should this be non-exhaustive? +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename = "H")] +struct Header { + generator: String, + version: String, + encoding: String, + max_kbyte: u32, + pid: u32, +} + +// TODO: should this be non-exhaustive? +#[derive(Debug, Deserialize, Serialize)] +struct FilePosition { + path: String, + position: ViewPosition, +} + +// TODO: should this be non-exhaustive? +#[derive(Debug, Deserialize, Serialize)] +enum EntryData { + Header(Header), + FilePosition(FilePosition), +} + +// TODO: should this be non-exhaustive? +#[derive(Debug, Deserialize, Serialize)] +struct Entry { + timestamp: u64, + data: EntryData, +} + +fn timestamp_now() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_secs() +} + +fn generate_header() -> Entry { + Entry { + timestamp: timestamp_now(), + data: EntryData::Header(Header { + generator: "helix".to_string(), + version: VERSION_AND_GIT_HASH.to_string(), + // TODO: is this necessary? helix doesn't seem to expose an option + // for internal encoding like nvim does + encoding: "utf-8".to_string(), + max_kbyte: 100, + pid: std::process::id(), + }), + } +} + +pub fn write_shada_file() { + // TODO: merge existing file if exists + + // TODO: do something about this unwrap + let shada_file = File::create(shada_file()).unwrap(); + let mut serializer = Serializer::new(shada_file); + + let header = generate_header(); + + // TODO: do something about this unwrap + header.serialize(&mut serializer).unwrap(); +} diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index a229f01ea..9ebec0051 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -18,6 +18,8 @@ use helix_core::{ VisualOffsetError::{PosAfterMaxRow, PosBeforeAnchorRow}, }; +use serde::{Deserialize, Serialize}; + use std::{ collections::{HashMap, VecDeque}, fmt, @@ -118,7 +120,7 @@ impl JumpList { } } -#[derive(Clone, Debug, PartialEq, Eq, Copy, Default)] +#[derive(Clone, Debug, PartialEq, Eq, Copy, Default, Serialize, Deserialize)] pub struct ViewPosition { pub anchor: usize, pub horizontal_offset: usize,