diff --git a/Cargo.toml b/Cargo.toml index 18a8519..d59267f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ native-tls = { version = "0.2.10", default-features = false } libc = { version = "0.2.126", default-features = false } rm_rf = { version = "0.6.2", default-features = false } spinoff = { version = "0.5.2", default-features = false } -textwrap = { version = "0.15.0", features = [ "terminal_size" ] } +textwrap = { version = "0.15.0", features = [ "terminal_size", "smawk" ] } chrono = { version = "0.4.22", default-features = false, features = [ "clock", "std", "wasmbind" ] } toml = { version = "0.5.9", default-features = false } crossterm = { version = "0.25.0", default-features = false } \ No newline at end of file diff --git a/src/internal/utils.rs b/src/internal/utils.rs index f4796ae..1d89ee2 100644 --- a/src/internal/utils.rs +++ b/src/internal/utils.rs @@ -3,7 +3,7 @@ use std::io; use std::io::Write; use std::process::{exit, Command, Stdio}; use std::time::UNIX_EPOCH; -use textwrap::{termwidth, wrap}; +use textwrap::wrap; use crate::internal::exit_code::AppExitCode; use crate::{internal, uwu}; @@ -64,7 +64,9 @@ pub fn log_info(msg: String) { } else { msg }; - let opts = textwrap::Options::new(termwidth()).subsequent_indent(" "); + + let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 2) + .subsequent_indent(" "); println!( "{} {}", @@ -79,7 +81,9 @@ pub fn log_warn(msg: String) { } else { msg }; - let opts = textwrap::Options::new(termwidth()).subsequent_indent(" "); + + let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 2) + .subsequent_indent(" "); println!( "{} {}", @@ -94,7 +98,9 @@ pub fn log_and_crash(msg: String, exit_code: AppExitCode) -> ! { } else { msg }; - let opts = textwrap::Options::new(termwidth()).subsequent_indent(" "); + + let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 2) + .subsequent_indent(" "); println!( "{} {}", @@ -134,7 +140,8 @@ pub fn prompt_yn(question: String, default_true: bool) -> bool { question }; - let opts = textwrap::Options::new(termwidth()).subsequent_indent(" "); + let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 2) + .subsequent_indent(" "); print!( "{} {} {}: ", @@ -168,7 +175,9 @@ impl Spinner { } else { text.to_string() }; - let opts = textwrap::Options::new(termwidth()).subsequent_indent(" "); + + let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 2) + .subsequent_indent(" "); let symbol = format!("{}", OK_SYMBOL.purple()); let text = format!("{}", wrap(&text, opts).join("\n").bold()); @@ -183,7 +192,9 @@ pub fn spinner_fn(text: String) -> Spinner { } else { text }; - let opts = textwrap::Options::new(termwidth()).subsequent_indent(" "); + + let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 2) + .subsequent_indent(" "); Spinner { spinner: spinoff::Spinner::new( diff --git a/src/main.rs b/src/main.rs index 061b2d4..7a1e28c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ use crate::internal::utils::pager; use crate::internal::{detect, init, sort, start_sudoloop, structs::Options}; use clap_complete::{Generator, Shell}; +use textwrap::{termwidth, wrap}; #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; @@ -177,8 +178,12 @@ fn cmd_search(args: &SearchArgs, options: Options) { "".to_string() }; + let opts = textwrap::Options::new(termwidth())t ; + + // Attempt to wrap result text + let results = wrap(&(repo_results + "\n" + &aur_results), opts).join("\n"); + // Check if results are longer than terminal height - let results = repo_results + &aur_results; if results.lines().count() > crossterm::terminal::size().unwrap().1 as usize { // If so, paginate results #[allow(clippy::let_underscore_drop)] diff --git a/src/operations/search.rs b/src/operations/search.rs index 87d7a9c..e186e3e 100644 --- a/src/operations/search.rs +++ b/src/operations/search.rs @@ -6,7 +6,7 @@ use crate::{log, Options}; use chrono::{Local, TimeZone}; use colored::Colorize; -use textwrap::{termwidth, wrap}; +use textwrap::wrap; #[allow(clippy::module_name_repetitions)] pub fn aur_search(query: &str, options: Options) -> String { @@ -20,7 +20,8 @@ pub fn aur_search(query: &str, options: Options) -> String { let mut results_vec = vec![]; for package in &res.results { // Define wrapping options - let opts = textwrap::Options::new(termwidth()).subsequent_indent(" "); + let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 4) + .subsequent_indent(" "); let result = format!( "{}{} {} {}\n {}", @@ -62,30 +63,76 @@ pub fn aur_search(query: &str, options: Options) -> String { results_vec.join("\n") } +struct SearchResult { + repo: String, + name: String, + version: String, + description: String, +} + #[allow(clippy::module_name_repetitions)] pub fn repo_search(query: &str, options: Options) -> String { // Initialise variables let verbosity = options.verbosity; // Query pacman for package info - let output = ShellCommand::pacman() - .arg("-Ss") + let output = ShellCommand::bash() + .args(&["-c", &format!("expac -Ss '%r\\\\%n\\\\%v\\\\%d' {}", query)]) .arg(query) .wait_with_output() .silent_unwrap(AppExitCode::PacmanError) .stdout; + // Split output into lines + let lines = output.trim().split('\n'); + + // Initialise results vector + let mut results_vec: Vec = vec![]; + + // Iterate over lines + for line in lines { + let parts: Vec<&str> = line.split('\\').collect(); + let res = SearchResult { + repo: parts[0].to_string(), + name: parts[1].to_string(), + version: parts[2].to_string(), + description: parts[3].to_string(), + }; + results_vec.push(res); + } + if verbosity >= 1 { log!( "Found {} results for \"{}\" in repos", - &output.split('\n').count() / 2, + &results_vec.len(), &query ); } + // Format output + let results_vec = results_vec + .into_iter() + .map(|res| { + let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 4) + .subsequent_indent(" "); + format!( + "{}{}{} {}\n {}", + res.repo.purple().bold(), + "/".purple().bold(), + res.name.bold(), + res.version.green().bold(), + if res.description.is_empty() { + "No description".to_string() + } else { + wrap(&res.description, opts).join("\n") + }, + ) + }) + .collect::>(); + if output.trim().is_empty() { "".to_string() } else { - output.trim().to_string() + results_vec.join("\n") } }