|
@@ -71,31 +71,29 @@ class BuildManPage(Command):
|
|
|
return txt.replace('-', '\\-')
|
|
|
|
|
|
def _write_header(self):
|
|
|
+
|
|
|
appname = self.distribution.get_name()
|
|
|
ret = []
|
|
|
- ret.append('.TH %s 1 %s\n' % (self._markup(appname),
|
|
|
- self._today.strftime('%Y\\-%m\\-%d')))
|
|
|
+
|
|
|
+ ret.append(self._parser.formatter_class._mk_title(self._parser._get_formatter(),
|
|
|
+ appname))
|
|
|
description = self.distribution.get_description()
|
|
|
+
|
|
|
if description:
|
|
|
name = self._markup('%s - %s' % (self._markup(appname),
|
|
|
description.splitlines()[0]))
|
|
|
else:
|
|
|
name = self._markup(appname)
|
|
|
ret.append('.SH NAME\n%s\n' % name)
|
|
|
-
|
|
|
- if isinstance(self._parser, argparse.ArgumentParser):
|
|
|
- self._parser.prog = self.distribution.get_name()
|
|
|
- synopsis = self._parser.format_usage().split(':', 1)[1]
|
|
|
- else:
|
|
|
- synopsis = self._parser.get_usage()
|
|
|
- if synopsis:
|
|
|
- synopsis = synopsis.replace('%s ' % appname, '')
|
|
|
- ret.append('.SH SYNOPSIS\n \\fB%s\\fR %s\n' % (self._markup(appname),
|
|
|
- synopsis))
|
|
|
- long_desc = self.distribution.get_long_description()
|
|
|
- if long_desc:
|
|
|
- long_desc = long_desc.replace('\n', '\n.br\n')
|
|
|
- ret.append('.SH DESCRIPTION\n%s\n' % self._markup(long_desc))
|
|
|
+
|
|
|
+
|
|
|
+ self._parser._prog = appname
|
|
|
+ ret.append(self._parser.formatter_class._mk_synopsis(self._parser._get_formatter(),
|
|
|
+ self._parser))
|
|
|
+
|
|
|
+ ret.append(self._parser.formatter_class._mk_description(self._parser._get_formatter(),
|
|
|
+ self.distribution))
|
|
|
+
|
|
|
return ''.join(ret)
|
|
|
|
|
|
def _write_options(self):
|
|
@@ -129,7 +127,23 @@ class BuildManPage(Command):
|
|
|
|
|
|
|
|
|
class ManPageFormatter(argparse.HelpFormatter):
|
|
|
-
|
|
|
+
|
|
|
+ def __init__(self,
|
|
|
+ prog,
|
|
|
+ indent_increment=2,
|
|
|
+ max_help_position=24,
|
|
|
+ width=None,
|
|
|
+ section=1):
|
|
|
+
|
|
|
+ super(ManPageFormatter, self).__init__(prog)
|
|
|
+
|
|
|
+ self._prog = prog
|
|
|
+ self._section = 1
|
|
|
+ self._today = datetime.date.today().strftime('%Y\\-%m\\-%d')
|
|
|
+
|
|
|
+ def _markup(self, txt):
|
|
|
+ return txt.replace('-', '\\-')
|
|
|
+
|
|
|
def _underline(self, string):
|
|
|
return "\\fI\\s-1" + string + "\\s0\\fR"
|
|
|
|
|
@@ -139,9 +153,37 @@ class ManPageFormatter(argparse.HelpFormatter):
|
|
|
if not string.strip().endswith('\\fR'):
|
|
|
string = string + '\\fR'
|
|
|
return string
|
|
|
-
|
|
|
+
|
|
|
+ def _mk_synopsis(self, parser):
|
|
|
+ self.add_usage(parser.usage, parser._actions,
|
|
|
+ parser._mutually_exclusive_groups, prefix='')
|
|
|
+ # TODO: Override _fromat_usage, work in progress
|
|
|
+ usage = self._format_usage(parser._prog, parser._actions,
|
|
|
+ parser._mutually_exclusive_groups, '')
|
|
|
+
|
|
|
+ usage = usage.replace('%s ' % parser._prog, '')
|
|
|
+ usage = '.SH SYNOPSIS\n \\fB%s\\fR %s\n' % (self._markup(parser._prog),
|
|
|
+ usage)
|
|
|
+ return usage
|
|
|
+
|
|
|
+ def _mk_title(self, prog):
|
|
|
+
|
|
|
+ return '.TH {0} {1} {2}\n'.format(prog, self._section,
|
|
|
+ self._today)
|
|
|
+
|
|
|
+ def _mk_description(self, distribution):
|
|
|
+
|
|
|
+ long_desc = distribution.get_long_description()
|
|
|
+
|
|
|
+ if long_desc:
|
|
|
+ long_desc = long_desc.replace('\n', '\n.br\n')
|
|
|
+ return '.SH DESCRIPTION\n%s\n' % self._markup(long_desc)
|
|
|
+ else:
|
|
|
+ return ''
|
|
|
+
|
|
|
@staticmethod
|
|
|
def format_options(parser):
|
|
|
+
|
|
|
formatter = parser._get_formatter()
|
|
|
|
|
|
# positionals, optionals and user-defined groups
|
|
@@ -179,5 +221,96 @@ class ManPageFormatter(argparse.HelpFormatter):
|
|
|
parts.append('%s %s' % (self._bold(option_string), args_string))
|
|
|
|
|
|
return ', '.join(parts)
|
|
|
+
|
|
|
+ def _format_usage(self, prog, actions, groups, prefix):
|
|
|
+
|
|
|
+ # if usage is specified, use that
|
|
|
+ # if usage is not None:
|
|
|
+ # usage = usage % dict(prog=self._prog)
|
|
|
+
|
|
|
+ # if no optionals or positionals are available, usage is just prog
|
|
|
+ #elif usage is None and not actions:
|
|
|
+ # usage = '%(prog)s' % dict(prog=self._prog)
|
|
|
+
|
|
|
+ # if optionals and positionals are available, calculate usage
|
|
|
+ #elif usage is None:
|
|
|
+ if True:
|
|
|
+ prog = '%(prog)s' % dict(prog=prog)
|
|
|
+
|
|
|
+ # split optionals from positionals
|
|
|
+ optionals = []
|
|
|
+ positionals = []
|
|
|
+ for action in actions:
|
|
|
+ if action.option_strings:
|
|
|
+ optionals.append(action)
|
|
|
+ else:
|
|
|
+ positionals.append(action)
|
|
|
+
|
|
|
+ # build full usage string
|
|
|
+ format = self._format_actions_usage
|
|
|
+ action_usage = format(optionals + positionals, groups)
|
|
|
+ usage = ' '.join([s for s in [prog, action_usage] if s])
|
|
|
+
|
|
|
+ # wrap the usage parts if it's too long
|
|
|
+ text_width = self._width - self._current_indent
|
|
|
+ if len(prefix) + len(usage) > text_width:
|
|
|
+
|
|
|
+ # break usage into wrappable parts
|
|
|
+ part_regexp = r'\(.*?\)+|\[.*?\]+|\S+'
|
|
|
+ opt_usage = format(optionals, groups)
|
|
|
+ pos_usage = format(positionals, groups)
|
|
|
+ opt_parts = _re.findall(part_regexp, opt_usage)
|
|
|
+ pos_parts = _re.findall(part_regexp, pos_usage)
|
|
|
+ assert ' '.join(opt_parts) == opt_usage
|
|
|
+ assert ' '.join(pos_parts) == pos_usage
|
|
|
+
|
|
|
+ # helper for wrapping lines
|
|
|
+ def get_lines(parts, indent, prefix=None):
|
|
|
+ lines = []
|
|
|
+ line = []
|
|
|
+ if prefix is not None:
|
|
|
+ line_len = len(prefix) - 1
|
|
|
+ else:
|
|
|
+ line_len = len(indent) - 1
|
|
|
+ for part in parts:
|
|
|
+ if line_len + 1 + len(part) > text_width:
|
|
|
+ lines.append(indent + ' '.join(line))
|
|
|
+ line = []
|
|
|
+ line_len = len(indent) - 1
|
|
|
+ line.append(part)
|
|
|
+ line_len += len(part) + 1
|
|
|
+ if line:
|
|
|
+ lines.append(indent + ' '.join(line))
|
|
|
+ if prefix is not None:
|
|
|
+ lines[0] = lines[0][len(indent):]
|
|
|
+ return lines
|
|
|
+
|
|
|
+ # if prog is short, follow it with optionals or positionals
|
|
|
+ if len(prefix) + len(prog) <= 0.75 * text_width:
|
|
|
+ indent = ' ' * (len(prefix) + len(prog) + 1)
|
|
|
+ if opt_parts:
|
|
|
+ lines = get_lines([prog] + opt_parts, indent, prefix)
|
|
|
+ lines.extend(get_lines(pos_parts, indent))
|
|
|
+ elif pos_parts:
|
|
|
+ lines = get_lines([prog] + pos_parts, indent, prefix)
|
|
|
+ else:
|
|
|
+ lines = [prog]
|
|
|
+
|
|
|
+ # if prog is long, put it on its own line
|
|
|
+ else:
|
|
|
+ indent = ' ' * len(prefix)
|
|
|
+ parts = opt_parts + pos_parts
|
|
|
+ lines = get_lines(parts, indent)
|
|
|
+ if len(lines) > 1:
|
|
|
+ lines = []
|
|
|
+ lines.extend(get_lines(opt_parts, indent))
|
|
|
+ lines.extend(get_lines(pos_parts, indent))
|
|
|
+ lines = [prog] + lines
|
|
|
+
|
|
|
+ # join lines into usage
|
|
|
+ usage = '\n'.join(lines)
|
|
|
+
|
|
|
+ # prefix with 'usage:'
|
|
|
+ return '%s%s\n\n' % (prefix, usage)
|
|
|
|
|
|
# build.sub_commands.append(('build_manpage', None))
|