Skip to content

Commit

Permalink
implement workspace commands
Browse files Browse the repository at this point in the history
  • Loading branch information
kirawi committed Jan 30, 2023
1 parent 7580d28 commit 1282066
Show file tree
Hide file tree
Showing 9 changed files with 323 additions and 150 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions helix-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ regex = "1"
bitflags = "1.3"
ahash = "0.8.2"
hashbrown = { version = "0.13.2", features = ["raw"] }
sha1_smol = "1.0"

log = "0.4"
serde = { version = "1.0", features = ["derive"] }
Expand Down
150 changes: 93 additions & 57 deletions helix-core/src/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use regex::Regex;
use std::io::{Read, Write};
use std::num::NonZeroUsize;
use std::time::{Duration, Instant};

#[derive(Debug, Clone)]
pub struct State {
pub doc: Rope,
Expand Down Expand Up @@ -69,62 +68,20 @@ struct Revision {

const HEADER_TAG: &str = "Helix Undofile 1\n";

pub fn serialize_history<W: Write>(
writer: &mut W,
history: &History,
mtime: u64,
hash: [u8; 20],
) -> std::io::Result<()> {
write_string(writer, HEADER_TAG)?;
write_usize(writer, history.current)?;
write_u64(writer, mtime)?;
writer.write_all(&hash)?;
write_vec(writer, &history.revisions, serialize_revision)?;
Ok(())
}
fn get_hash<R: Read>(reader: &mut R) -> std::io::Result<[u8; 20]> {
const BUF_SIZE: usize = 8192;

pub fn deserialize_history<R: Read>(reader: &mut R) -> std::io::Result<History> {
let header = read_string(reader)?;
if HEADER_TAG != header {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("missing undofile header"),
))
} else {
let timestamp = Instant::now();
let current = read_usize(reader)?;
let mtime = read_u64(reader)?;
let mut hash = [0u8; 20];
reader.read_exact(&mut hash)?;
let revisions = read_vec(reader, |reader| deserialize_revision(reader, timestamp))?;
Ok(History { current, revisions })
}
}

fn serialize_revision<W: Write>(writer: &mut W, revision: &Revision) -> std::io::Result<()> {
write_usize(writer, revision.parent)?;
write_usize(writer, revision.last_child.map(|n| n.get()).unwrap_or(0))?;
crate::transaction::serialize_transaction(writer, &revision.transaction)?;
crate::transaction::serialize_transaction(writer, &revision.inversion)?;

Ok(())
}
let mut buf = [0u8; BUF_SIZE];
let mut hash = sha1_smol::Sha1::new();
loop {
let total_read = reader.read(&mut buf)?;
if total_read == 0 {
break;
}

fn deserialize_revision<R: Read>(reader: &mut R, timestamp: Instant) -> std::io::Result<Revision> {
let parent = read_usize(reader)?;
let last_child = match read_usize(reader)? {
0 => None,
n => Some(unsafe { NonZeroUsize::new_unchecked(n) }),
};
let transaction = crate::transaction::deserialize_transaction(reader)?;
let inversion = crate::transaction::deserialize_transaction(reader)?;
Ok(Revision {
parent,
last_child,
transaction,
inversion,
timestamp,
})
hash.update(&buf[0..total_read]);
}
Ok(hash.digest().bytes())
}

impl Default for History {
Expand All @@ -143,7 +100,82 @@ impl Default for History {
}
}

impl Revision {
fn serialize<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
write_usize(writer, self.parent)?;
write_usize(writer, self.last_child.map(|n| n.get()).unwrap_or(0))?;
crate::transaction::serialize_transaction(writer, &self.transaction)?;
crate::transaction::serialize_transaction(writer, &self.inversion)?;

Ok(())
}

fn deserialize<R: Read>(reader: &mut R, timestamp: Instant) -> std::io::Result<Self> {
let parent = read_usize(reader)?;
let last_child = match read_usize(reader)? {
0 => None,
n => Some(unsafe { NonZeroUsize::new_unchecked(n) }),
};
let transaction = crate::transaction::deserialize_transaction(reader)?;
let inversion = crate::transaction::deserialize_transaction(reader)?;
Ok(Revision {
parent,
last_child,
transaction,
inversion,
timestamp,
})
}
}

