diff options
Diffstat (limited to 'cliapp/genman.py')
-rw-r--r-- | cliapp/genman.py | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/cliapp/genman.py b/cliapp/genman.py new file mode 100644 index 00000000..e81fcfee --- /dev/null +++ b/cliapp/genman.py @@ -0,0 +1,148 @@ +# Copyright (C) 2011 Lars Wirzenius +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +import optparse +import re + + +class ManpageGenerator(object): + + '''Fill in a manual page template from an OptionParser instance.''' + + def __init__(self, template, parser, arg_synopsis, cmd_synopsis): + self.template = template + self.parser = parser + self.arg_synopsis = arg_synopsis + self.cmd_synopsis = cmd_synopsis + + def sort_options(self, options): + return sorted(options, + key=lambda o: (o._long_opts + o._short_opts)[0]) + + def option_list(self, container): + return self.sort_options(container.option_list) + + @property + def options(self): + return self.option_list(self.parser) + + def format_template(self): + sections = (('SYNOPSIS', self.format_synopsis()), + ('OPTIONS', self.format_options())) + text = self.template + for section, contents in sections: + pattern = '\n.SH %s\n' % section + text = text.replace(pattern, pattern + contents) + return text + + def format_synopsis(self): + lines = [] + lines += ['.nh'] + lines += ['.B %s' % self.esc_dashes(self.parser.prog)] + + all_options = self.option_list(self.parser) + for group in self.parser.option_groups: + all_options += self.option_list(group) + for option in self.sort_options(all_options): + for spec in self.format_option_for_synopsis(option): + lines += ['.RB [ %s ]' % spec] + + if self.cmd_synopsis: + lines += ['.PP'] + for cmd in sorted(self.cmd_synopsis): + lines += ['.br', + '.B %s' % self.esc_dashes(self.parser.prog), + '.RI [ options ]', + self.esc_dashes(cmd)] + lines += self.format_argspec(self.cmd_synopsis[cmd]) + elif self.arg_synopsis: + lines += self.format_argspec(self.arg_synopsis) + + lines += ['.hy'] + return ''.join('%s\n' % line for line in lines) + + def format_option_for_synopsis(self, option): + if option.metavar: + suffix = '\\fR=\\fI%s' % self.esc_dashes(option.metavar) + else: + suffix = '' + for name in option._short_opts + option._long_opts: + yield '%s%s' % (self.esc_dashes(name), suffix) + + def format_options(self): + lines = [] + + for option in self.sort_options(self.parser.option_list): + lines += self.format_option_for_options(option) + + for group in self.parser.option_groups: + lines += ['.SS "%s"' % group.title] + for option in self.sort_options(group.option_list): + lines += self.format_option_for_options(option) + + return ''.join('%s\n' % line for line in lines) + + def format_option_for_options(self, option): + lines = [] + lines += ['.TP'] + shorts = [self.esc_dashes(x) for x in option._short_opts] + if option.metavar: + longs = ['%s =\\fI%s' % (self.esc_dashes(x), option.metavar) + for x in option._long_opts] + else: + longs = ['%s' % self.esc_dashes(x) + for x in option._long_opts] + lines += ['.BR ' + ' ", " '.join(shorts + longs)] + lines += [self.esc_dots(self.expand_default(option).strip())] + return lines + + def expand_default(self, option): + default = self.parser.defaults.get(option.dest) + if default is optparse.NO_DEFAULT or default is None: + default = 'none' + else: + default = str(default) + return option.help.replace('%default', default) + + def esc_dashes(self, optname): + return '\\-'.join(optname.split('-')) + + def esc_dots(self, line): + if line.startswith('.'): + return '\\' + line + else: + return line + + def format_argspec(self, argspec): + roman = re.compile(r'[^A-Z]+') + italic = re.compile(r'[A-Z]+') + words = ['.RI'] + while argspec: + m = roman.match(argspec) + if m: + words += [self.esc_dashes(m.group(0))] + argspec = argspec[m.end():] + else: + words += ['""'] + m = italic.match(argspec) + if m: + words += [self.esc_dashes(m.group(0))] + argspec = argspec[m.end():] + else: + words += ['""'] + return [' '.join(words)] + |