Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Rust
name: Rust & Shine

on:
push:
Expand All @@ -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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ target/
.idea/

Cargo.lock
*.log
*.log
zsh_test.sh
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand All @@ -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"
homepage = "https://github.com/donhk/rushstr"
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
2 changes: 1 addition & 1 deletion rushstr-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
8 changes: 3 additions & 5 deletions rushstr-core/src/utils/utilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,7 @@ pub fn create_db() -> anyhow::Result<Db> {
Ok(db)
}

/// The Zsh config snippet for integrating rushstr
const ZSHRC_CONF: &str = r#"
const ZSHRC_SNIPPET: &str = r#"
# RUSHSTR configuration - add this to ~/.zshrc
rushstr_no_tiocsti() {
zle -I
Expand All @@ -211,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
Expand All @@ -222,9 +220,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") {
let mut file = OpenOptions::new().create(true).append(true).open(&zshrc_path)?;
writeln!(file, "\n{ZSHRC_CONF}")?;
writeln!(file, "\n{ZSHRC_SNIPPET}")?;
}

Ok(())
Expand Down
5 changes: 3 additions & 2 deletions rushstr-tui/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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" }
75 changes: 70 additions & 5 deletions rushstr-tui/src/utils/calculator.rs
Original file line number Diff line number Diff line change
@@ -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<HItem> = 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
}
6 changes: 3 additions & 3 deletions rushstr-tui/src/ux/search_ui.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
8 changes: 4 additions & 4 deletions rushstr/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"
path = "src/main.rs"