Skip to content

Commit

Permalink
Merge pull request #3611 from fvicent/emphasize-lines
Browse files Browse the repository at this point in the history
Implement emphasize-lines directive (#3607)
  • Loading branch information
Kwpolska authored Apr 22, 2022
2 parents 14194fe + dcf471b commit 2389632
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ New in master
Features
--------

* Add ``emphasize_lines`` directive to code blocks (Issue #3607)
* Gallery index pages support the `status` flag (Issue #3598)
* Add ``start_at`` option to youtube directive (Issue #3603)

Expand Down
15 changes: 15 additions & 0 deletions docs/manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2694,6 +2694,21 @@ for ``code`` directive are provided: ``code-block`` and ``sourcecode``:
print("Our virtues and our failings are inseparable")
Certain lines might be highlighted via the ``emphasize-lines`` directive:

.. code:: restructuredtext
.. code-block:: python
:emphasize-lines: 3,5
def some_function():
interesting = False
print('This line is highlighted.')
print('This one is not...')
print('...but this one is.')
Line ranges are also supported, such as ``:emphasize-lines: 1-3,5-9,15``.

Listing
~~~~~~~

Expand Down
31 changes: 29 additions & 2 deletions nikola/plugins/compile/rest/listing.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ class CodeBlock(Directive):
'name': directives.unchanged,
'number-lines': directives.unchanged, # integer or None
'linenos': directives.unchanged,
'tab-width': directives.nonnegative_int}
'tab-width': directives.nonnegative_int,
'emphasize-lines': directives.unchanged_required}
has_content = True

def run(self):
Expand Down Expand Up @@ -103,7 +104,33 @@ def run(self):
else:
anchor_ref = 'rest_code_' + uuid.uuid4().hex

formatter = utils.NikolaPygmentsHTML(anchor_ref=anchor_ref, classes=classes, linenos=linenos, linenostart=linenostart)
linespec = self.options.get('emphasize-lines')
if linespec:
try:
nlines = len(self.content)
hl_lines = utils.parselinenos(linespec, nlines)
if any(i >= nlines for i in hl_lines):
raise self.error(
'line number spec is out of range(1-%d): %r' %
(nlines, self.options['emphasize-lines'])
)
hl_lines = [x + 1 for x in hl_lines if x < nlines]
except ValueError as err:
raise self.error(err)
else:
hl_lines = None

extra_kwargs = {}
if hl_lines is not None:
extra_kwargs['hl_lines'] = hl_lines

formatter = utils.NikolaPygmentsHTML(
anchor_ref=anchor_ref,
classes=classes,
linenos=linenos,
linenostart=linenostart,
**extra_kwargs
)
out = pygments.highlight(code, lexer, formatter)
node = nodes.raw('', out, format='html')

Expand Down
28 changes: 28 additions & 0 deletions nikola/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
from doit.cmdparse import CmdParse
from pkg_resources import resource_filename
from nikola.packages.pygments_better_html import BetterHtmlFormatter
from typing import List
from unidecode import unidecode

# Renames
Expand Down Expand Up @@ -2007,6 +2008,33 @@ def map_metadata(meta, key, config):
meta[meta_key] = hook(meta[meta_key])


def parselinenos(spec: str, total: int) -> List[int]:
"""Parse a line number spec.
Example: "1,2,4-6" -> [0, 1, 3, 4, 5]
"""
items = list()
parts = spec.split(',')
for part in parts:
try:
begend = part.strip().split('-')
if ['', ''] == begend:
raise ValueError
elif len(begend) == 1:
items.append(int(begend[0]) - 1)
elif len(begend) == 2:
start = int(begend[0] or 1) # left half open (cf. -10)
end = int(begend[1] or max(start, total)) # right half open (cf. 10-)
if start > end: # invalid range (cf. 10-1)
raise ValueError
items.extend(range(start - 1, end))
else:
raise ValueError
except Exception as exc:
raise ValueError('invalid line number spec: %r' % spec) from exc
return items


class ClassificationTranslationManager(object):
"""Keeps track of which classifications could be translated as which others.
Expand Down
20 changes: 20 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
get_translation_candidate,
write_metadata,
bool_from_meta,
parselinenos
)


Expand Down Expand Up @@ -619,3 +620,22 @@ class FakePost:

def __init__(self):
metadata_extractors.load_defaults(self, self.metadata_extractors_by)


def test_parselinenos():
assert parselinenos('1,2,3', 10) == [0, 1, 2]
assert parselinenos('4, 5, 6', 10) == [3, 4, 5]
assert parselinenos('-4', 10) == [0, 1, 2, 3]
assert parselinenos('7-9', 10) == [6, 7, 8]
assert parselinenos('7-', 10) == [6, 7, 8, 9]
assert parselinenos('1,7-', 10) == [0, 6, 7, 8, 9]
assert parselinenos('7-7', 10) == [6]
assert parselinenos('11-', 10) == [10]
with pytest.raises(ValueError):
parselinenos('1-2-3', 10)
with pytest.raises(ValueError):
parselinenos('abc-def', 10)
with pytest.raises(ValueError):
parselinenos('-', 10)
with pytest.raises(ValueError):
parselinenos('3-1', 10)

0 comments on commit 2389632

Please sign in to comment.