Skip to content

Commit 4b09baa

Browse files
committed
LaTeX: fix style arg lacking, refactor to achieve full LaTeX support
1 parent 7fab81e commit 4b09baa

File tree

3 files changed

+67
-31
lines changed

3 files changed

+67
-31
lines changed

sphinx/builders/latex/__init__.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import os
66
import os.path
7-
import re
87
import warnings
98
from pathlib import Path
109
from typing import TYPE_CHECKING
@@ -281,9 +280,6 @@ def add_block_style(self, style: str) -> None:
281280
"""Add a styler to the tracker of highlighting styles."""
282281
if style not in self.specialized_highlighters:
283282
pb = highlighting.PygmentsBridge(dest='latex', stylename=style)
284-
pb.formatter_args['commandprefix'] = 'PYG' + re.sub(
285-
r'[^a-zA-Z]', 'Z', style
286-
)
287283
self.specialized_highlighters[style] = pb
288284

289285
def get_bridge_for_style(self, style: str) -> highlighting.PygmentsBridge | None:
@@ -303,10 +299,11 @@ def write_stylesheet(self) -> None:
303299
f.write('\\NeedsTeXFormat{LaTeX2e}[1995/12/01]\n')
304300
f.write(
305301
'\\ProvidesPackage{sphinxhighlight}'
306-
'[2022/06/30 stylesheet for highlighting with pygments]\n'
302+
'[2025/06/15 stylesheet for highlighting with pygments]\n'
307303
)
308304
f.write(
309-
'% Its contents depend on pygments_style configuration variable.\n\n'
305+
'% Its contents depend on pygments_style configuration variable.\n'
306+
'% And also on encountered code-blocks :style-light: options.\n\n'
310307
)
311308
f.write(highlighter.get_stylesheet())
312309
if self.specialized_highlighters:

sphinx/highlighting.py

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
from __future__ import annotations
44

5-
import re
65
from functools import partial
6+
from hashlib import md5
77
from importlib import import_module
88
from typing import TYPE_CHECKING
99

