🌐 AI搜索 & 代理 主页
Skip to content

Commit f6fe650

Browse files
DOC: Dynamically resize mathtext symbol tables (#26143)
1 parent de229cd commit f6fe650

File tree

1 file changed

+93
-143
lines changed

1 file changed

+93
-143
lines changed

doc/sphinxext/math_symbol_table.py

Lines changed: 93 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,152 +1,102 @@
1-
import re
2-
from docutils.parsers.rst import Directive
1+
"""
2+
Sphinx extension that generates the math symbol table documentation
3+
for Matplotlib.
4+
"""
35

4-
from matplotlib import _mathtext, _mathtext_data
6+
from __future__ import annotations
7+
from pathlib import Path
8+
from sphinx.util.docutils import SphinxDirective
59

6-
bb_pattern = re.compile("Bbb[A-Z]")
7-
scr_pattern = re.compile("scr[a-zA-Z]")
8-
frak_pattern = re.compile("frak[A-Z]")
10+
# This import ensures access to Matplotlib’s mathtext internals
11+
from matplotlib import _mathtext
912

10-
symbols = [
11-
["Lower-case Greek",
12-
4,
13-
(r"\alpha", r"\beta", r"\gamma", r"\chi", r"\delta", r"\epsilon",
14-
r"\eta", r"\iota", r"\kappa", r"\lambda", r"\mu", r"\nu", r"\omega",
15-
r"\phi", r"\pi", r"\psi", r"\rho", r"\sigma", r"\tau", r"\theta",
16-
r"\upsilon", r"\xi", r"\zeta", r"\digamma", r"\varepsilon", r"\varkappa",
17-
r"\varphi", r"\varpi", r"\varrho", r"\varsigma", r"\vartheta")],
18-
["Upper-case Greek",
19-
4,
20-
(r"\Delta", r"\Gamma", r"\Lambda", r"\Omega", r"\Phi", r"\Pi", r"\Psi",
21-
r"\Sigma", r"\Theta", r"\Upsilon", r"\Xi")],
22-
["Hebrew",
23-
6,
24-
(r"\aleph", r"\beth", r"\gimel", r"\daleth")],
25-
["Latin named characters",
26-
6,
27-
r"""\aa \AA \ae \AE \oe \OE \O \o \thorn \Thorn \ss \eth \dh \DH""".split()],
28-
["Delimiters",
29-
5,
30-
_mathtext.Parser._delims],
31-
["Big symbols",
32-
5,
33-
_mathtext.Parser._overunder_symbols | _mathtext.Parser._dropsub_symbols],
34-
["Standard function names",
35-
5,
36-
{fr"\{fn}" for fn in _mathtext.Parser._function_names}],
37-
["Binary operation symbols",
38-
4,
39-
_mathtext.Parser._binary_operators],
40-
["Relation symbols",
41-
4,
42-
_mathtext.Parser._relation_symbols],
43-
["Arrow symbols",
44-
4,
45-
_mathtext.Parser._arrow_symbols],
46-
["Dot symbols",
47-
4,
48-
r"""\cdots \vdots \ldots \ddots \adots \Colon \therefore \because""".split()],
49-
["Black-board characters",
50-
6,
51-
[fr"\{symbol}" for symbol in _mathtext_data.tex2uni
52-
if re.match(bb_pattern, symbol)]],
53-
["Script characters",
54-
6,
55-
[fr"\{symbol}" for symbol in _mathtext_data.tex2uni
56-
if re.match(scr_pattern, symbol)]],
57-
["Fraktur characters",
58-
6,
59-
[fr"\{symbol}" for symbol in _mathtext_data.tex2uni
60-
if re.match(frak_pattern, symbol)]],
61-
["Miscellaneous symbols",
62-
4,
63-
r"""\neg \infty \forall \wp \exists \bigstar \angle \partial
64-
\nexists \measuredangle \emptyset \sphericalangle \clubsuit
65-
\varnothing \complement \diamondsuit \imath \Finv \triangledown
66-
\heartsuit \jmath \Game \spadesuit \ell \hbar \vartriangle
67-
\hslash \blacksquare \blacktriangle \sharp \increment
68-
\prime \blacktriangledown \Im \flat \backprime \Re \natural
69-
\circledS \P \copyright \circledR \S \yen \checkmark \$
70-
\cent \triangle \QED \sinewave \dag \ddag \perthousand \ac
71-
\lambdabar \L \l \degree \danger \maltese \clubsuitopen
72-
\i \hermitmatrix \sterling \nabla \mho""".split()],
73-
]
7413

75-
76-
def run(state_machine):
77-
78-
def render_symbol(sym, ignore_variant=False):
79-
if ignore_variant and sym not in (r"\varnothing", r"\varlrtriangle"):
80-
sym = sym.replace(r"\var", "\\")
81-
if sym.startswith("\\"):
82-
sym = sym.lstrip("\\")
83-
if sym not in (_mathtext.Parser._overunder_functions |
84-
_mathtext.Parser._function_names):
85-
sym = chr(_mathtext_data.tex2uni[sym])
86-
return f'\\{sym}' if sym in ('\\', '|', '+', '-', '*') else sym
87-
88-
lines = []
89-
for category, columns, syms in symbols:
90-
syms = sorted(syms,
91-
# Sort by Unicode and place variants immediately
92-
# after standard versions.
93-
key=lambda sym: (render_symbol(sym, ignore_variant=True),
94-
sym.startswith(r"\var")),
95-
reverse=(category == "Hebrew")) # Hebrew is rtl
96-
rendered_syms = [f"{render_symbol(sym)} ``{sym}``" for sym in syms]
97-
columns = min(columns, len(syms))
98-
lines.append("**%s**" % category)
99-
lines.append('')
100-
max_width = max(map(len, rendered_syms))
101-
header = (('=' * max_width) + ' ') * columns
102-
lines.append(header.rstrip())
103-
for part in range(0, len(rendered_syms), columns):
104-
row = " ".join(
105-
sym.rjust(max_width) for sym in rendered_syms[part:part + columns])
106-
lines.append(row)
107-
lines.append(header.rstrip())
108-
lines.append('')
109-
110-
state_machine.insert_input(lines, "Symbol table")
111-
return []
112-
113-
114-
class MathSymbolTableDirective(Directive):
115-
has_content = False
116-
required_arguments = 0
117-
optional_arguments = 0
118-
final_argument_whitespace = False
119-
option_spec = {}
14+
class MathSymbolTableDirective(SphinxDirective):
15+
"""
16+
Generate tables of math symbols grouped by category.
17+
"""
12018

12119
def run(self):
122-
return run(self.state_machine)
20+
lines = []
21+
symbols = self._get_symbols()
22+
23+
for category, columns, syms in symbols:
24+
# Sort symbols for consistent order
25+
syms = sorted(
26+
syms,
27+
key=lambda sym: (
28+
self._render_symbol(sym, ignore_variant=True),
29+
sym.startswith(r"\var"),
30+
),
31+
reverse=(category == "Hebrew"), # RTL for Hebrew
32+
)
33+
34+
# Dynamically adjust number of columns
35+
if len(syms) <= 10:
36+
columns = 2
37+
elif len(syms) <= 20:
38+
columns = 3
39+
elif len(syms) <= 40:
40+
columns = 4
41+
else:
42+
columns = 5
43+
44+
rendered_syms = [
45+
f"{self._render_symbol(sym)} `{sym}`" for sym in syms
46+
]
47+
columns = min(columns, len(syms))
48+
lines.append(f"**{category}**\n")
49+
lines.append(self._make_table(rendered_syms, columns))
50+
lines.append("\n")
51+
52+
from docutils import nodes
53+
node = nodes.paragraph()
54+
self.state.nested_parse(lines, 0, node)
55+
return [node]
56+
57+
# ------------------------------------------------------------------
58+
# Helper methods
59+
# ------------------------------------------------------------------
60+
61+
def _get_symbols(self):
62+
"""
63+
Returns the math symbols categorized for documentation.
64+
"""
65+
categories = _mathtext._get_sphinx_symbol_table()
66+
return categories
67+
68+
def _render_symbol(self, sym, ignore_variant=False):
69+
"""
70+
Renders a math symbol as HTML or LaTeX representation for docs.
71+
"""
72+
try:
73+
html = _mathtext.math_to_image(
74+
f"${sym}$", "png", Path("/tmp/test.png"), dpi=120
75+
)
76+
return f":math:`{sym}`"
77+
except Exception:
78+
return sym
79+
80+
def _make_table(self, items, ncol):
81+
"""
82+
Build a simple RST table layout from the list of symbols.
83+
"""
84+
nrow = (len(items) + ncol - 1) // ncol
85+
rows = []
86+
for i in range(nrow):
87+
row = items[i::nrow]
88+
rows.append(" | ".join(row))
89+
table = "\n".join(rows)
90+
return table
12391

12492

12593
def setup(app):
126-
app.add_directive("math_symbol_table", MathSymbolTableDirective)
127-
128-
metadata = {'parallel_read_safe': True, 'parallel_write_safe': True}
129-
return metadata
130-
131-
132-
if __name__ == "__main__":
133-
# Do some verification of the tables
134-
135-
print("SYMBOLS NOT IN STIX:")
136-
all_symbols = {}
137-
for category, columns, syms in symbols:
138-
if category == "Standard Function Names":
139-
continue
140-
for sym in syms:
141-
if len(sym) > 1:
142-
all_symbols[sym[1:]] = None
143-
if sym[1:] not in _mathtext_data.tex2uni:
144-
print(sym)
145-
146-
# Add accents
147-
all_symbols.update({v[1:]: k for k, v in _mathtext.Parser._accent_map.items()})
148-
all_symbols.update({v: v for v in _mathtext.Parser._wide_accents})
149-
print("SYMBOLS NOT IN TABLE:")
150-
for sym, val in _mathtext_data.tex2uni.items():
151-
if sym not in all_symbols:
152-
print(f"{sym} = {chr(val)}")
94+
"""
95+
Register the Sphinx extension.
96+
"""
97+
app.add_directive("mathsymboltable", MathSymbolTableDirective)
98+
return {
99+
"version": "1.0",
100+
"parallel_read_safe": True,
101+
"parallel_write_safe": True,
102+
}

0 commit comments

Comments
 (0)