#454: Add more index markup capabilities: marking see/seealso entries, and main entries for a given key.

This commit is contained in:
Georg Brandl 2011-01-07 19:00:29 +01:00
parent 98d884da6e
commit bf767d5222
21 changed files with 228 additions and 145 deletions

View File

@ -46,6 +46,9 @@ Release 1.1 (in development)
* #521: Added :confval:`linkcheck_ignore` config value. * #521: Added :confval:`linkcheck_ignore` config value.
* #454: Add more index markup capabilities: marking see/seealso entries,
and main entries for a given key.
* #516: Added new value of the :confval:`latex_show_urls` option to * #516: Added new value of the :confval:`latex_show_urls` option to
show the URLs in footnotes. show the URLs in footnotes.

View File

@ -113,11 +113,28 @@ mainly contained in information units, such as the language reference.
Likewise, ``triple: module; search; path`` is a shortcut that creates Likewise, ``triple: module; search; path`` is a shortcut that creates
three index entries, which are ``module; search path``, ``search; path, three index entries, which are ``module; search path``, ``search; path,
module`` and ``path; module search``. module`` and ``path; module search``.
see
``see: entry; other`` creates an index entry that refers from ``entry`` to
``other``.
seealso
Like ``see``, but inserts "see also" instead of "see".
module, keyword, operator, object, exception, statement, builtin module, keyword, operator, object, exception, statement, builtin
These all create two index entries. For example, ``module: hashlib`` These all create two index entries. For example, ``module: hashlib``
creates the entries ``module; hashlib`` and ``hashlib; module``. (These creates the entries ``module; hashlib`` and ``hashlib; module``. (These
are Python-specific and therefore deprecated.) are Python-specific and therefore deprecated.)
You can mark up "main" index entries by prefixing them with an exclamation
mark. The references to "main" entries are emphasized in the generated
index. For example, if two pages contain ::
.. index:: Python
and one page contains ::
.. index:: ! Python
then the backlink to the latter page is emphasized among the three backlinks.
For index directives containing only "single" entries, there is a shorthand For index directives containing only "single" entries, there is a shorthand
notation:: notation::
@ -125,6 +142,9 @@ mainly contained in information units, such as the language reference.
This creates four index entries. This creates four index entries.
.. versionchanged:: 1.1
Added ``see`` and ``seealso`` types, as well as marking main entries.
.. rst:role:: index .. rst:role:: index
While the :rst:dir:`index` directive is a block-level markup and links to the While the :rst:dir:`index` directive is a block-level markup and links to the

View File

@ -289,16 +289,17 @@ class EpubBuilder(StandaloneHTMLBuilder):
# Logic modeled from themes/basic/genindex.html # Logic modeled from themes/basic/genindex.html
for key, columns in tree: for key, columns in tree:
for entryname, (links, subitems) in columns: for entryname, (links, subitems) in columns:
for (i, link) in enumerate(links): for (i, (ismain, link)) in enumerate(links):
m = _refuri_re.match(link) m = _refuri_re.match(link)
if m: if m:
links[i] = self.fix_fragment(m.group(1), m.group(2)) links[i] = (ismain,
self.fix_fragment(m.group(1), m.group(2)))
for subentryname, subentrylinks in subitems: for subentryname, subentrylinks in subitems:
for (i, link) in enumerate(subentrylinks): for (i, (ismain, link)) in enumerate(subentrylinks):
m = _refuri_re.match(link) m = _refuri_re.match(link)
if m: if m:
subentrylinks[i] = \ subentrylinks[i] = (ismain,
self.fix_fragment(m.group(1), m.group(2)) self.fix_fragment(m.group(1), m.group(2)))
def handle_page(self, pagename, addctx, templatename='page.html', def handle_page(self, pagename, addctx, templatename='page.html',
outfilename=None, event_arg=None): outfilename=None, event_arg=None):

View File

@ -168,7 +168,7 @@ class CObject(ObjectDescription):
indextext = self.get_index_text(name) indextext = self.get_index_text(name)
if indextext: if indextext:
self.indexnode['entries'].append(('single', indextext, name, name)) self.indexnode['entries'].append(('single', indextext, name, ''))
def before_content(self): def before_content(self):
self.typename_set = False self.typename_set = False

