Skip to content

Commit 35ecd1f

Browse files
authored
Merge pull request #85 from OpenNTI/issue81
Add the ability to specify a :class: option to set CSS classes on programoutput nodes.
2 parents 2dfa468 + 04dc07f commit 35ecd1f

3 files changed

Lines changed: 45 additions & 1 deletion

File tree

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- Add an ``ansi`` extra to install ``erbsland-sphinx-ansi``. Version
1010
1.2.4 or later is required.
1111
- Explicitly list ``docutils`` as a dependency.
12+
- Add the ability to specify a ``:class:`` argument to set CSS classes
13+
on programoutput nodes.
1214

1315

1416
0.19 (2026-02-20)

src/sphinxcontrib/programoutput/__init__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class ProgramOutputDirective(rst.Directive):
128128
ellipsis=_slice, extraargs=unchanged,
129129
returncode=nonnegative_int, cwd=unchanged,
130130
caption=unchanged, name=unchanged,
131-
language=unchanged)
131+
language=unchanged, **{'class': unchanged})
132132

133133
def run(self):
134134
env = self.state.document.settings.env
@@ -151,9 +151,16 @@ def run(self):
151151
node['language'] = self.options.get('language', 'text')
152152
if 'ellipsis' in self.options:
153153
node['strip_lines'] = self.options['ellipsis']
154+
155+
classes = self.options.get('class', '').split() if 'class' in self.options else []
156+
if classes:
157+
node['classes'] = classes
158+
154159
if 'caption' in self.options:
155160
caption = self.options['caption'] or self.arguments[0]
156161
node = _container_wrapper(self, node, caption)
162+
if classes:
163+
node['classes'].extend(classes)
157164

158165
self.add_name(node)
159166
return [node]
@@ -347,6 +354,8 @@ def run_programs(app, doctree):
347354
output, app.config.programoutput_use_ansi, app
348355
)
349356
new_node['language'] = node['language']
357+
if 'classes' in node:
358+
new_node['classes'].extend(node['classes'])
350359
node.replace_self(new_node)
351360

352361

src/sphinxcontrib/programoutput/tests/test_directive.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,39 @@ def test_use_ansi_enabled_extension(self):
482482
'\x1b[31mspam\x1b[0m'
483483
)
484484

485+
@with_content("""\
486+
.. program-output:: echo spam
487+
:class: myclass""")
488+
def test_class(self):
489+
literal = self.doctree.next_node(literal_block)
490+
self.assertTrue(literal)
491+
self.assertIn('myclass', literal.get('classes'))
492+
self.assert_output(self.doctree, 'spam')
493+
self.assert_cache(self.app, 'echo spam', 'spam')
494+
495+
@with_content("""\
496+
.. program-output:: echo spam
497+
:class: myclass anotherclass""")
498+
def test_multiple_classes(self):
499+
literal = self.doctree.next_node(literal_block)
500+
self.assertTrue(literal)
501+
classes = literal.get('classes')
502+
self.assertIn('myclass', classes)
503+
self.assertIn('anotherclass', classes)
504+
self.assert_output(self.doctree, 'spam')
505+
self.assert_cache(self.app, 'echo spam', 'spam')
506+
507+
@with_content("""\
508+
.. program-output:: echo spam
509+
:class: myclass
510+
:caption: mycaption""")
511+
def test_class_with_caption(self):
512+
container_node = self.doctree.next_node(container)
513+
self.assertTrue(container_node)
514+
self.assertIn('myclass', container_node.get('classes'))
515+
self.assert_output(self.doctree, 'spam', caption='mycaption')
516+
self.assert_cache(self.app, 'echo spam', 'spam')
517+
485518
def test_suite():
486519
return unittest.defaultTestLoader.loadTestsFromName(__name__)
487520

0 commit comments

Comments
 (0)