#![deny(missing_docs)]
use unicode_width::{UnicodeWidthChar as _, UnicodeWidthStr as _};
pub mod attach;
mod blocking;
pub mod checkpoint;
pub mod clipboard;
mod delete;
pub mod edit;
pub mod fs;
mod list;
mod live;
mod move_file;
pub mod overlay;
pub mod process;
mod read;
pub mod remote;
mod search;
mod shell;
mod spill;
mod watch;
pub mod workspace;
mod write;
pub use blocking::blocking;
pub use delete::{Delete, DeleteArgs};
pub use edit::{Edit, EditArgs, Replacement};
pub use list::{FilesListed, List, ListArgs};
pub use live::Live;
pub use move_file::{Move, MoveArgs};
pub use read::{Read, ReadArgs, Seen, show_lines};
pub use search::{
Found, Hit, MAX_FILES, MAX_MATCHES, Search, SearchArgs, SearchHits, find, render,
};
pub use shell::{Shell, ShellArgs, ShellRun, split_status};
pub use spill::Spill;
pub use write::{Write, WriteArgs};
pub const MAX_LINES: usize = 2000;
pub const MAX_BYTES: usize = 50 * 1024;
pub const MAX_FILE_BYTES: u64 = 16 * 1024 * 1024;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct OutputLimit {
pub lines: usize,
pub bytes: usize,
}
impl Default for OutputLimit {
fn default() -> Self {
Self {
lines: MAX_LINES,
bytes: MAX_BYTES,
}
}
}
impl OutputLimit {
pub fn tail<'t>(&self, text: &'t str) -> (&'t str, usize) {
let starts: Vec<usize> = std::iter::once(0)
.chain(text.match_indices('\n').map(|(at, _)| at + 1))
.filter(|start| *start < text.len())
.collect();
let first = starts.len().saturating_sub(self.lines);
let kept = starts
.iter()
.enumerate()
.skip(first)
.find(|(_, start)| text.len() - **start <= self.bytes);
match kept {
Some((dropped, start)) => (text.get(*start..).unwrap_or_default(), dropped),
None => ("", starts.len()),
}
}
}
pub fn clip(line: &str, limit: usize) -> &str {
match line.char_indices().nth(limit) {
Some((end, _)) => line.get(..end).unwrap_or(line),
None => line,
}
}
pub fn shorten(text: &str, width: usize) -> String {
if text.width() <= width {
return text.to_owned();
}
let room = width.saturating_sub(1);
let mut used = 0;
let end = text
.char_indices()
.find(|(_, character)| {
used += character.width().unwrap_or(0);
used > room
})
.map_or(text.len(), |(at, _)| at);
match width {
0 => String::new(),
_ => format!("{}…", text.get(..end).unwrap_or_default()),
}
}
pub fn ago(elapsed: std::time::Duration) -> String {
let seconds = elapsed.as_secs();
match seconds {
0..60 => "just now".to_owned(),
60..3600 => format!("{}m ago", seconds / 60),
3600..86_400 => format!("{}h ago", seconds / 3600),
_ => format!("{}d ago", seconds / 86_400),
}
}
#[cfg(test)]
mod tests;