Commits

Georg Brandl committed 4464818

#14: allow distinct programs for cmdoption directive.

  • Participants
  • Parent commits d51d271

Comments (0)

Files changed (7)

File doc/markup/desc.rst

    .. moduleauthor:: John Idle <john@python.invalid>
 
 
-The directives you can use for module are:
+The directives you can use for module declarations are:
 
 .. directive:: .. module:: name
 
 
 .. _desc-units:
 
-Description units
------------------
+Object description units
+------------------------
 
 There are a number of directives used to describe specific features provided by
 modules.  Each directive requires one or more signatures to provide basic
    Like :dir:`method`, but indicates that the method is a static method.
 
    .. versionadded:: 0.4
-   
-.. directive:: .. cmdoption:: name args, name args, ...
-
-   Describes a command line option or switch.  Option argument names should be
-   enclosed in angle brackets.  Example::
-
-      .. cmdoption:: -m <module>, --module <module>
-
-         Run a module as a script.
-
-   The directive will create a cross-reference target named after the *first*
-   option, referencable by :role:`option` (in the example case, you'd use
-   something like ``:option:`-m```).
-
-.. directive:: .. envvar:: name
-
-   Describes an environment variable that the documented code uses or defines.
-
-
-There is also a generic version of these directives:
-
-.. directive:: .. describe:: text
-
-   This directive produces the same formatting as the specific ones explained
-   above but does not create index entries or cross-referencing targets.  It is
-   used, for example, to describe the directives in this document. Example::
-
-      .. describe:: opcode
-
-         Describes a Python bytecode instruction.
-
-Extensions may add more directives like that, using the
-:func:`~sphinx.application.Sphinx.add_description_unit` method.
 
 
 .. _signatures:
       :param limit: maximum number of stack frames to show
       :type limit: integer or None
       :rtype: list of strings
+
+
+Command-line program markup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There is a set of directives allowing documenting command-line programs:
+
+.. directive:: .. cmdoption:: name args, name args, ...
+
+   Describes a command line option or switch.  Option argument names should be
+   enclosed in angle brackets.  Example::
+
+      .. cmdoption:: -m <module>, --module <module>
+
+         Run a module as a script.
+
+   The directive will create a cross-reference target named after the *first*
+   option, referencable by :role:`option` (in the example case, you'd use
+   something like ``:option:`-m```).
+
+.. directive:: .. envvar:: name
+
+   Describes an environment variable that the documented code or program uses or
+   defines.
+
+
+.. directive:: .. program:: name
+
+   Like :dir:`currentmodule`, this directive produces no output.  Instead, it
+   serves to notify Sphinx that all following :dir:`cmdoption` directives
+   document options for the program called *name*.
+
+   If you use :dir:`program`, you have to qualify the references in your
+   :role:`option` roles by the program name, so if you have the following
+   situation ::
+
+      .. program:: rm
+
+      .. cmdoption:: -r
+
+         Work recursively.
+
+      .. program:: svn
+
+      .. cmdoption:: -r revision
+
+         Specify the revision to work upon.
+
+   then ``:option:`rm -r``` would refer to the first option, while
+   ``:option:`svn -r``` would refer to the second one.
+
+   The program name may contain spaces (in case you want to document subcommands
+   like ``svn add`` and ``svn commit`` separately).
+
+   .. versionadded:: 0.5
+
+
+Custom description units
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+There is also a generic version of these directives:
+
+.. directive:: .. describe:: text
+
+   This directive produces the same formatting as the specific ones explained
+   above but does not create index entries or cross-referencing targets.  It is
+   used, for example, to describe the directives in this document. Example::
+
+      .. describe:: opcode
+
+         Describes a Python bytecode instruction.
+
+Extensions may add more directives like that, using the
+:func:`~sphinx.application.Sphinx.add_description_unit` method.

File sphinx/directives/desc.py

 from docutils.parsers.rst import directives
 
 from sphinx import addnodes
+from sphinx.util import ws_re
 
 
-ws_re = re.compile(r'\s+')
-
 # ------ information units ---------------------------------------------------------
 
 def desc_index_text(desctype, module, name, add_modules):
 
 
 option_desc_re = re.compile(
-    r'(/|-|--)([-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)')
+    r'((?:/|-|--)[-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)')
 
 def parse_option_desc(signode, sig):
     """Transform an option description into RST nodes."""
     count = 0
     firstname = ''
     for m in option_desc_re.finditer(sig):
