From 9fb47c39d11c87c02b613daa51e23a9014f64e5d Mon Sep 17 00:00:00 2001 From: Frederick Alvarez Date: Thu, 15 May 2025 22:50:25 -0700 Subject: [PATCH 1/3] fix zsh integration --- rushstr-core/src/utils/utilities.rs | 20 ++++++-- rushstr-tui/src/utils/calculator.rs | 75 +++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 9 deletions(-) diff --git a/rushstr-core/src/utils/utilities.rs b/rushstr-core/src/utils/utilities.rs index f4e7a63..54f1bdc 100644 --- a/rushstr-core/src/utils/utilities.rs +++ b/rushstr-core/src/utils/utilities.rs @@ -199,8 +199,20 @@ pub fn create_db() -> anyhow::Result { Ok(db) } -/// The Zsh config snippet for integrating rushstr -const ZSHRC_CONF: &str = r#" +#[cfg(target_os = "macos")] +const ZSHRC_SNIPPET: &str = r#" +# RUSHSTR configuration - add this to ~/.zshrc +rushstr_widget() { + BUFFER=$(rushstr) + CURSOR=${#BUFFER} + zle reset-prompt +} +zle -N rushstr_widget +bindkey '^R' rushstr_widget +"#; + +#[cfg(not(target_os = "macos"))] +const ZSHRC_SNIPPET: &str = r#" # RUSHSTR configuration - add this to ~/.zshrc rushstr_no_tiocsti() { zle -I @@ -222,9 +234,9 @@ pub fn configure_zsh_profile() -> anyhow::Result<()> { let existing_content = read_to_string(&zshrc_path).unwrap_or_default(); - if !existing_content.contains("rushstr_no_tiocsti") { + if !existing_content.contains("rushstr_widget") && !existing_content.contains("rushstr_no_tiocsti") { let mut file = OpenOptions::new().create(true).append(true).open(&zshrc_path)?; - writeln!(file, "\n{ZSHRC_CONF}")?; + writeln!(file, "\n{ZSHRC_SNIPPET}")?; } Ok(()) diff --git a/rushstr-tui/src/utils/calculator.rs b/rushstr-tui/src/utils/calculator.rs index 8f40360..6c4ec02 100644 --- a/rushstr-tui/src/utils/calculator.rs +++ b/rushstr-tui/src/utils/calculator.rs @@ -1,10 +1,75 @@ use rushstr_core::{HIndex, HItem, HLines}; -/// convert an hindex to the hlines number +/// Converts a history index (`hindex`) into the total number of visual lines +/// (`hlines`) occupied by the history items up to and including that index. +/// +/// This is useful when rendering or navigating history entries where each item +/// may span multiple terminal lines (e.g., due to multi-line commands). +/// +/// # Arguments +/// +/// * `items` - A slice of `HItem` instances representing history entries. +/// * `hindex` - The index of the last item (inclusive) whose `hlines` should be +/// included in the total. +/// +/// # Returns +/// +/// The total number of `hlines` (visual terminal lines) for all items from +/// index 0 to `hindex`, inclusive. +/// +/// If `hindex` is out of bounds or the slice is empty, the function returns 0. pub fn hindex_to_hlines(items: &[HItem], hindex: HIndex) -> HLines { - let mut hlines = 0; - for item in &items[..=hindex] { - hlines += item.hlines(); + if items.is_empty() || hindex >= items.len() { + return 0; + } + items[..=hindex].iter().map(|item| item.hlines()).sum() +} + +#[cfg(test)] +mod tests { + use super::*; + + fn hitem_with_lines(lines: &[&str]) -> HItem { + HItem::new(lines.iter().map(|s| s.to_string()).collect()).unwrap() + } + + #[test] + fn test_empty_list() { + let items: Vec = vec![]; + assert_eq!(hindex_to_hlines(&items, 0), 0); + } + + #[test] + fn test_hindex_out_of_bounds() { + let items = vec![hitem_with_lines(&["echo hi"]), hitem_with_lines(&["ls", "-la"])]; + assert_eq!(hindex_to_hlines(&items, 5), 0); + } + + #[test] + fn test_single_item() { + let items = vec![hitem_with_lines(&["echo hello", "echo again"])]; + assert_eq!(hindex_to_hlines(&items, 0), 2); + } + + #[test] + fn test_multiple_items() { + let items = vec![ + hitem_with_lines(&["line1"]), + hitem_with_lines(&["line2a", "line2b"]), + hitem_with_lines(&["line3a", "line3b", "line3c"]), + ]; + assert_eq!(hindex_to_hlines(&items, 0), 1); + assert_eq!(hindex_to_hlines(&items, 1), 3); // 1 + 2 + assert_eq!(hindex_to_hlines(&items, 2), 6); // 1 + 2 + 3 + } + + #[test] + fn test_hindex_exactly_last() { + let items = vec![ + hitem_with_lines(&["a"]), + hitem_with_lines(&["b"]), + hitem_with_lines(&["c"]), + ]; + assert_eq!(hindex_to_hlines(&items, items.len() - 1), 3); } - hlines } From a0aa9c34d01cee631be50ac09804525327fabc51 Mon Sep 17 00:00:00 2001 From: Frederick Alvarez Date: Thu, 15 May 2025 22:54:39 -0700 Subject: [PATCH 2/3] chore: Release --- .gitignore | 3 ++- Cargo.toml | 3 ++- README.md | 10 ++++++++++ rushstr-core/Cargo.toml | 2 +- rushstr-core/src/utils/utilities.rs | 16 +--------------- rushstr-tui/Cargo.toml | 5 +++-- rushstr-tui/src/ux/search_ui.rs | 6 +++--- rushstr/Cargo.toml | 8 ++++---- 8 files changed, 26 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index f6d2e91..cc8d2b2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ target/ .idea/ Cargo.lock -*.log \ No newline at end of file +*.log +zsh_test.sh \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 659ac45..9cd9ade 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ resolver = "2" [workspace.dependencies] anyhow = "1" ratatui = { version = "0.29.0", features = ["all-widgets"] } +crossterm = { version = "0.29.0", features = ["use-dev-tty"] } temp-env = "0.3" serial_test = "3.2" clap = { version = "4.5.38", features = ["derive"] } @@ -25,4 +26,4 @@ description = "An interactive, Rust-powered shell history search tool inspired b license = "MIT" repository = "https://github.com/donhk/rushstr" documentation = "https://github.com/donhk/rushstr" -homepage = "https://github.com/donhk/rushstr" \ No newline at end of file +homepage = "https://github.com/donhk/rushstr" diff --git a/README.md b/README.md index e625028..543f509 100644 --- a/README.md +++ b/README.md @@ -85,4 +85,14 @@ cargo run # Run tests cargo test +``` + +--- + +## 🛠 Update the version + +```zsh +cargo release 1.4.0 --no-push --no-tag --no-publish --execute +cargo patch --no-push --no-tag --no-publish --execute +cargo minor --no-push --no-tag --no-publish --execute ``` \ No newline at end of file diff --git a/rushstr-core/Cargo.toml b/rushstr-core/Cargo.toml index b0d7240..6494404 100644 --- a/rushstr-core/Cargo.toml +++ b/rushstr-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rushstr-core" -version = "1.2.5" +version = "1.4.0" edition = "2024" description = "An interactive, Rust-powered shell history search tool inspired by hstr" license = "MIT" diff --git a/rushstr-core/src/utils/utilities.rs b/rushstr-core/src/utils/utilities.rs index 54f1bdc..e71eb4e 100644 --- a/rushstr-core/src/utils/utilities.rs +++ b/rushstr-core/src/utils/utilities.rs @@ -199,19 +199,6 @@ pub fn create_db() -> anyhow::Result { Ok(db) } -#[cfg(target_os = "macos")] -const ZSHRC_SNIPPET: &str = r#" -# RUSHSTR configuration - add this to ~/.zshrc -rushstr_widget() { - BUFFER=$(rushstr) - CURSOR=${#BUFFER} - zle reset-prompt -} -zle -N rushstr_widget -bindkey '^R' rushstr_widget -"#; - -#[cfg(not(target_os = "macos"))] const ZSHRC_SNIPPET: &str = r#" # RUSHSTR configuration - add this to ~/.zshrc rushstr_no_tiocsti() { @@ -223,7 +210,6 @@ rushstr_no_tiocsti() { } zle -N rushstr_no_tiocsti bindkey '\C-r' rushstr_no_tiocsti -export RUSHSTR_OUT=n "#; /// Appends the RUSHSTR Zsh integration config to ~/.zshrc if not already @@ -234,7 +220,7 @@ pub fn configure_zsh_profile() -> anyhow::Result<()> { let existing_content = read_to_string(&zshrc_path).unwrap_or_default(); - if !existing_content.contains("rushstr_widget") && !existing_content.contains("rushstr_no_tiocsti") { + if !existing_content.contains("rushstr_widget") { let mut file = OpenOptions::new().create(true).append(true).open(&zshrc_path)?; writeln!(file, "\n{ZSHRC_SNIPPET}")?; } diff --git a/rushstr-tui/Cargo.toml b/rushstr-tui/Cargo.toml index 524a75c..b034323 100644 --- a/rushstr-tui/Cargo.toml +++ b/rushstr-tui/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rushstr-tui" -version = "1.2.5" +version = "1.4.0" edition = "2024" description = "An interactive, Rust-powered shell history search tool inspired by hstr" license = "MIT" @@ -9,7 +9,8 @@ documentation = "https://github.com/donhk/rushstr" homepage = "https://github.com/donhk/rushstr" [dependencies] +crossterm.workspace = true ratatui.workspace = true anyhow.workspace = true arboard.workspace = true -rushstr-core = { path = "../rushstr-core", version = "1.2.5" } +rushstr-core = { path = "../rushstr-core", version = "1.4.0" } diff --git a/rushstr-tui/src/ux/search_ui.rs b/rushstr-tui/src/ux/search_ui.rs index ca4a5b3..b52db2a 100644 --- a/rushstr-tui/src/ux/search_ui.rs +++ b/rushstr-tui/src/ux/search_ui.rs @@ -1,11 +1,11 @@ use std::io::stdout; use arboard::Clipboard; -use ratatui::DefaultTerminal; -use ratatui::crossterm::event::{ +use crossterm::event::{ DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind, }; -use ratatui::crossterm::{event, execute}; +use crossterm::{event, execute}; +use ratatui::DefaultTerminal; use rushstr_core::{HItem, Store}; use crate::UiState; diff --git a/rushstr/Cargo.toml b/rushstr/Cargo.toml index e766e46..11ba162 100644 --- a/rushstr/Cargo.toml +++ b/rushstr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rushstr" -version = "1.2.5" +version = "1.4.0" edition = "2024" description = "An interactive, Rust-powered shell history search tool inspired by hstr" license = "MIT" @@ -11,9 +11,9 @@ homepage = "https://github.com/donhk/rushstr" [dependencies] anyhow.workspace = true clap.workspace = true -rushstr-core = { path = "../rushstr-core", version = "1.2.5" } -rushstr-tui = { path = "../rushstr-tui", version = "1.2.5" } +rushstr-core = { path = "../rushstr-core", version = "1.4.0" } +rushstr-tui = { path = "../rushstr-tui", version = "1.4.0" } [[bin]] name = "rushstr" -path = "src/main.rs" \ No newline at end of file +path = "src/main.rs" From 7359ed3c50279305fd581140fa6d50e62c97c97f Mon Sep 17 00:00:00 2001 From: Frederick Alvarez Date: Fri, 16 May 2025 14:15:57 -0700 Subject: [PATCH 3/3] fix ci.yml --- .github/workflows/ci.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 290064a..191159c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Rust +name: Rust & Shine on: push: @@ -11,12 +11,19 @@ env: jobs: build: - runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Clippy - run: cargo clippy --all-targets --all-features -- -D warnings - - name: Run tests - run: cargo test --verbose + - uses: actions/checkout@v4 + + - name: Set up Rust nightly + run: rustup default nightly + + - name: Install Clippy + run: rustup component add clippy + + - name: Clippy + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Run tests + run: cargo test --verbose