lib/ctioga2/commands/doc/help.rb in ctioga2-0.0 vs lib/ctioga2/commands/doc/help.rb in ctioga2-0.1
- old
+ new
@@ -12,14 +12,15 @@
# GNU General Public License for more details (in the COPYING file).
require 'ctioga2/utils'
require 'ctioga2/commands/commands'
require 'ctioga2/commands/parsers/command-line'
+require 'ctioga2/commands/doc/wordwrap'
module CTioga2
- Version::register_svn_info('$Revision: 34 $', '$Date: 2009-05-03 20:22:29 +0200 (Sun, 03 May 2009) $')
+ Version::register_svn_info('$Revision: 132 $', '$Date: 2010-01-14 23:18:19 +0100 (Thu, 14 Jan 2010) $')
module Commands
module Documentation
@@ -27,66 +28,176 @@
class CommandLineHelp
# How much space to leave for the options ?
attr_accessor :options_column_width
- def initialize
+ # How many columns do we have at all ?
+ attr_accessor :total_width
+
+ # Whether output has (moderate) terminal capabilities
+ attr_accessor :to_tty
+
+ # Whether we should send output to pager if output has
+ # terminal support.
+ attr_accessor :to_pager
+
+ # Styles, ie a hash 'object' (option, argument...) => ANSI
+ # color code.
+ attr_accessor :styles
+
+ # Color output ?
+ attr_accessor :color
+
+ # The default value for the #styles attribute.
+ DefaultStyles = {
+ 'switch' => "01",
+ 'title' => "01;04",
+ 'arguments' => '32',
+ 'options' => '34'
+ }
+
+ # Creates an object to display command-line help. Available
+ # values for the options are given by the hash
+ # CommandLineHelpOptions. Their meaning is:
+ #
+ # * 'pager': disables or enables the use of a pager when
+ # sending output to a terminal
+ def initialize(options)
@options_column_width = 20
+ @to_pager = if options.key? 'pager'
+ options['pager']
+ else
+ true
+ end
+
+ @styles = DefaultStyles.dup
+ @color = true
end
# Prints short help text suitable for a --help option about
# available commands, by groups (ungrouped last). It takes a
# list of all commands (_cmds_) and the list of _groups_ to
# display.
- #
- # TODO: word splitting.
- #
- # TODO: why not try color, too ;-) ???
+ #
+ # \todo maybe the part about sending to the pager should be
+ # factorized into a neat utility class ?
def print_commandline_options(cmds, groups)
+ @to_tty = false
+ if STDOUT.tty?
+ begin
+ require 'curses'
+ Curses.init_screen
+ @total_width = Curses.cols
+ Curses.close_screen
+ @to_tty = true
+ rescue
+ end
+ end
+ @total_width ||= 80 # 80 by default
+
+ # Disable color output if not a to a terminal
+ if ! @to_tty
+ @color = false
+ end
+
+ if @to_tty and @to_pager
+ # We pass -R as default value...
+ ENV['LESS'] = 'R'
+ output = IO::popen("pager", "w")
+ pager = true
+ else
+ output = $stdout
+ pager = false
+ end
+
for group in groups
- puts unless group == groups[0]
+ output.puts unless group == groups[0]
name = (group && group.name) || "Ungrouped commands"
if group && group.blacklisted
name << " (blacklisted)"
end
- puts name
+ output.puts style(name, 'title')
for cmd in cmds[group].sort {|a,b|
a.long_option <=> b.long_option
}
- strings = cmd.option_strings
- puts "#{leading_spaces}%2s%1s %-#{@options_column_width}s%s" %
- [
- strings[0], (strings[0] ? "," : " "),
- strings[1],
- if strings[1].size >= @options_column_width
- "\n#{total_leading_spaces}#{strings[2]}"
- else
- strings[2]
- end
- ]
- if cmd.has_options?
- puts "#{total_leading_spaces} options: %s" %
- cmd.optional_arguments.keys.sort.map {|x| "/#{x}"}.join(' ')
- end
+ output.puts format_one_entry(cmd)
end
end
-
+ output.close
end
protected
+ # Formats one entry of the commands
+ def format_one_entry(cmd)
+ sh, long, desc = cmd.option_strings
+
+ str = "#{leading_spaces}%2s%1s %-#{@options_column_width}s" %
+ [ sh, (sh ? "," : " "), long]
+
+ size = @total_width - total_leading_spaces.size
+
+ # Do the coloring: we need to parse option string first
+ if str =~ /(.*--\S+)(.*)/
+ switch = $1
+ args = $2
+ str = "#{style(switch,'switch')}#{style(args,'arguments')}"
+ end
+
+ # Now, add the description.
+ desc_lines = WordWrapper.wrap(desc, size)
+ if long.size >= @options_column_width
+ str += "\n#{total_leading_spaces}"
+ end
+ str += desc_lines.join("\n#{total_leading_spaces}")
+
+ if cmd.has_options?
+ op_start = ' options: '
+ options = cmd.optional_arguments.
+ keys.sort.map { |x| "/#{x}"}.join(' ')
+ opts_lines = WordWrapper.wrap(options, size - op_start.size)
+ str += "\n#{total_leading_spaces}#{style(op_start,'switch')}" +
+ style(opts_lines.join("\n#{total_leading_spaces}#{' ' * op_start.size}"), 'options')
+ end
+ return str
+ end
+
# Leading spaces to align a string with the other option texts
def total_leading_spaces
return "#{leading_spaces}#{" " *(@options_column_width + 4)}"
# 4: '-o, '
end
# Spaces before any 'short' option appears
def leading_spaces
return " "
end
-
+
+ # Colorizes some text with the given ANSI code.
+ #
+ # Word wrapping should be used *before*, as it will not work
+ # after.
+ def colorize(str, code)
+ # We split into lines, as I'm unsure color status is kept
+ # across lines
+ return str.split("\n").map {|s|
+ "\e[#{code}m#{s}\e[0m"
+ }.join("\n")
+ end
+
+ # Changes the style of the object.
+ def style(str, what)
+ if ! @color
+ return str
+ end
+ if @styles[what]
+ return colorize(str, @styles[what])
+ else
+ return str
+ end
+ end
+
end
end
end