shared/verbs: split verbs in two lines when the synopsis is > 25 characters
The help tests would not pass because in cases where the verb synopsis
is very long, we'd format the table badly if the terminal is fairly
narrow. I experimented with a few solutions, but overall, it's hard to
achieve very good layout with the automatic formatting. I think the
approach in this commit works the best: we end up with an two- or
three-line verb synopis, which is similar to what we did manually
before.
$ COLUMNS=80 build/localectl -h
...
Commands:
[status] Show current locale settings
set-locale LOCALE... Set system locale
list-locales Show known locales
set-keymap MAP [MAP] Set console and X11 keyboard mappings
list-keymaps Show known virtual console keyboard mappings
set-x11-keymap LAYOUT [MODEL Set X11 and console keyboard mappings
[VARIANT [OPTIONS]]]
list-x11-keymap-models Show known X11 keyboard mapping models
list-x11-keymap-layouts Show known X11 keyboard mapping layouts
list-x11-keymap-variants Show known X11 keyboard mapping variants
[LAYOUT]
list-x11-keymap-options Show known X11 keyboard mapping options
I think that almost nobody actually uses an 80 column terminal, and if
they do, they probably don't spend too much time looking at our --help
output there. So the goal here is to do something reasonable and robust
and get the tests to pass.
We can use strjoina here because the strings are fully under our
control.
This commit is contained in:
+69
-20
@@ -7,6 +7,7 @@
|
||||
#include "log.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "verbs.h"
|
||||
#include "virt.h"
|
||||
|
||||
@@ -160,6 +161,72 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
|
||||
return _dispatch_verb_with_args(strv_skip(argv, optind), verbs, verbs + n, userdata);
|
||||
}
|
||||
|
||||
#define VERB_SYNOPSIS_WIDTH_SANE 25
|
||||
|
||||
static const char* find_point_to_break(const char *s, size_t max_width) {
|
||||
/* Locate the first space, preferably after max_width, or the last space otherwise.
|
||||
* Return the part after the space. */
|
||||
|
||||
if (strlen(s) <= max_width)
|
||||
return NULL;
|
||||
|
||||
const char *p = strchr(s + max_width, ' ') ?: strrchr(s, ' ');
|
||||
return p ? p + 1 : NULL;
|
||||
}
|
||||
|
||||
static int verb_add_help_one(Table *table, const Verb *verb) {
|
||||
assert(table);
|
||||
assert(verb);
|
||||
|
||||
bool is_default = FLAGS_SET(verb->flags, VERB_DEFAULT);
|
||||
int r;
|
||||
|
||||
/* We indent the option string by two spaces. We could set the minimum cell width and
|
||||
* right-align for a similar result, but that'd be more work. This is only used for
|
||||
* display. */
|
||||
_cleanup_free_ char *s = strjoin(" ",
|
||||
is_default ? "[" : "",
|
||||
verb->verb,
|
||||
verb->argspec ? " " : "",
|
||||
strempty(verb->argspec),
|
||||
is_default ? "]" : "");
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
const char *ss = NULL;
|
||||
if (columns() < VERB_SYNOPSIS_WIDTH_SANE * 4) {
|
||||
/* If the synopsis is very wide, try to split it up. But do this only if the terminal
|
||||
* is not very wide. If it _is_ wide, the broken up synopsis would look silly. */
|
||||
const char *p = find_point_to_break(s, VERB_SYNOPSIS_WIDTH_SANE), *p2 = NULL;
|
||||
if (p) {
|
||||
const char *s1 = strndupa_safe(s, p - s), *s2 = NULL;
|
||||
|
||||
p2 = find_point_to_break(p, VERB_SYNOPSIS_WIDTH_SANE - 4); /* we indent by two spaces more */
|
||||
if (p2)
|
||||
s2 = strndupa_safe(p, p2 - p);
|
||||
|
||||
if (s2)
|
||||
ss = strjoina(s1, "\n ", s2, "\n ", p2);
|
||||
else
|
||||
ss = strjoina(s1, "\n ", p);
|
||||
}
|
||||
}
|
||||
|
||||
r = table_add_cell(table, NULL, TABLE_STRING, ss ?: s);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
_cleanup_strv_free_ char **t = strv_split(verb->help, /* separators= */ NULL);
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(table, TABLE_STRV_WRAPPED, t);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _verbs_get_help_table(
|
||||
const Verb verbs[],
|
||||
const Verb verbs_end[],
|
||||
@@ -192,27 +259,9 @@ int _verbs_get_help_table(
|
||||
/* No help string — we do not show the verb */
|
||||
continue;
|
||||
|
||||
bool is_default = FLAGS_SET(verb->flags, VERB_DEFAULT);
|
||||
|
||||
/* We indent the option string by two spaces. We could set the minimum cell width and
|
||||
* right-align for a similar result, but that'd be more work. This is only used for
|
||||
* display. */
|
||||
r = table_add_cell_stringf(table, NULL, " %s%s%s%s%s",
|
||||
is_default ? "[" : "",
|
||||
verb->verb,
|
||||
verb->argspec ? " " : "",
|
||||
strempty(verb->argspec),
|
||||
is_default ? "]" : "");
|
||||
r = verb_add_help_one(table, verb);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
_cleanup_strv_free_ char **s = strv_split(verb->help, /* separators= */ NULL);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(table, TABLE_STRV_WRAPPED, s);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
table_set_header(table, false);
|
||||
|
||||
Reference in New Issue
Block a user