-        prefix, optname, args = m.groups()
+        optname, args = m.groups()
         if count:
             signode += addnodes.desc_addname(', ', ', ')
-        signode += addnodes.desc_name(prefix+optname, prefix+optname)
+        signode += addnodes.desc_name(optname, optname)
         signode += addnodes.desc_addname(args, args)
         if not count:
             firstname = optname
             elif desctype == 'cmdoption':
                 optname = parse_option_desc(signode, sig)
                 if not noindex:
-                    targetname = 'cmdoption-' + optname
+                    targetname = optname.replace('/', '-')
+                    if env.currprogram:
+                        targetname = '-' + env.currprogram + targetname
+                    targetname = 'cmdoption' + targetname
                     signode['ids'].append(targetname)
                     state.document.note_explicit_target(signode)
-                    inode['entries'].append(('pair', _('command line option; %s') % sig,
-                                             targetname, targetname))
-                    env.note_reftarget('option', optname, targetname)
+                    inode['entries'].append(
+                        ('pair', _('%scommand line option; %s') %
+                         ((env.currprogram and env.currprogram + ' ' or ''), sig),
+                         targetname, targetname))
+                    env.note_progoption(optname, targetname)
                 continue
             elif desctype == 'describe':
                 signode.clear()

File sphinx/directives/other.py

 from docutils.parsers.rst import directives
 
 from sphinx import addnodes
-from sphinx.util import patfilter
-from sphinx.roles import caption_ref_re
 from sphinx.locale import pairindextypes
+from sphinx.util import patfilter, ws_re, caption_ref_re
 from sphinx.util.compat import make_admonition
 
 
 directives.register_directive('moduleauthor', author_directive)
 
 
+def program_directive(name, arguments, options, content, lineno,
+                      content_offset, block_text, state, state_machine):
+    env = state.document.settings.env
+    program = ws_re.sub('-', arguments[0].strip())
+    if program == 'None':
+        env.currprogram = None
+    else:
+        env.currprogram = program
+    return []
+
+program_directive.arguments = (1, 0, 1)
+directives.register_directive('program', program_directive)
+
+
 # ------ index markup --------------------------------------------------------------
 
 indextypes = [

File sphinx/environment.py

 
 # This is increased every time an environment attribute is added
 # or changed to properly invalidate pickle files.
-ENV_VERSION = 25
+ENV_VERSION = 26
 
 
 default_substitutions = set([
         self.modules = {}           # modname -> docname, synopsis, platform, deprecated
         self.labels = {}            # labelname -> docname, labelid, sectionname
         self.anonlabels = {}        # labelname -> docname, labelid
+        self.progoptions = {}       # (program, name) -> docname, labelid
         self.reftargets = {}        # (type, name) -> docname, labelid
-                                    # where type is term, token, option, envvar, citation
+                                    # where type is term, token, envvar, citation
 
         # Other inventories
         self.indexentries = {}      # docname -> list of
         self.currmodule = None      # current module name
         self.currclass = None       # current class name
         self.currdesc = None        # current descref name
+        self.currprogram = None     # current program name
         self.index_num = 0          # autonumber for index targets
         self.gloss_entries = set()  # existing definition labels
 
             for key, (fn, _) in self.reftargets.items():
                 if fn == docname:
                     del self.reftargets[key]
+            for key, (fn, _) in self.progoptions.items():
+                if fn == docname:
+                    del self.progoptions[key]
             for version, changes in self.versionchanges.items():
                 new = [change for change in changes if change[1] != docname]
                 changes[:] = new
         self.modules[modname] = (self.docname, synopsis, platform, deprecated)
         self.filemodules.setdefault(self.docname, []).append(modname)
 
+    def note_progoption(self, optname, labelid):
+        self.progoptions[self.currprogram, optname] = (self.docname, labelid)
+
     def note_reftarget(self, type, name, labelid):
         self.reftargets[type, name] = (self.docname, labelid)
 
                            'meth', 'cfunc', 'cmember', 'cdata', 'ctype', 'cmacro'))
 
     def resolve_references(self, doctree, fromdocname, builder):
-        reftarget_roles = set(('token', 'term', 'option', 'citation'))
+        reftarget_roles = set(('token', 'term', 'citation'))
         # add all custom xref types too
         reftarget_roles.update(i[0] for i in additional_xref_types.values())
 
                             newnode['refuri'] = builder.get_relative_uri(
                                 fromdocname, docname) + '#' + labelid
                         newnode.append(contnode)
+                elif typ == 'option':
+                    progname = node['refprogram']
+                    docname, labelid = self.progoptions.get((progname, target), ('', ''))
+                    if not docname:
+                        newnode = contnode
+                    else:
+                        newnode = nodes.reference('', '')
+                        if docname == fromdocname:
+                            newnode['refid'] = labelid
+                        else:
+                            newnode['refuri'] = builder.get_relative_uri(
+                                fromdocname, docname) + '#' + labelid
+                        newnode.append(contnode)
                 elif typ in reftarget_roles:
                     docname, labelid = self.reftargets.get((typ, target), ('', ''))
                     if not docname:

File sphinx/ext/autodoc.py

                       noindex=directives.flag)
     app.add_directive('autoattribute', auto_directive, 1, (1, 0, 1),
                       noindex=directives.flag)
