Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use my textwrap crate for wrapping help texts #845

Merged
merged 3 commits into from
May 29, 2017
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ bitflags = "0.8.0"
vec_map = "0.8"
unicode-width = "0.1.4"
unicode-segmentation = "~1.1.0" # 1.2.0 requires Rust 1.13.0
textwrap = "0.6.0"
strsim = { version = "0.6.0", optional = true }
ansi_term = { version = "0.9.0", optional = true }
term_size = { version = "0.3.0", optional = true }
Expand Down
23 changes: 23 additions & 0 deletions benches/05_ripgrep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,26 @@ use clap::{App, AppSettings, Arg, ArgSettings};

use test::Bencher;
use std::collections::HashMap;
use std::io::Cursor;

#[bench]
fn build_app_short(b: &mut Bencher) { b.iter(|| app_short()); }

#[bench]
fn build_app_long(b: &mut Bencher) { b.iter(|| app_long()); }

#[bench]
fn build_help_short(b: &mut Bencher) {
let app = app_short();
b.iter(|| build_help(&app));
}

#[bench]
fn build_help_long(b: &mut Bencher) {
let app = app_long();
b.iter(|| build_help(&app));
}

#[bench]
fn parse_clean(b: &mut Bencher) { b.iter(|| app_short().get_matches_from(vec!["rg", "pat"])); }

Expand Down Expand Up @@ -304,6 +317,16 @@ pub fn app_short() -> App<'static, 'static> { app(false, |k| USAGES[k].short) }
/// Build a clap application with long help strings.
pub fn app_long() -> App<'static, 'static> { app(true, |k| USAGES[k].long) }


/// Build the help text of an application.
fn build_help(app: &App) -> String {
let mut buf = Cursor::new(Vec::with_capacity(50));
app.write_help(&mut buf).unwrap();
let content = buf.into_inner();
String::from_utf8(content).unwrap()
}


/// Build a clap application parameterized by usage strings.
///
/// The function given should take a clap argument name and return a help
Expand Down
46 changes: 7 additions & 39 deletions src/app/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use app::usage;
use unicode_width::UnicodeWidthStr;
#[cfg(feature = "wrap_help")]
use term_size;
use unicode_segmentation::UnicodeSegmentation;
use textwrap;
use vec_map::VecMap;

#[cfg(not(feature = "wrap_help"))]
Expand Down Expand Up @@ -955,45 +955,13 @@ impl<'a> Help<'a> {
}

fn wrap_help(help: &mut String, longest_w: usize, avail_chars: usize) {
debugln!("Help::wrap_help: longest_w={}, avail_chars={}",
longest_w,
avail_chars);
debug!("Help::wrap_help: Enough space to wrap...");
// Keep previous behavior of not wrapping at all if one of the
// words would overflow the line.
if longest_w < avail_chars {
sdebugln!("Yes");
let mut prev_space = 0;
let mut j = 0;
for (idx, g) in (&*help.clone()).grapheme_indices(true) {
debugln!("Help::wrap_help:iter: idx={}, g={}", idx, g);
if g == "\n" {
debugln!("Help::wrap_help:iter: Newline found...");
debugln!("Help::wrap_help:iter: Still space...{:?}",
str_width(&help[j..idx]) < avail_chars);
if str_width(&help[j..idx]) < avail_chars {
j = idx;
continue;
}
} else if g != " " {
if idx != help.len() - 1 || str_width(&help[j..idx]) < avail_chars {
continue;
}
debugln!("Help::wrap_help:iter: Reached the end of the line and we're over...");
} else if str_width(&help[j..idx]) <= avail_chars {
debugln!("Help::wrap_help:iter: Space found with room...");
prev_space = idx;
continue;
}
debugln!("Help::wrap_help:iter: Adding Newline...");
j = prev_space;
debugln!("Help::wrap_help:iter: prev_space={}, j={}", prev_space, j);
debugln!("Help::wrap_help:iter: Removing...{}", j);
debugln!("Help::wrap_help:iter: Char at {}: {:?}", j, &help[j..j + 1]);
help.remove(j);
help.insert(j, '\n');
prev_space = idx;
}
} else {
sdebugln!("No");
*help = help.lines()
.map(|line| textwrap::fill(line, avail_chars))
.collect::<Vec<String>>()
.join("\n");
}
}

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ extern crate bitflags;
extern crate vec_map;
#[cfg(feature = "wrap_help")]
extern crate term_size;
extern crate textwrap;
extern crate unicode_segmentation;
#[cfg(feature = "color")]
extern crate atty;
Expand Down
28 changes: 28 additions & 0 deletions tests/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,22 @@ FLAGS:
(Defaults to something)
-V, --version Prints version information";

static WRAPPING_NEWLINE_CHARS: &'static str = "ctest 0.1

USAGE:
ctest [mode]

FLAGS:
-h, --help Prints help information
-V, --version Prints version information

ARGS:
<mode> x, max, maximum 20 characters, contains
symbols.
l, long Copy-friendly, 14
characters, contains symbols.
m, med, medium Copy-friendly, 8
characters, contains symbols.";

static ISSUE_688: &'static str = "ctest 0.1

Expand Down Expand Up @@ -663,6 +679,18 @@ fn final_word_wrapping() {
assert!(test::compare_output(app, "ctest --help", FINAL_WORD_WRAPPING, false));
}

#[test]
fn wrapping_newline_chars() {
let app = App::new("ctest")
.version("0.1")
.set_term_width(60)
.arg(Arg::with_name("mode")
.help("x, max, maximum 20 characters, contains symbols.{n}\
l, long Copy-friendly, 14 characters, contains symbols.{n}\
m, med, medium Copy-friendly, 8 characters, contains symbols.{n}"));
assert!(test::compare_output(app, "ctest --help", WRAPPING_NEWLINE_CHARS, false));
}

#[test]
fn old_newline_chars() {
let app = App::new("ctest")
Expand Down