Sphinx 7.1.2__py3-none-any.whl → 7.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of Sphinx might be problematic. Click here for more details.

Files changed (313) hide show
  1. sphinx/__init__.py +6 -6
  2. sphinx/__main__.py +3 -1
  3. sphinx/addnodes.py +35 -22
  4. sphinx/application.py +40 -38
  5. sphinx/builders/__init__.py +16 -12
  6. sphinx/builders/_epub_base.py +15 -11
  7. sphinx/builders/changes.py +6 -4
  8. sphinx/builders/dirhtml.py +4 -2
  9. sphinx/builders/dummy.py +6 -4
  10. sphinx/builders/epub3.py +16 -8
  11. sphinx/builders/gettext.py +40 -43
  12. sphinx/builders/html/__init__.py +166 -196
  13. sphinx/builders/html/_assets.py +116 -0
  14. sphinx/builders/html/transforms.py +4 -2
  15. sphinx/builders/latex/__init__.py +12 -7
  16. sphinx/builders/latex/theming.py +5 -2
  17. sphinx/builders/latex/transforms.py +6 -3
  18. sphinx/builders/linkcheck.py +18 -11
  19. sphinx/builders/manpage.py +6 -4
  20. sphinx/builders/singlehtml.py +16 -9
  21. sphinx/builders/texinfo.py +11 -6
  22. sphinx/builders/text.py +8 -3
  23. sphinx/builders/xml.py +9 -4
  24. sphinx/cmd/build.py +27 -14
  25. sphinx/cmd/make_mode.py +13 -4
  26. sphinx/cmd/quickstart.py +13 -4
  27. sphinx/config.py +17 -14
  28. sphinx/deprecation.py +4 -2
  29. sphinx/directives/__init__.py +44 -12
  30. sphinx/directives/code.py +5 -4
  31. sphinx/directives/other.py +92 -44
  32. sphinx/directives/patches.py +1 -1
  33. sphinx/domains/__init__.py +11 -8
  34. sphinx/domains/c.py +67 -57
  35. sphinx/domains/changeset.py +3 -2
  36. sphinx/domains/citation.py +2 -1
  37. sphinx/domains/cpp.py +136 -93
  38. sphinx/domains/index.py +9 -5
  39. sphinx/domains/javascript.py +32 -19
  40. sphinx/domains/math.py +5 -3
  41. sphinx/domains/python.py +69 -57
  42. sphinx/domains/rst.py +20 -11
  43. sphinx/domains/std.py +21 -15
  44. sphinx/environment/__init__.py +97 -65
  45. sphinx/environment/adapters/indexentries.py +13 -10
  46. sphinx/environment/adapters/toctree.py +485 -308
  47. sphinx/environment/collectors/__init__.py +3 -4
  48. sphinx/environment/collectors/asset.py +10 -4
  49. sphinx/environment/collectors/dependencies.py +7 -4
  50. sphinx/environment/collectors/metadata.py +7 -5
  51. sphinx/environment/collectors/title.py +5 -3
  52. sphinx/environment/collectors/toctree.py +13 -8
  53. sphinx/errors.py +1 -1
  54. sphinx/events.py +5 -5
  55. sphinx/ext/apidoc.py +49 -27
  56. sphinx/ext/autodoc/__init__.py +179 -161
  57. sphinx/ext/autodoc/directive.py +10 -6
  58. sphinx/ext/autodoc/importer.py +22 -13
  59. sphinx/ext/autodoc/mock.py +4 -1
  60. sphinx/ext/autodoc/preserve_defaults.py +80 -12
  61. sphinx/ext/autodoc/type_comment.py +14 -10
  62. sphinx/ext/autodoc/typehints.py +7 -3
  63. sphinx/ext/autosectionlabel.py +6 -3
  64. sphinx/ext/autosummary/__init__.py +21 -15
  65. sphinx/ext/autosummary/generate.py +176 -126
  66. sphinx/ext/coverage.py +93 -8
  67. sphinx/ext/doctest.py +28 -17
  68. sphinx/ext/duration.py +19 -17
  69. sphinx/ext/extlinks.py +11 -6
  70. sphinx/ext/githubpages.py +8 -7
  71. sphinx/ext/graphviz.py +61 -17
  72. sphinx/ext/ifconfig.py +7 -4
  73. sphinx/ext/imgconverter.py +4 -2
  74. sphinx/ext/imgmath.py +29 -23
  75. sphinx/ext/inheritance_diagram.py +41 -27
  76. sphinx/ext/intersphinx.py +45 -38
  77. sphinx/ext/linkcode.py +8 -5
  78. sphinx/ext/mathjax.py +13 -9
  79. sphinx/ext/napoleon/__init__.py +3 -3
  80. sphinx/ext/napoleon/docstring.py +40 -31
  81. sphinx/ext/todo.py +10 -7
  82. sphinx/ext/viewcode.py +46 -25
  83. sphinx/extension.py +1 -1
  84. sphinx/highlighting.py +20 -12
  85. sphinx/io.py +5 -4
  86. sphinx/jinja2glue.py +24 -19
  87. sphinx/locale/__init__.py +8 -2
  88. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  89. sphinx/locale/ar/LC_MESSAGES/sphinx.po +756 -740
  90. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  91. sphinx/locale/bg/LC_MESSAGES/sphinx.po +754 -738
  92. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  93. sphinx/locale/bn/LC_MESSAGES/sphinx.po +755 -739
  94. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  95. sphinx/locale/ca/LC_MESSAGES/sphinx.po +768 -752
  96. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  97. sphinx/locale/cak/LC_MESSAGES/sphinx.po +754 -738
  98. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  99. sphinx/locale/cs/LC_MESSAGES/sphinx.po +758 -742
  100. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  101. sphinx/locale/cy/LC_MESSAGES/sphinx.po +759 -743
  102. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  103. sphinx/locale/da/LC_MESSAGES/sphinx.po +760 -744
  104. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  105. sphinx/locale/de/LC_MESSAGES/sphinx.po +759 -743
  106. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  107. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +754 -738
  108. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  109. sphinx/locale/el/LC_MESSAGES/sphinx.po +763 -747
  110. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  111. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +754 -738
  112. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  113. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +754 -738
  114. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  115. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +768 -752
  116. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  117. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +754 -738
  118. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  119. sphinx/locale/eo/LC_MESSAGES/sphinx.po +754 -738
  120. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  121. sphinx/locale/es/LC_MESSAGES/sphinx.po +767 -751
  122. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +754 -738
  124. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  125. sphinx/locale/et/LC_MESSAGES/sphinx.po +762 -746
  126. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  127. sphinx/locale/eu/LC_MESSAGES/sphinx.po +755 -739
  128. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  129. sphinx/locale/fa/LC_MESSAGES/sphinx.po +766 -750
  130. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  131. sphinx/locale/fi/LC_MESSAGES/sphinx.po +754 -738
  132. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  133. sphinx/locale/fr/LC_MESSAGES/sphinx.po +768 -752
  134. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  135. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +754 -738
  136. sphinx/locale/gl/LC_MESSAGES/sphinx.js +60 -0
  137. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  138. sphinx/locale/gl/LC_MESSAGES/sphinx.po +3695 -0
  139. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  140. sphinx/locale/he/LC_MESSAGES/sphinx.po +755 -739
  141. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  142. sphinx/locale/hi/LC_MESSAGES/sphinx.po +763 -747
  143. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  144. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +754 -738
  145. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  146. sphinx/locale/hr/LC_MESSAGES/sphinx.po +760 -744
  147. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  148. sphinx/locale/hu/LC_MESSAGES/sphinx.po +759 -743
  149. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  150. sphinx/locale/id/LC_MESSAGES/sphinx.po +765 -749
  151. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  152. sphinx/locale/is/LC_MESSAGES/sphinx.po +760 -744
  153. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  154. sphinx/locale/it/LC_MESSAGES/sphinx.po +760 -744
  155. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  156. sphinx/locale/ja/LC_MESSAGES/sphinx.po +767 -751
  157. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  158. sphinx/locale/ka/LC_MESSAGES/sphinx.po +759 -743
  159. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  160. sphinx/locale/ko/LC_MESSAGES/sphinx.po +767 -751
  161. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  162. sphinx/locale/lt/LC_MESSAGES/sphinx.po +755 -739
  163. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  164. sphinx/locale/lv/LC_MESSAGES/sphinx.po +755 -739
  165. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/mk/LC_MESSAGES/sphinx.po +754 -738
  167. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +755 -739
  169. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  170. sphinx/locale/ne/LC_MESSAGES/sphinx.po +755 -739
  171. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/nl/LC_MESSAGES/sphinx.po +760 -744
  173. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/pl/LC_MESSAGES/sphinx.po +762 -745
  175. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  176. sphinx/locale/pt/LC_MESSAGES/sphinx.po +754 -738
  177. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +768 -752
  179. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  180. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +755 -739
  181. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  182. sphinx/locale/ro/LC_MESSAGES/sphinx.po +759 -743
  183. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  184. sphinx/locale/ru/LC_MESSAGES/sphinx.po +760 -744
  185. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  186. sphinx/locale/si/LC_MESSAGES/sphinx.po +754 -738
  187. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  188. sphinx/locale/sk/LC_MESSAGES/sphinx.po +765 -749
  189. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  190. sphinx/locale/sl/LC_MESSAGES/sphinx.po +755 -739
  191. sphinx/locale/sphinx.pot +748 -740
  192. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  193. sphinx/locale/sq/LC_MESSAGES/sphinx.po +768 -752
  194. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  195. sphinx/locale/sr/LC_MESSAGES/sphinx.po +754 -738
  196. sphinx/locale/sr@latin/LC_MESSAGES/sphinx.mo +0 -0
  197. sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po +754 -738
  198. sphinx/locale/sr_RS/LC_MESSAGES/sphinx.mo +0 -0
  199. sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po +754 -738
  200. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  201. sphinx/locale/sv/LC_MESSAGES/sphinx.po +755 -739
  202. sphinx/locale/ta/LC_MESSAGES/sphinx.mo +0 -0
  203. sphinx/locale/ta/LC_MESSAGES/sphinx.po +754 -738
  204. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  205. sphinx/locale/te/LC_MESSAGES/sphinx.po +754 -738
  206. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  207. sphinx/locale/tr/LC_MESSAGES/sphinx.po +763 -747
  208. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  209. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +760 -749
  210. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  211. sphinx/locale/ur/LC_MESSAGES/sphinx.po +759 -748
  212. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  213. sphinx/locale/vi/LC_MESSAGES/sphinx.po +754 -738
  214. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  215. sphinx/locale/yue/LC_MESSAGES/sphinx.po +754 -738
  216. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.mo +0 -0
  217. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +768 -752
  218. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  219. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +754 -738
  220. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  221. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +767 -751
  222. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  223. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +754 -738
  224. sphinx/parsers.py +5 -4
  225. sphinx/project.py +52 -34
  226. sphinx/pycode/__init__.py +2 -1
  227. sphinx/pycode/ast.py +7 -13
  228. sphinx/pycode/parser.py +42 -38
  229. sphinx/registry.py +35 -29
  230. sphinx/roles.py +9 -4
  231. sphinx/search/__init__.py +5 -17
  232. sphinx/search/da.py +1 -1
  233. sphinx/search/de.py +1 -1
  234. sphinx/search/en.py +1 -1
  235. sphinx/search/es.py +1 -1
  236. sphinx/search/fi.py +1 -1
  237. sphinx/search/fr.py +1 -1
  238. sphinx/search/hu.py +1 -1
  239. sphinx/search/it.py +1 -1
  240. sphinx/search/ja.py +1 -1
  241. sphinx/search/nl.py +1 -1
  242. sphinx/search/no.py +1 -1
  243. sphinx/search/pt.py +1 -1
  244. sphinx/search/ro.py +1 -1
  245. sphinx/search/ru.py +1 -1
  246. sphinx/search/sv.py +1 -1
  247. sphinx/search/tr.py +1 -1
  248. sphinx/search/zh.py +1 -1
  249. sphinx/testing/fixtures.py +23 -30
  250. sphinx/testing/path.py +9 -0
  251. sphinx/testing/restructuredtext.py +13 -5
  252. sphinx/testing/util.py +20 -63
  253. sphinx/texinputs/sphinxlatexobjects.sty +15 -15
  254. sphinx/themes/agogo/static/agogo.css_t +10 -4
  255. sphinx/themes/basic/layout.html +1 -1
  256. sphinx/themes/basic/static/basic.css_t +4 -0
  257. sphinx/themes/basic/static/documentation_options.js_t +1 -2
  258. sphinx/themes/basic/static/searchtools.js +17 -9
  259. sphinx/themes/basic/static/sphinx_highlight.js +13 -3
  260. sphinx/themes/bizstyle/static/bizstyle.css_t +4 -0
  261. sphinx/themes/classic/theme.conf +1 -1
  262. sphinx/themes/epub/static/epub.css_t +6 -1
  263. sphinx/themes/haiku/theme.conf +1 -1
  264. sphinx/themes/nature/static/nature.css_t +4 -0
  265. sphinx/themes/nonav/static/nonav.css_t +6 -1
  266. sphinx/themes/pyramid/static/pyramid.css_t +4 -0
  267. sphinx/themes/scrolls/static/scrolls.css_t +4 -0
  268. sphinx/themes/scrolls/theme.conf +1 -1
  269. sphinx/themes/sphinxdoc/static/sphinxdoc.css_t +4 -0
  270. sphinx/theming.py +9 -7
  271. sphinx/transforms/__init__.py +79 -3
  272. sphinx/transforms/compact_bullet_list.py +6 -3
  273. sphinx/transforms/i18n.py +26 -10
  274. sphinx/transforms/post_transforms/__init__.py +21 -8
  275. sphinx/transforms/post_transforms/code.py +6 -3
  276. sphinx/transforms/post_transforms/images.py +13 -9
  277. sphinx/util/__init__.py +21 -92
  278. sphinx/util/cfamily.py +7 -4
  279. sphinx/util/display.py +3 -2
  280. sphinx/util/docfields.py +7 -6
  281. sphinx/util/docstrings.py +1 -1
  282. sphinx/util/docutils.py +41 -31
  283. sphinx/util/fileutil.py +9 -6
  284. sphinx/util/i18n.py +21 -18
  285. sphinx/util/images.py +2 -1
  286. sphinx/util/index_entries.py +27 -0
  287. sphinx/util/inspect.py +83 -67
  288. sphinx/util/inventory.py +4 -2
  289. sphinx/util/logging.py +9 -6
  290. sphinx/util/matching.py +5 -2
  291. sphinx/util/math.py +6 -3
  292. sphinx/util/nodes.py +70 -31
  293. sphinx/util/osutil.py +22 -40
  294. sphinx/util/parallel.py +4 -1
  295. sphinx/util/rst.py +7 -3
  296. sphinx/util/tags.py +11 -4
  297. sphinx/util/template.py +17 -14
  298. sphinx/util/typing.py +61 -20
  299. sphinx/versioning.py +6 -4
  300. sphinx/writers/html.py +1 -1
  301. sphinx/writers/html5.py +32 -24
  302. sphinx/writers/latex.py +67 -53
  303. sphinx/writers/manpage.py +9 -5
  304. sphinx/writers/texinfo.py +11 -9
  305. sphinx/writers/text.py +14 -9
  306. sphinx/writers/xml.py +3 -2
  307. {sphinx-7.1.2.dist-info → sphinx-7.2.0.dist-info}/METADATA +7 -5
  308. sphinx-7.2.0.dist-info/RECORD +568 -0
  309. sphinx/testing/comparer.py +0 -97
  310. sphinx-7.1.2.dist-info/RECORD +0 -564
  311. {sphinx-7.1.2.dist-info → sphinx-7.2.0.dist-info}/LICENSE +0 -0
  312. {sphinx-7.1.2.dist-info → sphinx-7.2.0.dist-info}/WHEEL +0 -0
  313. {sphinx-7.1.2.dist-info → sphinx-7.2.0.dist-info}/entry_points.txt +0 -0