View File

@ -852,7 +852,7 @@ class CPPObject(ObjectDescription):
indextext = self.get_index_text(name) indextext = self.get_index_text(name)
if indextext: if indextext:
self.indexnode['entries'].append(('single', indextext, name, name)) self.indexnode['entries'].append(('single', indextext, theid, ''))
def before_content(self): def before_content(self):
lastname = self.names and self.names[-1] lastname = self.names and self.names[-1]

View File

@ -96,7 +96,7 @@ class JSObject(ObjectDescription):
indextext = self.get_index_text(objectname, name_obj) indextext = self.get_index_text(objectname, name_obj)
if indextext: if indextext:
self.indexnode['entries'].append(('single', indextext, self.indexnode['entries'].append(('single', indextext,
fullname, fullname)) fullname, ''))
def get_index_text(self, objectname, name_obj): def get_index_text(self, objectname, name_obj):
name, obj = name_obj name, obj = name_obj

View File

@ -221,7 +221,7 @@ class PyObject(ObjectDescription):
indextext = self.get_index_text(modname, name_cls) indextext = self.get_index_text(modname, name_cls)
if indextext: if indextext:
self.indexnode['entries'].append(('single', indextext, self.indexnode['entries'].append(('single', indextext,
fullname, fullname)) fullname, ''))
def before_content(self): def before_content(self):
# needed for automatic qualification of members (reset in subclasses) # needed for automatic qualification of members (reset in subclasses)
@ -399,7 +399,7 @@ class PyModule(Directive):
if not noindex: if not noindex:
indextext = _('%s (module)') % modname indextext = _('%s (module)') % modname
inode = addnodes.index(entries=[('single', indextext, inode = addnodes.index(entries=[('single', indextext,
'module-' + modname, modname)]) 'module-' + modname, '')])
ret.append(inode) ret.append(inode)
return ret return ret

View File

@ -48,7 +48,7 @@ class ReSTMarkup(ObjectDescription):
indextext = self.get_index_text(self.objtype, name) indextext = self.get_index_text(self.objtype, name)
if indextext: if indextext:
self.indexnode['entries'].append(('single', indextext, self.indexnode['entries'].append(('single', indextext,
targetname, targetname)) targetname, ''))
def get_index_text(self, objectname, name): def get_index_text(self, objectname, name):
if self.objtype == 'directive': if self.objtype == 'directive':

View File

@ -61,7 +61,7 @@ class GenericObject(ObjectDescription):
indextype = 'single' indextype = 'single'
indexentry = self.indextemplate % (name,) indexentry = self.indextemplate % (name,)
self.indexnode['entries'].append((indextype, indexentry, self.indexnode['entries'].append((indextype, indexentry,
targetname, targetname)) targetname, ''))
self.env.domaindata['std']['objects'][self.objtype, name] = \ self.env.domaindata['std']['objects'][self.objtype, name] = \
self.env.docname, targetname self.env.docname, targetname
@ -82,8 +82,8 @@ class EnvVarXRefRole(XRefRole):
tgtid = 'index-%s' % env.new_serialno('index') tgtid = 'index-%s' % env.new_serialno('index')
indexnode = addnodes.index() indexnode = addnodes.index()
indexnode['entries'] = [ indexnode['entries'] = [
('single', varname, tgtid, varname), ('single', varname, tgtid, ''),
('single', _('environment variable; %s') % varname, tgtid, varname) ('single', _('environment variable; %s') % varname, tgtid, '')
] ]
targetnode = nodes.target('', '', ids=[tgtid]) targetnode = nodes.target('', '', ids=[tgtid])
document.note_explicit_target(targetnode) document.note_explicit_target(targetnode)
@ -118,7 +118,7 @@ class Target(Directive):
indextype = indexentry[:colon].strip() indextype = indexentry[:colon].strip()
indexentry = indexentry[colon+1:].strip() indexentry = indexentry[colon+1:].strip()
inode = addnodes.index(entries=[(indextype, indexentry, inode = addnodes.index(entries=[(indextype, indexentry,
targetname, targetname)]) targetname, '')])
ret.insert(0, inode) ret.insert(0, inode)
name = self.name name = self.name
if ':' in self.name: if ':' in self.name:
@ -161,7 +161,7 @@ class Cmdoption(ObjectDescription):
self.indexnode['entries'].append( self.indexnode['entries'].append(
('pair', _('%scommand line option; %s') % ('pair', _('%scommand line option; %s') %
((currprogram and currprogram + ' ' or ''), sig), ((currprogram and currprogram + ' ' or ''), sig),
targetname, targetname)) targetname, ''))
self.env.domaindata['std']['progoptions'][currprogram, name] = \ self.env.domaindata['std']['progoptions'][currprogram, name] = \
self.env.docname, targetname self.env.docname, targetname
@ -293,8 +293,8 @@ class Glossary(Directive):
termtexts.append(termtext) termtexts.append(termtext)
# add an index entry too # add an index entry too
indexnode = addnodes.index() indexnode = addnodes.index()
indexnode['entries'] = [('single', termtext, new_id, termtext)] indexnode['entries'] = [('single', termtext, new_id, 'main')]
termnodes += indexnode termnodes.append(indexnode)
termnodes.extend(res[0]) termnodes.extend(res[0])
termnodes.append(addnodes.termsep()) termnodes.append(addnodes.termsep())
# make a single "term" node with all the terms, separated by termsep # make a single "term" node with all the terms, separated by termsep

