瀏覽代碼

More improvements to build_manpage

oz123 10 年之前
父節點
當前提交
0c94710519
共有 1 個文件被更改,包括 151 次插入18 次删除
  1. 151 18
      build_manpage.py

+ 151 - 18
build_manpage.py

@@ -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))