+    app.add_directive('autoprogram', auto_directive, 1, (1, 0, 1),
+                      noindex=directives.flag)
     # deprecated: remove in some future version.
     app.add_config_value('automodule_skip_lines', 0, True)
     app.add_config_value('autoclass_content', 'class', True)

File sphinx/roles.py

 from docutils.parsers.rst import roles
 
 from sphinx import addnodes
+from sphinx.util import ws_re, caption_ref_re
 
-ws_re = re.compile(r'\s+')
-caption_ref_re = re.compile(r'^([^<]+?)\s*<(.+)>$')
 
 generic_docroles = {
     'command' : nodes.strong,
             # fallback: everything after '<' is the target
             target = text[brace+1:]
             title = text[:brace]
-    # special target  for Python object cross-references
+    # special target for Python object cross-references
     if typ in ('data', 'exc', 'func', 'class', 'const', 'attr', 'meth', 'mod', 'obj'):
         # fix-up parentheses in link title
         if titleistarget:
             target = target[1:]
             pnode['refspecific'] = True
     # some other special cases for the target
-    elif typ == 'option' and target[0] in '-/':
-        # strip option marker from target
-        target = target[1:]
+    elif typ == 'option':
+        program = env.currprogram
+        if titleistarget:
+            if ' ' in title and not (title.startswith('/') or title.startswith('-')):
+                program, target = re.split(' (?=-|--|/)', title, 1)
+                program = ws_re.sub('-', program)
+                target = target.strip()
+        elif ' ' in target:
+            program, target = re.split(' (?=-|--|/)', target, 1)
+            program = ws_re.sub('-', program)
+        pnode['refprogram'] = program
     elif typ == 'term':
         # normalize whitespace in definition terms (if the term reference is
         # broken over a line, a newline will be in target)
     'obj': xfileref_role,
     'cfunc' : xfileref_role,
     'cmember': xfileref_role,
-    'cdata' : xfileref_role,
-    'ctype' : xfileref_role,
-    'cmacro' : xfileref_role,
+    'cdata': xfileref_role,
+    'ctype': xfileref_role,
+    'cmacro': xfileref_role,
 
-    'mod' : xfileref_role,
+    'mod': xfileref_role,
 
     'keyword': xfileref_role,
     'ref': xfileref_role,
-    'token' : xfileref_role,
+    'token': xfileref_role,
     'term': xfileref_role,
     'option': xfileref_role,
 
-    'menuselection' : menusel_role,
-    'file' : emph_literal_role,
-    'samp' : emph_literal_role,
+    'menuselection': menusel_role,
+    'file': emph_literal_role,
+    'samp': emph_literal_role,
 }
 
 for rolename, func in specific_docroles.iteritems():

File sphinx/util/__init__.py

 from os import path
 
 
+# Generally useful regular expressions.
+ws_re = re.compile(r'\s+')
+caption_ref_re = re.compile(r'^([^<]+?)\s*<(.+)>$')
+
+
 # SEP separates path elements in the canonical file names
 #
 # Define SEP as a manifest constant, not so much because we expect it to change