diff options
| author | 2025-08-24 16:51:12 +0000 | |
|---|---|---|
| committer | 2025-08-24 16:51:12 +0000 | |
| commit | 5dabc731cfb0f90afd1fc69b2603070149da43be (patch) | |
| tree | 846e9d54ec0120d500b0516d5280593936b4b12e | |
| parent | 07b88dccb464dca3bad51e7ad270625872f565ae (diff) | |
| download | soon-5dabc731cfb0f90afd1fc69b2603070149da43be.tar.gz soon-5dabc731cfb0f90afd1fc69b2603070149da43be.zip | |
Comprehensive upgrade: v0.2.0 with improved features and code quality
Co-authored-by: HsiangNianian <44714368+HsiangNianian@users.noreply.github.com>
| -rw-r--r-- | Cargo.lock | 6 | ||||
| -rw-r--r-- | Cargo.toml | 6 | ||||
| -rw-r--r-- | README.md | 62 | ||||
| -rw-r--r-- | src/main.rs | 103 |
4 files changed, 111 insertions, 66 deletions
@@ -127,9 +127,9 @@ dependencies = [ [[package]] name = "counter" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f009fcafa949dc1fc46a762dae84d0c2687d3b550906b633c4979d58d2c6ae52" +checksum = "337a5b3c62043f6a4f5331bdb467a7f7a2099dce8522bcded6d4ebda1214fa4c" dependencies = [ "num-traits", ] @@ -246,7 +246,7 @@ dependencies = [ [[package]] name = "soon" -version = "0.1.6" +version = "0.2.0" dependencies = [ "clap", "colored", @@ -1,6 +1,6 @@ [package] name = "soon" -version = "0.1.6" +version = "0.2.0" edition = "2021" description = "Predict your next shell command based on history β like shell autocomplete, but MORE stupid" license = "MIT" @@ -13,7 +13,7 @@ name = "soon" path = "src/main.rs" [dependencies] -clap = { version = "4.0", features = ["derive"] } +clap = { version = "4.5", features = ["derive"] } colored = "3.0.0" -counter = "0.6.0" +counter = "0.7.0" dirs = "6.0.0" @@ -10,7 +10,9 @@ <img align="right" src="https://repology.org/badge/vertical-allrepos/soon.svg?columns=2" /> - π Shell-aware (supports Bash, Zsh, Fish) -- π Shows your most used commands +- π Shows your most used commands and analyzes patterns +- π§ Smart learning from command history +- π Easy update management and version checking - π i18n support (EN/δΈζ) (WIP) - π‘ Designed for clarity β **not** an autocomplete tool, but a prediction assistant. @@ -39,7 +41,7 @@ pip install soon-bin ## Usage ```shell -»»»» soon help 0|00:00:54 +»»»» soon help Predict your next shell command based on history Usage: soon [OPTIONS] [COMMAND] @@ -47,18 +49,18 @@ Usage: soon [OPTIONS] [COMMAND] Commands: now Show the most likely next command stats Show most used commands - learn Train prediction (WIP) + learn Train prediction and analyze command patterns which Display detected current shell version Show version information - update Update self [WIP] + update Check for updates and show installation options show-cache Show cached main commands show-internal-cache Show internal cache commands cache Cache a command to soon cache (for testing) help Print this message or the help of the given subcommand(s) Options: - --shell <SHELL> - --ngram <NGRAM> [default: 3] + --shell <SHELL> Override shell type (bash, zsh, fish, etc.) + --ngram <NGRAM> Set n-gram size for prediction accuracy [default: 3] --debug Enable debug output -h, --help Print help -V, --version Print version @@ -66,28 +68,28 @@ Options: ### Main Commands -| Command | Description | -|-----------------------|--------------------------------------------------| -| `now` | Show the most likely next command | -| `stats` | Show most used commands | -| `learn` | Train prediction (WIP) | -| `which` | Display detected current shell | -| `version` | Show version information | -| `update` | Update self (WIP) | -| `show-cache` | Show cached main commands | -| `show-internal-cache` | Show internal cache commands | -| `cache <NUM>` | Set cache size to `<NUM>` and refresh cache | -| `help` | Print this message or the help of subcommands | +| Command | Description | +|-----------------------|------------------------------------------------------| +| `now` | Show the most likely next command | +| `stats` | Show most used commands | +| `learn` | Train prediction and analyze command patterns | +| `which` | Display detected current shell | +| `version` | Show version information | +| `update` | Check for updates and show installation options | +| `show-cache` | Show cached main commands | +| `show-internal-cache` | Show internal cache commands | +| `cache <NUM>` | Set cache size to `<NUM>` and refresh cache | +| `help` | Print this message or the help of subcommands | ### Options -| Option | Description | -|------------------|---------------------------------------------| -| `--shell <SHELL>`| Specify shell type (bash, zsh, fish, etc.) | -| `--ngram <NGRAM>`| Set n-gram length for prediction (default: 3)| -| `--debug` | Enable debug output | -| `-h, --help` | Print help | -| `-V, --version` | Print version | +| Option | Description | +|------------------|-----------------------------------------------------| +| `--shell <SHELL>`| Override shell type (bash, zsh, fish, etc.) | +| `--ngram <NGRAM>`| Set n-gram size for prediction accuracy (default: 3)| +| `--debug` | Enable debug output | +| `-h, --help` | Print help | +| `-V, --version` | Print version | --- @@ -98,6 +100,16 @@ Options: soon now ``` +#### Analyze your command patterns and learn insights +```shell +soon learn +``` + +#### Check for updates +```shell +soon update +``` + #### Show your most used commands ```shell soon stats diff --git a/src/main.rs b/src/main.rs index 736d41e..245810f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,9 +16,9 @@ use std::path::PathBuf; struct Cli { #[command(subcommand)] command: Option<Commands>, - #[arg(long)] + #[arg(long, help = "Override shell type (bash, zsh, fish, etc.)")] shell: Option<String>, - #[arg(long, default_value_t = 3)] + #[arg(long, default_value_t = 3, help = "Set n-gram size for prediction accuracy")] ngram: usize, #[arg(long, help = "Enable debug output")] debug: bool, @@ -30,13 +30,13 @@ enum Commands { Now, /// Show most used commands Stats, - /// Train prediction (WIP) + /// Train prediction and analyze command patterns Learn, /// Display detected current shell Which, /// Show version information Version, - /// Update self [WIP] + /// Check for updates and show installation options Update, /// Show cached main commands ShowCache, @@ -67,6 +67,7 @@ fn history_path(shell: &str) -> Option<PathBuf> { #[derive(Debug)] struct HistoryItem { cmd: String, + #[allow(dead_code)] // Reserved for future features like directory-aware predictions path: Option<String>, } @@ -78,6 +79,7 @@ fn load_history(shell: &str) -> Vec<HistoryItem> { if !path.exists() { eprintln!("β οΈ History file not found: {}", path.display()); + eprintln!("π‘ Tip: Use your shell first to build up command history, or specify a different shell with --shell"); return vec![]; } @@ -249,26 +251,6 @@ fn soon_show_internal_cache() { println!("\n{}: {}", "Cache path".dimmed(), path.display()); } -fn cache_main_cmd(cmd: &str) { - let cmd = main_cmd(cmd); - if cmd.is_empty() { - return; - } - - let path = get_cache_path(); - let mut file = match OpenOptions::new().append(true).create(true).open(&path) { - Ok(f) => f, - Err(e) => { - eprintln!("β οΈ Failed to open cache file: {}", e); - return; - } - }; - - if let Err(e) = writeln!(file, "{}", cmd) { - eprintln!("β οΈ Failed to write to cache: {}", e); - } -} - fn is_ignored_command(cmd: &str) -> bool { let ignored = ["soon", "cd", "ls", "pwd", "exit", "clear"]; ignored.contains(&cmd) @@ -434,7 +416,7 @@ fn soon_now(shell: &str, ngram: usize, debug: bool) { } } -fn soon_cache(shell: &str, ngram: usize, cmd: &str) { +fn soon_cache(shell: &str, ngram: usize, _cmd: &str) { overwrite_soon_cache_from_history(shell, ngram); println!("Cached main commands refreshed from history."); println!("(Tip: soon cache now always reflects the latest {ngram} main commands from your history.)"); @@ -481,11 +463,48 @@ fn soon_stats(shell: &str) { ); } -fn soon_learn(_shell: &str) { - println!( - "{}", - "π§ [soon learn] feature under development...".yellow() - ); +fn soon_learn(shell: &str) { + println!("{}", "π§ Analyzing command patterns...".cyan().bold()); + + let history = load_history(shell); + if history.is_empty() { + eprintln!("{}", "β οΈ No history found to learn from".red()); + return; + } + + // Analyze command patterns + let total_commands = history.len(); + let unique_commands: std::collections::HashSet<String> = history + .iter() + .map(|h| main_cmd(&h.cmd).to_string()) + .collect(); + + println!("π Learning insights:"); + println!(" β’ Total commands in history: {}", total_commands); + println!(" β’ Unique command types: {}", unique_commands.len()); + + if total_commands > 0 { + let repetition_rate = ((total_commands - unique_commands.len()) as f64 / total_commands as f64) * 100.0; + println!(" β’ Command repetition rate: {:.1}%", repetition_rate); + } + + // Find most common command patterns + let mut cmd_counts = std::collections::HashMap::new(); + for item in &history { + let cmd = main_cmd(&item.cmd); + *cmd_counts.entry(cmd.to_string()).or_insert(0) += 1; + } + + let mut sorted_cmds: Vec<_> = cmd_counts.iter().collect(); + sorted_cmds.sort_by(|a, b| b.1.cmp(a.1)); + + println!("\nπ‘ Top command patterns:"); + for (i, (cmd, count)) in sorted_cmds.iter().take(5).enumerate() { + let percentage = (**count as f64 / total_commands as f64) * 100.0; + println!(" {}: {} ({:.1}% of usage)", i + 1, cmd, percentage); + } + + println!("\nβ¨ Learning complete! Use 'soon now' for predictions."); } fn soon_which(shell: &str) { @@ -505,10 +524,21 @@ fn soon_version() { } fn soon_update() { - println!( - "{}", - "π [soon update] feature under development...".yellow() - ); + println!("{}", "π Checking for updates...".cyan().bold()); + + let current_version = env!("CARGO_PKG_VERSION"); + println!("Current version: {}", current_version.green()); + + println!("\nπ¦ Update options:"); + println!(" β’ Cargo: {}", "cargo install soon".dimmed()); + println!(" β’ Python: {}", "pip install --upgrade soon-bin".dimmed()); + println!(" β’ Arch Linux: {}", "paru -Syu soon".dimmed()); + println!(" β’ From source: {}", "git pull && cargo install --path .".dimmed()); + + println!("\nπ More info: {}", "https://github.com/HsiangNianian/soon".blue().underline()); + + // In a future version, this could check GitHub releases API for newer versions + println!("\nπ‘ Future enhancement: automatic version checking will be added."); } fn main() { @@ -516,7 +546,10 @@ fn main() { let shell = cli.shell.clone().unwrap_or_else(detect_shell); if shell == "unknown" && !matches!(cli.command, Some(Commands::Which)) { - eprintln!("{}", "β οΈ Unknown shell. Please specify with --shell.".red()); + eprintln!("{}", "β οΈ Unknown shell detected.".red()); + eprintln!("π‘ Please specify your shell with: --shell <SHELL>"); + eprintln!(" Supported shells: bash, zsh, fish"); + eprintln!(" Example: soon now --shell zsh"); std::process::exit(1); } |