@@ -74,24 +74,26 @@
7474
% to fix problems for the 5.0.0 inline code highlighting (captions!).
7575
% The \text is from amstext, a dependency of sphinx.sty. It is here only
7676
% to avoid build errors if for some reason expansion is in math mode.
77-
\def\PYG{override}Zbs{{\text\textbackslash}}
78-
\def\PYG{override}Zus{{\_}}
79-
\def\PYG{override}Zob{{\{{}}
80-
\def\PYG{override}Zcb{{\}}}}
81-
\def\PYG{override}Zca{{\text\textasciicircum}}
82-
\def\PYG{override}Zam{{\&}}
83-
\def\PYG{override}Zlt{{\text\textless}}
84-
\def\PYG{override}Zgt{{\text\textgreater}}
85-
\def\PYG{override}Zsh{{\#}}
86-
\def\PYG{override}Zpc{{\%}}
87-
\def\PYG{override}Zdl{{\$}}
88-
\def\PYG{override}Zhy{{\sphinxhyphen}}% defined in sphinxlatexstyletext.sty
89-
\def\PYG{override}Zsq{{\text\textquotesingle}}
90-
\def\PYG{override}Zdq{{"}}
91-
\def\PYG{override}Zti{{\text\textasciitilde}}
77+
\def\PYGZbs{{\text\textbackslash}}
78+
\def\PYGZus{{\_}}
79+
\def\PYGZob{{\{{}}
80+
\def\PYGZcb{{\}}}}
81+
\def\PYGZca{{\text\textasciicircum}}
82+
\def\PYGZam{{\&}}
83+
\def\PYGZlt{{\text\textless}}
84+
\def\PYGZgt{{\text\textgreater}}
85+
\def\PYGZsh{{\#}}
86+
\def\PYGZpc{{\%}}
87+
\def\PYGZdl{{\$}}
88+
\def\PYGZhy{{\sphinxhyphen}}% defined in sphinxlatexstyletext.sty
89+
\def\PYGZsq{{\text\textquotesingle}}
90+
\def\PYGZdq{{"}}
91+
\def\PYGZti{{\text\textasciitilde}}
9292
\makeatletter
9393
% use \protected to allow syntax highlighting in captions
94-
\protected\def\PYG{override}#1#2{{\PYG{override}@reset\PYG{override}@toks#1+\relax+{{\PYG{override}@do{{#2}}}}}}
94+
\def\PYG@#1#2{{\PYG@reset\PYG@toks#1+\relax+{{\PYG@do{{#2}}}}}}
95+
\protected\def\PYG{\csname PYG\ifdefined\sphinxpygmentsstylename
96+
\sphinxpygmentsstylename\else @\fi\endcsname}
9597
\makeatother
9698
"""
9799

@@ -246,24 +248,37 @@ def get_stylesheet(self, selectors: list[int] | str | None = None) -> str:
246248
else:
247249
if selectors:
248250
if isinstance(selectors, str):
249-
tex_name = re.sub(r'[^a-zA-Z]', 'Z', selectors)
251+
_tex_name = md5(selectors.encode()).hexdigest()[:6] # noqa: S324
252+
for d, l in [
253+
('0', 'G'),
254+
('1', 'H'),
255+
('2', 'I'),
256+
('3', 'J'),
257+
('4', 'K'),
258+
('5', 'L'),
259+
('6', 'M'),
260+
('7', 'N'),
261+
('8', 'O'),
262+
('9', 'P'),
263+
]:
264+
_tex_name = _tex_name.replace(d, l)
250265
else:
251266
logger.error(
252267
__(
253-
'Encountered %s in selectors field; expected a string for the '
254-
'LaTeX formatter, using default values as fallback.\n'
255-
'Please report his error.'
268+
'Encountered %s in selectors field; expected a string '
269+
'for the LaTeX formatter. Please report this error.'
256270
),
257271
type(selectors),
258272
type='misc',
259273
subtype='highlighting_failure',
260274
)
261-
tex_name = ''
275+
# not using '' as we don't want \PYG being overwritten.
276+
_tex_name = 'INVALID'
262277
stylesheet = self.formatter(
263-
commandprefix='PYG' + tex_name
278+
style=selectors, commandprefix='PYG' + _tex_name
264279
).get_style_defs()
265-
sphinx_redefs = _LATEX_ADD_STYLES.format(override=tex_name)
280+
sphinx_redefs = ''
266281
else:
267282
stylesheet = formatter.get_style_defs()
268-
sphinx_redefs = _LATEX_ADD_STYLES.format(override='')
283+
sphinx_redefs = _LATEX_ADD_STYLES
269284
return stylesheet + sphinx_redefs

sphinx/writers/latex.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import re
1010
from collections import defaultdict
11+
from hashlib import md5
1112
from pathlib import Path
1213
from typing import TYPE_CHECKING, cast
1314

@@ -2258,6 +2259,7 @@ def visit_literal_block(self, node: Element) -> None:
22582259
opts = self.config.highlight_options.get(lang, {})
22592260

22602261
# As blocks are processed, we discover specified styles.
2262+
_texstylename = ''
22612263
if node.get('style-light'):
22622264
code_style = node.get('style-light')
22632265
self.builder.add_block_style(style=code_style)
@@ -2275,6 +2277,20 @@ def visit_literal_block(self, node: Element) -> None:
22752277
location=node,
22762278
**highlight_args,
22772279
)
2280+
_texstylename = md5(code_style.encode()).hexdigest()[:6] # noqa: S324
2281+
for d, l in [
2282+
('0', 'G'),
2283+
('1', 'H'),
2284+
('2', 'I'),
2285+
('3', 'J'),
2286+
('4', 'K'),
2287+
('5', 'L'),
2288+
('6', 'M'),
2289+
('7', 'N'),
2290+
('8', 'O'),
2291+
('9', 'P'),
2292+
]:
2293+
_texstylename = _texstylename.replace(d, l)
22782294
else:
22792295
hlcode = self.highlighter.highlight_block(
22802296
node.rawsource,
@@ -2284,6 +2300,10 @@ def visit_literal_block(self, node: Element) -> None:
22842300
location=node,
22852301
**highlight_args,
22862302
)
2303+
if _texstylename:
2304+
self.body.append(
2305+
CR + f'\\def\\sphinxpygmentsstylename{{{_texstylename}}}'
2306+
)
22872307
if self.in_footnote:
22882308
self.body.append(CR + r'\sphinxSetupCodeBlockInFootnote')
22892309
hlcode = hlcode.replace(r'\begin{Verbatim}', r'\begin{sphinxVerbatim}')
@@ -2300,6 +2320,8 @@ def visit_literal_block(self, node: Element) -> None:
23002320
# get consistent trailer
23012321
hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim}
23022322
if self.table and not self.in_footnote:
2323+
# TODO: probably add a % at end to avoid a space token if for a
2324+
# block with style-light option
23032325
hlcode += r'\end{sphinxVerbatimintable}'
23042326
else:
23052327
hlcode += r'\end{sphinxVerbatim}'
@@ -2310,6 +2332,8 @@ def visit_literal_block(self, node: Element) -> None:
23102332
self.body.append(CR + hlcode + CR)
23112333
if hllines:
23122334
self.body.append(r'\sphinxresetverbatimhllines' + CR)
2335+
if _texstylename:
2336+
self.body.append(r'\let\sphinxpygmentsstylename\undefined' + CR)
23132337
raise nodes.SkipNode
23142338

23152339
def depart_literal_block(self, node: Element) -> None:

0 commit comments

Comments
 (0)