116 lines
2.8 KiB
V
116 lines
2.8 KiB
V
module cli
|
|
|
|
import term
|
|
import strings
|
|
|
|
const (
|
|
c_base_indent = 2
|
|
c_abbrev_indent = 5
|
|
c_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,
|
|
}
|
|
}
|
|
|
|
fn help_func(help_cmd Command) {
|
|
cmd := help_cmd.parent
|
|
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(c_abbrev_indent-(flag.abbrev.len+1), 1))
|
|
flag_name = '-${flag.abbrev}${abbrev_indent}--${flag.name}'
|
|
} else {
|
|
abbrev_indent := ' '.repeat(max(c_abbrev_indent-(flag.abbrev.len), 1))
|
|
flag_name = '${abbrev_indent}--${flag.name}'
|
|
}
|
|
mut required := ''
|
|
if flag.required {
|
|
required = ' (required)'
|
|
}
|
|
|
|
base_indent := ' '.repeat(c_base_indent)
|
|
description_indent := ' '.repeat(max(c_description_indent-flag_name.len, 1))
|
|
help += '${base_indent}${flag_name}${description_indent}' +
|
|
pretty_description(flag.description + required) + '\n'
|
|
}
|
|
help += '\n'
|
|
}
|
|
if cmd.commands.len > 0 {
|
|
help += 'Commands:\n'
|
|
for command in cmd.commands {
|
|
base_indent := ' '.repeat(c_base_indent)
|
|
description_indent := ' '.repeat(max(c_description_indent-command.name.len, 1))
|
|
|
|
help += '${base_indent}${command.name}${description_indent}' +
|
|
pretty_description(command.description) + '\n'
|
|
}
|
|
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 c_description_indent > width {
|
|
return s
|
|
}
|
|
indent := ' '.repeat(c_description_indent + 2)
|
|
chars_per_line := width - c_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))
|
|
|
|
for k, line in s.split('\n') {
|
|
if k != 0 { acc.write('\n${indent}') }
|
|
mut i := chars_per_line - 2
|
|
mut j := 0
|
|
for ; i < line.len; i += chars_per_line - 2 {
|
|
for line.str[i] != ` ` { i-- }
|
|
// indent was already done the first iteration
|
|
if j != 0 { acc.write(indent) }
|
|
acc.writeln(line[j..i].trim_space())
|
|
j = i
|
|
}
|
|
// We need this even though it should never happen
|
|
if j != 0 {
|
|
acc.write(indent)
|
|
}
|
|
acc.write(line[j..].trim_space())
|
|
}
|
|
return acc.str()
|
|
}
|
|
|
|
fn max(a, b int) int {
|
|
return if a > b {a} else {b}
|
|
}
|