2
2
3
3
import os
4
4
from itertools import groupby , count
5
- from operator import itemgetter
6
5
import json
7
6
8
- import sphinx
9
- from sphinx .util import logging
7
+ from sphinx .util import logging , parselinenos
10
8
from sphinx .util .fileutil import copy_asset
11
9
from sphinx .transforms import SphinxTransform
12
10
from sphinx .errors import ExtensionError
26
24
27
25
import nbformat
28
26
29
- from ipywidgets import Widget
30
27
import ipywidgets .embed
31
28
32
29
from ._version import __version__
@@ -136,6 +133,8 @@ class JupyterCell(Directive):
136
133
If provided, the code will be shown below the cell output.
137
134
linenos : bool
138
135
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.
139
138
raises : comma separated list of exception types
140
139
If provided, a comma-separated list of exception type names that
141
140
the cell may raise. If one of the listed execption types is raised
@@ -159,11 +158,14 @@ class JupyterCell(Directive):
159
158
'hide-output' : directives .flag ,
160
159
'code-below' : directives .flag ,
161
160
'linenos' : directives .flag ,
161
+ 'emphasize-lines' : directives .unchanged_required ,
162
162
'raises' : csv_option ,
163
163
'stderr' : directives .flag ,
164
164
}
165
165
166
166
def run (self ):
167
+ location = self .state_machine .get_source_and_line (self .lineno )
168
+
167
169
if self .arguments :
168
170
# As per 'sphinx.directives.code.LiteralInclude'
169
171
env = self .state .document .settings .env
@@ -172,7 +174,7 @@ def run(self):
172
174
if self .content :
173
175
logger .warning (
174
176
'Ignoring inline code in Jupyter cell included from "{}"'
175
- .format (rel_filename )
177
+ .format (rel_filename ), location = location
176
178
)
177
179
try :
178
180
with open (filename ) as f :
@@ -185,6 +187,23 @@ def run(self):
185
187
self .assert_has_content ()
186
188
content = self .content
187
189
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
+
188
207
return [JupyterCellNode (
189
208
'' ,
190
209
docutils .nodes .literal_block (
@@ -194,6 +213,7 @@ def run(self):
194
213
hide_output = ('hide-output' in self .options ),
195
214
code_below = ('code-below' in self .options ),
196
215
linenos = ('linenos' in self .options ),
216
+ emphasize_lines = hl_lines ,
197
217
raises = self .options .get ('raises' ),
198
218
stderr = ('stderr' in self .options ),
199
219
)]
@@ -408,12 +428,18 @@ def apply(self):
408
428
linenostart = 1
409
429
for node in nodes :
410
430
source = node .children [0 ]
431
+ nlines = source .rawsource .count ("\n " ) + 1
432
+
411
433
if linenos_config or continue_linenos or node ["linenos" ]:
412
434
source ["linenos" ] = True
413
435
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
416
438
439
+ hl_lines = node ['emphasize_lines' ]
440
+ if hl_lines :
441
+ highlight_args = source .setdefault ('highlight_args' , {})
442
+ highlight_args ['hl_lines' ] = hl_lines
417
443
418
444
# Add code cell CSS class
419
445
for node in nodes :
0 commit comments