Module: Space::Core::CLI::Help

Defined in:
lib/space_core/cli/help.rb

Overview

Colourful replacement for dry-cli’s plain ‘Usage` listing — the “global help” shown by `space`, `architect`, and every bare namespace (`space repo`, `worktree`, …). Per-command help still flows through dry-cli’s Banner, whose content we like; only the listing is reskinned.

We reopen Dry::CLI::Usage.call (below) to delegate here, so BOTH the intercepted top-level help and dry-cli’s own bare-namespace path get the same treatment from one place. Colour follows CLI.help_pastel, set once per invocation from the output stream’s tty-ness and –color, so piped and test output stay plain. The ‘src` binary never loads space_core, so its own plain Usage is untouched.

Constant Summary collapse

TAGLINE =
"date-prefixed workspaces; repos provisioned on fibers at copy-on-write speed"

Class Method Summary collapse

Class Method Details

.arguments(command) ⇒ Object



82
83
84
85
86
87
88
# File 'lib/space_core/cli/help.rb', line 82

def arguments(command)
  return "" unless command.respond_to?(:required_arguments)

  names = command.required_arguments.map { |arg| arg.name.to_s.upcase }
  names += command.optional_arguments.map { |arg| "[#{arg.name.to_s.upcase}]" }
  names.empty? ? "" : " #{names.join(" ")}"
end


72
73
74
75
76
77
78
79
80
# File 'lib/space_core/cli/help.rb', line 72

def banner(node)
  if node.command && node.leaf? && node.children?
    " [ARGUMENT|SUBCOMMAND]"
  elsif node.leaf?
    arguments(node.command)
  else
    " [SUBCOMMAND]"
  end
end

.call(result, pastel: CLI.help_pastel) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/space_core/cli/help.rb', line 23

def call(result, pastel: CLI.help_pastel)
  rows  = listing(result)
  width = rows.map { |label, _| label.length }.max || 0

  lines = rows.map do |label, description|
    painted = pastel.cyan(label.ljust(width))
    description ? "  #{painted}   #{pastel.dim("# #{description}")}" : "  #{painted}"
  end

  [header(result, pastel), pastel.bold("Commands:"), *lines, footer(result, pastel)]
    .compact.join("\n")
end

.description(node) ⇒ Object



90
91
92
93
94
# File 'lib/space_core/cli/help.rb', line 90

def description(node)
  return unless node.leaf? && node.command.respond_to?(:description)

  node.command.description
end


45
46
47
# File 'lib/space_core/cli/help.rb', line 45

def footer(result, pastel)
  "\n#{pastel.dim("Run `#{program_prefix(result)} <command> --help` for details on a command.")}"
end

.header(result, pastel) ⇒ Object

The richer header only makes sense at the true root (‘space` / `architect`), not on every sub-namespace listing.



38
39
40
41
42
43
# File 'lib/space_core/cli/help.rb', line 38

def header(result, pastel)
  return unless result.names.empty?

  "#{pastel.bold.cyan("space-architect")} #{pastel.dim(Space::Core::VERSION)} " \
    "#{pastel.dim("#{TAGLINE}")}\n"
end

.label(result, name, node) ⇒ Object



68
69
70
# File 'lib/space_core/cli/help.rb', line 68

def label(result, name, node)
  "#{program_prefix(result)} #{name}#{banner(node)}"
end

.listing(result) ⇒ Object

[label_with_banner, description_or_nil], …

sorted by command name.



60
61
62
63
64
65
66
# File 'lib/space_core/cli/help.rb', line 60

def listing(result)
  result.children.sort_by { |name, _| name }.filter_map do |name, node|
    next if node.hidden

    [label(result, name, node), description(node)]
  end
end

.program_prefix(result) ⇒ Object

“space” at the root, “space repo” inside a namespace. The ‘space`/`architect` binaries inject their name into ARGV, so $PROGRAM_NAME and the leading namespace segment can collide (“space space …”); drop the duplicate.



52
53
54
55
56
57
# File 'lib/space_core/cli/help.rb', line 52

def program_prefix(result)
  prog  = File.basename($PROGRAM_NAME)
  names = result.names.dup
  names.shift if names.first == prog
  [prog, *names].join(" ")
end