#14: allow distinct programs for cmdoption directive.

This commit is contained in:
Georg Brandl 2008-11-09 18:28:36 +01:00
parent 7b62fc8f2d
commit fb26a2a5fc
7 changed files with 157 additions and 64 deletions

View File

@ -17,7 +17,7 @@ typical module section might start like this::
.. moduleauthor:: John Idle <john@python.invalid> .. 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 .. directive:: .. module:: name
@ -67,8 +67,8 @@ The directives you can use for module are:
.. _desc-units: .. _desc-units:
Description units Object description units
----------------- ------------------------
There are a number of directives used to describe specific features provided by There are a number of directives used to describe specific features provided by
modules. Each directive requires one or more signatures to provide basic modules. Each directive requires one or more signatures to provide basic
@ -205,39 +205,6 @@ The directives are:
.. versionadded:: 0.4 .. 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: .. _signatures:
@ -308,3 +275,77 @@ This will render like this:
:param limit: maximum number of stack frames to show :param limit: maximum number of stack frames to show
:type limit: integer or None :type limit: integer or None
:rtype: list of strings :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.

View File

@ -14,10 +14,9 @@ from docutils import nodes
from docutils.parsers.rst import directives from docutils.parsers.rst import directives
from sphinx import addnodes from sphinx import addnodes
from sphinx.util import ws_re
ws_re = re.compile(r'\s+')
# ------ information units --------------------------------------------------------- # ------ information units ---------------------------------------------------------
def desc_index_text(desctype, module, name, add_modules): def desc_index_text(desctype, module, name, add_modules):
@ -352,17 +351,17 @@ def parse_c_signature(signode, sig, desctype):
option_desc_re = re.compile( 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): def parse_option_desc(signode, sig):
"""Transform an option description into RST nodes.""" """Transform an option description into RST nodes."""
count = 0 count = 0
firstname = '' firstname = ''
for m in option_desc_re.finditer(sig): for m in option_desc_re.finditer(sig):
prefix, optname, args = m.groups() optname, args = m.groups()
if count: if count:
signode += addnodes.desc_addname(', ', ', ') signode += addnodes.desc_addname(', ', ', ')
signode += addnodes.desc_name(prefix+optname, prefix+optname) signode += addnodes.desc_name(optname, optname)
signode += addnodes.desc_addname(args, args) signode += addnodes.desc_addname(args, args)
if not count: if not count:
firstname = optname firstname = optname
@ -402,12 +401,17 @@ def desc_directive(desctype, arguments, options, content, lineno,
elif desctype == 'cmdoption': elif desctype == 'cmdoption':
optname = parse_option_desc(signode, sig) optname = parse_option_desc(signode, sig)
if not noindex: if not noindex:
targetname = 'cmdoption-' + optname targetname = optname.replace('/', '-')
if env.currprogram:
targetname = '-' + env.currprogram + targetname
targetname = 'cmdoption' + targetname
signode['ids'].append(targetname) signode['ids'].append(targetname)
state.document.note_explicit_target(signode) state.document.note_explicit_target(signode)
inode['entries'].append(('pair', _('command line option; %s') % sig, inode['entries'].append(
('pair', _('%scommand line option; %s') %
((env.currprogram and env.currprogram + ' ' or ''), sig),
targetname, targetname)) targetname, targetname))
env.note_reftarget('option', optname, targetname) env.note_progoption(optname, targetname)
continue continue
elif desctype == 'describe': elif desctype == 'describe':
signode.clear() signode.clear()

View File

@ -14,9 +14,8 @@ from docutils import nodes
from docutils.parsers.rst import directives from docutils.parsers.rst import directives
from sphinx import addnodes from sphinx import addnodes
from sphinx.util import patfilter
from sphinx.roles import caption_ref_re
from sphinx.locale import pairindextypes from sphinx.locale import pairindextypes
from sphinx.util import patfilter, ws_re, caption_ref_re
from sphinx.util.compat import make_admonition from sphinx.util.compat import make_admonition
@ -159,6 +158,20 @@ directives.register_directive('sectionauthor', author_directive)
directives.register_directive('moduleauthor', author_directive) 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 -------------------------------------------------------------- # ------ index markup --------------------------------------------------------------
indextypes = [ indextypes = [

View File

@ -57,7 +57,7 @@ default_settings = {
# This is increased every time an environment attribute is added # This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files. # or changed to properly invalidate pickle files.
ENV_VERSION = 25 ENV_VERSION = 26
default_substitutions = set([ default_substitutions = set([
@ -266,8 +266,9 @@ class BuildEnvironment:
self.modules = {} # modname -> docname, synopsis, platform, deprecated self.modules = {} # modname -> docname, synopsis, platform, deprecated
self.labels = {} # labelname -> docname, labelid, sectionname self.labels = {} # labelname -> docname, labelid, sectionname
self.anonlabels = {} # labelname -> docname, labelid self.anonlabels = {} # labelname -> docname, labelid
self.progoptions = {} # (program, name) -> docname, labelid
self.reftargets = {} # (type, 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 # Other inventories
self.indexentries = {} # docname -> list of self.indexentries = {} # docname -> list of
@ -281,6 +282,7 @@ class BuildEnvironment:
self.currmodule = None # current module name self.currmodule = None # current module name
self.currclass = None # current class name self.currclass = None # current class name
self.currdesc = None # current descref name self.currdesc = None # current descref name
self.currprogram = None # current program name
self.index_num = 0 # autonumber for index targets self.index_num = 0 # autonumber for index targets
self.gloss_entries = set() # existing definition labels self.gloss_entries = set() # existing definition labels
@ -331,6 +333,9 @@ class BuildEnvironment:
for key, (fn, _) in self.reftargets.items(): for key, (fn, _) in self.reftargets.items():
if fn == docname: if fn == docname:
del self.reftargets[key] 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(): for version, changes in self.versionchanges.items():
new = [change for change in changes if change[1] != docname] new = [change for change in changes if change[1] != docname]
changes[:] = new changes[:] = new
@ -826,6 +831,9 @@ class BuildEnvironment:
self.modules[modname] = (self.docname, synopsis, platform, deprecated) self.modules[modname] = (self.docname, synopsis, platform, deprecated)
self.filemodules.setdefault(self.docname, []).append(modname) 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): def note_reftarget(self, type, name, labelid):
self.reftargets[type, name] = (self.docname, labelid) self.reftargets[type, name] = (self.docname, labelid)
@ -968,7 +976,7 @@ class BuildEnvironment:
'meth', 'cfunc', 'cmember', 'cdata', 'ctype', 'cmacro')) 'meth', 'cfunc', 'cmember', 'cdata', 'ctype', 'cmacro'))
def resolve_references(self, doctree, fromdocname, builder): 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 # add all custom xref types too
reftarget_roles.update(i[0] for i in additional_xref_types.values()) reftarget_roles.update(i[0] for i in additional_xref_types.values())
@ -1028,6 +1036,19 @@ class BuildEnvironment:
newnode['refuri'] = builder.get_relative_uri( newnode['refuri'] = builder.get_relative_uri(
fromdocname, docname) + '#' + labelid fromdocname, docname) + '#' + labelid
newnode.append(contnode) 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: elif typ in reftarget_roles:
docname, labelid = self.reftargets.get((typ, target), ('', '')) docname, labelid = self.reftargets.get((typ, target), ('', ''))
if not docname: if not docname:

View File

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

View File

@ -15,9 +15,8 @@ from docutils import nodes, utils
from docutils.parsers.rst import roles from docutils.parsers.rst import roles
from sphinx import addnodes 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 = { generic_docroles = {
'command' : nodes.strong, 'command' : nodes.strong,
@ -166,9 +165,17 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
target = target[1:] target = target[1:]
pnode['refspecific'] = True pnode['refspecific'] = True
# some other special cases for the target # some other special cases for the target
elif typ == 'option' and target[0] in '-/': elif typ == 'option':
# strip option marker from target program = env.currprogram
target = target[1:] 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': elif typ == 'term':
# normalize whitespace in definition terms (if the term reference is # normalize whitespace in definition terms (if the term reference is
# broken over a line, a newline will be in target) # broken over a line, a newline will be in target)

View File

@ -18,6 +18,11 @@ import traceback
from os import path 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 # SEP separates path elements in the canonical file names
# #
# Define SEP as a manifest constant, not so much because we expect it to change # Define SEP as a manifest constant, not so much because we expect it to change