diff --git a/CHANGES b/CHANGES index 39ae924b2..a596f75d6 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,13 @@ Release 1.0 (in development) allowing styles to customize their appearance. Domain-specific roles get two classes, ``domain`` and ``domain-rolename``. +* In HTML output, references now get the class ``internal`` if they + are internal to the whole project, as opposed to internal to the + current page. + +* The ``menuselection`` and ``guilabel`` roles now support ampersand + accelerators. + * New more compact doc field syntax is now recognized: ``:param type name: description``. diff --git a/doc/config.rst b/doc/config.rst index 126679d12..cc314034d 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -200,7 +200,7 @@ General configuration The name of the default :ref:`domain `. Can also be ``None`` to disable a default domain. The default is ``'py'``. Those objects in other domains (whether the domain name is given explicitly, or selected by a - :dir:`default-domain` directive) will have the domain name explicitly + :rst:dir:`default-domain` directive) will have the domain name explicitly prepended when named (e.g., when the default domain is C, Python functions will be named "Python function", not just "function"). diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst index 558f813d6..a41d43b9d 100644 --- a/doc/markup/inline.rst +++ b/doc/markup/inline.rst @@ -182,6 +182,11 @@ in a different style: labels, window titles, field names, menu and menu selection names, and even values in selection lists. + .. versionchanged:: 1.0 + An accelerator key for the GUI label can be included using an ampersand; + this will be stripped and displayed underlined in the output (example: + ``:guilabel:`&Cancel```). To include a literal ampersand, double it. + .. rst:role:: kbd Mark a sequence of keystrokes. What form the key sequence takes may depend @@ -227,6 +232,9 @@ in a different style: ellipsis some operating systems use to indicate that the command opens a dialog, the indicator should be omitted from the selection name. + ``menuselection`` also supports ampersand accelerators just like + :rst:role:`guilabel`. + .. rst:role:: mimetype The name of a MIME type, or a component of a MIME type (the major or minor diff --git a/doc/theming.rst b/doc/theming.rst index 3a00150b7..ca6e8b2d6 100644 --- a/doc/theming.rst +++ b/doc/theming.rst @@ -105,6 +105,9 @@ These themes are: doesn't scroll out of view for long body content. This may not work well with all browsers. Defaults to false. + - **externalrefs** (true or false): Display external links differently from + internal links. Defaults to false. + There are also various color and font options that can change the color scheme without having to write a custom stylesheet: diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 2dbca0375..94665dcf7 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -607,7 +607,7 @@ class StandaloneHTMLBuilder(Builder): # the parent node here. continue uri = node['uri'] - reference = nodes.reference() + reference = nodes.reference('', '', internal=True) if uri in self.images: reference['refuri'] = posixpath.join(self.imgpath, self.images[uri]) diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 3c8dff43e..68bff15e2 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -441,7 +441,7 @@ class StandardDomain(Domain): # 'precede a section header.', node.line) if not docname: return None - newnode = nodes.reference('', '') + newnode = nodes.reference('', '', internal=True) innernode = nodes.emphasis(sectname, sectname) if docname == fromdocname: newnode['refid'] = labelid diff --git a/sphinx/environment.py b/sphinx/environment.py index 007616245..c8b3f018c 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -1008,9 +1008,9 @@ class BuildEnvironment: else: anchorname = '#' + sectionnode['ids'][0] numentries[0] += 1 - reference = nodes.reference('', '', refuri=docname, - anchorname=anchorname, - *nodetext) + reference = nodes.reference( + '', '', internal=True, refuri=docname, + anchorname=anchorname, *nodetext) para = addnodes.compact_paragraph('', '', reference) item = nodes.list_item('', para) if maxdepth == 0 or depth < maxdepth: @@ -1148,7 +1148,7 @@ class BuildEnvironment: for (title, ref) in refs: try: if url_re.match(ref): - reference = nodes.reference('', '', + reference = nodes.reference('', '', internal=False, refuri=ref, anchorname='', *[nodes.Text(title)]) para = addnodes.compact_paragraph('', '', reference) @@ -1160,7 +1160,7 @@ class BuildEnvironment: ref = toctreenode['parent'] if not title: title = clean_astext(self.titles[ref]) - reference = nodes.reference('', '', + reference = nodes.reference('', '', internal=True, refuri=ref, anchorname='', *[nodes.Text(title)]) @@ -1275,7 +1275,7 @@ class BuildEnvironment: else: caption = clean_astext(self.titles[docname]) innernode = nodes.emphasis(caption, caption) - newnode = nodes.reference('', '') + newnode = nodes.reference('', '', internal=True) newnode['refuri'] = builder.get_relative_uri( fromdocname, docname) newnode.append(innernode) diff --git a/sphinx/ext/extlinks.py b/sphinx/ext/extlinks.py index 36f4d697d..9a29918a5 100644 --- a/sphinx/ext/extlinks.py +++ b/sphinx/ext/extlinks.py @@ -46,7 +46,7 @@ def make_link_role(base_url, prefix): title = full_url else: title = prefix + part - pnode = nodes.reference(title, title, refuri=full_url) + pnode = nodes.reference(title, title, internal=False, refuri=full_url) return [pnode], [] return role diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index 677730097..1608c2307 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -201,10 +201,8 @@ def missing_reference(app, env, node, contnode): if objtype not in inventory or target not in inventory[objtype]: continue proj, version, uri, dispname = inventory[objtype][target] - newnode = nodes.reference('', '') - newnode['refuri'] = uri - newnode['reftitle'] = '(in %s v%s)' % (proj, version) - newnode['class'] = 'external-xref' + newnode = nodes.reference('', '', internal=False, refuri=uri, + reftitle='(in %s v%s)' % (proj, version)) if dispname == '-': newnode.append(contnode) else: diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py index ac3629194..a6c33202e 100644 --- a/sphinx/ext/todo.py +++ b/sphinx/ext/todo.py @@ -113,9 +113,8 @@ def process_todo_nodes(app, doctree, fromdocname): para += nodes.Text(desc1, desc1) # Create a reference - newnode = nodes.reference('', '') + newnode = nodes.reference('', '', internal=True) innernode = nodes.emphasis(_('original entry'), _('original entry')) - newnode['refdocname'] = todo_info['docname'] try: newnode['refuri'] = app.builder.get_relative_uri( fromdocname, todo_info['docname']) diff --git a/sphinx/roles.py b/sphinx/roles.py index 489d6ae8b..0164d7576 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -24,7 +24,6 @@ from sphinx.util.nodes import split_explicit_title generic_docroles = { 'command' : nodes.strong, 'dfn' : nodes.emphasis, - 'guilabel' : nodes.strong, 'kbd' : nodes.literal, 'mailheader' : addnodes.literal_emphasis, 'makevar' : nodes.strong, @@ -40,7 +39,6 @@ for rolename, nodeclass in generic_docroles.iteritems(): role = roles.CustomRole(rolename, generic, {'classes': [rolename]}) roles.register_local_role(rolename, role) - # -- generic cross-reference role ---------------------------------------------- class XRefRole(object): @@ -184,7 +182,7 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, return [prb], [msg] ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum sn = nodes.strong('PEP '+text, 'PEP '+text) - rn = nodes.reference('', '', refuri=ref, classes=[typ]) + rn = nodes.reference('', '', internal=False, refuri=ref, classes=[typ]) rn += sn return [indexnode, targetnode, rn], [] elif typ == 'rfc': @@ -199,17 +197,36 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, return [prb], [msg] ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum sn = nodes.strong('RFC '+text, 'RFC '+text) - rn = nodes.reference('', '', refuri=ref, classes=[typ]) + rn = nodes.reference('', '', internal=False, refuri=ref, classes=[typ]) rn += sn return [indexnode, targetnode, rn], [] -def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): - return [nodes.emphasis( - rawtext, utils.unescape(text).replace('-->', u'\N{TRIANGULAR BULLET}'), - classes=[typ])], [] - return role +_amp_re = re.compile(r'(?', u'\N{TRIANGULAR BULLET}') + spans = _amp_re.split(text) + + node = nodes.emphasis(rawtext=rawtext) + for i, span in enumerate(spans): + span = span.replace('&&', '&') + if i == 0: + if len(span) > 0: + textnode = nodes.Text(span) + node += textnode + continue + accel_node = nodes.inline() + letter_node = nodes.Text(span[0]) + accel_node += letter_node + accel_node['classes'].append('accelerator') + node += accel_node + textnode = nodes.Text(span[1:]) + node += textnode + + node['classes'].append(typ) + return [node], [] _litvar_re = re.compile('{([^}]+)}') @@ -249,6 +266,7 @@ specific_docroles = { 'pep': indexmarkup_role, 'rfc': indexmarkup_role, + 'guilabel': menusel_role, 'menuselection': menusel_role, 'file': emph_literal_role, 'samp': emph_literal_role, diff --git a/sphinx/themes/basic/static/basic.css b/sphinx/themes/basic/static/basic.css index 7cfacd5b9..c317cc0a3 100644 --- a/sphinx/themes/basic/static/basic.css +++ b/sphinx/themes/basic/static/basic.css @@ -368,6 +368,14 @@ dl.glossary dt { margin-left: 1.5em; } +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + /* -- code displays --------------------------------------------------------- */ pre { diff --git a/sphinx/themes/default/static/default.css_t b/sphinx/themes/default/static/default.css_t index 579f57a4c..8afa1a95f 100644 --- a/sphinx/themes/default/static/default.css_t +++ b/sphinx/themes/default/static/default.css_t @@ -147,7 +147,8 @@ div.sphinxsidebar input { font-size: 1em; } -/* -- body styles ----------------------------------------------------------- */ + +/* -- hyperlink styles ------------------------------------------------------ */ a { color: {{ theme_linkcolor }}; @@ -163,6 +164,20 @@ a:hover { text-decoration: underline; } +{% if theme_externalrefs %} +a.external { + text-decoration: none; + border-bottom: 1px dashed {{ theme_linkcolor }}; +} + +a.external:hover { + text-decoration: none; + border-bottom: none; +} +{% endif %} + +/* -- body styles ----------------------------------------------------------- */ + div.body h1, div.body h2, div.body h3, diff --git a/sphinx/themes/default/theme.conf b/sphinx/themes/default/theme.conf index 5035fae59..67eb0d66e 100644 --- a/sphinx/themes/default/theme.conf +++ b/sphinx/themes/default/theme.conf @@ -7,6 +7,8 @@ pygments_style = sphinx rightsidebar = false stickysidebar = false +externalrefs = false + footerbgcolor = #11303d footertextcolor = #ffffff sidebarbgcolor = #1c4e63 diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 04185436d..a4033947a 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -80,7 +80,7 @@ def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc): def make_refnode(builder, fromdocname, todocname, targetid, child, title=None): """Shortcut to create a reference node.""" - node = nodes.reference('', '') + node = nodes.reference('', '', internal=True) if fromdocname == todocname: node['refid'] = targetid else: diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 00496fa28..3fe1326a9 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -157,14 +157,28 @@ class HTMLTranslator(BaseTranslator): # overwritten def visit_reference(self, node): - BaseTranslator.visit_reference(self, node) - if node.hasattr('reftitle'): - # ugly hack to add a title attribute - starttag = self.body[-1] - if not starttag.startswith('' % posixpath.join( - self.builder.dlpath, node['filename'])) + self.body.append( + '' % + posixpath.join(self.builder.dlpath, node['filename'])) self.context.append('') else: self.context.append('') diff --git a/tests/root/markup.txt b/tests/root/markup.txt index 65156e7e6..7b68ba268 100644 --- a/tests/root/markup.txt +++ b/tests/root/markup.txt @@ -70,6 +70,8 @@ Body directives b +.. _admonition-section: + Admonitions ^^^^^^^^^^^ @@ -84,6 +86,8 @@ Admonitions Warning text. +.. _some-label: + .. tip:: Tip text. @@ -95,7 +99,7 @@ Inline markup * :command:`command` * :dfn:`dfn` -* :guilabel:`guilabel` +* :guilabel:`guilabel with &accelerator` * :kbd:`kbd` * :mailheader:`mailheader` * :makevar:`makevar` @@ -105,6 +109,7 @@ Inline markup * :program:`program` * :regexp:`regexp` * :menuselection:`File --> Close` +* :menuselection:`&File --> &Print` * :file:`a/{varpart}/b` * :samp:`print {i}` @@ -115,6 +120,8 @@ Inline markup * :envvar:`HOME` * :keyword:`with` * :token:`try statement ` +* :ref:`admonition-section` +* :ref:`here ` * :doc:`subdir/includes` * ``:download:`` is tested in includes.txt * :option:`Python -c option ` diff --git a/tests/test_build_html.py b/tests/test_build_html.py index bfa0f1cff..1593c8ffc 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -104,13 +104,20 @@ HTML_XPATH = { ".//li/tt/span[@class='pre']": '^a/$', ".//li/tt/em/span[@class='pre']": '^varpart$', ".//li/tt/em/span[@class='pre']": '^i$', - ".//a[@href='http://www.python.org/dev/peps/pep-0008']/strong": 'PEP 8', - ".//a[@href='http://tools.ietf.org/html/rfc1.html']/strong": 'RFC 1', - ".//a[@href='objects.html#envvar-HOME']/tt/span[@class='pre']": 'HOME', - ".//a[@href='#with']/tt/span[@class='pre']": '^with$', - ".//a[@href='#grammar-token-try_stmt']/tt/span": '^statement$', - ".//a[@href='subdir/includes.html']/em": 'Including in subdir', - ".//a[@href='objects.html#cmdoption-python-c']/em": 'Python -c option', + ".//a[@href='http://www.python.org/dev/peps/pep-0008']" + "[@class='pep reference external']/strong": 'PEP 8', + ".//a[@href='http://tools.ietf.org/html/rfc1.html']" + "[@class='rfc reference external']/strong": 'RFC 1', + ".//a[@href='objects.html#envvar-HOME']" + "[@class='reference internal']/tt/span[@class='pre']": 'HOME', + ".//a[@href='#with']" + "[@class='reference internal']/tt/span[@class='pre']": '^with$', + ".//a[@href='#grammar-token-try_stmt']" + "[@class='reference internal']/tt/span": '^statement$', + ".//a[@href='subdir/includes.html']" + "[@class='reference internal']/em": 'Including in subdir', + ".//a[@href='objects.html#cmdoption-python-c']" + "[@class='reference internal']/em": 'Python -c option', # abbreviations ".//abbr[@title='abbreviation']": '^abbr$', # version stuff @@ -139,7 +146,7 @@ HTML_XPATH = { 'objects.html': { ".//dt[@id='mod.Cls.meth1']": '', ".//dt[@id='errmod.Error']": '', - ".//a[@href='#mod.Cls']": '', + ".//a[@href='#mod.Cls'][@class='reference internal']": '', ".//dl[@class='userdesc']": '', ".//dt[@id='userdesc-myobj']": '', ".//a[@href='#userdesc-myobj']": '', @@ -168,7 +175,8 @@ HTML_XPATH = { ".//li[@class='toctree-l2']/a": 'Inline markup', ".//title": 'Sphinx ', ".//div[@class='footer']": 'Georg Brandl & Team', - ".//a[@href='http://python.org/']": '', + ".//a[@href='http://python.org/']" + "[@class='reference external']": '', ".//li/a[@href='genindex.html']/em": 'Index', ".//li/a[@href='py-modindex.html']/em": 'Module Index', ".//li/a[@href='search.html']/em": 'Search Page', diff --git a/tests/test_markup.py b/tests/test_markup.py index 0277d327e..fab43a287 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -97,6 +97,12 @@ def test_inline(): u'

a \N{TRIANGULAR BULLET} b

', '\\emph{a \\(\\rightarrow\\) b}') + # interpolation of ampersands in guilabel/menuselection + yield (verify, ':guilabel:`&Foo -&&- &Bar`', + u'

Foo ' + '-&- Bar

', + '\\emph{\\DUspan{accelerator}{F}oo -\\&- \\DUspan{accelerator}{B}ar}') + # non-interpolation of dashes in option role yield (verify_re, ':option:`--with-option`', '

--with-option

$',