# encoding: utf-8 require 'claide/command/banner/prettifier' module CLAide class Command # Creates the formatted banner to present as help of the provided command # class. # class Banner # @return [Class] The command for which the banner should be created. # attr_accessor :command # @param [Class] command @see command # def initialize(command) @command = command end # @return [String] The banner for the command. # def formatted_banner sections = [ ['Usage', formatted_usage_description], ['Commands', formatted_subcommand_summaries], ['Options', formatted_options_description] ] banner = sections.map do |(title, body)| [prettify_title("#{title}:"), body] unless body.empty? end.compact.join("\n\n") banner end private # @!group Banner sections #-----------------------------------------------------------------------# # @return [String] The indentation of the text. # TEXT_INDENT = 6 # @return [Fixnum] The maximum width of the text. # MAX_WIDTH = TEXT_INDENT + 80 # @return [Fixnum] The minimum between a name and its description. # DESCRIPTION_SPACES = 3 # @return [Fixnum] The minimum between a name and its description. # SUBCOMMAND_BULLET_SIZE = 2 # @return [String] The section describing the usage of the command. # def formatted_usage_description message = command.description || command.summary || '' message = Helper.format_markdown(message, TEXT_INDENT, MAX_WIDTH) message = prettify_message(command, message) "#{signature}\n\n#{message}" end # @return [String] The signature of the command. # def signature result = prettify_signature( command.full_command, signature_sub_command, signature_arguments) result.insert(0, '$ ') result.insert(0, ' ' * (TEXT_INDENT - '$ '.size)) end # @return [String] The subcommand indicator of the signature. # def signature_sub_command if command.subcommands.any? command.default_subcommand ? '[COMMAND]' : 'COMMAND' end '' end # @return [String] The arguments of the signature. # def signature_arguments command.arguments.reduce('') do |memo, (name, type)| name = "[#{name}]" if type == :optional memo << ' ' << name end.lstrip end # @return [String] The section describing the subcommands of the command. # # @note The plus sign emphasizes the that the subcommands are added to # the command. The square brackets conveys a sense of direction # and indicates the gravitational force towards the default # command. # def formatted_subcommand_summaries subcommands = subcommands_for_banner subcommands.map do |subcommand| name = subcommand.command bullet = (name == command.default_subcommand) ? '>' : '+' name = "#{bullet} #{name}" pretty_name = prettify_subcommand(name) entry_description(pretty_name, subcommand.summary, name.size) end.join("\n") end # @return [String] The section describing the options of the command. # def formatted_options_description options = command.options options.map do |name, description| pretty_name = prettify_option_name(name) entry_description(pretty_name, description, name.size) end.join("\n") end # @return [String] The line describing a single entry (subcommand or # option). # def entry_description(name, description, name_width) max_name_width = compute_max_name_width desc_start = max_name_width + (TEXT_INDENT - 2) + DESCRIPTION_SPACES result = ' ' * (TEXT_INDENT - 2) result << name result << ' ' * DESCRIPTION_SPACES result << ' ' * (max_name_width - name_width) result << Helper.wrap_with_indent(description, desc_start, MAX_WIDTH) end # @!group Overrides #-----------------------------------------------------------------------# # @return [String] A decorated title. # def prettify_title(title) Prettifier.prettify_title(title) end # @return [String] A decorated textual representation of the command. # def prettify_signature(command, subcommand, argument) Prettifier.prettify_signature(command, subcommand, argument) end # @return [String] A decorated command description. # def prettify_message(command, message) Prettifier.prettify_message(command, message) end # @return [String] A decorated textual representation of the subcommand # name. # def prettify_subcommand(name) Prettifier.prettify_subcommand(name) end # @return [String] A decorated textual representation of the option name. # # def prettify_option_name(name) Prettifier.prettify_option_name(name) end # @!group Private helpers #-----------------------------------------------------------------------# # @return [Array] The list of the subcommands to use in the # banner. # def subcommands_for_banner command.subcommands_for_command_lookup.reject do |subcommand| subcommand.summary.nil? end.sort_by(&:command) end # @return [Fixnum] The width of the largest command name or of the # largest option name. Used to align all the descriptions. # def compute_max_name_width widths = [] widths << command.options.map { |option| option.first.size } widths << subcommands_for_banner.map do |cmd| cmd.command.size + SUBCOMMAND_BULLET_SIZE end.max widths.flatten.compact.max || 1 end end end end