Skip to content

Commit b9f88e6

Browse files
committed
added lineno-start directive, fix for #110
1 parent 635f948 commit b9f88e6

File tree

4 files changed

+67
-119
lines changed

4 files changed

+67
-119
lines changed

doc/source/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,6 @@ produces:
193193
print('B')
194194
print('C')
195195

196-
197196
To add *line numbers from a specific line* to the source code, use the
198197
``lineno-start`` directive::
199198

@@ -216,6 +215,7 @@ produces:
216215
You may also emphasize particular lines in the source code with ``:emphasize-lines:``::
217216

218217
.. jupyter-execute::
218+
:lineno-start: 2
219219
:emphasize-lines: 2,5-6
220220

221221
d = {
@@ -229,6 +229,7 @@ You may also emphasize particular lines in the source code with ``:emphasize-lin
229229
produces:
230230

231231
.. jupyter-execute::
232+
:lineno-start: 2
232233
:emphasize-lines: 2,5-6
233234

234235
d = {

jupyter_sphinx/ast.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ class JupyterCell(Directive):
4545
If provided, the code will be shown below the cell output.
4646
linenos : bool
4747
If provided, the code will be shown with line numbering.
48+
lineno-start: nonnegative int
49+
If provided, the code will be show with line numbering beginning from
50+
specified line.
4851
emphasize-lines : comma separated list of line numbers
4952
If provided, the specified lines will be highlighted.
5053
raises : comma separated list of exception types
@@ -70,6 +73,7 @@ class JupyterCell(Directive):
7073
"hide-output": directives.flag,
7174
"code-below": directives.flag,
7275
"linenos": directives.flag,
76+
"lineno-start": directives.nonnegative_int,
7377
"emphasize-lines": directives.unchanged_required,
7478
"raises": csv_option,
7579
"stderr": directives.flag,
@@ -78,6 +82,7 @@ class JupyterCell(Directive):
7882
def run(self):
7983
# This only works lazily because the logger is inited by Sphinx
8084
from . import logger
85+
8186
location = self.state_machine.get_source_and_line(self.lineno)
8287

8388
if self.arguments:
@@ -103,6 +108,7 @@ def run(self):
103108

104109
# The code fragment is taken from CodeBlock directive almost unchanged:
105110
# https://github.com/sphinx-doc/sphinx/blob/0319faf8f1503453b6ce19020819a8cf44e39f13/sphinx/directives/code.py#L134-L148
111+
106112
emphasize_linespec = self.options.get("emphasize-lines")
107113
if emphasize_linespec:
108114
try:
@@ -129,6 +135,7 @@ def run(self):
129135
hide_output=("hide-output" in self.options),
130136
code_below=("code-below" in self.options),
131137
linenos=("linenos" in self.options),
138+
linenostart=(self.options.get("lineno-start")),
132139
emphasize_lines=hl_lines,
133140
raises=self.options.get("raises"),
134141
stderr=("stderr" in self.options),

jupyter_sphinx/execute.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,20 +171,25 @@ def apply(self):
171171
source = node.children[0]
172172
source.attributes["language"] = lexer
173173

174-
# Add line numbers to code cells if jupyter_sphinx_linenos or
175-
# jupyter_sphinx_continue_linenos are set in the configuration,
176-
# or the linenos directive is set.
177-
# Update current line numbers from cell if jupyter_sphinx_continue_linenos
178-
# is set.
174+
# Add line numbering
175+
179176
linenostart = 1
177+
180178
for node in nodes:
181179
source = node.children[0]
182180
nlines = source.rawsource.count("\n") + 1
181+
show_numbering = (
182+
linenos_config or node["linenos"] or node["linenostart"]
183+
)
183184

184-
if linenos_config or continue_linenos or node["linenos"]:
185+
if show_numbering:
185186
source["linenos"] = True
186-
if continue_linenos:
187-
source["highlight_args"] = {"linenostart": linenostart}
187+
if node["linenostart"]:
188+
linenostart = node["linenostart"]
189+
if node["linenostart"] or continue_linenos:
190+
source["highlight_args"] = {"linenostart": linenostart}
191+
else:
192+
linenostart = 1
188193
linenostart += nlines
189194

190195
hl_lines = node["emphasize_lines"]
@@ -275,6 +280,7 @@ def setup(app):
275280
"""
276281
# To avoid circular imports we'll lazily import
277282
from . import setup as jssetup
283+
278284
js.logger.warning(
279285
(
280286
"`jupyter-sphinx` was initialized with the "

tests/test_execute.py

Lines changed: 44 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ def doctree():
2424
apps = []
2525
syspath = sys.path[:]
2626

27-
def doctree(source, config=None, return_warnings=False, entrypoint="jupyter_sphinx"):
27+
def doctree(
28+
source, config=None, return_warnings=False, entrypoint="jupyter_sphinx"
29+
):
2830
src_dir = tempfile.mkdtemp()
2931
source_trees.append(src_dir)
3032
with open(os.path.join(src_dir, "conf.py"), "w") as f:
@@ -54,123 +56,53 @@ def doctree(source, config=None, return_warnings=False, entrypoint="jupyter_sphi
5456
shutil.rmtree(tree)
5557

5658

57-
def test_basic(doctree):
59+
def test_continue_linenos_conf_option(doctree):
60+
# Test no linenumbering without linenos config or lineno-start directive
5861
source = """
5962
.. jupyter-execute::
6063
6164
2 + 2
62-
"""
63-
tree = doctree(source)
64-
cell, = tree.traverse(JupyterCellNode)
65-
assert cell.attributes["code_below"] is False
66-
assert cell.attributes["hide_code"] is False
67-
assert cell.attributes["hide_output"] is False
68-
assert cell.attributes["linenos"] is False
69-
assert cell.children[0].rawsource.strip() == "2 + 2"
70-
assert cell.children[1].rawsource.strip() == "4"
71-
72-
73-
def test_basic_old_entrypoint(doctree):
74-
source = """
75-
.. jupyter-execute::
7665
77-
2 + 2
7866
"""
79-
tree = doctree(source, entrypoint="jupyter_sphinx.execute")
80-
cell, = tree.traverse(JupyterCellNode)
81-
assert cell.attributes["code_below"] is False
82-
assert cell.attributes["hide_code"] is False
83-
assert cell.attributes["hide_output"] is False
84-
assert cell.attributes["linenos"] is False
85-
assert cell.children[0].rawsource.strip() == "2 + 2"
86-
assert cell.children[1].rawsource.strip() == "4"
8767

88-
89-
def test_hide_output(doctree):
90-
source = """
91-
.. jupyter-execute::
92-
:hide-output:
93-
94-
2 + 2
95-
"""
96-
tree = doctree(source)
97-
cell, = tree.traverse(JupyterCellNode)
98-
assert cell.attributes["hide_output"] is True
99-
assert len(cell.children) == 1
68+
tree = doctree(source, config="jupyter_sphinx_continue_linenos = True")
69+
(cell,) = tree.traverse(JupyterCellNode)
70+
assert "linenos" not in cell.children[0].attributes
10071
assert cell.children[0].rawsource.strip() == "2 + 2"
72+
assert cell.children[1].rawsource.strip() == "4"
10173

102-
103-
def test_hide_code(doctree):
104-
source = """
105-
.. jupyter-execute::
106-
:hide-code:
107-
108-
2 + 2
109-
"""
110-
tree = doctree(source)
111-
cell, = tree.traverse(JupyterCellNode)
112-
assert cell.attributes["hide_code"] is True
113-
assert len(cell.children) == 1
114-
assert cell.children[0].rawsource.strip() == "4"
115-
116-
117-
def test_code_below(doctree):
74+
# Test continuous line numbeirng
11875
source = """
11976
.. jupyter-execute::
120-
:code-below:
12177
12278
2 + 2
123-
"""
124-
tree = doctree(source)
125-
cell, = tree.traverse(JupyterCellNode)
126-
assert cell.attributes["code_below"] is True
127-
assert cell.children[0].rawsource.strip() == "4"
128-
assert cell.children[1].rawsource.strip() == "2 + 2"
12979
130-
131-
def test_linenos(doctree):
132-
source = """
13380
.. jupyter-execute::
134-
:linenos:
13581
136-
2 + 2
137-
"""
138-
tree = doctree(source)
139-
cell, = tree.traverse(JupyterCellNode)
140-
assert cell.attributes["linenos"] is True
141-
assert len(cell.children) == 2
142-
assert cell.children[0].rawsource.strip() == "2 + 2"
143-
assert cell.children[1].rawsource.strip() == "4"
144-
source = """
145-
.. jupyter-execute::
146-
:linenos:
147-
:code-below:
82+
3 + 3
14883
149-
2 + 2
15084
"""
151-
tree = doctree(source)
152-
cell, = tree.traverse(JupyterCellNode)
153-
assert len(cell.children) == 2
154-
assert cell.attributes["linenos"] is True
15585

86+
tree = doctree(
87+
source,
88+
config="jupyter_sphinx_linenos = True\n"
89+
"jupyter_sphinx_continue_linenos = True",
90+
)
15691

157-
def test_linenos_conf_option(doctree):
158-
source = """
159-
.. jupyter-execute::
160-
161-
2 + 2
162-
"""
163-
tree = doctree(source, config="jupyter_sphinx_linenos = True")
164-
cell, = tree.traverse(JupyterCellNode)
165-
assert cell.children[0].attributes["linenos"]
166-
assert "highlight_args" not in cell.children[0].attributes
167-
assert cell.children[0].rawsource.strip() == "2 + 2"
168-
assert cell.children[1].rawsource.strip() == "4"
92+
cell0, cell1 = tree.traverse(JupyterCellNode)
93+
assert cell0.children[0].attributes["linenos"]
94+
assert cell0.children[0].rawsource.strip() == "2 + 2"
95+
assert cell0.children[1].rawsource.strip() == "4"
16996

97+
assert cell1.children[0].attributes["linenos"]
98+
assert cell1.children[0].attributes["highlight_args"]["linenostart"] == 2
99+
assert cell1.children[0].rawsource.strip() == "3 + 3"
100+
assert cell1.children[1].rawsource.strip() == "6"
170101

171-
def test_continue_linenos_conf_option(doctree):
102+
# Line number should continue after lineno-start option
172103
source = """
173104
.. jupyter-execute::
105+
:lineno-start: 7
174106
175107
2 + 2
176108
@@ -179,16 +111,18 @@ def test_continue_linenos_conf_option(doctree):
179111
3 + 3
180112
181113
"""
182-
continue_linenos_config = "jupyter_sphinx_continue_linenos = True"
183-
tree = doctree(source, config=continue_linenos_config)
114+
tree = doctree(
115+
source,
116+
config="jupyter_sphinx_linenos = True\n"
117+
"jupyter_sphinx_continue_linenos = True",
118+
)
184119
cell0, cell1 = tree.traverse(JupyterCellNode)
185-
assert cell0.children[0].attributes["linenos"]
186-
assert cell0.children[0].attributes["highlight_args"]["linenostart"] == 1
120+
assert cell0.children[0].attributes["highlight_args"]["linenostart"] == 7
187121
assert cell0.children[0].rawsource.strip() == "2 + 2"
188122
assert cell0.children[1].rawsource.strip() == "4"
189123

190124
assert cell1.children[0].attributes["linenos"]
191-
assert cell1.children[0].attributes["highlight_args"]["linenostart"] == 2
125+
assert cell1.children[0].attributes["highlight_args"]["linenostart"] == 8
192126
assert cell1.children[0].rawsource.strip() == "3 + 3"
193127
assert cell1.children[1].rawsource.strip() == "6"
194128

@@ -272,7 +206,7 @@ def test_raises(doctree):
272206
raise ValueError()
273207
"""
274208
tree = doctree(source)
275-
cell, = tree.traverse(JupyterCellNode)
209+
(cell,) = tree.traverse(JupyterCellNode)
276210
assert "ValueError" in cell.children[1].rawsource
277211

278212
source = """
@@ -282,7 +216,7 @@ def test_raises(doctree):
282216
raise ValueError()
283217
"""
284218
tree = doctree(source)
285-
cell, = tree.traverse(JupyterCellNode)
219+
(cell,) = tree.traverse(JupyterCellNode)
286220
assert "ValueError" in cell.children[1].rawsource
287221

288222

@@ -306,8 +240,8 @@ def test_javascript(doctree):
306240
Javascript('window.alert("Hello world!")')
307241
"""
308242
tree = doctree(source)
309-
node, = list(tree.traverse(raw))
310-
text, = node.children
243+
(node,) = list(tree.traverse(raw))
244+
(text,) = node.children
311245
assert "world" in text
312246

313247

@@ -318,7 +252,7 @@ def test_stdout(doctree):
318252
print('hello world')
319253
"""
320254
tree = doctree(source)
321-
cell, = tree.traverse(JupyterCellNode)
255+
(cell,) = tree.traverse(JupyterCellNode)
322256
assert len(cell.children) == 2
323257
assert cell.children[1].rawsource.strip() == "hello world"
324258

@@ -333,7 +267,7 @@ def test_stderr(doctree):
333267

334268
tree, warnings = doctree(source, return_warnings=True)
335269
assert "hello world" in warnings
336-
cell, = tree.traverse(JupyterCellNode)
270+
(cell,) = tree.traverse(JupyterCellNode)
337271
assert len(cell.children) == 1 # no output
338272

339273
source = """
@@ -344,7 +278,7 @@ def test_stderr(doctree):
344278
print('hello world', file=sys.stderr)
345279
"""
346280
tree = doctree(source)
347-
cell, = tree.traverse(JupyterCellNode)
281+
(cell,) = tree.traverse(JupyterCellNode)
348282
assert len(cell.children) == 2
349283
assert "stderr" in cell.children[1].attributes["classes"]
350284
assert cell.children[1].astext().strip() == "hello world"
@@ -361,7 +295,7 @@ def test_thebe_hide_output(doctree):
361295
2 + 2
362296
"""
363297
tree = doctree(source, thebe_config)
364-
cell, = tree.traverse(JupyterCellNode)
298+
(cell,) = tree.traverse(JupyterCellNode)
365299
assert cell.attributes["hide_output"] is True
366300
assert len(cell.children) == 1
367301

@@ -379,7 +313,7 @@ def test_thebe_hide_code(doctree):
379313
2 + 2
380314
"""
381315
tree = doctree(source, thebe_config)
382-
cell, = tree.traverse(JupyterCellNode)
316+
(cell,) = tree.traverse(JupyterCellNode)
383317
assert cell.attributes["hide_code"] is True
384318
assert len(cell.children) == 2
385319

@@ -403,7 +337,7 @@ def test_thebe_code_below(doctree):
403337
2 + 2
404338
"""
405339
tree = doctree(source, thebe_config)
406-
cell, = tree.traverse(JupyterCellNode)
340+
(cell,) = tree.traverse(JupyterCellNode)
407341
assert cell.attributes["code_below"] is True
408342

409343
output = cell.children[0]
@@ -461,5 +395,5 @@ def test_latex(doctree):
461395

462396
for start, end in delimiter_pairs:
463397
tree = doctree(source.format(start, end))
464-
cell, = tree.traverse(JupyterCellNode)
398+
(cell,) = tree.traverse(JupyterCellNode)
465399
assert cell.children[1].astext() == r"\int"

0 commit comments

Comments
 (0)