Skip to content

Commit d58ef88

Browse files
authored
Merge pull request #95 from espdev/emphasize-lines
Add emphasize-lines option
2 parents b14e20a + bd7ae35 commit d58ef88

File tree

3 files changed

+86
-7
lines changed

3 files changed

+86
-7
lines changed

doc/source/index.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,32 @@ produces:
193193
print('B')
194194
print('C')
195195

196+
You may also emphasize particular lines in the source code with ``:emphasize-lines:``::
197+
198+
.. jupyter-execute::
199+
:emphasize-lines: 2,5-6
200+
201+
d = {
202+
'a': 1,
203+
'b': 2,
204+
'c': 3,
205+
'd': 4,
206+
'e': 5,
207+
}
208+
209+
produces:
210+
211+
.. jupyter-execute::
212+
:emphasize-lines: 2,5-6
213+
214+
d = {
215+
'a': 1,
216+
'b': 2,
217+
'c': 3,
218+
'd': 4,
219+
'e': 5,
220+
}
221+
196222
Controlling exceptions
197223
----------------------
198224

jupyter_sphinx/execute.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22

33
import os
44
from itertools import groupby, count
5-
from operator import itemgetter
65
import json
76

8-
import sphinx
9-
from sphinx.util import logging
7+
from sphinx.util import logging, parselinenos
108
from sphinx.util.fileutil import copy_asset
119
from sphinx.transforms import SphinxTransform
1210
from sphinx.errors import ExtensionError
@@ -26,7 +24,6 @@
2624

2725
import nbformat
2826

29-
from ipywidgets import Widget
3027
import ipywidgets.embed
3128

3229
from ._version import __version__
@@ -136,6 +133,8 @@ class JupyterCell(Directive):
136133
If provided, the code will be shown below the cell output.
137134
linenos : bool
138135
If provided, the code will be shown with line numbering.
136+
emphasize-lines : comma separated list of line numbers
137+
If provided, the specified lines will be highlighted.
139138
raises : comma separated list of exception types
140139
If provided, a comma-separated list of exception type names that
141140
the cell may raise. If one of the listed execption types is raised
@@ -159,11 +158,14 @@ class JupyterCell(Directive):
159158
'hide-output': directives.flag,
160159
'code-below': directives.flag,
161160
'linenos': directives.flag,
161+
'emphasize-lines': directives.unchanged_required,
162162
'raises': csv_option,
163163
'stderr': directives.flag,
164164
}
165165

166166
def run(self):
167+
location = self.state_machine.get_source_and_line(self.lineno)
168+
167169
if self.arguments:
168170
# As per 'sphinx.directives.code.LiteralInclude'
169171
env = self.state.document.settings.env
@@ -172,7 +174,7 @@ def run(self):
172174
if self.content:
173175
logger.warning(
174176
'Ignoring inline code in Jupyter cell included from "{}"'
175-
.format(rel_filename)
177+
.format(rel_filename), location=location
176178
)
177179
try:
178180
with open(filename) as f:
@@ -185,6 +187,23 @@ def run(self):
185187
self.assert_has_content()
186188
content = self.content
187189

190+
# The code fragment is taken from CodeBlock directive almost unchanged:
191+
# https://github.com/sphinx-doc/sphinx/blob/0319faf8f1503453b6ce19020819a8cf44e39f13/sphinx/directives/code.py#L134-L148
192+
emphasize_linespec = self.options.get('emphasize-lines')
193+
if emphasize_linespec:
194+
try:
195+
nlines = len(content)
196+
hl_lines = parselinenos(emphasize_linespec, nlines)
197+
if any(i >= nlines for i in hl_lines):
198+
logger.warning(
199+
'Line number spec is out of range(1-{}): {}'.format(
200+
nlines, emphasize_linespec), location=location)
201+
hl_lines = [i + 1 for i in hl_lines if i < nlines]
202+
except ValueError as err:
203+
return [self.state.document.reporter.warning(err, line=self.lineno)]
204+
else:
205+
hl_lines = []
206+
188207
return [JupyterCellNode(
189208
'',
190209
docutils.nodes.literal_block(
@@ -194,6 +213,7 @@ def run(self):
194213
hide_output=('hide-output' in self.options),
195214
code_below=('code-below' in self.options),
196215
linenos=('linenos' in self.options),
216+
emphasize_lines=hl_lines,
197217
raises=self.options.get('raises'),
198218
stderr=('stderr' in self.options),
199219
)]
@@ -408,12 +428,18 @@ def apply(self):
408428
linenostart = 1
409429
for node in nodes:
410430
source = node.children[0]
431+
nlines = source.rawsource.count("\n") + 1
432+
411433
if linenos_config or continue_linenos or node["linenos"]:
412434
source["linenos"] = True
413435
if continue_linenos:
414-
source["highlight_args"] = {'linenostart': linenostart}
415-
linenostart += source.rawsource.count("\n") + 1
436+
source['highlight_args'] = {'linenostart': linenostart}
437+
linenostart += nlines
416438

439+
hl_lines = node['emphasize_lines']
440+
if hl_lines:
441+
highlight_args = source.setdefault('highlight_args', {})
442+
highlight_args['hl_lines'] = hl_lines
417443

418444
# Add code cell CSS class
419445
for node in nodes:

tests/test_execute.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,33 @@ def test_continue_linenos_conf_option(doctree):
180180
assert cell1.children[1].rawsource.strip() == "6"
181181

182182

183+
def test_emphasize_lines(doctree):
184+
source = '''
185+
.. jupyter-execute::
186+
:emphasize-lines: 1,3-5
187+
188+
1 + 1
189+
2 + 2
190+
3 + 3
191+
4 + 4
192+
5 + 5
193+
194+
.. jupyter-execute::
195+
:emphasize-lines: 2, 4
196+
197+
1 + 1
198+
2 + 2
199+
3 + 3
200+
4 + 4
201+
5 + 5
202+
'''
203+
tree = doctree(source)
204+
cell0, cell1 = tree.traverse(JupyterCellNode)
205+
206+
assert cell0.attributes['emphasize_lines'] == [1, 3, 4, 5]
207+
assert cell1.attributes['emphasize_lines'] == [2, 4]
208+
209+
183210
def test_execution_environment_carries_over(doctree):
184211
source = '''
185212
.. jupyter-execute::

0 commit comments

Comments
 (0)