texsmith 0.0.2.dev0__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.
- texsmith/__init__.py +107 -0
- texsmith/_alias.py +59 -0
- texsmith/adapters/__init__.py +6 -0
- texsmith/adapters/docker.py +258 -0
- texsmith/adapters/handlers/__init__.py +3 -0
- texsmith/adapters/handlers/_assets.py +363 -0
- texsmith/adapters/handlers/_helpers.py +62 -0
- texsmith/adapters/handlers/_mermaid.py +122 -0
- texsmith/adapters/handlers/admonitions.py +267 -0
- texsmith/adapters/handlers/basic.py +228 -0
- texsmith/adapters/handlers/blocks.py +851 -0
- texsmith/adapters/handlers/code.py +309 -0
- texsmith/adapters/handlers/inline.py +937 -0
- texsmith/adapters/handlers/links.py +239 -0
- texsmith/adapters/handlers/media.py +380 -0
- texsmith/adapters/latex/__init__.py +10 -0
- texsmith/adapters/latex/engines/__init__.py +726 -0
- texsmith/adapters/latex/engines/latex/__init__.py +26 -0
- texsmith/adapters/latex/engines/latex/log.py +720 -0
- texsmith/adapters/latex/engines/latex/runner.py +28 -0
- texsmith/adapters/latex/engines/tectonic/__init__.py +38 -0
- texsmith/adapters/latex/formatter.py +297 -0
- texsmith/adapters/latex/latexmk.py +148 -0
- texsmith/adapters/latex/partials/acronym.tex +1 -0
- texsmith/adapters/latex/partials/add.tex +1 -0
- texsmith/adapters/latex/partials/addition.tex +1 -0
- texsmith/adapters/latex/partials/blockquote.tex +3 -0
- texsmith/adapters/latex/partials/callout.tex +3 -0
- texsmith/adapters/latex/partials/choices.tex +5 -0
- texsmith/adapters/latex/partials/citation.tex +1 -0
- texsmith/adapters/latex/partials/codeblock.tex +7 -0
- texsmith/adapters/latex/partials/codeblock_listings.tex +5 -0
- texsmith/adapters/latex/partials/codeblock_pygments.tex +5 -0
- texsmith/adapters/latex/partials/codeblock_verbatim.tex +12 -0
- texsmith/adapters/latex/partials/codeinline.tex +1 -0
- texsmith/adapters/latex/partials/codeinlinett.tex +1 -0
- texsmith/adapters/latex/partials/comment.tex +1 -0
- texsmith/adapters/latex/partials/del.tex +1 -0
- texsmith/adapters/latex/partials/deletion.tex +1 -0
- texsmith/adapters/latex/partials/description_list.tex +5 -0
- texsmith/adapters/latex/partials/enquote.tex +1 -0
- texsmith/adapters/latex/partials/epigraph.tex +1 -0
- texsmith/adapters/latex/partials/exercises_solutions.tex +7 -0
- texsmith/adapters/latex/partials/figure.tex +33 -0
- texsmith/adapters/latex/partials/figure_tcolorbox.tex +15 -0
- texsmith/adapters/latex/partials/footnote.tex +3 -0
- texsmith/adapters/latex/partials/glossary.tex +1 -0
- texsmith/adapters/latex/partials/heading.tex +14 -0
- texsmith/adapters/latex/partials/highlight.tex +25 -0
- texsmith/adapters/latex/partials/horizontal_rule.tex +1 -0
- texsmith/adapters/latex/partials/href.tex +1 -0
- texsmith/adapters/latex/partials/icon.tex +1 -0
- texsmith/adapters/latex/partials/include.tex +1 -0
- texsmith/adapters/latex/partials/index.tex +1 -0
- texsmith/adapters/latex/partials/italic.tex +1 -0
- texsmith/adapters/latex/partials/keystroke.tex +46 -0
- texsmith/adapters/latex/partials/label.tex +1 -0
- texsmith/adapters/latex/partials/list_acronyms.tex +5 -0
- texsmith/adapters/latex/partials/list_glossary.tex +8 -0
- texsmith/adapters/latex/partials/multicolumn.tex +3 -0
- texsmith/adapters/latex/partials/ordered_list.tex +5 -0
- texsmith/adapters/latex/partials/pagestyle.tex +1 -0
- texsmith/adapters/latex/partials/ref.tex +1 -0
- texsmith/adapters/latex/partials/regex.tex +1 -0
- texsmith/adapters/latex/partials/smallcaps.tex +1 -0
- texsmith/adapters/latex/partials/strikethrough.tex +1 -0
- texsmith/adapters/latex/partials/strong.tex +1 -0
- texsmith/adapters/latex/partials/subscript.tex +1 -0
- texsmith/adapters/latex/partials/substitution.tex +1 -0
- texsmith/adapters/latex/partials/superscript.tex +1 -0
- texsmith/adapters/latex/partials/tabbed.tex +3 -0
- texsmith/adapters/latex/partials/table.tex +48 -0
- texsmith/adapters/latex/partials/underline.tex +1 -0
- texsmith/adapters/latex/partials/unordered_list.tex +5 -0
- texsmith/adapters/latex/partials/url.tex +1 -0
- texsmith/adapters/latex/pygments.py +98 -0
- texsmith/adapters/latex/pyxindy.py +64 -0
- texsmith/adapters/latex/renderer.py +220 -0
- texsmith/adapters/latex/tectonic.py +366 -0
- texsmith/adapters/latex/utils.py +86 -0
- texsmith/adapters/markdown/__init__.py +342 -0
- texsmith/adapters/plugins/__init__.py +8 -0
- texsmith/adapters/plugins/material.py +174 -0
- texsmith/adapters/plugins/snippet.py +1734 -0
- texsmith/adapters/transformers/__init__.py +122 -0
- texsmith/adapters/transformers/base.py +140 -0
- texsmith/adapters/transformers/strategies.py +1456 -0
- texsmith/adapters/transformers/utils.py +59 -0
- texsmith/api/__init__.py +80 -0
- texsmith/api/_utils.py +56 -0
- texsmith/api/document.py +645 -0
- texsmith/api/pipeline.py +229 -0
- texsmith/api/service.py +648 -0
- texsmith/api/templates.py +287 -0
- texsmith/core/__init__.py +6 -0
- texsmith/core/bibliography/__init__.py +57 -0
- texsmith/core/bibliography/collection.py +354 -0
- texsmith/core/bibliography/doi.py +194 -0
- texsmith/core/bibliography/issues.py +15 -0
- texsmith/core/bibliography/parsing.py +45 -0
- texsmith/core/callouts.py +99 -0
- texsmith/core/config.py +191 -0
- texsmith/core/context.py +242 -0
- texsmith/core/conversion/__init__.py +103 -0
- texsmith/core/conversion/core.py +780 -0
- texsmith/core/conversion/debug.py +87 -0
- texsmith/core/conversion/inputs.py +474 -0
- texsmith/core/conversion/renderer.py +578 -0
- texsmith/core/conversion/templates.py +646 -0
- texsmith/core/conversion_contexts.py +95 -0
- texsmith/core/diagnostics.py +118 -0
- texsmith/core/exceptions.py +41 -0
- texsmith/core/fonts/__init__.py +3 -0
- texsmith/core/fragments/__init__.py +716 -0
- texsmith/core/fragments/base.py +117 -0
- texsmith/core/metadata.py +280 -0
- texsmith/core/mustache.py +86 -0
- texsmith/core/partials.py +20 -0
- texsmith/core/rules.py +394 -0
- texsmith/core/templates/__init__.py +58 -0
- texsmith/core/templates/base.py +343 -0
- texsmith/core/templates/builtins.py +68 -0
- texsmith/core/templates/context_usage.py +137 -0
- texsmith/core/templates/loader.py +303 -0
- texsmith/core/templates/manifest.py +861 -0
- texsmith/core/templates/runtime.py +347 -0
- texsmith/core/templates/text.py +14 -0
- texsmith/core/templates/wrapper.py +340 -0
- texsmith/core/user_dir.py +179 -0
- texsmith/devtools.py +28 -0
- texsmith/extensions/__init__.py +162 -0
- texsmith/extensions/index/__init__.py +21 -0
- texsmith/extensions/index/markdown.py +94 -0
- texsmith/extensions/index/mkdocs_plugin.py +136 -0
- texsmith/extensions/index/registry.py +57 -0
- texsmith/extensions/index/renderer.py +183 -0
- texsmith/extensions/index/templates/index.tex +1 -0
- texsmith/extensions/latex_raw.py +115 -0
- texsmith/extensions/latex_text.py +117 -0
- texsmith/extensions/mermaid.py +267 -0
- texsmith/extensions/missing_footnotes.py +125 -0
- texsmith/extensions/multi_citations.py +54 -0
- texsmith/extensions/progressbar/__init__.py +9 -0
- texsmith/extensions/progressbar/markdown.py +212 -0
- texsmith/extensions/progressbar/renderer.py +117 -0
- texsmith/extensions/smallcaps.py +48 -0
- texsmith/extensions/texlogos/__init__.py +10 -0
- texsmith/extensions/texlogos/markdown.py +236 -0
- texsmith/extensions/texlogos/renderer.py +81 -0
- texsmith/extensions/texlogos/specs.py +66 -0
- texsmith/fonts/__init__.py +57 -0
- texsmith/fonts/cache.py +46 -0
- texsmith/fonts/constants.py +60 -0
- texsmith/fonts/coverage.py +275 -0
- texsmith/fonts/downloader.py +103 -0
- texsmith/fonts/fallback.py +438 -0
- texsmith/fonts/html_scripts.py +168 -0
- texsmith/fonts/logging.py +127 -0
- texsmith/fonts/pipeline.py +338 -0
- texsmith/fonts/scripts.py +493 -0
- texsmith/fonts/ucharclasses.py +164 -0
- texsmith/fragments/__init__.py +11 -0
- texsmith/fragments/bibliography/__init__.py +65 -0
- texsmith/fragments/bibliography/fragment.toml +3 -0
- texsmith/fragments/bibliography/ts-bibliography-backmatter.jinja.tex +35 -0
- texsmith/fragments/bibliography/ts-bibliography.jinja.tex +10 -0
- texsmith/fragments/callouts/__init__.py +88 -0
- texsmith/fragments/callouts/fragment.toml +3 -0
- texsmith/fragments/callouts/ts-callouts.jinja.sty +129 -0
- texsmith/fragments/code/__init__.py +82 -0
- texsmith/fragments/code/fragment.toml +3 -0
- texsmith/fragments/code/ts-code.jinja.sty +140 -0
- texsmith/fragments/extra/__init__.py +180 -0
- texsmith/fragments/extra/fragment.toml +3 -0
- texsmith/fragments/extra/ts-extra.jinja.tex +13 -0
- texsmith/fragments/fonts/__init__.py +880 -0
- texsmith/fragments/fonts/fragment.toml +3 -0
- texsmith/fragments/fonts/ts-fonts.jinja.sty +292 -0
- texsmith/fragments/frame/__init__.py +170 -0
- texsmith/fragments/frame/fragment.toml +3 -0
- texsmith/fragments/frame/ts-frame.tex.jinja +49 -0
- texsmith/fragments/geometry/__init__.py +169 -0
- texsmith/fragments/geometry/fragment.toml +3 -0
- texsmith/fragments/geometry/paper.py +526 -0
- texsmith/fragments/geometry/ts_geometry.tex.jinja +53 -0
- texsmith/fragments/glossary/__init__.py +67 -0
- texsmith/fragments/glossary/fragment.toml +3 -0
- texsmith/fragments/glossary/ts-glossary-backmatter.jinja.tex +5 -0
- texsmith/fragments/glossary/ts-glossary.jinja.sty +28 -0
- texsmith/fragments/index/__init__.py +66 -0
- texsmith/fragments/index/fragment.toml +3 -0
- texsmith/fragments/index/ts-index-backmatter.jinja.tex +3 -0
- texsmith/fragments/index/ts-index.jinja.sty +12 -0
- texsmith/fragments/keystrokes/__init__.py +69 -0
- texsmith/fragments/keystrokes/fragment.toml +3 -0
- texsmith/fragments/keystrokes/ts-keystrokes.jinja.sty +20 -0
- texsmith/fragments/todolist/__init__.py +71 -0
- texsmith/fragments/todolist/fragment.toml +3 -0
- texsmith/fragments/todolist/ts-todolist.jinja.sty +21 -0
- texsmith/fragments/typesetting/__init__.py +214 -0
- texsmith/fragments/typesetting/fragment.toml +3 -0
- texsmith/fragments/typesetting/ts-typesetting.tex.jinja +81 -0
- texsmith/index.py +26 -0
- texsmith/plugins/__init__.py +14 -0
- texsmith/progressbar.py +8 -0
- texsmith/quotes.py +40 -0
- texsmith/smart_dashes.py +66 -0
- texsmith/templates/__init__.py +3 -0
- texsmith/templates/article/README.md +33 -0
- texsmith/templates/article/__init__.py +281 -0
- texsmith/templates/article/template/manifest.toml +125 -0
- texsmith/templates/article/template/mermaid-config.json +18 -0
- texsmith/templates/article/template/template.tex +74 -0
- texsmith/templates/book/README.md +26 -0
- texsmith/templates/book/__init__.py +75 -0
- texsmith/templates/book/overrides/codeblock.tex +7 -0
- texsmith/templates/book/overrides/codeinline.tex +1 -0
- texsmith/templates/book/template/fixtoc.sty +81 -0
- texsmith/templates/book/template/manifest.toml +198 -0
- texsmith/templates/book/template/template.tex +314 -0
- texsmith/templates/common/__init__.py +1 -0
- texsmith/templates/common/latexmkrc +46 -0
- texsmith/templates/letter/README.md +59 -0
- texsmith/templates/letter/__init__.py +495 -0
- texsmith/templates/letter/demo.md +31 -0
- texsmith/templates/letter/fonts/modernline bold.otf +0 -0
- texsmith/templates/letter/fonts/modernline.otf +0 -0
- texsmith/templates/letter/manifest.toml +197 -0
- texsmith/templates/letter/template/callouts.jinja.sty +290 -0
- texsmith/templates/letter/template/template.tex +123 -0
- texsmith/templates/snippet/README.md +21 -0
- texsmith/templates/snippet/__init__.py +80 -0
- texsmith/templates/snippet/template/manifest.toml +66 -0
- texsmith/templates/snippet/template/template.tex +47 -0
- texsmith/texlogos.py +15 -0
- texsmith/ui/__init__.py +6 -0
- texsmith/ui/cli/__init__.py +22 -0
- texsmith/ui/cli/_options.py +338 -0
- texsmith/ui/cli/app.py +65 -0
- texsmith/ui/cli/bibliography.py +300 -0
- texsmith/ui/cli/commands/__init__.py +14 -0
- texsmith/ui/cli/commands/render.py +1128 -0
- texsmith/ui/cli/commands/templates.py +397 -0
- texsmith/ui/cli/diagnostics.py +36 -0
- texsmith/ui/cli/presenter.py +663 -0
- texsmith/ui/cli/state.py +263 -0
- texsmith/ui/cli/utils.py +235 -0
- texsmith-0.0.2.dev0.dist-info/METADATA +187 -0
- texsmith-0.0.2.dev0.dist-info/RECORD +252 -0
- texsmith-0.0.2.dev0.dist-info/WHEEL +4 -0
- texsmith-0.0.2.dev0.dist-info/entry_points.txt +22 -0
- texsmith-0.0.2.dev0.dist-info/licenses/LICENSE.md +21 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, ClassVar
|
|
7
|
+
|
|
8
|
+
from texsmith.core.fragments.base import BaseFragment, FragmentPiece
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class IndexConfig:
|
|
13
|
+
has_index: bool
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def from_context(cls, context: Mapping[str, Any]) -> IndexConfig:
|
|
17
|
+
has_flag = context.get("has_index")
|
|
18
|
+
entries = context.get("index_terms")
|
|
19
|
+
return cls(has_index=bool(has_flag) or bool(entries))
|
|
20
|
+
|
|
21
|
+
def inject_into(self, context: dict[str, Any]) -> None:
|
|
22
|
+
context["ts_index_enabled"] = self.has_index
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class IndexFragment(BaseFragment[IndexConfig]):
|
|
26
|
+
name: ClassVar[str] = "ts-index"
|
|
27
|
+
description: ClassVar[str] = "Index helpers with backmatter insertion."
|
|
28
|
+
pieces: ClassVar[list[FragmentPiece]] = [
|
|
29
|
+
FragmentPiece(
|
|
30
|
+
template_path=Path(__file__).with_name("ts-index.jinja.sty"),
|
|
31
|
+
kind="package",
|
|
32
|
+
slot="extra_packages",
|
|
33
|
+
),
|
|
34
|
+
FragmentPiece(
|
|
35
|
+
template_path=Path(__file__).with_name("ts-index-backmatter.jinja.tex"),
|
|
36
|
+
kind="inline",
|
|
37
|
+
slot="fragment_backmatter",
|
|
38
|
+
),
|
|
39
|
+
]
|
|
40
|
+
attributes: ClassVar[dict[str, Any]] = {}
|
|
41
|
+
config_cls: ClassVar[type[IndexConfig]] = IndexConfig
|
|
42
|
+
source: ClassVar[Path] = Path(__file__).with_name("ts-index.jinja.sty")
|
|
43
|
+
context_defaults: ClassVar[dict[str, Any]] = {"extra_packages": "", "fragment_backmatter": ""}
|
|
44
|
+
|
|
45
|
+
def build_config(
|
|
46
|
+
self, context: Mapping[str, Any], overrides: Mapping[str, Any] | None = None
|
|
47
|
+
) -> IndexConfig:
|
|
48
|
+
_ = overrides
|
|
49
|
+
return self.config_cls.from_context(context)
|
|
50
|
+
|
|
51
|
+
def inject(
|
|
52
|
+
self,
|
|
53
|
+
config: IndexConfig,
|
|
54
|
+
context: dict[str, Any],
|
|
55
|
+
overrides: Mapping[str, Any] | None = None,
|
|
56
|
+
) -> None:
|
|
57
|
+
_ = overrides
|
|
58
|
+
config.inject_into(context)
|
|
59
|
+
|
|
60
|
+
def should_render(self, config: IndexConfig) -> bool:
|
|
61
|
+
return config.has_index
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
fragment = IndexFragment()
|
|
65
|
+
|
|
66
|
+
__all__ = ["IndexConfig", "IndexFragment", "fragment"]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
\ProvidesPackage{ts-index}[2025/11/21 TeXSmith index helpers (generated)]
|
|
2
|
+
|
|
3
|
+
\BLOCK{ set resolved_index_engine = index_engine|default("makeindex")|lower }
|
|
4
|
+
\BLOCK{ if resolved_index_engine in ["texindy", "pyxindy"] }
|
|
5
|
+
\RequirePackage[xindy]{imakeidx}
|
|
6
|
+
\BLOCK{ else }
|
|
7
|
+
\RequirePackage{imakeidx}
|
|
8
|
+
\BLOCK{ endif }
|
|
9
|
+
|
|
10
|
+
\makeindex
|
|
11
|
+
|
|
12
|
+
\endinput
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, ClassVar
|
|
7
|
+
|
|
8
|
+
from texsmith.core.fragments.base import BaseFragment, FragmentPiece
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class KeystrokesConfig:
|
|
13
|
+
uses_keystrokes: bool
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def from_context(cls, context: Mapping[str, Any]) -> KeystrokesConfig:
|
|
17
|
+
return cls(uses_keystrokes=_detect_keystrokes(context))
|
|
18
|
+
|
|
19
|
+
def inject_into(self, context: dict[str, Any]) -> None:
|
|
20
|
+
context["ts_keystrokes_enabled"] = self.uses_keystrokes
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class KeystrokesFragment(BaseFragment[KeystrokesConfig]):
|
|
24
|
+
name: ClassVar[str] = "ts-keystrokes"
|
|
25
|
+
description: ClassVar[str] = "Keyboard shortcut rendering helpers loaded only when needed."
|
|
26
|
+
pieces: ClassVar[list[FragmentPiece]] = [
|
|
27
|
+
FragmentPiece(
|
|
28
|
+
template_path=Path(__file__).with_name("ts-keystrokes.jinja.sty"),
|
|
29
|
+
kind="package",
|
|
30
|
+
slot="extra_packages",
|
|
31
|
+
)
|
|
32
|
+
]
|
|
33
|
+
attributes: ClassVar[dict[str, Any]] = {}
|
|
34
|
+
config_cls: ClassVar[type[KeystrokesConfig]] = KeystrokesConfig
|
|
35
|
+
source: ClassVar[Path] = Path(__file__).with_name("ts-keystrokes.jinja.sty")
|
|
36
|
+
context_defaults: ClassVar[dict[str, Any]] = {"extra_packages": ""}
|
|
37
|
+
|
|
38
|
+
def build_config(
|
|
39
|
+
self, context: Mapping[str, Any], overrides: Mapping[str, Any] | None = None
|
|
40
|
+
) -> KeystrokesConfig:
|
|
41
|
+
_ = overrides
|
|
42
|
+
return self.config_cls.from_context(context)
|
|
43
|
+
|
|
44
|
+
def inject(
|
|
45
|
+
self,
|
|
46
|
+
config: KeystrokesConfig,
|
|
47
|
+
context: dict[str, Any],
|
|
48
|
+
overrides: Mapping[str, Any] | None = None,
|
|
49
|
+
) -> None:
|
|
50
|
+
_ = overrides
|
|
51
|
+
config.inject_into(context)
|
|
52
|
+
|
|
53
|
+
def should_render(self, config: KeystrokesConfig) -> bool:
|
|
54
|
+
return config.uses_keystrokes
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _detect_keystrokes(context: Mapping[str, Any]) -> bool:
|
|
58
|
+
tokens = ("\\keystroke{", "\\keystrokes{")
|
|
59
|
+
for value in context.values():
|
|
60
|
+
if not isinstance(value, str):
|
|
61
|
+
continue
|
|
62
|
+
if any(token in value for token in tokens):
|
|
63
|
+
return True
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
fragment = KeystrokesFragment()
|
|
68
|
+
|
|
69
|
+
__all__ = ["KeystrokesConfig", "KeystrokesFragment", "fragment"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
\NeedsTeXFormat{LaTeX2e}
|
|
2
|
+
\ProvidesPackage{ts-keystrokes}[2025/11/21 TeXSmith keystroke helpers]
|
|
3
|
+
|
|
4
|
+
\RequirePackage{tikz}
|
|
5
|
+
\usetikzlibrary{shadows}
|
|
6
|
+
|
|
7
|
+
\newcommand*\keystroke[1]{%
|
|
8
|
+
\tikz[baseline=(key.base)]%
|
|
9
|
+
\node[%
|
|
10
|
+
draw,
|
|
11
|
+
fill=gray!10,
|
|
12
|
+
drop shadow={shadow xshift=0.25ex,shadow yshift=-0.25ex,fill=black!70,opacity=0.75},
|
|
13
|
+
rectangle,
|
|
14
|
+
rounded corners=1.5pt,
|
|
15
|
+
inner ysep=0.1em,
|
|
16
|
+
inner xsep=0.5em,
|
|
17
|
+
line width=0.5pt,
|
|
18
|
+
font=\scriptsize\sffamily
|
|
19
|
+
](key) {#1\strut};%
|
|
20
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, ClassVar
|
|
7
|
+
|
|
8
|
+
from texsmith.core.fragments.base import BaseFragment, FragmentPiece
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class TodolistConfig:
|
|
13
|
+
uses_todolist: bool
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def from_context(cls, context: Mapping[str, Any]) -> TodolistConfig:
|
|
17
|
+
return cls(uses_todolist=_detect_todolist(context))
|
|
18
|
+
|
|
19
|
+
def inject_into(self, context: dict[str, Any]) -> None:
|
|
20
|
+
context["ts_todolist_enabled"] = self.uses_todolist
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TodolistFragment(BaseFragment[TodolistConfig]):
|
|
24
|
+
name: ClassVar[str] = "ts-todolist"
|
|
25
|
+
description: ClassVar[str] = (
|
|
26
|
+
"Todolist helper commands loaded when checklist macros are present."
|
|
27
|
+
)
|
|
28
|
+
pieces: ClassVar[list[FragmentPiece]] = [
|
|
29
|
+
FragmentPiece(
|
|
30
|
+
template_path=Path(__file__).with_name("ts-todolist.jinja.sty"),
|
|
31
|
+
kind="package",
|
|
32
|
+
slot="extra_packages",
|
|
33
|
+
)
|
|
34
|
+
]
|
|
35
|
+
attributes: ClassVar[dict[str, Any]] = {}
|
|
36
|
+
config_cls: ClassVar[type[TodolistConfig]] = TodolistConfig
|
|
37
|
+
source: ClassVar[Path] = Path(__file__).with_name("ts-todolist.jinja.sty")
|
|
38
|
+
context_defaults: ClassVar[dict[str, Any]] = {"extra_packages": ""}
|
|
39
|
+
|
|
40
|
+
def build_config(
|
|
41
|
+
self, context: Mapping[str, Any], overrides: Mapping[str, Any] | None = None
|
|
42
|
+
) -> TodolistConfig:
|
|
43
|
+
_ = overrides
|
|
44
|
+
return self.config_cls.from_context(context)
|
|
45
|
+
|
|
46
|
+
def inject(
|
|
47
|
+
self,
|
|
48
|
+
config: TodolistConfig,
|
|
49
|
+
context: dict[str, Any],
|
|
50
|
+
overrides: Mapping[str, Any] | None = None,
|
|
51
|
+
) -> None:
|
|
52
|
+
_ = overrides
|
|
53
|
+
config.inject_into(context)
|
|
54
|
+
|
|
55
|
+
def should_render(self, config: TodolistConfig) -> bool:
|
|
56
|
+
return config.uses_todolist
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _detect_todolist(context: Mapping[str, Any]) -> bool:
|
|
60
|
+
tokens = ("\\done", "\\wontfix", "\\begin{todolist}", "\\todolist")
|
|
61
|
+
for value in context.values():
|
|
62
|
+
if not isinstance(value, str):
|
|
63
|
+
continue
|
|
64
|
+
if any(token in value for token in tokens):
|
|
65
|
+
return True
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
fragment = TodolistFragment()
|
|
70
|
+
|
|
71
|
+
__all__ = ["TodolistConfig", "TodolistFragment", "fragment"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
\NeedsTeXFormat{LaTeX2e}
|
|
2
|
+
\ProvidesPackage{ts-todolist}[2025/11/21 TeXSmith todolist helpers]
|
|
3
|
+
|
|
4
|
+
\RequirePackage{enumitem}
|
|
5
|
+
\RequirePackage{amssymb}
|
|
6
|
+
\RequirePackage{pifont}
|
|
7
|
+
|
|
8
|
+
\newlist{todolist}{itemize}{2}
|
|
9
|
+
\setlist[todolist]{label=$\square$, leftmargin=*}
|
|
10
|
+
|
|
11
|
+
\newcommand{\cmark}{\ding{51}}% check mark
|
|
12
|
+
\newcommand{\xmark}{\ding{55}}% cross mark
|
|
13
|
+
|
|
14
|
+
\newcommand{\done}{%
|
|
15
|
+
\rlap{$\square$}{\raisebox{2pt}{\large\hspace{1pt}\cmark}}%
|
|
16
|
+
\hspace{-2.5pt}%
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
\newcommand{\wontfix}{%
|
|
20
|
+
\rlap{$\square$}{\large\hspace{1pt}\xmark}%
|
|
21
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, ClassVar
|
|
7
|
+
|
|
8
|
+
from texsmith.core.fragments.base import BaseFragment, FragmentPiece
|
|
9
|
+
from texsmith.core.templates.manifest import TemplateAttributeSpec, TemplateError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class TypesettingConfig:
|
|
14
|
+
indent_mode: str | None
|
|
15
|
+
parskip: str | None
|
|
16
|
+
leading_mode: str | None
|
|
17
|
+
leading_value: str | None
|
|
18
|
+
lineno_enabled: bool
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def from_context(cls, context: Mapping[str, Any]) -> TypesettingConfig:
|
|
22
|
+
paragraph = context.get("typesetting_paragraph")
|
|
23
|
+
leading = context.get("typesetting_leading")
|
|
24
|
+
lineno = bool(context.get("typesetting_lineno"))
|
|
25
|
+
|
|
26
|
+
indent_mode, parskip = _normalise_paragraph(paragraph)
|
|
27
|
+
leading_mode, leading_value = _normalise_leading(leading)
|
|
28
|
+
|
|
29
|
+
return cls(
|
|
30
|
+
indent_mode=indent_mode,
|
|
31
|
+
parskip=parskip,
|
|
32
|
+
leading_mode=leading_mode,
|
|
33
|
+
leading_value=leading_value,
|
|
34
|
+
lineno_enabled=lineno,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def enabled(self) -> bool:
|
|
38
|
+
return (
|
|
39
|
+
any(
|
|
40
|
+
option is not None for option in (self.indent_mode, self.parskip, self.leading_mode)
|
|
41
|
+
)
|
|
42
|
+
or self.lineno_enabled
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def inject_into(self, context: dict[str, Any]) -> None:
|
|
46
|
+
context["ts_typesetting_indent_mode"] = self.indent_mode
|
|
47
|
+
context["ts_typesetting_parskip"] = self.parskip
|
|
48
|
+
context["ts_typesetting_leading_mode"] = self.leading_mode
|
|
49
|
+
context["ts_typesetting_leading_value"] = self.leading_value
|
|
50
|
+
context["ts_typesetting_enable_lineno"] = self.lineno_enabled
|
|
51
|
+
context["ts_typesetting_enabled"] = self.enabled()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TypesettingFragment(BaseFragment[TypesettingConfig]):
|
|
55
|
+
name: ClassVar[str] = "ts-typesetting"
|
|
56
|
+
description: ClassVar[str] = "Paragraph spacing, line spacing, and line numbers controls."
|
|
57
|
+
pieces: ClassVar[list[FragmentPiece]] = [
|
|
58
|
+
FragmentPiece(
|
|
59
|
+
template_path=Path(__file__).with_name("ts-typesetting.tex.jinja"),
|
|
60
|
+
kind="inline",
|
|
61
|
+
slot="extra_packages",
|
|
62
|
+
)
|
|
63
|
+
]
|
|
64
|
+
attributes: ClassVar[dict[str, TemplateAttributeSpec]] = {
|
|
65
|
+
"typesetting_paragraph": TemplateAttributeSpec(
|
|
66
|
+
default=None,
|
|
67
|
+
type="mapping",
|
|
68
|
+
sources=[
|
|
69
|
+
"typesetting.paragraph",
|
|
70
|
+
"paragraph",
|
|
71
|
+
],
|
|
72
|
+
),
|
|
73
|
+
"typesetting_leading": TemplateAttributeSpec(
|
|
74
|
+
default=None,
|
|
75
|
+
type="string",
|
|
76
|
+
allow_empty=True,
|
|
77
|
+
sources=[
|
|
78
|
+
"typesetting.leading",
|
|
79
|
+
"leading",
|
|
80
|
+
],
|
|
81
|
+
),
|
|
82
|
+
"typesetting_lineno": TemplateAttributeSpec(
|
|
83
|
+
default=None,
|
|
84
|
+
type="boolean",
|
|
85
|
+
sources=[
|
|
86
|
+
"typesetting.lineno",
|
|
87
|
+
"lineno",
|
|
88
|
+
],
|
|
89
|
+
),
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
config_cls: ClassVar[type[TypesettingConfig]] = TypesettingConfig
|
|
93
|
+
source: ClassVar[Path] = Path(__file__).with_name("ts-typesetting.tex.jinja")
|
|
94
|
+
context_defaults: ClassVar[dict[str, Any]] = {"extra_packages": ""}
|
|
95
|
+
|
|
96
|
+
def build_config(
|
|
97
|
+
self, context: Mapping[str, Any], overrides: Mapping[str, Any] | None = None
|
|
98
|
+
) -> TypesettingConfig:
|
|
99
|
+
_ = overrides
|
|
100
|
+
return self.config_cls.from_context(context)
|
|
101
|
+
|
|
102
|
+
def inject(
|
|
103
|
+
self,
|
|
104
|
+
config: TypesettingConfig,
|
|
105
|
+
context: dict[str, Any],
|
|
106
|
+
overrides: Mapping[str, Any] | None = None,
|
|
107
|
+
) -> None:
|
|
108
|
+
_ = overrides
|
|
109
|
+
config.inject_into(context)
|
|
110
|
+
|
|
111
|
+
def should_render(self, config: TypesettingConfig) -> bool:
|
|
112
|
+
return config.enabled()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _normalise_paragraph(payload: Any) -> tuple[str | None, str | None]:
|
|
116
|
+
indent_mode: str | None = None
|
|
117
|
+
spacing: str | None = None
|
|
118
|
+
|
|
119
|
+
if payload is None:
|
|
120
|
+
return (indent_mode, spacing)
|
|
121
|
+
|
|
122
|
+
if isinstance(payload, Mapping):
|
|
123
|
+
indent_mode = _normalise_indent(payload.get("indent"))
|
|
124
|
+
spacing = _normalise_spacing(payload.get("spacing"))
|
|
125
|
+
return (indent_mode, spacing)
|
|
126
|
+
|
|
127
|
+
indent_mode = _normalise_indent(payload)
|
|
128
|
+
return (indent_mode, spacing)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _normalise_indent(value: Any) -> str | None:
|
|
132
|
+
if value is None:
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
if isinstance(value, bool):
|
|
136
|
+
return "always" if value else "none"
|
|
137
|
+
|
|
138
|
+
if isinstance(value, (int, float)):
|
|
139
|
+
return "always" if value else "none"
|
|
140
|
+
|
|
141
|
+
if isinstance(value, str):
|
|
142
|
+
token = value.strip().lower()
|
|
143
|
+
if not token:
|
|
144
|
+
return None
|
|
145
|
+
if token == "auto":
|
|
146
|
+
return "auto"
|
|
147
|
+
if token in {"true", "yes", "on", "indent", "indented"}:
|
|
148
|
+
return "always"
|
|
149
|
+
if token in {"false", "no", "off", "none"}:
|
|
150
|
+
return "none"
|
|
151
|
+
raise TemplateError("paragraph.indent must be one of: true, false, auto.")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _normalise_spacing(value: Any) -> str | None:
|
|
155
|
+
if value is None:
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
if isinstance(value, (int, float)):
|
|
159
|
+
formatted = _format_number(float(value))
|
|
160
|
+
return f"{formatted}pt"
|
|
161
|
+
|
|
162
|
+
if isinstance(value, str):
|
|
163
|
+
trimmed = value.strip()
|
|
164
|
+
return trimmed or None
|
|
165
|
+
|
|
166
|
+
raise TemplateError("paragraph.spacing must be a string or number.")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _normalise_leading(value: Any) -> tuple[str | None, str | None]:
|
|
170
|
+
if value is None:
|
|
171
|
+
return (None, None)
|
|
172
|
+
|
|
173
|
+
if isinstance(value, (int, float)):
|
|
174
|
+
factor = float(value)
|
|
175
|
+
if factor <= 0:
|
|
176
|
+
raise TemplateError("leading must be a positive value.")
|
|
177
|
+
return ("factor", _format_number(factor))
|
|
178
|
+
|
|
179
|
+
if isinstance(value, str):
|
|
180
|
+
trimmed = value.strip()
|
|
181
|
+
if not trimmed:
|
|
182
|
+
return (None, None)
|
|
183
|
+
|
|
184
|
+
token = trimmed.lower()
|
|
185
|
+
if token in {"single", "singlespace", "singlespacing"}:
|
|
186
|
+
return ("single", None)
|
|
187
|
+
if token in {"onehalf", "one-half", "onehalfspacing", "one half"}:
|
|
188
|
+
return ("onehalf", None)
|
|
189
|
+
if token in {"double", "doublespacing", "double-spacing"}:
|
|
190
|
+
return ("double", None)
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
factor = float(trimmed)
|
|
194
|
+
except ValueError:
|
|
195
|
+
return ("length", trimmed)
|
|
196
|
+
|
|
197
|
+
if factor <= 0:
|
|
198
|
+
raise TemplateError("leading must be a positive value.")
|
|
199
|
+
return ("factor", _format_number(factor))
|
|
200
|
+
|
|
201
|
+
raise TemplateError(
|
|
202
|
+
"leading must be 'single', 'onehalf', 'double', a length, or a positive number."
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _format_number(value: float) -> str:
|
|
207
|
+
rounded = f"{value:.3f}"
|
|
208
|
+
cleaned = rounded.rstrip("0").rstrip(".")
|
|
209
|
+
return cleaned or "0"
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
fragment = TypesettingFragment()
|
|
213
|
+
|
|
214
|
+
__all__ = ["TypesettingConfig", "TypesettingFragment", "fragment"]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
\BLOCK{ if ts_typesetting_enabled }
|
|
2
|
+
% Typesetting controls (paragraphs, leading, lineno)
|
|
3
|
+
\makeatletter
|
|
4
|
+
\BLOCK{ if ts_typesetting_indent_mode }
|
|
5
|
+
\BLOCK{ if ts_typesetting_indent_mode == "always" }
|
|
6
|
+
\@afterindenttrue
|
|
7
|
+
\BLOCK{ elif ts_typesetting_indent_mode == "auto" }
|
|
8
|
+
\@afterindentfalse
|
|
9
|
+
\BLOCK{ elif ts_typesetting_indent_mode == "none" }
|
|
10
|
+
\setlength{\parindent}{0pt}
|
|
11
|
+
\@afterindentfalse
|
|
12
|
+
\BLOCK{ endif }
|
|
13
|
+
\BLOCK{ endif }
|
|
14
|
+
\makeatother
|
|
15
|
+
|
|
16
|
+
\BLOCK{ if ts_typesetting_parskip }
|
|
17
|
+
\setlength{\parskip}{\VAR{ts_typesetting_parskip}}
|
|
18
|
+
\BLOCK{ endif }
|
|
19
|
+
|
|
20
|
+
\BLOCK{ if ts_typesetting_leading_mode }
|
|
21
|
+
\makeatletter
|
|
22
|
+
\@ifclassloaded{memoir}{%
|
|
23
|
+
\providecommand{\tsTypesettingApplySpacing}[1]{%
|
|
24
|
+
\ifcsname Spacing\endcsname
|
|
25
|
+
\Spacing{##1}%
|
|
26
|
+
\else
|
|
27
|
+
\renewcommand{\baselinestretch}{##1}\selectfont
|
|
28
|
+
\fi
|
|
29
|
+
}%
|
|
30
|
+
\providecommand{\tsTypesettingSingle}{%
|
|
31
|
+
\ifcsname SingleSpacing\endcsname
|
|
32
|
+
\SingleSpacing
|
|
33
|
+
\else
|
|
34
|
+
\tsTypesettingApplySpacing{1}%
|
|
35
|
+
\fi
|
|
36
|
+
}%
|
|
37
|
+
\providecommand{\tsTypesettingOnehalf}{%
|
|
38
|
+
\ifcsname OnehalfSpacing\endcsname
|
|
39
|
+
\OnehalfSpacing
|
|
40
|
+
\else
|
|
41
|
+
\tsTypesettingApplySpacing{1.5}%
|
|
42
|
+
\fi
|
|
43
|
+
}%
|
|
44
|
+
\providecommand{\tsTypesettingDouble}{%
|
|
45
|
+
\ifcsname DoubleSpacing\endcsname
|
|
46
|
+
\DoubleSpacing
|
|
47
|
+
\else
|
|
48
|
+
\tsTypesettingApplySpacing{2}%
|
|
49
|
+
\fi
|
|
50
|
+
}%
|
|
51
|
+
}{%
|
|
52
|
+
\@ifpackageloaded{setspace}{}{%
|
|
53
|
+
\RequirePackage{setspace}%
|
|
54
|
+
}%
|
|
55
|
+
\providecommand{\tsTypesettingApplySpacing}[1]{\setstretch{##1}}%
|
|
56
|
+
\providecommand{\tsTypesettingSingle}{\singlespacing}%
|
|
57
|
+
\providecommand{\tsTypesettingOnehalf}{\onehalfspacing}%
|
|
58
|
+
\providecommand{\tsTypesettingDouble}{\doublespacing}%
|
|
59
|
+
}
|
|
60
|
+
\makeatother
|
|
61
|
+
|
|
62
|
+
\BLOCK{ if ts_typesetting_leading_mode == "single" }
|
|
63
|
+
\tsTypesettingSingle
|
|
64
|
+
\BLOCK{ elif ts_typesetting_leading_mode == "onehalf" }
|
|
65
|
+
\tsTypesettingOnehalf
|
|
66
|
+
\BLOCK{ elif ts_typesetting_leading_mode == "double" }
|
|
67
|
+
\tsTypesettingDouble
|
|
68
|
+
\BLOCK{ elif ts_typesetting_leading_mode == "factor" }
|
|
69
|
+
\tsTypesettingApplySpacing{\VAR{ts_typesetting_leading_value}}
|
|
70
|
+
\BLOCK{ elif ts_typesetting_leading_mode == "length" }
|
|
71
|
+
\setlength{\baselineskip}{\VAR{ts_typesetting_leading_value}}
|
|
72
|
+
\BLOCK{ endif }
|
|
73
|
+
\BLOCK{ endif }
|
|
74
|
+
|
|
75
|
+
\BLOCK{ if ts_typesetting_enable_lineno }
|
|
76
|
+
\@ifpackageloaded{lineno}{}{%
|
|
77
|
+
\RequirePackage{lineno}%
|
|
78
|
+
}
|
|
79
|
+
\AtBeginDocument{\linenumbers}
|
|
80
|
+
\BLOCK{ endif }
|
|
81
|
+
\BLOCK{ endif }
|
texsmith/index.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Public API exposing TeXSmith's hashtag index extension."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .extensions.index import (
|
|
6
|
+
IndexEntry,
|
|
7
|
+
IndexRegistry,
|
|
8
|
+
TexsmithIndexExtension,
|
|
9
|
+
clear_registry,
|
|
10
|
+
get_registry,
|
|
11
|
+
)
|
|
12
|
+
from .extensions.index.markdown import makeExtension
|
|
13
|
+
from .extensions.index.mkdocs_plugin import IndexPlugin
|
|
14
|
+
from .extensions.index.renderer import register as register_renderer
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"IndexEntry",
|
|
19
|
+
"IndexPlugin",
|
|
20
|
+
"IndexRegistry",
|
|
21
|
+
"TexsmithIndexExtension",
|
|
22
|
+
"clear_registry",
|
|
23
|
+
"get_registry",
|
|
24
|
+
"makeExtension",
|
|
25
|
+
"register_renderer",
|
|
26
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Compatibility layer exposing plugin integrations to documentation.
|
|
2
|
+
|
|
3
|
+
TeXSmith consolidated its plugin utilities under
|
|
4
|
+
``texsmith.adapters.plugins``. mkdocstrings still references the historical
|
|
5
|
+
``texsmith.plugins`` namespace, so we re-export the maintained modules here to
|
|
6
|
+
keep the public import path available.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from texsmith.adapters.plugins import material, snippet
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
__all__ = ["material", "snippet"]
|
texsmith/progressbar.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Public entry points for the progress bar Markdown extension."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .extensions.progressbar import ProgressBarExtension, makeExtension, register_renderer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
__all__ = ["ProgressBarExtension", "makeExtension", "register_renderer"]
|
texsmith/quotes.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Markdown extension converting straight double quotes into semantic ``<q>`` tags."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from re import Match
|
|
6
|
+
import xml.etree.ElementTree as ElementTree
|
|
7
|
+
|
|
8
|
+
from markdown import Extension, Markdown
|
|
9
|
+
from markdown.inlinepatterns import InlineProcessor
|
|
10
|
+
from markdown.util import AtomicString
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class _QuoteInlineProcessor(InlineProcessor):
|
|
14
|
+
"""Wrap straight double quotes in ``<q>`` elements."""
|
|
15
|
+
|
|
16
|
+
def handleMatch( # type: ignore[override] # noqa: N802
|
|
17
|
+
self,
|
|
18
|
+
match: Match[str],
|
|
19
|
+
data: str,
|
|
20
|
+
) -> tuple[ElementTree.Element, int, int]:
|
|
21
|
+
text = match.group(1)
|
|
22
|
+
element = ElementTree.Element("q")
|
|
23
|
+
element.text = AtomicString(text)
|
|
24
|
+
return element, match.start(0), match.end(0)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TexsmithQuotesExtension(Extension):
|
|
28
|
+
"""Register the quote inline processor."""
|
|
29
|
+
|
|
30
|
+
def extendMarkdown(self, md: Markdown) -> None: # type: ignore[override] # noqa: N802
|
|
31
|
+
pattern = r'(?<!\\)"([^"\n]+?)"'
|
|
32
|
+
processor = _QuoteInlineProcessor(pattern, md)
|
|
33
|
+
md.inlinePatterns.register(processor, "texsmith_quotes", 65)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def makeExtension(**kwargs: object) -> TexsmithQuotesExtension: # noqa: N802
|
|
37
|
+
return TexsmithQuotesExtension(**kwargs)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__all__ = ["TexsmithQuotesExtension", "makeExtension"]
|