impl History {
pub fn serialize<W: Write, R: Read>(
&self,
writer: &mut W,
text: &mut R,
last_saved_revision: usize,
last_mtime: u64,
) -> std::io::Result<()> {
write_string(writer, HEADER_TAG)?;
write_usize(writer, self.current)?;
write_usize(writer, last_saved_revision)?;
write_u64(writer, last_mtime)?;
writer.write_all(&get_hash(text)?)?;
write_vec(writer, &self.revisions, |writer, rev| rev.serialize(writer))?;
Ok(())
}

pub fn deserialize<R: Read>(
reader: &mut R,
text: &mut R,
last_mtime: u64,
) -> std::io::Result<(usize, Self)> {
let header = read_string(reader)?;
if HEADER_TAG != header {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"missing undofile header",
))
} else {
let timestamp = Instant::now();
let current = read_usize(reader)?;
let last_saved_revision = read_usize(reader)?;
let mtime = read_u64(reader)?;
let mut hash = [0u8; 20];
reader.read_exact(&mut hash)?;

if mtime != last_mtime && hash != get_hash(text)? {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"outdated undo file",
));
}

let revisions = read_vec(reader, |reader| Revision::deserialize(reader, timestamp))?;
Ok((last_saved_revision, History { current, revisions }))
}
}

pub fn commit_revision(&mut self, transaction: &Transaction, original: &State) {
self.commit_revision_at_timestamp(transaction, original, Instant::now());
}
Expand Down Expand Up @@ -708,8 +740,12 @@ mod test {
selection: Selection::point(0),
};
history.commit_revision(&transaction, &state);
serialize_history(&mut buf, &history).unwrap();
deserialize_history(&mut buf.as_slice()).unwrap();

let text = Vec::new();
history
.serialize(&mut buf, &mut text.as_slice(), 0, 0)
.unwrap();
History::deserialize(&mut buf.as_slice(), &mut text.as_slice(), 0).unwrap();
true
}
);
Expand Down
26 changes: 25 additions & 1 deletion helix-core/src/path.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use etcetera::home_dir;
use std::path::{Component, Path, PathBuf};
use std::{
ffi::OsString,
path::{Component, Path, PathBuf},
str::Utf8Error,
};

/// Replaces users home directory from `path` with tilde `~` if the directory
/// is available, otherwise returns the path unchanged.
Expand Down Expand Up @@ -141,3 +145,23 @@ pub fn get_truncated_path<P: AsRef<Path>>(path: P) -> PathBuf {
ret.push(file);
ret
}

pub fn path_as_bytes<P: AsRef<Path>>(path: P) -> Vec<u8> {
let path = path.as_ref();

#[cfg(windows)]
return path.to_str().unwrap().into();

#[cfg(unix)]
return std::os::unix::ffi::OsStrExt::as_bytes(path.as_os_str()).into();
}

pub fn path_from_bytes(slice: &[u8]) -> Result<PathBuf, Utf8Error> {
#[cfg(windows)]
return Ok(PathBuf::from(std::str::from_utf8(slice)));

#[cfg(unix)]
return Ok(PathBuf::from(
<std::ffi::OsStr as std::os::unix::ffi::OsStrExt>::from_bytes(slice),
));
}
38 changes: 38 additions & 0 deletions helix-term/src/commands/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1822,6 +1822,30 @@ fn run_shell_command(
Ok(())
}

fn save_workspace(
cx: &mut compositor::Context,
_args: &[Cow<str>],
event: PromptEvent,
) -> anyhow::Result<()> {
if event != PromptEvent::Validate {
return Ok(());
}

cx.editor.save_workspace()
}

fn open_workspace(
cx: &mut compositor::Context,
_args: &[Cow<str>],
event: PromptEvent,
) -> anyhow::Result<()> {
if event != PromptEvent::Validate {
return Ok(());
}

cx.editor.open_workspace()
}

pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
TypableCommand {
name: "quit",
Expand Down Expand Up @@ -2337,6 +2361,20 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
fun: run_shell_command,
completer: Some(completers::filename),
},
TypableCommand {
name: "save-workspace",
aliases: &["sw"],
doc: "Save open document undo history",
fun: save_workspace,
completer: None,
},
TypableCommand {
name: "open-workspace",
aliases: &["ow"],
doc: "Open document undo history",
fun: open_workspace,
completer: None,
},
];

pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =
Expand Down
2 changes: 1 addition & 1 deletion helix-view/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ helix-vcs = { version = "0.6", path = "../helix-vcs" }

winapi = "0.3"
sha1_smol = "1.0"
either = "1.8"
walkdir = "2.3"

# Conversion traits
once_cell = "1.17"
Expand Down
Loading

0 comments on commit 1282066

Please sign in to comment.