View File

@ -37,7 +37,7 @@ from docutils.transforms import Transform
from docutils.transforms.parts import ContentsFilter from docutils.transforms.parts import ContentsFilter
from sphinx import addnodes from sphinx import addnodes
from sphinx.util import url_re, get_matching_docs, docname_join, \ from sphinx.util import url_re, get_matching_docs, docname_join, split_into, \
FilenameUniqDict FilenameUniqDict
from sphinx.util.nodes import clean_astext, make_refnode, extract_messages from sphinx.util.nodes import clean_astext, make_refnode, extract_messages
from sphinx.util.osutil import movefile, SEP, ustrftime from sphinx.util.osutil import movefile, SEP, ustrftime
@ -1484,56 +1484,50 @@ class BuildEnvironment:
"""Create the real index from the collected index entries.""" """Create the real index from the collected index entries."""
new = {} new = {}
def add_entry(word, subword, dic=new): def add_entry(word, subword, link=True, dic=new):
entry = dic.get(word) entry = dic.get(word)
if not entry: if not entry:
dic[word] = entry = [[], {}] dic[word] = entry = [[], {}]
if subword: if subword:
add_entry(subword, '', dic=entry[1]) add_entry(subword, '', link=link, dic=entry[1])
else: elif link:
try: try:
entry[0].append(builder.get_relative_uri('genindex', fn) uri = builder.get_relative_uri('genindex', fn) + '#' + tid
+ '#' + tid)
except NoUri: except NoUri:
pass pass
else:
entry[0].append((main, uri))
for fn, entries in self.indexentries.iteritems(): for fn, entries in self.indexentries.iteritems():
# new entry types must be listed in directives/other.py! # new entry types must be listed in directives/other.py!
for type, value, tid, alias in entries: for type, value, tid, main in entries:
try:
if type == 'single': if type == 'single':
try: try:
entry, subentry = value.split(';', 1) entry, subentry = split_into(2, 'single', value)
except ValueError: except ValueError:
entry, subentry = value, '' entry, = split_into(1, 'single', value)
if not entry: subentry = ''
self.warn(fn, 'invalid index entry %r' % value) add_entry(entry, subentry)
continue
add_entry(entry.strip(), subentry.strip())
elif type == 'pair': elif type == 'pair':
try: first, second = split_into(2, 'pair', value)
first, second = map(lambda x: x.strip(),
value.split(';', 1))
if not first or not second:
raise ValueError
except ValueError:
self.warn(fn, 'invalid pair index entry %r' % value)
continue
add_entry(first, second) add_entry(first, second)
add_entry(second, first) add_entry(second, first)
elif type == 'triple': elif type == 'triple':
try: first, second, third = split_into(3, 'triple', value)
first, second, third = map(lambda x: x.strip(),
value.split(';', 2))
if not first or not second or not third:
raise ValueError
except ValueError:
self.warn(fn, 'invalid triple index entry %r' % value)
continue
add_entry(first, second+' '+third) add_entry(first, second+' '+third)
add_entry(second, third+', '+first) add_entry(second, third+', '+first)
add_entry(third, first+' '+second) add_entry(third, first+' '+second)
elif type == 'see':
first, second = split_into(2, 'see', value)
add_entry(first, _('see %s') % second, link=False)
elif type == 'seealso':
first, second = split_into(2, 'see', value)
add_entry(first, _('see also %s') % second, link=False)
else: else:
self.warn(fn, 'unknown index entry type %r' % type) self.warn(fn, 'unknown index entry type %r' % type)
except ValueError, err:
self.warn(fn, str(err))
# sort the index entries; put all symbols at the front, even those # sort the index entries; put all symbols at the front, even those
# following the letters in ASCII, this is where the chr(127) comes from # following the letters in ASCII, this is where the chr(127) comes from

