Skip to content

Commit

Permalink
Better format --help
Browse files Browse the repository at this point in the history
Align subcommands help with options help; make rendering consistent
and handle term width correctly.
  • Loading branch information
1st1 committed Aug 7, 2021
1 parent 6d684d3 commit 121de78
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 22 deletions.
26 changes: 14 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ termcolor = "1.1.0"
async-listen = "0.2.0"
sha1 = "0.6.0"
hex = "0.4.3"
textwrap = "0.13.4"
textwrap = {version="0.14.2", features=["terminal_size"]}
log = "0.4.8"
env_logger = "0.8.2"
os-release = "0.1.0"
Expand Down
71 changes: 62 additions & 9 deletions src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ use crate::server;

pub mod describe;

const MAX_TERM_WIDTH: usize = 90;
const MIN_TERM_WIDTH: usize = 50;

static CONNECTION_ARG_HINT: &str = "\
Run `edgedb project init` or use any of `-H`, `-P`, `-I` arguments \
to specify connection parameters. See `--help` for details";
Expand All @@ -34,9 +37,9 @@ const CONN_OPTIONS_GROUP: &str =
"CONNECTION OPTIONS (`edgedb --help-connect` to see the full list)";

const EDGEDB_ABOUT: &str = "\
Use the `edgedb` command-line tool to spin up local instances,\n\
manage EdgeDB projects, create and apply migrations, and more.\n\
\n\
Use the `edgedb` command-line tool to spin up local instances, \
manage EdgeDB projects, create and apply migrations, and more. \
\n\n\
Running `edgedb` without a subcommand opens an interactive shell.";

pub trait PropagateArgs {
Expand Down Expand Up @@ -281,6 +284,36 @@ fn say_option_is_deprecated(option_name: &str, suggestion: &str) {
fn make_subcommand_help<T: describe::Describe>() -> String {
use std::fmt::Write;

let width = term_width();

// When the terminal is wider than 82 characters clap aligns
// the flags description text to the right of the flag name,
// when it is narrower than 82, the description goes below
// the option name. We want to align the subcommand description
// with the option description, hence there's some hand-tuning
// of the padding here.
let padding: usize = if width > 82 { 28 } else { 24 };

let extra_padding: usize = 4 + 1;
let details_width: usize = width - padding - extra_padding;

let wrap = |text: &str| {
if text.len() <= details_width {
return text.to_string();
}

let text = textwrap::fill(text, details_width);
let mut lines = text.lines();
let mut new_lines = vec![lines.nth(0).unwrap().to_string()];
for line in lines {
new_lines.push(
format!(" {:padding$} {}", " ", line, padding=padding)
);
}

new_lines.join("\n")
};

let mut buf = String::with_capacity(4096);

write!(&mut buf, "SUBCOMMANDS:\n").unwrap();
Expand All @@ -301,16 +334,19 @@ fn make_subcommand_help<T: describe::Describe>() -> String {
if subcmd.hidden {
continue;
}
writeln!(&mut buf, " {:28} {}",
writeln!(&mut buf, " {:padding$} {}",
format!("{} {}", cmd.name, subcmd.name),
sdescr.help_title,
wrap(sdescr.help_title),
padding=padding
).unwrap();
}
buf.push('\n');
empty_line = true;
} else {
writeln!(&mut buf, " {:28} {}",
cmd.name, cdescr.help_title).unwrap();
writeln!(&mut buf, " {:padding$} {}",
cmd.name, wrap(cdescr.help_title),
padding=padding
).unwrap();
empty_line = false;
}
}
Expand Down Expand Up @@ -338,7 +374,8 @@ fn print_full_connection_options() {
let app = <ConnectionOptions as clap::IntoApp>::into_app();

let mut new_app = clap::App::new("edgedb-connect")
.setting(clap::AppSettings::DeriveDisplayOrder);
.setting(clap::AppSettings::DeriveDisplayOrder)
.term_width(term_width());

for arg in app.get_arguments() {
let arg_name = arg.get_name();
Expand Down Expand Up @@ -446,11 +483,27 @@ fn get_deprecated_matches(mismatch_cmd: &str) -> Option<clap::ArgMatches> {
Some(app.get_matches_from(new_args))
}

fn term_width() -> usize {
use std::cmp;

// clap::App::max_term_width() works poorly in conjunction
// with clap::App::term_width(); it appears that one call
// disables the effect of the other. Therefore we want to
// calculate the acceptable term width ourselves and use
// that to configure clap and to render subcommands help.

cmp::max(
cmp::min(textwrap::termwidth(), MAX_TERM_WIDTH),
MIN_TERM_WIDTH
)
}

impl Options {
pub fn from_args_and_env() -> anyhow::Result<Options> {
let app = <RawOptions as clap::IntoApp>::into_app()
.name("edgedb")
.about(EDGEDB_ABOUT);
.about(EDGEDB_ABOUT)
.term_width(term_width());
let app = update_main_help(app);
let matches = get_matches(app);
let tmp: RawOptions = <RawOptions as clap::FromArgMatches>
Expand Down

0 comments on commit 121de78

Please sign in to comment.