v/vlib/cli/help.v

114 lines
2.7 KiB
V
Raw Normal View History

2019-11-21 13:03:12 +01:00
module cli
2020-04-26 13:49:31 +02:00
import term
import strings
2019-11-21 13:03:12 +01:00
const (
BASE_INDENT = 2
ABBREV_INDENT = 5
DESCRIPTION_INDENT = 20
)
fn help_flag() Flag {
return Flag{
flag: .bool,
name: 'help',
abbrev: 'h',
description: 'Prints help information',
}
}
fn help_cmd() Command {
return Command{
name: 'help',
description: 'Prints help information',
execute: help_func,
2020-01-02 18:09:24 +01:00
parent: 0
2019-11-21 13:03:12 +01:00
}
}
2020-04-02 16:04:53 +02:00
fn help_func(help_cmd Command) {
cmd := help_cmd.parent
2019-11-21 13:03:12 +01:00
full_name := cmd.full_name()
mut help := ''
help += 'Usage: ${full_name}'
if cmd.flags.len > 0 { help += ' [FLAGS]'}
if cmd.commands.len > 0 { help += ' [COMMANDS]'}
help += '\n\n'
if cmd.description != '' {
help += '${cmd.description}\n\n'
}
if cmd.flags.len > 0 {
help += 'Flags:\n'
for flag in cmd.flags {
mut flag_name := ''
if flag.abbrev != '' {
abbrev_indent := ' '.repeat(max(ABBREV_INDENT-(flag.abbrev.len+1), 1))
2019-11-21 13:03:12 +01:00
flag_name = '-${flag.abbrev}${abbrev_indent}--${flag.name}'
} else {
abbrev_indent := ' '.repeat(max(ABBREV_INDENT-(flag.abbrev.len), 1))
2019-11-21 13:03:12 +01:00
flag_name = '${abbrev_indent}--${flag.name}'
}
mut required := ''
if flag.required {
required = ' (required)'
}
base_indent := ' '.repeat(BASE_INDENT)
description_indent := ' '.repeat(max(DESCRIPTION_INDENT-flag_name.len, 1))
help += '${base_indent}${flag_name}${description_indent}' +
pretty_description(flag.description + required) + '\n'
2019-11-21 13:03:12 +01:00
}
help += '\n'
}
if cmd.commands.len > 0 {
help += 'Commands:\n'
for command in cmd.commands {
base_indent := ' '.repeat(BASE_INDENT)
description_indent := ' '.repeat(max(DESCRIPTION_INDENT-command.name.len, 1))
2019-11-21 13:03:12 +01:00
help += '${base_indent}${command.name}${description_indent}' +
pretty_description(command.description) + '\n'
2019-11-21 13:03:12 +01:00
}
help += '\n'
}
print(help)
}
// pretty_description resizes description text depending on terminal width.
// Essentially, smart wrap-around
fn pretty_description(s string) string {
width, _ := term.get_terminal_size()
// Don't prettify if the terminal is that small, it won't be pretty anyway.
if s.len + DESCRIPTION_INDENT < width || DESCRIPTION_INDENT > width {
return s
}
indent := ' '.repeat(DESCRIPTION_INDENT + 1)
chars_per_line := width - DESCRIPTION_INDENT
// Give us enough room, better a little bigger than smaller
mut acc := strings.new_builder(((s.len / chars_per_line) + 1) * (width + 1))
mut i := chars_per_line - 1
mut j := 0
for ; i < s.len ; i += chars_per_line - 1 {
for s.str[i] != ` ` { i-- }
// indent was already done the first iteration
if j != 0 { acc.write(indent) }
acc.writeln(s[j..i])
j = i
}
// We need this even though it should never happen
if j != 0 {
acc.write(indent)
}
acc.write(s[j..])
return acc.str()
}
fn max(a, b int) int {
return if a > b {a} else {b}
}