diff options
author | Ellie Huxtable <e@elm.sh> | 2021-04-21 18:13:51 +0100 |
---|---|---|
committer | Ellie Huxtable <e@elm.sh> | 2021-04-21 21:26:44 +0100 |
commit | 4a50ce366639ca9dac7324d6a47d6a0e6c7fccdf (patch) | |
tree | 7ffd8848f675e1377f750cc0757768d074a5ac05 /src | |
parent | a9b117aad7e6bd09c7ea188258924dc02855db05 (diff) |
Bugfixes, show time ago, perf improvements
Also allow unique listing and more ergonomic cwd usage
Diffstat (limited to 'src')
-rw-r--r-- | src/command/history.rs | 38 | ||||
-rw-r--r-- | src/command/login.rs | 26 | ||||
-rw-r--r-- | src/command/mod.rs | 20 | ||||
-rw-r--r-- | src/command/register.rs | 31 | ||||
-rw-r--r-- | src/command/search.rs | 91 | ||||
-rw-r--r-- | src/command/stats.rs | 2 | ||||
-rw-r--r-- | src/main.rs | 19 | ||||
-rw-r--r-- | src/shell/atuin.zsh | 6 |
8 files changed, 135 insertions, 98 deletions
diff --git a/src/command/history.rs b/src/command/history.rs index 2b691bac..a88aeae2 100644 --- a/src/command/history.rs +++ b/src/command/history.rs @@ -1,7 +1,6 @@ use std::env; use eyre::Result; -use fork::{fork, Fork}; use structopt::StructOpt; use atuin_client::database::Database; @@ -44,6 +43,12 @@ pub enum Cmd { aliases=&["se", "sea", "sear", "searc"], )] Search { query: Vec<String> }, + + #[structopt( + about="get the last command ran", + aliases=&["la", "las"], + )] + Last {}, } fn print_list(h: &[History]) { @@ -74,22 +79,24 @@ impl Cmd { } let mut h = db.load(id)?; + + if h.duration > 0 { + debug!("cannot end history - already has duration"); + + // returning OK as this can occur if someone Ctrl-c a prompt + return Ok(()); + } + h.exit = *exit; h.duration = chrono::Utc::now().timestamp_nanos() - h.timestamp.timestamp_nanos(); db.update(&h)?; if settings.should_sync()? { - match fork() { - Ok(Fork::Parent(child)) => { - debug!("launched sync background process with PID {}", child); - } - Ok(Fork::Child) => { - debug!("running periodic background sync"); - sync::sync(settings, false, db).await?; - } - Err(_) => println!("Fork failed"), - } + debug!("running periodic background sync"); + sync::sync(settings, false, db).await?; + } else { + debug!("sync disabled! not syncing"); } Ok(()) @@ -107,7 +114,7 @@ impl Cmd { let session = env::var("ATUIN_SESSION")?; let history = match params { - (false, false) => db.list()?, + (false, false) => db.list(None, false)?, (true, false) => db.query(QUERY_SESSION, &[session.as_str()])?, (false, true) => db.query(QUERY_DIR, &[cwd.as_str()])?, (true, true) => { @@ -126,6 +133,13 @@ impl Cmd { Ok(()) } + + Self::Last {} => { + let last = db.last()?; + print_list(&[last]); + + Ok(()) + } } } } diff --git a/src/command/login.rs b/src/command/login.rs index eaeb297f..eacb2105 100644 --- a/src/command/login.rs +++ b/src/command/login.rs @@ -1,10 +1,10 @@ -use std::collections::HashMap; use std::fs::File; use std::io::prelude::*; -use eyre::{eyre, Result}; +use eyre::Result; use structopt::StructOpt; +use atuin_client::api_client; use atuin_client::settings::Settings; #[derive(StructOpt)] @@ -22,25 +22,15 @@ pub struct Cmd { impl Cmd { pub fn run(&self, settings: &Settings) -> Result<()> { - let mut map = HashMap::new(); - map.insert("username", self.username.clone()); - map.insert("password", self.password.clone()); - - let url = format!("{}/login", settings.sync_address); - let client = reqwest::blocking::Client::new(); - - let resp = client.post(url).json(&map).send()?; - - if resp.status() != reqwest::StatusCode::OK { - return Err(eyre!("invalid login details")); - } - - let session = resp.json::<HashMap<String, String>>()?; - let session = session["session"].clone(); + let session = api_client::login( + settings.sync_address.as_str(), + self.username.as_str(), + self.password.as_str(), + )?; let session_path = settings.session_path.as_str(); let mut file = File::create(session_path)?; - file.write_all(session.as_bytes())?; + file.write_all(session.session.as_bytes())?; let key_path = settings.key_path.as_str(); let mut file = File::create(key_path)?; diff --git a/src/command/mod.rs b/src/command/mod.rs index 6fd52613..805ad9f0 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -43,7 +43,18 @@ pub enum AtuinCmd { Uuid, #[structopt(about = "interactive history search")] - Search { query: Vec<String> }, + Search { + #[structopt(long, short, about = "filter search result by directory")] + cwd: Option<String>, + + #[structopt(long, short, about = "filter search result by exit code")] + exit: Option<i64>, + + #[structopt(long, short, about = "open interactive search UI")] + interactive: bool, + + query: Vec<String>, + }, #[structopt(about = "sync with the configured server")] Sync { @@ -76,7 +87,12 @@ impl AtuinCmd { Self::Server(server) => server.run(&server_settings).await, Self::Stats(stats) => stats.run(&mut db, &client_settings), Self::Init => init::init(), - Self::Search { query } => search::run(&query, &mut db), + Self::Search { + cwd, + exit, + interactive, + query, + } => search::run(cwd, exit, interactive, &query, &mut db), Self::Sync { force } => sync::run(&client_settings, force, &mut db).await, Self::Login(l) => l.run(&client_settings), diff --git a/src/command/register.rs b/src/command/register.rs index 1126645a..acf9b1a3 100644 --- a/src/command/register.rs +++ b/src/command/register.rs @@ -1,10 +1,10 @@ -use std::collections::HashMap; use std::fs::File; use std::io::prelude::*; -use eyre::{eyre, Result}; +use eyre::Result; use structopt::StructOpt; +use atuin_client::api_client; use atuin_client::settings::Settings; #[derive(StructOpt)] @@ -21,34 +21,11 @@ pub struct Cmd { } pub fn run(settings: &Settings, username: &str, email: &str, password: &str) -> Result<()> { - let mut map = HashMap::new(); - map.insert("username", username); - map.insert("email", email); - map.insert("password", password); - - let url = format!("{}/user/{}", settings.sync_address, username); - let resp = reqwest::blocking::get(url)?; - - if resp.status().is_success() { - println!("Username is already in use! Please try another."); - return Ok(()); - } - - let url = format!("{}/register", settings.sync_address); - let client = reqwest::blocking::Client::new(); - let resp = client.post(url).json(&map).send()?; - - if !resp.status().is_success() { - println!("Failed to register user - please check your details and try again"); - return Err(eyre!("failed to register user")); - } - - let session = resp.json::<HashMap<String, String>>()?; - let session = session["session"].clone(); + let session = api_client::register(settings.sync_address.as_str(), username, email, password)?; let path = settings.session_path.as_str(); let mut file = File::create(path)?; - file.write_all(session.as_bytes())?; + file.write_all(session.session.as_bytes())?; Ok(()) } diff --git a/src/command/search.rs b/src/command/search.rs index 773c112f..b074371e 100644 --- a/src/command/search.rs +++ b/src/command/search.rs @@ -1,7 +1,6 @@ use eyre::Result; -use itertools::Itertools; -use std::io::stdout; use std::time::Duration; +use std::{io::stdout, ops::Sub}; use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen}; use tui::{ @@ -31,7 +30,7 @@ struct State { #[allow(clippy::clippy::cast_sign_loss)] impl State { - fn durations(&self) -> Vec<String> { + fn durations(&self) -> Vec<(String, String)> { self.results .iter() .map(|h| { @@ -40,7 +39,33 @@ impl State { let duration = humantime::format_duration(duration).to_string(); let duration: Vec<&str> = duration.split(' ').collect(); - duration[0].to_string() + let ago = chrono::Utc::now().sub(h.timestamp); + let ago = humantime::format_duration(ago.to_std().unwrap()).to_string(); + let ago: Vec<&str> = ago.split(' ').collect(); + + ( + duration[0] + .to_string() + .replace("days", "d") + .replace("day", "d") + .replace("weeks", "w") + .replace("week", "w") + .replace("months", "mo") + .replace("month", "mo") + .replace("years", "y") + .replace("year", "y"), + ago[0] + .to_string() + .replace("days", "d") + .replace("day", "d") + .replace("weeks", "w") + .replace("week", "w") + .replace("months", "mo") + .replace("month", "mo") + .replace("years", "y") + .replace("year", "y") + + " ago", + ) }) .collect() } @@ -51,9 +76,9 @@ impl State { r: tui::layout::Rect, ) { let durations = self.durations(); - let max_length = durations - .iter() - .fold(0, |largest, i| std::cmp::max(largest, i.len())); + let max_length = durations.iter().fold(0, |largest, i| { + std::cmp::max(largest, i.0.len() + i.1.len()) + }); let results: Vec<ListItem> = self .results @@ -64,10 +89,10 @@ impl State { let mut command = Span::raw(command); - let mut duration = durations[i].clone(); + let (duration, mut ago) = durations[i].clone(); - while duration.len() < max_length { - duration.push(' '); + while (duration.len() + ago.len()) < max_length { + ago = " ".to_owned() + ago.as_str(); } let duration = Span::styled( @@ -79,6 +104,8 @@ impl State { }), ); + let ago = Span::styled(ago, Style::default().fg(Color::Blue)); + if let Some(selected) = self.results_state.selected() { if selected == i { command.style = @@ -86,7 +113,8 @@ impl State { } } - let spans = Spans::from(vec![duration, Span::raw(" "), command]); + let spans = + Spans::from(vec![duration, Span::raw(" "), ago, Span::raw(" "), command]); ListItem::new(spans) }) @@ -103,12 +131,12 @@ impl State { fn query_results(app: &mut State, db: &mut impl Database) { let results = match app.input.as_str() { - "" => db.list(), + "" => db.list(Some(200), true), i => db.prefix_search(i), }; if let Ok(results) = results { - app.results = results.into_iter().rev().unique().collect(); + app.results = results; } if app.results.is_empty() { @@ -120,7 +148,8 @@ fn query_results(app: &mut State, db: &mut impl Database) { fn key_handler(input: Key, db: &mut impl Database, app: &mut State) -> Option<String> { match input { - Key::Esc | Key::Char('\n') => { + Key::Esc => return Some(String::from("")), + Key::Char('\n') => { let i = app.results_state.selected().unwrap_or(0); return Some( @@ -268,9 +297,37 @@ fn select_history(query: &[String], db: &mut impl Database) -> Result<String> { } } -pub fn run(query: &[String], db: &mut impl Database) -> Result<()> { - let item = select_history(query, db)?; - eprintln!("{}", item); +pub fn run( + cwd: Option<String>, + exit: Option<i64>, + interactive: bool, + query: &[String], + db: &mut impl Database, +) -> Result<()> { + let dir = if let Some(cwd) = cwd { + if cwd == "." { + let current = std::env::current_dir()?; + let current = current.as_os_str(); + let current = current.to_str().unwrap(); + + Some(current.to_owned()) + } else { + Some(cwd) + } + } else { + None + }; + + if interactive { + let item = select_history(query, db)?; + eprintln!("{}", item); + } else { + let results = db.search(dir, exit, query.join(" ").as_str())?; + + for i in &results { + println!("{}", i.command); + } + } Ok(()) } diff --git a/src/command/stats.rs b/src/command/stats.rs index 0da303d7..5c9a9dbb 100644 --- a/src/command/stats.rs +++ b/src/command/stats.rs @@ -94,7 +94,7 @@ impl Cmd { } Self::All => { - let history = db.list()?; + let history = db.list(None, false)?; compute_stats(&history)?; diff --git a/src/main.rs b/src/main.rs index c116d1f3..184c0323 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,6 @@ #![allow(clippy::use_self)] // not 100% reliable use eyre::Result; -use fern::colors::{Color, ColoredLevelConfig}; use structopt::{clap::AppSettings, StructOpt}; #[macro_use] @@ -32,23 +31,7 @@ impl Atuin { #[tokio::main] async fn main() -> Result<()> { - let colors = ColoredLevelConfig::new() - .warn(Color::Yellow) - .error(Color::Red); - - fern::Dispatch::new() - .format(move |out, message, record| { - out.finish(format_args!( - "{} [{}] {}", - chrono::Local::now().to_rfc3339(), - colors.color(record.level()), - message - )) - }) - .level(log::LevelFilter::Info) - .level_for("sqlx", log::LevelFilter::Warn) - .chain(std::io::stdout()) - .apply()?; + pretty_env_logger::init(); Atuin::from_args().run().await } diff --git a/src/shell/atuin.zsh b/src/shell/atuin.zsh index d6d58f53..cdef5e54 100644 --- a/src/shell/atuin.zsh +++ b/src/shell/atuin.zsh @@ -15,8 +15,8 @@ _atuin_precmd(){ [[ -z "${ATUIN_HISTORY_ID}" ]] && return - atuin history end $ATUIN_HISTORY_ID --exit $EXIT - export ATUIN_HISTORY_ID="" + + (RUST_LOG=error atuin history end $ATUIN_HISTORY_ID --exit $EXIT &) > /dev/null 2>&1 } _atuin_search(){ @@ -27,7 +27,7 @@ _atuin_search(){ echoti rmkx # swap stderr and stdout, so that the tui stuff works # TODO: not this - output=$(atuin search $BUFFER 3>&1 1>&2 2>&3) + output=$(RUST_LOG=error atuin search -i $BUFFER 3>&1 1>&2 2>&3) echoti smkx if [[ -n $output ]] ; then |