diff options
author | Matthew Peveler <matt.peveler@gmail.com> | 2021-10-28 11:59:03 -1000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-28 11:59:03 -1000 |
commit | 29197204241ae2b9fdf209b5af6898275c20264f (patch) | |
tree | 4529a898f12cc151ebb65cdae26b44e44844a825 | |
parent | 8e721eb8e7da622f6201b6c5937a5f33baac2cc6 (diff) | |
download | asciidoc-py3-29197204241ae2b9fdf209b5af6898275c20264f.tar.gz |
Fix handling escaped attributes inside macros (#206)
-rw-r--r-- | asciidoc/asciidoc.py | 24 | ||||
-rw-r--r-- | tests/test_asciidoc.py | 65 |
2 files changed, 79 insertions, 10 deletions
diff --git a/asciidoc/asciidoc.py b/asciidoc/asciidoc.py index 4c323f1..c5fc2b8 100644 --- a/asciidoc/asciidoc.py +++ b/asciidoc/asciidoc.py @@ -799,12 +799,12 @@ def subs_attrs(lines, dictionary=None): n = 0 result = start for c in text[start:]: - # Skip braces that are followed by a backslash. - if result == len(text) - 1 or text[result + 1] != '\\': - if c == '{': - n = n + 1 - elif c == '}': - n = n - 1 + if result == len(text): + break + if c == '{': + n = n + 1 + elif c == '}': + n = n - 1 result = result + 1 if n == 0: break @@ -872,6 +872,7 @@ def subs_attrs(lines, dictionary=None): for reo in [reo1, reo2]: pos = 0 while True: + line = line.replace('\\{', '{\\') mo = reo.search(line, pos) if not mo: break @@ -971,6 +972,7 @@ def subs_attrs(lines, dictionary=None): assert False, 'illegal attribute: %s' % attr s = str(s) line = line[:mo.start()] + s + line[end:] + line = line.replace('{\\', '\\{') pos = mo.start() + len(s) # Drop line if it contains unsubstituted {name} references. skipped = re.search(r'(?s)\{[^\\\W][-\w]*?\}(?!\\)', line) @@ -991,7 +993,7 @@ def subs_attrs(lines, dictionary=None): break expr = mo.group('expr') action = mo.group('action') - expr = expr.replace('{\\', '{') + expr = expr.replace('{\\', '\\{') expr = expr.replace('}\\', '}') s = system(action, expr, attrs=dictionary) if dictionary is not None and action in ('counter', 'counter2', 'set', 'set2'): @@ -1006,8 +1008,9 @@ def subs_attrs(lines, dictionary=None): if skipped: break if not skipped: - # Remove backslash from escaped entries. - line = line.replace('{\\', '{') + # Put back slash for leftmost curly brace for subsequent parses of + # escaped attributes. We don't need the escaped right curly braces though. + line = line.replace('{\\', '\\{') line = line.replace('}\\', '}') result.append(line) if string_result: @@ -4441,7 +4444,8 @@ class Writer: def write_line(self, line=None): if not (self.skip_blank_lines and (not line or not line.strip())): - self.f.write((line or '') + self.newline) + # Replace out any escaped attributes with non-escaped versions + self.f.write((re.sub(r'\\({[a-zA-Z0-9_][a-zA-Z0-9_\-]*)', '\\1', line) or '') + self.newline) self.lines_out = self.lines_out + 1 def write(self, *args, **kwargs): diff --git a/tests/test_asciidoc.py b/tests/test_asciidoc.py new file mode 100644 index 0000000..71d36fe --- /dev/null +++ b/tests/test_asciidoc.py @@ -0,0 +1,65 @@ +from asciidoc import asciidoc +import io +import pytest + + +@pytest.mark.parametrize( + "input,expected", + ( + ( + '{attach}file.txt', + '<div class="paragraph"><p></p></div>\r\n' + ), + ( + '\\{attach}file{0}.txt', + '<div class="paragraph"><p></p></div>\r\n' + ), + ( + '\\{attach}file.txt', + '<div class="paragraph"><p>{attach}file.txt</p></div>\r\n' + ), + ( + '\\{0}file.txt', + '<div class="paragraph"><p>{0}file.txt</p></div>\r\n' + ), + ( + 'link:{attach}file.txt[file]', + '<div class="paragraph"><p></p></div>\r\n' + ), + ( + 'link:\\{attach}file.txt[file]', + '<div class="paragraph"><p>' + + '<a href="{attach}file.txt">file</a></p></div>\r\n' + ), + ( + 'link:\\{attach}file\\{0}.txt[file\\{bar}too\\{1}]', + '<div class="paragraph"><p><a href="{attach}file{0}.txt">' + + 'file{bar}too{1}</a></p></div>\r\n' + ), + ( + 'image:\\{attach}file.jpg[]', + '<div class="paragraph"><p><span class="image">\r\n' + + '<img src="{attach}file.jpg" alt="{attach}file.jpg" />\r\n' + + '</span></p></div>\r\n' + ), + ( + 'image:\\{attach}file.jpg[foo]', + '<div class="paragraph"><p><span class="image">\r\n' + + '<img src="{attach}file.jpg" alt="foo" />\r\n</span></p></div>\r\n' + ), + ( + 'image:\\{attach}file.jpg[\\{bar}?]', + '<div class="paragraph"><p><span class="image">\r\n' + + '<img src="{attach}file.jpg" alt="{bar}?" />\r\n</span></p></div>\r\n' + ), + ) +) +def test_ignore_attribute(input, expected): + infile = io.StringIO(input) + outfile = io.StringIO() + options = [ + ('--out-file', outfile), + ('--no-header-footer', '') + ] + asciidoc.execute('asciidoc', options, [infile]) + assert outfile.getvalue() == expected |