aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/old/vlib/cli/help.v
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/old/vlib/cli/help.v')
-rw-r--r--v_windows/v/old/vlib/cli/help.v172
1 files changed, 172 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/cli/help.v b/v_windows/v/old/vlib/cli/help.v
new file mode 100644
index 0000000..6ce3e41
--- /dev/null
+++ b/v_windows/v/old/vlib/cli/help.v
@@ -0,0 +1,172 @@
+module cli
+
+import term
+import strings
+
+const (
+ base_indent_len = 2
+ min_description_indent_len = 20
+ spacing = 2
+)
+
+fn help_flag(with_abbrev bool) Flag {
+ sabbrev := if with_abbrev { 'h' } else { '' }
+ return Flag{
+ flag: .bool
+ name: 'help'
+ abbrev: sabbrev
+ description: 'Prints help information.'
+ }
+}
+
+fn help_cmd() Command {
+ return Command{
+ name: 'help'
+ usage: '<command>'
+ description: 'Prints help information.'
+ execute: print_help_for_command
+ }
+}
+
+fn print_help_for_command(help_cmd Command) ? {
+ if help_cmd.args.len > 0 {
+ mut cmd := help_cmd.parent
+ for arg in help_cmd.args {
+ mut found := false
+ for sub_cmd in cmd.commands {
+ if sub_cmd.name == arg {
+ cmd = unsafe { &sub_cmd }
+ found = true
+ break
+ }
+ }
+ if !found {
+ args := help_cmd.args.join(' ')
+ println('Invalid command: $args')
+ return
+ }
+ }
+ print(cmd.help_message())
+ } else {
+ if help_cmd.parent != 0 {
+ print(help_cmd.parent.help_message())
+ }
+ }
+}
+
+fn (cmd Command) help_message() string {
+ mut help := ''
+ help += 'Usage: $cmd.full_name()'
+ if cmd.flags.len > 0 {
+ help += ' [flags]'
+ }
+ if cmd.commands.len > 0 {
+ help += ' [commands]'
+ }
+ if cmd.usage.len > 0 {
+ help += ' $cmd.usage'
+ } else {
+ for i in 0 .. cmd.required_args {
+ help += ' <arg$i>'
+ }
+ }
+ help += '\n'
+ if cmd.description != '' {
+ help += '\n$cmd.description\n'
+ }
+ mut abbrev_len := 0
+ mut name_len := cli.min_description_indent_len
+ if cmd.flags.have_abbrev() {
+ for flag in cmd.flags {
+ abbrev_len = max(abbrev_len, flag.abbrev.len + cli.spacing + 1) // + 1 for '-' in front
+ name_len = max(name_len, abbrev_len + flag.name.len + cli.spacing + 2) // + 2 for '--' in front
+ }
+ for command in cmd.commands {
+ name_len = max(name_len, command.name.len + cli.spacing)
+ }
+ } else {
+ for flag in cmd.flags {
+ name_len = max(name_len, abbrev_len + flag.name.len + cli.spacing + 1) // + 1 for '-' in front
+ }
+ for command in cmd.commands {
+ name_len = max(name_len, command.name.len + cli.spacing)
+ }
+ }
+ if cmd.flags.len > 0 {
+ help += '\nFlags:\n'
+ for flag in cmd.flags {
+ mut flag_name := ''
+ if flag.abbrev != '' && cmd.flags.have_abbrev() {
+ abbrev_indent := ' '.repeat(abbrev_len - flag.abbrev.len - 1) // - 1 for '-' in front
+ flag_name = '-$flag.abbrev$abbrev_indent--$flag.name'
+ } else if cmd.flags.have_abbrev() {
+ abbrev_indent := ' '.repeat(abbrev_len)
+ flag_name = '$abbrev_indent--$flag.name'
+ } else {
+ flag_name = '-$flag.name'
+ }
+ mut required := ''
+ if flag.required {
+ required = ' (required)'
+ }
+ base_indent := ' '.repeat(cli.base_indent_len)
+ description_indent := ' '.repeat(name_len - flag_name.len)
+ help += '$base_indent$flag_name$description_indent' +
+ pretty_description(flag.description + required, cli.base_indent_len + name_len) +
+ '\n'
+ }
+ }
+ if cmd.commands.len > 0 {
+ help += '\nCommands:\n'
+ for command in cmd.commands {
+ base_indent := ' '.repeat(cli.base_indent_len)
+ description_indent := ' '.repeat(name_len - command.name.len)
+ help += '$base_indent$command.name$description_indent' +
+ pretty_description(command.description, name_len) + '\n'
+ }
+ }
+ return help
+}
+
+// pretty_description resizes description text depending on terminal width.
+// Essentially, smart wrap-around
+fn pretty_description(s string, indent_len int) string {
+ width, _ := term.get_terminal_size()
+ // Don't prettify if the terminal is that small, it won't be pretty anyway.
+ if indent_len > width {
+ return s
+ }
+ indent := ' '.repeat(indent_len)
+ chars_per_line := width - indent_len
+ // 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_string('\n$indent')
+ }
+ mut i := chars_per_line - 2
+ mut j := 0
+ for ; i < line.len; i += chars_per_line - 2 {
+ for line[i] != ` ` {
+ i--
+ }
+ // indent was already done the first iteration
+ if j != 0 {
+ acc.write_string(indent)
+ }
+ acc.writeln(line[j..i].trim_space())
+ j = i
+ }
+ // We need this even though it should never happen
+ if j != 0 {
+ acc.write_string(indent)
+ }
+ acc.write_string(line[j..].trim_space())
+ }
+ return acc.str()
+}
+
+fn max(a int, b int) int {
+ res := if a > b { a } else { b }
+ return res
+}