View File

@ -170,6 +170,7 @@ versionlabels = {
'deprecated': l_('Deprecated since version %s'), 'deprecated': l_('Deprecated since version %s'),
} }
# XXX Python specific
pairindextypes = { pairindextypes = {
'module': l_('module'), 'module': l_('module'),
'keyword': l_('keyword'), 'keyword': l_('keyword'),

View File

@ -170,8 +170,8 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
inliner.document.note_explicit_target(targetnode) inliner.document.note_explicit_target(targetnode)
if typ == 'pep': if typ == 'pep':
indexnode['entries'] = [ indexnode['entries'] = [
('single', _('Python Enhancement Proposals!PEP %s') % text, ('single', _('Python Enhancement Proposals; PEP %s') % text,
targetid, 'PEP %s' % text)] targetid, '')]
anchor = '' anchor = ''
anchorindex = text.find('#') anchorindex = text.find('#')
if anchorindex > 0: if anchorindex > 0:
@ -190,8 +190,7 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
rn += sn rn += sn
return [indexnode, targetnode, rn], [] return [indexnode, targetnode, rn], []
elif typ == 'rfc': elif typ == 'rfc':
indexnode['entries'] = [('single', 'RFC; RFC %s' % text, indexnode['entries'] = [('single', 'RFC; RFC %s' % text, targetid, '')]
targetid, 'RFC %s' % text)]
anchor = '' anchor = ''
anchorindex = text.find('#') anchorindex = text.find('#')
if anchorindex > 0: if anchorindex > 0:
@ -282,7 +281,13 @@ def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
entries = process_index_entry(target, targetid) entries = process_index_entry(target, targetid)
# otherwise we just create a "single" entry # otherwise we just create a "single" entry
else: else:
entries = [('single', target, targetid, target)] # but allow giving main entry
main = ''
if target.startswith('!'):
target = target[1:]
title = title[1:]
main = 'main'
entries = [('single', target, targetid, main)]
indexnode = addnodes.index() indexnode = addnodes.index()
indexnode['entries'] = entries indexnode['entries'] = entries
textnode = nodes.Text(title, title) textnode = nodes.Text(title, title)

View File

@ -175,23 +175,6 @@
} }
% Index-entry generation support.
%
% Command to generate two index entries (using subentries)
\newcommand{\indexii}[2]{\index{#1!#2}\index{#2!#1}}
% And three entries (using only one level of subentries)
\newcommand{\indexiii}[3]{\index{#1!#2 #3}\index{#2!#3, #1}\index{#3!#1 #2}}
% And four (again, using only one level of subentries)
\newcommand{\indexiv}[4]{
\index{#1!#2 #3 #4}
\index{#2!#3 #4, #1}
\index{#3!#4, #1 #2}
\index{#4!#1 #2 #3}
}
% \moduleauthor{name}{email} % \moduleauthor{name}{email}
\newcommand{\moduleauthor}[2]{} \newcommand{\moduleauthor}[2]{}

View File

@ -7,31 +7,48 @@
:copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
#} #}
{% macro indexentries(firstname, links) %}
<dt>
{%- if links -%}
<a href="{{ links[0][1] }}">
{%- if links[0][0] %}<strong>{% endif -%}
{{ firstname|e }}
{%- if links[0][0] %}</strong>{% endif -%}
</a>
{%- for ismain, link in links[1:] -%}
, <a href="{{ link }}">{% if ismain %}<strong>{% endif -%}
[{{ loop.index }}]
{%- if ismain %}</strong>{% endif -%}
</a>
{%- endfor %}
{%- else %}
{{ firstname|e }}
{%- endif %}
</dt>
{% endmacro %}
{% extends "layout.html" %} {% extends "layout.html" %}
{% set title = _('Index') %} {% set title = _('Index') %}
{% block body %} {% block body %}
<h1 id="index">{% trans key=key %}Index &ndash; {{ key }}{% endtrans %}</h1> <h1 id="index">{% trans key=key %}Index &ndash; {{ key }}{% endtrans %}</h1>
<table width="100%" class="indextable"><tr> <table width="100%" class="indextable"><tr>
{%- for column in entries|slice(2) if column %} {%- for column in entries|slice(2) if column %}
<td width="33%" valign="top"><dl> <td width="33%" valign="top"><dl>
{%- for entryname, (links, subitems) in column %} {%- for entryname, (links, subitems) in column %}
<dt>{% if links %}<a href="{{ links[0] }}">{{ entryname|e }}</a> {{ indexentries(entryname, links) }}
{%- for link in links[1:] %}, <a href="{{ link }}">[{{ loop.index }}]</a>{% endfor %}
{%- else %}{{ entryname|e }}{% endif %}</dt>
{%- if subitems %} {%- if subitems %}
<dd><dl> <dd><dl>
{%- for subentryname, subentrylinks in subitems %} {%- for subentryname, subentrylinks in subitems %}
<dt><a href="{{ subentrylinks[0] }}">{{ subentryname|e }}</a> {{ indexentries(subentryname, subentrylinks) }}
{%- for link in subentrylinks[1:] %}, <a href="{{ link }}">[{{ loop.index }}]</a>{% endfor -%}
</dt>
{%- endfor %} {%- endfor %}
</dl></dd> </dl></dd>
{%- endif -%} {%- endif -%}
{%- endfor %} {%- endfor %}
</dl></td> </dl></td>
{%- endfor %} {%- endfor %}
</tr></table> </tr></table>
{% endblock %} {% endblock %}

View File

@ -7,39 +7,57 @@
:copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
#} #}
{% macro indexentries(firstname, links) %}
<dt>
{%- if links -%}
<a href="{{ links[0][1] }}">
{%- if links[0][0] %}<strong>{% endif -%}
{{ firstname|e }}
{%- if links[0][0] %}</strong>{% endif -%}
</a>
{%- for ismain, link in links[1:] -%}
, <a href="{{ link }}">{% if ismain %}<strong>{% endif -%}
[{{ loop.index }}]
{%- if ismain %}</strong>{% endif -%}
</a>
{%- endfor %}
{%- else %}
{{ firstname|e }}
{%- endif %}
</dt>
{% endmacro %}
{% extends "layout.html" %} {% extends "layout.html" %}
{% set title = _('Index') %} {% set title = _('Index') %}
{% block body %} {% block body %}
<h1 id="index">{{ _('Index') }}</h1> <h1 id="index">{{ _('Index') }}</h1>
<div class="genindex-jumpbox"> <div class="genindex-jumpbox">
{% for key, dummy in genindexentries -%} {% for key, dummy in genindexentries -%}
<a href="#{{ key }}"><strong>{{ key }}</strong></a> {% if not loop.last %}| {% endif %} <a href="#{{ key }}"><strong>{{ key }}</strong></a>
{% if not loop.last %}| {% endif %}
{%- endfor %} {%- endfor %}
</div> </div>
{%- for key, entries in genindexentries %} {%- for key, entries in genindexentries %}
<h2 id="{{ key }}">{{ key }}</h2> <h2 id="{{ key }}">{{ key }}</h2>
<table width="100%" class="indextable genindextable"><tr> <table width="100%" class="indextable genindextable"><tr>
{%- for column in entries|slice(2) if column %} {%- for column in entries|slice(2) if column %}
<td width="33%" valign="top"><dl> <td width="33%" valign="top"><dl>
{%- for entryname, (links, subitems) in column %} {%- for entryname, (links, subitems) in column %}
<dt>{% if links %}<a href="{{ links[0] }}">{{ entryname|e }}</a> {{ indexentries(entryname, links) }}
{%- for link in links[1:] %}, <a href="{{ link }}">[{{ loop.index }}]</a>{% endfor %}
{%- else %}{{ entryname|e }}{% endif %}</dt>
{%- if subitems %} {%- if subitems %}
<dd><dl> <dd><dl>
{%- for subentryname, subentrylinks in subitems %} {%- for subentryname, subentrylinks in subitems %}
<dt><a href="{{ subentrylinks[0] }}">{{ subentryname|e }}</a> {{ indexentries(subentryname, subentrylinks) }}
{%- for link in subentrylinks[1:] %}, <a href="{{ link }}">[{{ loop.index }}]</a>{% endfor -%}
</dt>
{%- endfor %} {%- endfor %}
</dl></dd> </dl></dd>
{%- endif -%} {%- endif -%}
{%- endfor %} {%- endfor %}
</dl></td> </dl></td>
{%- endfor %} {%- endfor %}
</tr></table> </tr></table>
{% endfor %} {% endfor %}

View File

@ -285,6 +285,14 @@ def rpartition(s, t):
return '', s return '', s
def split_into(n, type, value):
"""Split an index entry into a given number of parts at semicolons."""
parts = map(lambda x: x.strip(), value.split(';', n-1))
if sum(1 for part in parts if part) < n:
raise ValueError('invalid %s index entry %r' % (type, value))
return parts
def format_exception_cut_frames(x=1): def format_exception_cut_frames(x=1):
"""Format an exception with traceback, but only the last x frames.""" """Format an exception with traceback, but only the last x frames."""
typ, val, tb = sys.exc_info() typ, val, tb = sys.exc_info()

View File

@ -74,17 +74,22 @@ def split_explicit_title(text):
indextypes = [ indextypes = [
'single', 'pair', 'double', 'triple', 'single', 'pair', 'double', 'triple', 'see', 'seealso',
] ]
def process_index_entry(entry, targetid): def process_index_entry(entry, targetid):
indexentries = [] indexentries = []
entry = entry.strip() entry = entry.strip()
oentry = entry
main = ''
if entry.startswith('!'):
main = 'main'
entry = entry[1:].lstrip()
for type in pairindextypes: for type in pairindextypes:
if entry.startswith(type+':'): if entry.startswith(type+':'):
value = entry[len(type)+1:].strip() value = entry[len(type)+1:].strip()
value = pairindextypes[type] + '; ' + value value = pairindextypes[type] + '; ' + value
indexentries.append(('pair', value, targetid, value)) indexentries.append(('pair', value, targetid, main))
break break
else: else:
for type in indextypes: for type in indextypes:
@ -92,15 +97,19 @@ def process_index_entry(entry, targetid):
value = entry[len(type)+1:].strip() value = entry[len(type)+1:].strip()
if type == 'double': if type == 'double':
type = 'pair' type = 'pair'
indexentries.append((type, value, targetid, value)) indexentries.append((type, value, targetid, main))
break break
# shorthand notation for single entries # shorthand notation for single entries
else: else:
for value in entry.split(','): for value in oentry.split(','):
value = value.strip() value = value.strip()
main = ''
if value.startswith('!'):
main = 'main'
value = value[1:].lstrip()
if not value: if not value:
continue continue
indexentries.append(('single', value, targetid, value)) indexentries.append(('single', value, targetid, main))
return indexentries return indexentries

View File

@ -23,6 +23,7 @@ from sphinx import addnodes
from sphinx import highlighting from sphinx import highlighting
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
from sphinx.locale import admonitionlabels, versionlabels, _ from sphinx.locale import admonitionlabels, versionlabels, _
from sphinx.util import split_into
from sphinx.util.osutil import ustrftime from sphinx.util.osutil import ustrftime
from sphinx.util.pycompat import any from sphinx.util.pycompat import any
from sphinx.util.texescape import tex_escape_map, tex_replace_map from sphinx.util.texescape import tex_escape_map, tex_replace_map
@ -1066,26 +1067,33 @@ class LaTeXTranslator(nodes.NodeVisitor):
if not node.get('inline', True): if not node.get('inline', True):
self.body.append('\n') self.body.append('\n')
entries = node['entries'] entries = node['entries']
for type, string, tid, _ in entries: for type, string, tid, ismain in entries:
m = ''
if ismain:
m = '|textbf'
try:
if type == 'single': if type == 'single':
self.body.append(r'\index{%s}' % p = scre.sub('!', self.encode(string))
scre.sub('!', self.encode(string))) self.body.append(r'\index{%s%s}' % (p, m))
elif type == 'pair': elif type == 'pair':
parts = tuple(self.encode(x.strip()) p1, p2 = map(self.encode, split_into(2, 'pair', string))
for x in string.split(';', 1)) self.body.append(r'\index{%s!%s%s}\index{%s!%s%s}' %
try: (p1, p2, m, p2, p1, m))
self.body.append(r'\indexii{%s}{%s}' % parts)
except TypeError:
self.builder.warn('invalid pair index entry %r' % string)
elif type == 'triple': elif type == 'triple':
parts = tuple(self.encode(x.strip()) p1, p2, p3 = map(self.encode, split_into(3, 'triple', string))
for x in string.split(';', 2)) self.body.append(
try: r'\index{%s!%s %s%s}\index{%s!%s, %s%s}\index{%s!%s %s%s}' %
self.body.append(r'\indexiii{%s}{%s}{%s}' % parts) (p1, p2, p3, m, p2, p3, p1, m, p3, p1, p2, m))
except TypeError: elif type == 'see':
self.builder.warn('invalid triple index entry %r' % string) p1, p2 = map(self.encode, split_into(2, 'see', string))
self.body.append(r'\index{%s|see{%s}}' % (p1, p2))
elif type == 'seealso':
p1, p2 = map(self.encode, split_into(2, 'seealso', string))
self.body.append(r'\index{%s|see{%s}}' % (p1, p2))
else: else:
self.builder.warn('unknown index entry type %s found' % type) self.builder.warn('unknown index entry type %s found' % type)
except ValueError, err:
self.builder.warn(str(err))
raise nodes.SkipNode raise nodes.SkipNode
def visit_raw(self, node): def visit_raw(self, node):

View File

@ -277,6 +277,8 @@ Index markup
double: entry; double double: entry; double
triple: index; entry; triple triple: index; entry; triple
keyword: with keyword: with
see: from; to
seealso: fromalso; toalso
Invalid index markup... Invalid index markup...
@ -285,6 +287,11 @@ Invalid index markup...
pair: pair:
keyword: keyword:
.. index::
!Main, !Other
!single: entry; pair
:index:`!Main`
.. _ölabel: .. _ölabel:

View File

@ -49,7 +49,7 @@ http://sphinx.pocoo.org/domains.html
HTML_WARNINGS = ENV_WARNINGS + """\ HTML_WARNINGS = ENV_WARNINGS + """\
%(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*' %(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*'
%(root)s/markup.txt:: WARNING: invalid index entry u'' %(root)s/markup.txt:: WARNING: invalid single index entry u''
%(root)s/markup.txt:: WARNING: invalid pair index entry u'' %(root)s/markup.txt:: WARNING: invalid pair index entry u''
%(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; ' %(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; '
""" """
@ -232,6 +232,14 @@ HTML_XPATH = {
'_static/statictmpl.html': [ '_static/statictmpl.html': [
(".//project", 'Sphinx <Tests>'), (".//project", 'Sphinx <Tests>'),
], ],
'genindex.html': [
# index entries
(".//a/strong", "Main"),
(".//a/strong", "[1]"),
(".//a/strong", "Other"),
(".//a", "entry"),
(".//dt/a", "double"),
]
} }
if pygments: if pygments:

View File

@ -30,6 +30,7 @@ latex_warnfile = StringIO()
LATEX_WARNINGS = ENV_WARNINGS + """\ LATEX_WARNINGS = ENV_WARNINGS + """\
None:None: WARNING: no matching candidate for image URI u'foo.\\*' None:None: WARNING: no matching candidate for image URI u'foo.\\*'
WARNING: invalid pair index entry u'' WARNING: invalid pair index entry u''
WARNING: invalid pair index entry u'keyword; '
""" """
if sys.version_info >= (3, 0): if sys.version_info >= (3, 0):