doc/check-seclevel.py
changeset 17648 07f1ac17b722
child 21792 e15c991fe2ec
equal deleted inserted replaced
17647:d34ba4991188 17648:07f1ac17b722
       
     1 #!/usr/bin/env python
       
     2 #
       
     3 # checkseclevel - checking section title levels in each online help documents
       
     4 
       
     5 import sys, os
       
     6 import optparse
       
     7 
       
     8 # import from the live mercurial repo
       
     9 sys.path.insert(0, "..")
       
    10 # fall back to pure modules if required C extensions are not available
       
    11 sys.path.append(os.path.join('..', 'mercurial', 'pure'))
       
    12 from mercurial import demandimport; demandimport.enable()
       
    13 from mercurial.commands import table
       
    14 from mercurial.help import helptable
       
    15 from mercurial import extensions
       
    16 from mercurial import minirst
       
    17 from mercurial import util
       
    18 
       
    19 _verbose = False
       
    20 
       
    21 def verbose(msg):
       
    22     if _verbose:
       
    23         print msg
       
    24 
       
    25 def error(msg):
       
    26     sys.stderr.write('%s\n' % msg)
       
    27 
       
    28 level2mark = ['"', '=', '-', '.', '#']
       
    29 reservedmarks = ['"']
       
    30 
       
    31 mark2level = {}
       
    32 for m, l in zip(level2mark, xrange(len(level2mark))):
       
    33     if m not in reservedmarks:
       
    34         mark2level[m] = l
       
    35 
       
    36 initlevel_topic = 0
       
    37 initlevel_cmd = 1
       
    38 initlevel_ext = 1
       
    39 initlevel_ext_cmd = 3
       
    40 
       
    41 def showavailables(initlevel):
       
    42     error('    available marks and order of them in this help: %s' %
       
    43           (', '.join(['%r' % (m * 4) for m in level2mark[initlevel + 1:]])))
       
    44 
       
    45 def checkseclevel(doc, name, initlevel):
       
    46     verbose('checking "%s"' % name)
       
    47     blocks, pruned = minirst.parse(doc, 0, ['verbose'])
       
    48     errorcnt = 0
       
    49     curlevel = initlevel
       
    50     for block in blocks:
       
    51         if block['type'] != 'section':
       
    52             continue
       
    53         mark = block['underline']
       
    54         title = block['lines'][0]
       
    55         if (mark not in mark2level) or (mark2level[mark] <= initlevel):
       
    56             error('invalid section mark %r for "%s" of %s' %
       
    57                   (mark * 4, title, name))
       
    58             showavailables(initlevel)
       
    59             errorcnt += 1
       
    60             continue
       
    61         nextlevel = mark2level[mark]
       
    62         if curlevel < nextlevel and curlevel + 1 != nextlevel:
       
    63             error('gap of section level at "%s" of %s' %
       
    64                   (title, name))
       
    65             showavailables(initlevel)
       
    66             errorcnt += 1
       
    67             continue
       
    68         verbose('appropriate section level for "%s %s"' %
       
    69                 (mark * (nextlevel * 2), title))
       
    70         curlevel = nextlevel
       
    71 
       
    72     return errorcnt
       
    73 
       
    74 def checkcmdtable(cmdtable, namefmt, initlevel):
       
    75     errorcnt = 0
       
    76     for k, entry in cmdtable.items():
       
    77         name = k.split("|")[0].lstrip("^")
       
    78         if not entry[0].__doc__:
       
    79             verbose('skip checking %s: no help document' %
       
    80                     (namefmt % name))
       
    81             continue
       
    82         errorcnt += checkseclevel(entry[0].__doc__,
       
    83                                   namefmt % name,
       
    84                                   initlevel)
       
    85     return errorcnt
       
    86 
       
    87 def checkhghelps():
       
    88     errorcnt = 0
       
    89     for names, sec, doc in helptable:
       
    90         if util.safehasattr(doc, '__call__'):
       
    91             doc = doc()
       
    92         errorcnt += checkseclevel(doc,
       
    93                                   '%s help topic' % names[0],
       
    94                                   initlevel_topic)
       
    95 
       
    96     errorcnt += checkcmdtable(table, '%s command', initlevel_cmd)
       
    97 
       
    98     for name in sorted(extensions.enabled().keys() +
       
    99                        extensions.disabled().keys()):
       
   100         mod = extensions.load(None, name, None)
       
   101         if not mod.__doc__:
       
   102             verbose('skip checking %s extension: no help document' % name)
       
   103             continue
       
   104         errorcnt += checkseclevel(mod.__doc__,
       
   105                                   '%s extension' % name,
       
   106                                   initlevel_ext)
       
   107 
       
   108         cmdtable = getattr(mod, 'cmdtable', None)
       
   109         if cmdtable:
       
   110             errorcnt += checkcmdtable(cmdtable,
       
   111                                       '%s command of ' + name + ' extension',
       
   112                                       initlevel_ext_cmd)
       
   113     return errorcnt
       
   114 
       
   115 def checkfile(filename, initlevel):
       
   116     if filename == '-':
       
   117         filename = 'stdin'
       
   118         doc = sys.stdin.read()
       
   119     else:
       
   120         fp = open(filename)
       
   121         try:
       
   122             doc = fp.read()
       
   123         finally:
       
   124             fp.close()
       
   125 
       
   126     verbose('checking input from %s with initlevel %d' %
       
   127             (filename, initlevel))
       
   128     return checkseclevel(doc, 'input from %s' % filename, initlevel)
       
   129 
       
   130 if __name__ == "__main__":
       
   131     optparser = optparse.OptionParser("""%prog [options]
       
   132 
       
   133 This checks all help documents of Mercurial (topics, commands,
       
   134 extensions and commands of them), if no file is specified by --file
       
   135 option.
       
   136 """)
       
   137     optparser.add_option("-v", "--verbose",
       
   138                          help="enable additional output",
       
   139                          action="store_true")
       
   140     optparser.add_option("-f", "--file",
       
   141                          help="filename to read in (or '-' for stdin)",
       
   142                          action="store", default="")
       
   143 
       
   144     optparser.add_option("-t", "--topic",
       
   145                          help="parse file as help topic",
       
   146                          action="store_const", dest="initlevel", const=0)
       
   147     optparser.add_option("-c", "--command",
       
   148                          help="parse file as help of core command",
       
   149                          action="store_const", dest="initlevel", const=1)
       
   150     optparser.add_option("-e", "--extension",
       
   151                          help="parse file as help of extension",
       
   152                          action="store_const", dest="initlevel", const=1)
       
   153     optparser.add_option("-C", "--extension-command",
       
   154                          help="parse file as help of extension command",
       
   155                          action="store_const", dest="initlevel", const=3)
       
   156 
       
   157     optparser.add_option("-l", "--initlevel",
       
   158                          help="set initial section level manually",
       
   159                          action="store", type="int", default=0)
       
   160 
       
   161     (options, args) = optparser.parse_args()
       
   162 
       
   163     _verbose = options.verbose
       
   164 
       
   165     if options.file:
       
   166         if checkfile(options.file, options.initlevel):
       
   167             sys.exit(1)
       
   168     else:
       
   169         if checkhghelps():
       
   170             sys.exit(1)