diff --git a/Cargo.lock b/Cargo.lock index 30bf2f1..850922d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,7 +93,7 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "lolcat" -version = "1.5.0" +version = "1.5.1" dependencies = [ "atty", "clap", diff --git a/Cargo.toml b/Cargo.toml index a2f7e76..bc51baf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lolcat" -version = "1.5.0" +version = "1.5.1" authors = ["Umang Raghuvanshi "] description = "The good ol' lolcat, now with fearless concurrency." license = "MIT" diff --git a/src/cat.rs b/src/cat.rs index c9c38f6..0a7302b 100644 --- a/src/cat.rs +++ b/src/cat.rs @@ -16,33 +16,87 @@ pub struct Control { pub background_mode: bool, pub dialup_mode: bool, pub print_color: bool, - pub terminal_width_plus_one: u16, + pub word_wrap: bool, + pub terminal_width: u16, +} + +pub fn print_lines_lol, S: AsRef>(lines: I, c: &mut Control) { + if c.terminal_width == 0b11111111_11111111 { + generic_print_lines_lol::(lines, c); + } else if !c.word_wrap { + generic_print_lines_lol::(lines, c); + } else { + generic_print_lines_lol::(lines, c); + } } // This used to have more of a reason to exist, however now all its functionality is in // print_chars_lol(). It takes in an iterator over lines and prints them all. // At the end, it resets the foreground color -pub fn print_lines_lol, S: AsRef>(lines: I, c: &mut Control) { +pub fn generic_print_lines_lol< + const LINE_WRAP: bool, + const WORD_WRAP: bool, + I: Iterator, + S: AsRef, +>( + lines: I, + c: &mut Control, +) { + let mut word_wrap_buffer = String::new(); for line in lines { - print_chars_lol(line.as_ref().chars().chain(Some('\n')), c, false); + generic_print_chars_lol::( + line.as_ref().chars().chain(Some('\n')), + c, + &mut word_wrap_buffer, + ); } if c.print_color { print!("\x1b[39m"); } } +pub fn print_chars_lol>( + mut iter: I, + c: &mut Control, + constantly_flush: bool, +) { + let mut word_wrap_buffer: String = String::new(); + if constantly_flush { + if c.terminal_width == 0b11111111_11111111 { + generic_print_chars_lol::(&mut iter, c, &mut word_wrap_buffer); + } else if !c.word_wrap { + generic_print_chars_lol::(&mut iter, c, &mut word_wrap_buffer); + } else { + generic_print_chars_lol::(&mut iter, c, &mut word_wrap_buffer); + } + } else { + if c.terminal_width == 0b11111111_11111111 { + generic_print_chars_lol::(&mut iter, c, &mut word_wrap_buffer); + } else if !c.word_wrap { + generic_print_chars_lol::(&mut iter, c, &mut word_wrap_buffer); + } else { + generic_print_chars_lol::(&mut iter, c, &mut word_wrap_buffer); + } + } +} + // Takes in s an iterator over characters // duplicates escape sequences, otherwise prints printable characters with colored_print // Print newlines correctly, resetting background // If constantly_flush is on, it won't wait till a newline to flush stdout -pub fn print_chars_lol>( +pub fn generic_print_chars_lol< + const LINE_WRAP: bool, + const WORD_WRAP: bool, + const CONSTANTLY_FLUSH: bool, + I: Iterator, +>( mut iter: I, c: &mut Control, - constantly_flush: bool, + word_wrap_buffer: &mut String, ) { let mut seed_at_start_of_line = c.seed; let mut ignoring_whitespace = c.background_mode; - let mut printed_chars_on_line_plus_one = 1u16; + let mut printed_chars_on_line: u16 = 0; if !c.print_color { for character in iter { @@ -114,52 +168,174 @@ pub fn print_chars_lol>( // Newlines print escape sequences to end background prints, and in dialup mode sleep, and // reset the seed of the coloring and the value of ignore_whitespace '\n' => { + if WORD_WRAP { + print_word_lol( + word_wrap_buffer, + c, + &mut seed_at_start_of_line, + &mut ignoring_whitespace, + &mut printed_chars_on_line, + ); + } handle_newline( c, &mut seed_at_start_of_line, &mut ignoring_whitespace, - &mut printed_chars_on_line_plus_one, + &mut printed_chars_on_line, ); } - // If not an escape sequence or a newline, print a colorful escape sequence and then the - // character - _ => { - if printed_chars_on_line_plus_one == c.terminal_width_plus_one { - handle_newline( + // Do the same thing as normal characters if word wrap is not on. Or if word wrap is on, handle actual printing + ' ' => { + if WORD_WRAP { + print_word_lol( + word_wrap_buffer, c, &mut seed_at_start_of_line, &mut ignoring_whitespace, - &mut printed_chars_on_line_plus_one, + &mut printed_chars_on_line, + ); + if printed_chars_on_line == c.terminal_width - 1 { + handle_newline( + c, + &mut seed_at_start_of_line, + &mut ignoring_whitespace, + &mut printed_chars_on_line, + ); + } else { + print_char_lol::( + character, + c, + &mut seed_at_start_of_line, + &mut ignoring_whitespace, + &mut printed_chars_on_line, + ); + } + } else { + print_char_lol::( + character, + c, + &mut seed_at_start_of_line, + &mut ignoring_whitespace, + &mut printed_chars_on_line, ); } - // In background mode, don't print colorful whitespace until the first printable character - if ignoring_whitespace && character.is_whitespace() { - print!("{}", character); - continue; + } + // If not an escape sequence or a newline, print a colorful escape sequence and then the + // character. Or, if word wrap is on, add to the word buffer + _ => { + if WORD_WRAP { + // If the buffer is fully the width of the terminal, we must print that now + if word_wrap_buffer.len() as u16 + 1 == c.terminal_width { + print_word_lol( + word_wrap_buffer, + c, + &mut seed_at_start_of_line, + &mut ignoring_whitespace, + &mut printed_chars_on_line, + ); + handle_newline( + c, + &mut seed_at_start_of_line, + &mut ignoring_whitespace, + &mut printed_chars_on_line, + ); + } + word_wrap_buffer.push(character); } else { - ignoring_whitespace = false; + print_char_lol::( + character, + c, + &mut seed_at_start_of_line, + &mut ignoring_whitespace, + &mut printed_chars_on_line, + ); } - - colored_print(character, c); - c.seed += 1.0; - printed_chars_on_line_plus_one += 1; } } // If we should constantly flush, flush after each completed sequence, and also reset // colors because otherwise weird things happen - if constantly_flush { + if CONSTANTLY_FLUSH { reset_colors(c); stdout().flush().unwrap(); } } } +fn print_char_lol( + character: char, + c: &mut Control, + seed_at_start_of_line: &mut f64, + ignoring_whitespace: &mut bool, + printed_chars_on_line: &mut u16, +) { + if LINE_WRAP && *printed_chars_on_line == c.terminal_width { + handle_newline( + c, + seed_at_start_of_line, + ignoring_whitespace, + printed_chars_on_line, + ); + } + // In background mode, don't print colorful whitespace until the first printable character + if *ignoring_whitespace { + if character.is_whitespace() { + print!("{}", character); + } else { + *ignoring_whitespace = false; + colored_print(character, c); + } + } else { + colored_print(character, c); + } + + c.seed += 1.0; + if LINE_WRAP { + *printed_chars_on_line += 1; + } +} + +fn print_word_lol( + word: &mut String, + c: &mut Control, + seed_at_start_of_line: &mut f64, + ignoring_whitespace: &mut bool, + printed_chars_on_line: &mut u16, +) { + if *printed_chars_on_line + word.len() as u16 >= c.terminal_width { + handle_newline( + c, + seed_at_start_of_line, + ignoring_whitespace, + printed_chars_on_line, + ); + } + // In background mode, don't print colorful whitespace until the first printable character + if *ignoring_whitespace { + for character in word.chars() { + if *ignoring_whitespace && character.is_whitespace() { + print!("{}", character); + } else { + *ignoring_whitespace = false; + colored_print(character, c); + c.seed += 1.0; + } + } + } else { + for character in word.chars() { + colored_print(character, c); + c.seed += 1.0; + } + } + *printed_chars_on_line += word.len() as u16; + word.clear(); +} + fn handle_newline( c: &mut Control, seed_at_start_of_line: &mut f64, ignoring_whitespace: &mut bool, - printed_chars_on_line_plus_one: &mut u16, + printed_chars_on_line: &mut u16, ) { if c.print_color { // Reset the background color only, as we don't have to reset the foreground till @@ -178,7 +354,7 @@ fn handle_newline( *seed_at_start_of_line += 1.0; c.seed = *seed_at_start_of_line; // Reset the seed, but bump it a bit *ignoring_whitespace = c.background_mode; - *printed_chars_on_line_plus_one = 1u16; + *printed_chars_on_line = 0; } fn reset_colors(c: &Control) { diff --git a/src/main.rs b/src/main.rs index 896bad5..82398eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ fn main() { let mut filename: String = "".to_string(); let mut c = parse_cli_args(&mut filename); - if filename == "" { + if is_stdin(&filename) { let stdin = io::stdin(); // For lifetime reasons cat::print_chars_lol( BufReader::new(stdin.lock()).chars().map(|r| r.unwrap()), @@ -37,6 +37,10 @@ fn lolcat_file(filename: &str, c: &mut cat::Control) -> Result<(), io::Error> { Ok(()) } +fn is_stdin(filename: &String) -> bool { + filename == "" +} + fn parse_cli_args(filename: &mut String) -> cat::Control { let matches = lolcat_clap_app().get_matches(); @@ -56,12 +60,18 @@ fn parse_cli_args(filename: &mut String) -> cat::Control { let print_color = matches.is_present("force-color") || atty::is(Stream::Stdout); - // If the terminal width is passed, use that. Else, get the size of the terminal. Else, use 0 (no overflow) - let terminal_width: Result = matches.value_of("width") - .unwrap_or("") - .parse(); + // If the terminal width is passed, use that. Else, get the size of the terminal. Else, use max u16 + // If 0 is passed, then use max u16 + let terminal_width: Result = + matches.value_of("width").unwrap_or("").parse(); let terminal_width: u16 = match terminal_width { - Ok(width) => width, + Ok(width) => { + if width == 0 { + 0b11111111_11111111 + } else { + width + } + } Err(_) => { let size = termsize::get(); match size { @@ -71,6 +81,12 @@ fn parse_cli_args(filename: &mut String) -> cat::Control { } }; + let word_wrap = matches + .value_of("word-wrap") + .unwrap_or("") + .parse() + .unwrap_or(!is_stdin(&filename)); + let mut retval = cat::Control { seed, spread, @@ -78,7 +94,8 @@ fn parse_cli_args(filename: &mut String) -> cat::Control { background_mode: matches.is_present("background"), dialup_mode: matches.is_present("dialup"), print_color: print_color, - terminal_width_plus_one: terminal_width.wrapping_add(1), + terminal_width: terminal_width, + word_wrap: word_wrap, }; if matches.is_present("help") { @@ -137,7 +154,7 @@ fn lolcat_clap_app() -> App<'static, 'static> { Arg::with_name("background") .short("B") .long("bg") - .help("Background mode - If selected the background will be rainbow. Default false") + .help("Background mode - If given, the background will be rainbow.") .takes_value(false), ) .arg( @@ -157,7 +174,13 @@ fn lolcat_clap_app() -> App<'static, 'static> { .arg( Arg::with_name("width") .long("terminal-width") - .help("Terminal width - Set a custom terminal wrapping width, or 0 for unlimited") + .help("Terminal width - Set a custom terminal wrapping width, or 0 for unlimited (Default: your terminal's width)") + .takes_value(true), + ) + .arg( + Arg::with_name("word-wrap") + .long("word-wrap") + .help("Allow setting word wrapping behavior (Default: true for files, false for stdin)") .takes_value(true), ) .arg(