Skip to content

Commit 40fed74

Browse files
committed
Rework role-with-double-backticks to avoid some false positives. closes #34.
1 parent 15cc0d0 commit 40fed74

File tree

3 files changed

+75
-26
lines changed

3 files changed

+75
-26
lines changed

sphinxlint.py

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,17 @@ def match_size(re_match):
215215
def clean_paragraph(paragraph):
216216
paragraph = escape2null(paragraph)
217217
while True:
218-
potential_inline_literal = min(inline_literal_re.finditer(paragraph, overlapped=True),key=match_size, default=None)
218+
potential_inline_literal = min(
219+
inline_literal_re.finditer(paragraph, overlapped=True),
220+
key=match_size,
221+
default=None,
222+
)
219223
if potential_inline_literal is None:
220224
break
221-
paragraph = paragraph[:potential_inline_literal.start()] + paragraph[potential_inline_literal.end():]
225+
paragraph = (
226+
paragraph[: potential_inline_literal.start()]
227+
+ paragraph[potential_inline_literal.end() :]
228+
)
222229
paragraph = normal_role_re.sub("", paragraph)
223230
return paragraph.replace("\x00", "\\")
224231

@@ -352,31 +359,40 @@ def inline_markup_gen(start_string, end_string):
352359
inline_markup_gen('**', '**') geneates a regex matching strong
353360
emphasis inline markup.
354361
"""
362+
ascii_allowed_before = r"""[-:/'"<(\[{]"""
363+
unicode_allowed_before = r"[\p{Ps}\p{Pi}\p{Pf}\p{Pd}\p{Po}]"
364+
ascii_allowed_after = r"""[-.,:;!?/'")\]}>]"""
365+
unicode_allowed_after = r"[\p{Pe}\p{Pi}\p{Pf}\p{Pd}\p{Po}]"
355366
return re.compile(
356-
r"""
357-
(?<!\x00) # Both inline markup start-string and end-string must not be preceded by an unescaped backslash
358-
(?<= # Inline markup start-strings must:
367+
fr"""
368+
(?<!\x00) # Both inline markup start-string and end-string must not be preceded by
369+
# an unescaped backslash
370+
371+
(?<= # Inline markup start-strings must:
359372
^| # start a text block
360373
\s| # or be immediately preceded by whitespace,
361-
[-:/'"<([{]| # one of the ASCII characters
362-
[\p{Ps}\p{Pi}\p{Pf}\p{Pd}\p{Po}] # or a similar non-ASCII punctuation character.
374+
{ascii_allowed_before}| # one of the ASCII characters
375+
{unicode_allowed_before} # or a similar non-ASCII punctuation character.
376+
)
377+
378+
(?P<inline_markup>
379+
{start_string} # Inline markup start
380+
\S # Inline markup start-strings must be immediately followed by
381+
# non-whitespace.
382+
# The inline markup end-string must be separated by at least one
383+
# character from the start-string.
384+
{QUOTE_PAIRS_NEGATIVE_LOOKBEHIND}
385+
.*?
386+
(?<=\S) # Inline markup end-strings must be immediately preceded by non-whitespace.
387+
{end_string} # Inline markup end
363388
)
364-
(?P<inline_markup>"""
365-
+ start_string
366-
+ r""" # Inline markup start
367-
\S # Inline markup start-strings must be immediately followed by non-whitespace.
368-
# The inline markup end-string must be separated by at least one character from the start-string.
369-
"""
370-
+ QUOTE_PAIRS_NEGATIVE_LOOKBEHIND
371-
+ ".*?"
372-
+ end_string
373-
+ r""") # Inline markup end
389+
374390
(?= # Inline markup end-strings must
375391
$| # end a text block or
376392
\s| # be immediately followed by whitespace,
377393
\x00|
378-
[-.,:;!?/'")\]}>]| # one of the ASCII characters
379-
[\p{Pe}\p{Pi}\p{Pf}\p{Pd}\p{Po}] # or a similar non-ASCII punctuation character.
394+
{ascii_allowed_after}| # one of the ASCII characters
395+
{unicode_allowed_after} # or a similar non-ASCII punctuation character.
380396
)
381397
""",
382398
flags=re.VERBOSE | re.DOTALL,
@@ -390,8 +406,7 @@ def inline_markup_gen(start_string, end_string):
390406
f":{SIMPLENAME}:{interpreted_text_re.pattern}", flags=re.VERBOSE | re.DOTALL
391407
)
392408
backtick_in_front_of_role = re.compile(
393-
rf"(^|\s)`:{SIMPLENAME}:{interpreted_text_re.pattern}",
394-
flags=re.VERBOSE|re.DOTALL
409+
rf"(^|\s)`:{SIMPLENAME}:{interpreted_text_re.pattern}", flags=re.VERBOSE | re.DOTALL
395410
)
396411

397412

@@ -525,12 +540,37 @@ def check_role_with_double_backticks(file, lines, options=None):
525540
526541
Bad: :fct:``sum``
527542
Good: :fct:`sum`
543+
544+
The hard thing is that :fct:``sum`` is a legitimate
545+
restructuredtext construction:
546+
547+
:fct: is just plain text.
548+
``sum`` is an inline literal.
549+
550+
So to properly detect this one we're searching for actual inline
551+
literals that have a role tag.
528552
"""
529-
for lno, line in enumerate(lines, start=1):
530-
if "`" not in line:
553+
for paragraph_lno, paragraph in paragraphs(lines):
554+
if "`" not in paragraph:
531555
continue
532-
if double_backtick_role.search(line):
533-
yield lno, "role use a single backtick, double backtick found."
556+
if paragraph.count("|") > 4:
557+
return # we don't handle tables yet.
558+
paragraph = escape2null(paragraph)
559+
while True:
560+
inline_literal = min(
561+
inline_literal_re.finditer(paragraph, overlapped=True),
562+
key=match_size,
563+
default=None,
564+
)
565+
if inline_literal is None:
566+
break
567+
before = paragraph[: inline_literal.start()]
568+
if re.search(ROLE_TAG + "$", before):
569+
error_offset = paragraph[: inline_literal.start()].count("\n")
570+
yield paragraph_lno + error_offset, "role use a single backtick, double backtick found."
571+
paragraph = (
572+
paragraph[: inline_literal.start()] + paragraph[inline_literal.end() :]
573+
)
534574

535575

536576
@checker(".rst")
@@ -661,7 +701,10 @@ def hide_non_rst_blocks(lines, hidden_block_cb=None):
661701
in_literal = len(re.match(" *", line).group(0))
662702
block_line_start = lineno
663703
assert not excluded_lines
664-
if re.match(r" *\.\. ", line) and type_of_explicit_markup(line) == "comment":
704+
if (
705+
re.match(r" *\.\. ", line)
706+
and type_of_explicit_markup(line) == "comment"
707+
):
665708
line = "\n"
666709
output.append(line)
667710
if excluded_lines and hidden_block_cb:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
This is a legit pip option: ``--only-binary :all:`` in an inline
2+
literal.
3+
4+
Here's the long version:
5+
``pip install --require-hashes --no-deps --only-binary :all:``.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Use a Sphinx ``:func:``, ``:meth:``, or ``:class:``.

0 commit comments

Comments
 (0)