@@ -57,12 +57,12 @@ const _removeChildren = (element) => {
57
57
  const _escapeRegExp = (string) =>
58
58
  string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
59
59
 
60
- const _displayItem = (item, searchTerms) => {
60
+ const _displayItem = (item, searchTerms, highlightTerms) => {
61
61
  const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
62
- const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT;
63
62
  const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
64
63
  const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
65
64
  const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
65
+ const contentRoot = document.documentElement.dataset.content_root;
66
66
 
67
67
  const [docName, title, anchor, descr, score, _filename] = item;
68
68
 
@@ -75,20 +75,24 @@ const _displayItem = (item, searchTerms) => {
75
75
  if (dirname.match(/\/index\/$/))
76
76
  dirname = dirname.substring(0, dirname.length - 6);
77
77
  else if (dirname === "index/") dirname = "";
78
- requestUrl = docUrlRoot + dirname;
78
+ requestUrl = contentRoot + dirname;
79
79
  linkUrl = requestUrl;
80
80
  } else {
81
81
  // normal html builders
82
- requestUrl = docUrlRoot + docName + docFileSuffix;
82
+ requestUrl = contentRoot + docName + docFileSuffix;
83
83
  linkUrl = docName + docLinkSuffix;
84
84
  }
85
85
  let linkEl = listItem.appendChild(document.createElement("a"));
86
86
  linkEl.href = linkUrl + anchor;
87
87
  linkEl.dataset.score = score;
88
88
  linkEl.innerHTML = title;
89
- if (descr)
89
+ if (descr) {
90
90
  listItem.appendChild(document.createElement("span")).innerHTML =
91
91
  " (" + descr + ")";
92
+ // highlight search terms in the description
93
+ if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
94
+ highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
95
+ }
92
96
  else if (showSearchSummary)
93
97
  fetch(requestUrl)
94
98
  .then((responseData) => responseData.text())
@@ -97,6 +101,9 @@ const _displayItem = (item, searchTerms) => {
97
101
  listItem.appendChild(
98
102
  Search.makeSearchSummary(data, searchTerms)
99
103
  );
104
+ // highlight search terms in the summary
105
+ if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
106
+ highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
100
107
  });
101
108
  Search.output.appendChild(listItem);
102
109
  };
@@ -115,14 +122,15 @@ const _finishSearch = (resultCount) => {
115
122
  const _displayNextItem = (
116
123
  results,
117
124
  resultCount,
118
- searchTerms
125
+ searchTerms,
126
+ highlightTerms,
119
127
  ) => {
120
128
  // results left, load the summary and display it
121
129
  // this is intended to be dynamic (don't sub resultsCount)
122
130
  if (results.length) {
123
- _displayItem(results.pop(), searchTerms);
131
+ _displayItem(results.pop(), searchTerms, highlightTerms);
124
132
  setTimeout(
125
- () => _displayNextItem(results, resultCount, searchTerms),
133
+ () => _displayNextItem(results, resultCount, searchTerms, highlightTerms),
126
134
  5
127
135
  );
128
136
  }
@@ -360,7 +368,7 @@ const Search = {
360
368
  // console.info("search results:", Search.lastresults);
361
369
 
362
370
  // print the results
363
- _displayNextItem(results, results.length, searchTerms);
371
+ _displayNextItem(results, results.length, searchTerms, highlightTerms);
364
372
  },
365
373
 
366
374
  /**
@@ -29,14 +29,19 @@ const _highlight = (node, addItems, text, className) => {
29
29
  }
30
30
 
31
31
  span.appendChild(document.createTextNode(val.substr(pos, text.length)));
32
+ const rest = document.createTextNode(val.substr(pos + text.length));
32
33
  parent.insertBefore(
33
34
  span,
34
35
  parent.insertBefore(
35
- document.createTextNode(val.substr(pos + text.length)),
36
+ rest,
36
37
  node.nextSibling
37
38
  )
38
39
  );
39
40
  node.nodeValue = val.substr(0, pos);
41
+ /* There may be more occurrences of search term in this node. So call this
42
+ * function recursively on the remaining fragment.
43
+ */
44
+ _highlight(rest, addItems, text, className);
40
45
 
41
46
  if (isInSVG) {
42
47
  const rect = document.createElementNS(
@@ -140,5 +145,10 @@ const SphinxHighlight = {
140
145
  },
141
146
  };
142
147
 
143
- _ready(SphinxHighlight.highlightSearchWords);
144
- _ready(SphinxHighlight.initEscapeListener);
148
+ _ready(() => {
149
+ /* Do not call highlightSearchWords() when we are on the search page.
150
+ * It will highlight words from the *previous* search query.
151
+ */
152
+ if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords();
153
+ SphinxHighlight.initEscapeListener();
154
+ });
@@ -181,6 +181,10 @@ a:hover {
181
181
  text-decoration: underline;
182
182
  }
183
183
 
184
+ a:visited {
185
+ color: #551a8b;
186
+ }
187
+
184
188
  div.body a {
185
189
  text-decoration: underline;
186
190
  }
@@ -24,7 +24,7 @@ headbgcolor = #f2f2f2
24
24
  headtextcolor = #20435c
25
25
  headlinkcolor = #c60f0f
26
26
  linkcolor = #355f7c
27
- visitedlinkcolor = #355f7c
27
+ visitedlinkcolor = #551a8b
28
28
  codebgcolor = unset
29
29
  codetextcolor = unset
30
30
 
@@ -26,11 +26,16 @@ div.clearer {
26
26
  clear: both;
27
27
  }
28
28
 
29
- a:link, a:visited {
29
+ a:link {
30
30
  color: #3333ff;
31
31
  text-decoration: underline;
32
32
  }
33
33
 
34
+ a:visited {
35
+ color: #551a8b;
36
+ text-decoration: underline;
37
+ }
38
+
34
39
  img {
35
40
  border: 0;
36
41
  max-width: 100%;
@@ -10,5 +10,5 @@ body_max_width = 70em
10
10
  textcolor = #333333
11
11
  headingcolor = #0c3762
12
12
  linkcolor = #dc3c01
13
- visitedlinkcolor = #892601
13
+ visitedlinkcolor = #551a8b
14
14
  hoverlinkcolor = #ff4500
@@ -142,6 +142,10 @@ a:hover {
142
142
  text-decoration: underline;
143
143
  }
144
144
 
145
+ a:visited {
146
+ color: #551A8B;
147
+ }
148
+
145
149
  div.body h1,
146
150
  div.body h2,
147
151
  div.body h3,
@@ -15,11 +15,16 @@ div.clearer {
15
15
  clear: both;
16
16
  }
17
17
 
18
- a:link, a:visited {
18
+ a:link {
19
19
  color: #3333ff;
20
20
  text-decoration: underline;
21
21
  }
22
22
 
23
+ a:visited {
24
+ color: #551a8b;
25
+ text-decoration: underline;
26
+ }
27
+
23
28
  img {
24
29
  border: 0;
25
30
  max-width: 100%;
@@ -181,6 +181,10 @@ a:hover, a:hover .pre {
181
181
  text-decoration: underline;
182
182
  }
183
183
 
184
+ a:visited {
185
+ color: #551a8b;
186
+ }
187
+
184
188
  div.body h1,
185
189
  div.body h2,
186
190
  div.body h3,
@@ -187,6 +187,10 @@ a:hover {
187
187
  color: {{ theme_visitedlinkcolor }};
188
188
  }
189
189
 
190
+ a:visited {
191
+ color: {{ theme_visitedlinkcolor }};
192
+ }
193
+
190
194
  pre {
191
195
  background-image: url(metal.png);
192
196
  border-top: 1px solid #ccc;
@@ -9,5 +9,5 @@ body_max_width = 680
9
9
  headerbordercolor = #1752b4
10
10
  subheadlinecolor = #0d306b
11
11
  linkcolor = #1752b4
12
- visitedlinkcolor = #444
12
+ visitedlinkcolor = #551a8b
13
13
  admonitioncolor = #28437f
@@ -151,6 +151,10 @@ a:hover {
151
151
  color: #2491CF;
152
152
  }
153
153
 
154
+ a:visited {
155
+ color: #551A8B;
156
+ }
157
+
154
158
  div.body a {
155
159
  text-decoration: underline;
156
160
  }
sphinx/theming.py CHANGED
@@ -16,6 +16,8 @@ if sys.version_info >= (3, 10):
16
16
  else:
17
17
  from importlib_metadata import entry_points
18
18
 
19
+ import contextlib
20
+
19
21
  from sphinx import package_dir
20
22
  from sphinx.errors import ThemeError
21
23
  from sphinx.locale import __
@@ -107,17 +109,18 @@ class Theme:
107
109
  'searched theme configs') % (section, name)) from exc
108
110
  return default
109
111
 
110
- def get_options(self, overrides: dict[str, Any] = {}) -> dict[str, Any]:
112
+ def get_options(self, overrides: dict[str, Any] | None = None) -> dict[str, Any]:
111
113
  """Return a dictionary of theme options and their values."""
114
+ if overrides is None:
115
+ overrides = {}
116
+
112
117
  if self.base:
113
118
  options = self.base.get_options()
114
119
  else:
115
120
  options = {}
116
121
 
117
- try:
122
+ with contextlib.suppress(configparser.NoSectionError):
118
123
  options.update(self.config.items('options'))
119
- except configparser.NoSectionError:
120
- pass
121
124
 
122
125
  for option, value in overrides.items():
123
126
  if option not in options:
@@ -130,10 +133,9 @@ class Theme:
130
133
  def cleanup(self) -> None:
131
134
  """Remove temporary directories."""
132
135
  if self.rootdir:
133
- try:
136
+ with contextlib.suppress(Exception):
134
137
  shutil.rmtree(self.rootdir)
135
- except Exception:
136
- pass
138
+
137
139
  if self.base:
138
140
  self.base.cleanup()
139
141
 
@@ -4,10 +4,9 @@ from __future__ import annotations
4
4
 
5
5
  import re
6
6
  import unicodedata
7
- from typing import TYPE_CHECKING, Any, Generator, cast
7
+ from typing import TYPE_CHECKING, Any, cast
8
8
 
9
9
  from docutils import nodes
10
- from docutils.nodes import Node, Text
11
10
  from docutils.transforms import Transform, Transformer
12
11
  from docutils.transforms.parts import ContentsFilter
13
12
  from docutils.transforms.universal import SmartQuotes
@@ -15,7 +14,6 @@ from docutils.utils import normalize_language_tag
15
14
  from docutils.utils.smartquotes import smartchars
16
15
 
17
16
  from sphinx import addnodes
18
- from sphinx.config import Config
19
17
  from sphinx.locale import _, __
20
18
  from sphinx.util import logging
21
19
  from sphinx.util.docutils import new_document
@@ -23,7 +21,12 @@ from sphinx.util.i18n import format_date
23
21
  from sphinx.util.nodes import apply_source_workaround, is_smartquotable
24
22
 
25
23
  if TYPE_CHECKING:
24
+ from collections.abc import Generator
25
+
26
+ from docutils.nodes import Node, Text
27
+
26
28
  from sphinx.application import Sphinx
29
+ from sphinx.config import Config
27
30
  from sphinx.domains.std import StandardDomain
28
31
  from sphinx.environment import BuildEnvironment
29
32
 
@@ -412,6 +415,78 @@ class GlossarySorter(SphinxTransform):
412
415
  )
413
416
 
414
417
 
418
+ class ReorderConsecutiveTargetAndIndexNodes(SphinxTransform):
419
+ """Index nodes interspersed between target nodes prevent other
420
+ Transformations from combining those target nodes,
421
+ e.g. ``PropagateTargets``. This transformation reorders them:
422
+
423
+ Given the following ``document`` as input::
424
+
425
+ <document>
426
+ <target ids="id1" ...>
427
+ <index entries="...1...">
428
+ <target ids="id2" ...>
429
+ <target ids="id3" ...>
430
+ <index entries="...2...">
431
+ <target ids="id4" ...>
432
+
433
+ The transformed result will be::
434
+
435
+ <document>
436
+ <index entries="...1...">
437
+ <index entries="...2...">
438
+ <target ids="id1" ...>
439
+ <target ids="id2" ...>
440
+ <target ids="id3" ...>
441
+ <target ids="id4" ...>
442
+ """
443
+
444
+ # This transform MUST run before ``PropagateTargets``.
445
+ default_priority = 220
446
+
447
+ def apply(self, **kwargs: Any) -> None:
448
+ for target in self.document.findall(nodes.target):
449
+ _reorder_index_target_nodes(target)
450
+
451
+
452
+ def _reorder_index_target_nodes(start_node: nodes.target) -> None:
453
+ """Sort target and index nodes.
454
+
455
+ Find all consecutive target and index nodes starting from ``start_node``,
456
+ and move all index nodes to before the first target node.
457
+ """
458
+ nodes_to_reorder: list[nodes.target | addnodes.index] = []
459
+
460
+ # Note that we cannot use 'condition' to filter,
461
+ # as we want *consecutive* target & index nodes.
462
+ node: nodes.Node
463
+ for node in start_node.findall(descend=False, siblings=True):
464
+ if isinstance(node, (nodes.target, addnodes.index)):
465
+ nodes_to_reorder.append(node)
466
+ continue
467
+ break # must be a consecutive run of target or index nodes
468
+
469
+ if len(nodes_to_reorder) < 2:
470
+ return # Nothing to reorder
471
+
472
+ parent = nodes_to_reorder[0].parent
473
+ if parent == nodes_to_reorder[-1].parent:
474
+ first_idx = parent.index(nodes_to_reorder[0])
475
+ last_idx = parent.index(nodes_to_reorder[-1])
476
+ if first_idx + len(nodes_to_reorder) - 1 == last_idx:
477
+ parent[first_idx:last_idx + 1] = sorted(nodes_to_reorder, key=_sort_key)
478
+
479
+
480
+ def _sort_key(node: nodes.Node) -> int:
481
+ # Must be a stable sort.
482
+ if isinstance(node, addnodes.index):
483
+ return 0
484
+ if isinstance(node, nodes.target):
485
+ return 1
486
+ msg = f'_sort_key called with unexpected node type {type(node)!r}'
487
+ raise ValueError(msg)
488
+
489
+
415
490
  def setup(app: Sphinx) -> dict[str, Any]:
416
491
  app.add_transform(ApplySourceWorkaround)
417
492
  app.add_transform(ExtraTranslatableNodes)
@@ -428,6 +503,7 @@ def setup(app: Sphinx) -> dict[str, Any]:
428
503
  app.add_transform(DoctreeReadEvent)
429
504
  app.add_transform(ManpageLink)
430
505
  app.add_transform(GlossarySorter)
506
+ app.add_transform(ReorderConsecutiveTargetAndIndexNodes)
431
507
 
432
508
  return {
433
509
  'version': 'builtin',
@@ -2,15 +2,18 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, cast
5
+ from typing import TYPE_CHECKING, Any, cast
6
6
 
7
7
  from docutils import nodes
8
- from docutils.nodes import Node
9
8
 
10
9
  from sphinx import addnodes
11
- from sphinx.application import Sphinx
12
10
  from sphinx.transforms import SphinxTransform
13
11
 
12
+ if TYPE_CHECKING:
13
+ from docutils.nodes import Node
14
+
15
+ from sphinx.application import Sphinx
16
+
14
17
 
15
18
  class RefOnlyListChecker(nodes.GenericNodeVisitor):
16
19
  """Raise `nodes.NodeFound` if non-simple list item is encountered.
sphinx/transforms/i18n.py CHANGED
@@ -6,20 +6,20 @@ import contextlib
6
6
  from os import path
7
7
  from re import DOTALL, match
8
8
  from textwrap import indent
9
- from typing import TYPE_CHECKING, Any, Sequence, TypeVar
9
+ from typing import TYPE_CHECKING, Any, TypeVar
10
10
 
11
11
  from docutils import nodes
12
12
  from docutils.io import StringInput
13
13
 
14
14
  from sphinx import addnodes
15
- from sphinx.config import Config
16
15
  from sphinx.domains.std import make_glossary_term, split_term_classifiers
17
16
  from sphinx.errors import ConfigError
18
17
  from sphinx.locale import __
19
18
  from sphinx.locale import init as init_locale
20
19
  from sphinx.transforms import SphinxTransform
21
- from sphinx.util import get_filetype, logging, split_index_msg
20
+ from sphinx.util import get_filetype, logging
22
21
  from sphinx.util.i18n import docname_to_domain
22
+ from sphinx.util.index_entries import split_index_msg
23
23
  from sphinx.util.nodes import (
24
24
  IMAGE_TYPE_NODES,
25
25
  LITERAL_TYPE_NODES,
@@ -29,7 +29,10 @@ from sphinx.util.nodes import (
29
29
  )
30
30
 
31
31
  if TYPE_CHECKING:
32
+ from collections.abc import Sequence
33
+
32
34
  from sphinx.application import Sphinx
35
+ from sphinx.config import Config
33
36
 
34
37
 
35
38
  logger = logging.getLogger(__name__)
@@ -311,7 +314,8 @@ class _NodeUpdater:
311
314
  return (
312
315
  node["refdomain"],
313
316
  node["reftype"],
314
- node['reftarget'],)
317
+ node['reftarget'],
318
+ )
315
319
 
316
320
  for old in old_xrefs:
317
321
  key = get_ref_key(old)
@@ -350,9 +354,16 @@ class Locale(SphinxTransform):
350
354
  if not has_catalog:
351
355
  return
352
356
 
357
+ catalogues = [getattr(catalog, '_catalog', None)]
358
+ while (catalog := catalog._fallback) is not None: # type: ignore[attr-defined]
359
+ catalogues.append(getattr(catalog, '_catalog', None))
360
+ merged: dict[str, str] = {}
361
+ for catalogue in filter(None, reversed(catalogues)): # type: dict[str, str]
362
+ merged |= catalogue
363
+
353
364
  # phase1: replace reference ids with translated names
354
365
  for node, msg in extract_messages(self.document):
355
- msgstr = catalog.gettext(msg)
366
+ msgstr = merged.get(msg, '')
356
367
 
357
368
  # There is no point in having #noqa on literal blocks because
358
369
  # they cannot contain references. Recognizing it would just
@@ -361,10 +372,14 @@ class Locale(SphinxTransform):
361
372
  if not isinstance(node, LITERAL_TYPE_NODES):
362
373
  msgstr, _ = parse_noqa(msgstr)
363
374
 
364
- if not msgstr or msgstr == msg or not msgstr.strip():
375
+ if msgstr.strip() == '':
365
376
  # as-of-yet untranslated
366
377
  node['translated'] = False
367
378
  continue
379
+ if msgstr == msg:
380
+ # identical source and translated messages
381
+ node['translated'] = True
382
+ continue
368
383
 
369
384
  # Avoid "Literal block expected; none found." warnings.
370
385
  # If msgstr ends with '::' then it cause warning message at
@@ -413,7 +428,7 @@ class Locale(SphinxTransform):
413
428
  if node.setdefault('translated', False): # to avoid double translation
414
429
  continue # skip if the node is already translated by phase1
415
430
 
416
- msgstr = catalog.gettext(msg)
431
+ msgstr = merged.get(msg, '')
417
432
  noqa = False
418
433
 
419
434
  # See above.
@@ -507,7 +522,7 @@ class Locale(SphinxTransform):
507
522
  msg_parts = split_index_msg(entry_type, value)
508
523
  msgstr_parts = []
509
524
  for part in msg_parts:
510
- msgstr = catalog.gettext(part)
525
+ msgstr = merged.get(part, '')
511
526
  if not msgstr:
512
527
  msgstr = part
513
528
  msgstr_parts.append(msgstr)
@@ -565,8 +580,9 @@ class AddTranslationClasses(SphinxTransform):
565
580
  add_translated = False
566
581
  add_untranslated = True
567
582
  else:
568
- raise ConfigError('translation_progress_classes must be'
569
- ' True, False, "translated" or "untranslated"')
583
+ msg = ('translation_progress_classes must be '
584
+ 'True, False, "translated" or "untranslated"')
585
+ raise ConfigError(msg)
570
586
 
571
587
  for node in self.document.findall(NodeMatcher(translated=Any)): # type: nodes.Element
572
588
  if node['translated']:
@@ -3,15 +3,12 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import re
6
- from typing import Any, Sequence, cast
6
+ from typing import TYPE_CHECKING, Any, cast
7
7
 
8
8
  from docutils import nodes
9
9
  from docutils.nodes import Element, Node
10
10
 
11
11
  from sphinx import addnodes
12
- from sphinx.addnodes import pending_xref
13
- from sphinx.application import Sphinx
14
- from sphinx.domains import Domain
15
12
  from sphinx.errors import NoUri
16
13
  from sphinx.locale import __
17
14
  from sphinx.transforms import SphinxTransform
@@ -19,6 +16,13 @@ from sphinx.util import logging
19
16
  from sphinx.util.docutils import SphinxTranslator
20
17
  from sphinx.util.nodes import find_pending_xref_condition, process_only_nodes
21
18
 
19
+ if TYPE_CHECKING:
20
+ from collections.abc import Sequence
21
+
22
+ from sphinx.addnodes import pending_xref
23
+ from sphinx.application import Sphinx
24
+ from sphinx.domains import Domain
25
+
22
26
  logger = logging.getLogger(__name__)
23
27
 
24
28
 
@@ -246,18 +250,27 @@ class SigElementFallbackTransform(SphinxPostTransform):
246
250
  # subclass of SphinxTranslator supports desc_sig_element nodes automatically.
247
251
  return
248
252
 
249
- # for the leaf elements (desc_sig_element), the translator should support _all_
250
- if not all(has_visitor(translator, node) for node in addnodes.SIG_ELEMENTS):
253
+ # for the leaf elements (desc_sig_element), the translator should support _all_,
254
+ # unless there exists a generic visit_desc_sig_element default visitor
255
+ if (not all(has_visitor(translator, node) for node in addnodes.SIG_ELEMENTS)
256
+ and not has_visitor(translator, addnodes.desc_sig_element)):
251
257
  self.fallback(addnodes.desc_sig_element)
252
258
 
253
259
  if not has_visitor(translator, addnodes.desc_inline):
254
260
  self.fallback(addnodes.desc_inline)
255
261
 
256
- def fallback(self, nodeType: Any) -> None:
257
- for node in self.document.findall(nodeType):
262
+ def fallback(self, node_type: Any) -> None:
263
+ """Translate nodes of type *node_type* to docutils inline nodes.
264
+
265
+ The original node type name is stored as a string in a private
266
+ ``_sig_node_type`` attribute if the latter did not exist.
267
+ """
268
+ for node in self.document.findall(node_type):
258
269
  newnode = nodes.inline()
259
270
  newnode.update_all_atts(node)
260
271
  newnode.extend(node)
272
+ # Only set _sig_node_type if not defined by the user
273
+ newnode.setdefault('_sig_node_type', node.tagname)
261
274
  node.replace_self(newnode)
262
275
 
263
276
 
@@ -3,17 +3,20 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import sys
6
- from typing import Any, NamedTuple
6
+ from typing import TYPE_CHECKING, Any, NamedTuple
7
7
 
8
8
  from docutils import nodes
9
- from docutils.nodes import Node, TextElement
10
9
  from pygments.lexers import PythonConsoleLexer, guess_lexer
11
10
 
12
11
  from sphinx import addnodes
13
- from sphinx.application import Sphinx
14
12
  from sphinx.ext import doctest
15
13
  from sphinx.transforms import SphinxTransform
16
14
 
15
+ if TYPE_CHECKING:
16
+ from docutils.nodes import Node, TextElement
17
+
18
+ from sphinx.application import Sphinx
19
+
17
20
 
18
21
  class HighlightSetting(NamedTuple):
19
22
  language: str