mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix #789: `:samp:
` role supports to escape curly braces with backslash
This commit is contained in:
parent
93837bb01e
commit
622119a9d4
1
CHANGES
1
CHANGES
@ -23,6 +23,7 @@ Incompatible changes
|
||||
:py:meth:`.Sphinx.add_transform()`
|
||||
* #4827: All ``substitution_definition`` nodes are removed from doctree on
|
||||
reading phase
|
||||
* #789: ``:samp:`` role supports to escape curly braces with backslash
|
||||
|
||||
Deprecated
|
||||
----------
|
||||
|
@ -406,6 +406,9 @@ different style:
|
||||
If you don't need the "variable part" indication, use the standard
|
||||
````code```` instead.
|
||||
|
||||
.. versionchanged:: 1.8
|
||||
Allowed to escape curly braces with backslash
|
||||
|
||||
There is also an :rst:role:`index` role to generate index entries.
|
||||
|
||||
The following roles generate external links:
|
||||
|
@ -284,6 +284,7 @@ def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
|
||||
|
||||
_litvar_re = re.compile('{([^}]+)}')
|
||||
parens_re = re.compile(r'(\\*{|\\*})')
|
||||
|
||||
|
||||
def emph_literal_role(typ, rawtext, text, lineno, inliner,
|
||||
@ -296,17 +297,43 @@ def emph_literal_role(typ, rawtext, text, lineno, inliner,
|
||||
else:
|
||||
typ = typ.lower()
|
||||
|
||||
text = utils.unescape(text)
|
||||
pos = 0
|
||||
retnode = nodes.literal(role=typ.lower(), classes=[typ])
|
||||
for m in _litvar_re.finditer(text): # type: ignore
|
||||
if m.start() > pos:
|
||||
txt = text[pos:m.start()]
|
||||
retnode += nodes.Text(txt, txt)
|
||||
retnode += nodes.emphasis(m.group(1), m.group(1))
|
||||
pos = m.end()
|
||||
if pos < len(text):
|
||||
retnode += nodes.Text(text[pos:], text[pos:])
|
||||
parts = list(parens_re.split(utils.unescape(text)))
|
||||
stack = ['']
|
||||
for part in parts:
|
||||
matched = parens_re.match(part)
|
||||
if matched:
|
||||
backslashes = len(part) - 1
|
||||
if backslashes % 2 == 1: # escaped
|
||||
stack[-1] += "\\" * int((backslashes - 1) / 2) + part[-1]
|
||||
elif part[-1] == '{': # rparen
|
||||
stack[-1] += "\\" * int(backslashes / 2)
|
||||
if len(stack) >= 2 and stack[-2] == "{":
|
||||
# nested
|
||||
stack[-1] += "{"
|
||||
else:
|
||||
# start emphasis
|
||||
stack.append('{')
|
||||
stack.append('')
|
||||
else: # lparen
|
||||
stack[-1] += "\\" * int(backslashes / 2)
|
||||
if len(stack) == 3 and stack[1] == "{" and len(stack[2]) > 0:
|
||||
# emphasized word found
|
||||
if stack[0]:
|
||||
retnode += nodes.Text(stack[0], stack[0])
|
||||
retnode += nodes.emphasis(stack[2], stack[2])
|
||||
stack = ['']
|
||||
else:
|
||||
# emphasized word not found; the rparen is not a special symbol
|
||||
stack.append('}')
|
||||
stack = [''.join(stack)]
|
||||
else:
|
||||
stack[-1] += part
|
||||
if ''.join(stack):
|
||||
# remaining is treated as Text
|
||||
text = ''.join(stack)
|
||||
retnode += nodes.Text(text, text)
|
||||
|
||||
return [retnode], []
|
||||
|
||||
|
||||
|
81
tests/test_roles.py
Normal file
81
tests/test_roles.py
Normal file
@ -0,0 +1,81 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_roles
|
||||
~~~~~~~~~~
|
||||
|
||||
Test sphinx.roles
|
||||
|
||||
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from docutils import nodes
|
||||
from mock import Mock
|
||||
|
||||
from sphinx.roles import emph_literal_role
|
||||
from sphinx.testing.util import assert_node
|
||||
|
||||
|
||||
def test_samp():
|
||||
# normal case
|
||||
text = 'print 1+{variable}'
|
||||
ret, msg = emph_literal_role('samp', text, text, 0, Mock())
|
||||
assert_node(ret, ([nodes.literal, ("print 1+",
|
||||
[nodes.emphasis, "variable"])],))
|
||||
assert msg == []
|
||||
|
||||
# two emphasis items
|
||||
text = 'print {1}+{variable}'
|
||||
ret, msg = emph_literal_role('samp', text, text, 0, Mock())
|
||||
assert_node(ret, ([nodes.literal, ("print ",
|
||||
[nodes.emphasis, "1"],
|
||||
"+",
|
||||
[nodes.emphasis, "variable"])],))
|
||||
assert msg == []
|
||||
|
||||
# empty curly brace
|
||||
text = 'print 1+{}'
|
||||
ret, msg = emph_literal_role('samp', text, text, 0, Mock())
|
||||
assert_node(ret, ([nodes.literal, "print 1+{}"],))
|
||||
assert msg == []
|
||||
|
||||
# half-opened variable
|
||||
text = 'print 1+{variable'
|
||||
ret, msg = emph_literal_role('samp', text, text, 0, Mock())
|
||||
assert_node(ret, ([nodes.literal, "print 1+{variable"],))
|
||||
assert msg == []
|
||||
|
||||
# nested
|
||||
text = 'print 1+{{variable}}'
|
||||
ret, msg = emph_literal_role('samp', text, text, 0, Mock())
|
||||
assert_node(ret, ([nodes.literal, ("print 1+",
|
||||
[nodes.emphasis, "{variable"],
|
||||
"}")],))
|
||||
assert msg == []
|
||||
|
||||
# emphasized item only
|
||||
text = '{variable}'
|
||||
ret, msg = emph_literal_role('samp', text, text, 0, Mock())
|
||||
assert_node(ret, ([nodes.literal, nodes.emphasis, "variable"],))
|
||||
assert msg == []
|
||||
|
||||
# escaping
|
||||
text = r'print 1+\{variable}'
|
||||
ret, msg = emph_literal_role('samp', text, text, 0, Mock())
|
||||
assert_node(ret, ([nodes.literal, "print 1+{variable}"],))
|
||||
assert msg == []
|
||||
|
||||
# escaping (2)
|
||||
text = r'print 1+\{{variable}\}'
|
||||
ret, msg = emph_literal_role('samp', text, text, 0, Mock())
|
||||
assert_node(ret, ([nodes.literal, ("print 1+{",
|
||||
[nodes.emphasis, "variable"],
|
||||
"}")],))
|
||||
assert msg == []
|
||||
|
||||
# escape a backslash
|
||||
text = r'print 1+\\{variable}'
|
||||
ret, msg = emph_literal_role('samp', text, text, 0, Mock())
|
||||
assert_node(ret, ([nodes.literal, ("print 1+\\",
|
||||
[nodes.emphasis, "variable"])],))
|
||||
assert msg == []
|
Loading…
Reference in New Issue
Block a user