Skip to content

Commit 4fec4c5

Browse files
committed
🎉 Init commit
0 parents  commit 4fec4c5

20 files changed

+1022
-0
lines changed

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# generated on execution
2+
__pycache__
3+
4+
# generated on `npm link mathjax@3`
5+
node_modules
6+
package.json
7+
package-lock.json
8+
9+
# generated on `pip install -e .`
10+
*.egg-info/
11+
12+
# generated on `python3 setup.py build`
13+
build/
14+
15+
# generated on `pyenv local 3.8.12`
16+
.python-version

adoc_math/__main__.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import argparse as argp
2+
3+
from . import i_impl
4+
5+
NAME = "adoc-math" # Cannot import from setup.py, src
6+
DESCRIPTION = """Use MathJax (Latex or AsciiMath) in your AsciiDoc projects!"""
7+
8+
list_of_files = dict(
9+
metavar="files",
10+
action="store",
11+
nargs="*",
12+
default=list(),
13+
)
14+
15+
16+
def parse_args():
17+
p = argp.ArgumentParser(
18+
prog=NAME,
19+
description=DESCRIPTION,
20+
)
21+
22+
p.add_argument(
23+
"target_files", help="List of files to run `adoc-math` on.", **list_of_files
24+
)
25+
26+
p.add_argument(
27+
"--exclude",
28+
help="List of files to be excluded; overrides `target_files`.",
29+
**list_of_files,
30+
)
31+
32+
p.add_argument(
33+
"--include",
34+
help="List of files to be included; overrides `target_files` and `--exclude`.",
35+
**list_of_files,
36+
)
37+
38+
# p.add_argument("--default-alignment", help="Default alignment")
39+
40+
p.add_argument(
41+
"--default-lang",
42+
help="Default language. Can be TEX (LaTeX) or AMATH (AsciiMath).",
43+
)
44+
45+
p.add_argument(
46+
"--default-max-lines",
47+
help="To ensure that you don't forget to close a cell, there is an option to set the maximum number of lines that a cell can have. This option sets the default value, but it can be overridden: as `$$max_lines=10\nx\n$$`. Max lines option (either through CLI or through a cell attribute) has no effect on inline cells, since they span just one line. Default: 6.",
48+
)
49+
p.add_argument(
50+
"--filename-snippet-length",
51+
type=int,
52+
help="Created files (svg) will have several parts. One of them is a snippet of the cell contents. This option controls how many characters that snippet will be. Default: 4.",
53+
)
54+
55+
p.add_argument(
56+
"--filename-bytes-length",
57+
type=int,
58+
help="Another part of the svg file name is a random byte sequence. (This is necessary since the snippet only has e.g. four characters and also because not all Latex characters are allowed as filenames.) This option controls how many bytes that sequence will be. Default: 4.",
59+
)
60+
61+
p.add_argument(
62+
"--images-dir",
63+
help="If you use `:imagesdir:` in your project, this should be the same value as that. In particular, it shouldn't be a path, but rather a string specifying the Unix-style subdirectory of `pwd`. For example, `assets/imgs`, or just `imgs.` It is used to determine the path part of the image macro (e.g. image::{path}.svg[]). In Asciidoc, `:imagesdir:` is by default `pwd`, so that is the default value here as well.",
64+
)
65+
66+
p.add_argument(
67+
"--output-dir",
68+
help="The subdirectory of `--images-dir` that will contain the svgs. By default, it will be `imgs/adoc-math` if `--images-dir` is not defined, and `adoc-math` if it is defined.",
69+
)
70+
71+
args = p.parse_args()
72+
return args
73+
74+
75+
def main():
76+
args = parse_args()
77+
78+
print(args)
79+
80+
# The idea here is that the arguments specified in `parse_args` will have value
81+
# None if they are not specified by the user. If we just use something like `i_impl.AdocMath(default_alignment=args.default_alignment`
82+
# etc (for all other arguments), then we'll lose the default values
83+
# as specified in AdocMath (e.g. default_alignment = Alignment.ALIGN)
84+
# So instead, let's just filter out those that are None
85+
# (more specifically those that are falsy), and pass in the rest
86+
kwargs = {key: val for key, val in vars(args).items() if val}
87+
adoc_math = i_impl.AdocMath(**kwargs)
88+
adoc_math.run()
89+
90+
91+
if __name__ == "__main__":
92+
main()

adoc_math/_common/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .e_utils import *

adoc_math/_common/a_imports.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import enum
2+
import os
3+
import subprocess
4+
import dataclasses
5+
import pathlib as plib
6+
import re
7+
import logging
8+
import xml.etree.ElementTree
9+
import xml.dom.minidom
10+
import sys
11+
import collections
12+
import collections.abc
13+
from typing import (
14+
Dict,
15+
List,
16+
NamedTuple,
17+
MutableSequence,
18+
Set,
19+
Tuple,
20+
Union,
21+
Callable,
22+
Iterable,
23+
Type,
24+
NewType,
25+
TypeVar,
26+
Optional,
27+
)
28+
import logging
29+
import inspect
30+
import pprint
31+
import xml.dom.minidom
32+
import random

adoc_math/_common/b_types.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from .c_constants import *
2+
3+
OptionsRaw = NewType("OptionsRaw", str)
4+
OptionRaw = NewType("OptionRaw", str)
5+
AssignmentOpt = NewType("AssignmentOpt", str)
6+
SkipCount = NewType("SkipCount", int)
7+
Lineno = NewType("Lineno", int)
8+
Linenum = NewType("Linenum", int)
9+
"""Line number
10+
11+
We're using the following terminology:
12+
* lineno is line number 0-indexed
13+
* this follows e.g. python ast's lineno
14+
* linenum is line number 1-indexed
15+
* this follows e.g. editors (e.g. vs code)"""
16+
MaxLines = NewType("MaxLines", int)
17+
LineInFile = NewType("LineIFile", str)
18+
Command = NewType("Command", str)
19+
StdOut = NewType("StdOut", str)
20+
StdErr = NewType("StdErr", str)
21+
XmlStr = NewType("XmlStr", str)
22+
CellContent = NewType("CellContent", str)
23+
FileName = NewType("FileName", str)
24+
SnippetPart = NewType("SnippetPart", str)
25+
DiffContent = NewType("DiffContent", str)
26+
LinesInserted = NewType("LinesInserted", int)
27+
28+
29+
class OutputMode(str, enum.Enum):
30+
OVERWRITE = "OVERWRITE"
31+
DIFF = "DIFF"
32+
33+
34+
class Lang(str, enum.Enum):
35+
AMATH = "AMATH"
36+
TEX = "TEX"
37+
38+
39+
# class Alignment(str, enum.Enum):
40+
# ALIGN = "ALIGN"
41+
# DONT_ALIGN = "DONT_ALIGN"
42+
43+
44+
class Opts(NamedTuple):
45+
lang: Lang
46+
# alignment: Alignment
47+
max_lines: MaxLines
48+
49+
50+
class BlockCell(NamedTuple):
51+
path: plib.Path
52+
linenum: Linenum # 1-indexed line num of opening tag
53+
closing_tag_linenum: Linenum
54+
opts: Opts
55+
content: CellContent
56+
57+
58+
class InlineCell(NamedTuple):
59+
path: plib.Path
60+
linenum: Linenum
61+
opts: Opts
62+
content: CellContent
63+
64+
65+
Cell = Union[InlineCell, BlockCell]

adoc_math/_common/c_constants.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from .a_imports import *
2+
3+
LOGGING_LEVEL = logging.DEBUG
4+
5+
# Note: whitespace before $$ is not allowed
6+
REGEX_BLOCK_OPENING_TAG_SRC = r"^\$\$(?P<opts>\S*)\s*$"
7+
REGEX_BLOCK_OPENING_TAG = re.compile(REGEX_BLOCK_OPENING_TAG_SRC)
8+
9+
# Note: whitespace before $$ is not allowed
10+
REGEX_BLOCK_CLOSING_TAG_SRC = r"^\$\$\s*$"
11+
REGEX_BLOCK_CLOSING_TAG = re.compile(REGEX_BLOCK_CLOSING_TAG_SRC)
12+
13+
REGEX_INLINE_SRC = r"^\$(?P<content>.*?)\$(?P<opts>\S*)\s*$"
14+
REGEX_INLINE = re.compile(REGEX_INLINE_SRC)
15+
16+
REPO = "https://github.com/hacker-dom/asciidoc-mathjax"
17+
18+
DEAD_CODE_MSG = (
19+
f"""This case should not happen. Please create an issue at {REPO}/issues/new."""
20+
)

adoc_math/_common/d_exceptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .b_types import *
2+
3+
4+
class AdocMathException(Exception):
5+
pass

adoc_math/_common/e_utils.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from .d_exceptions import *
2+
3+
4+
def get_logger(file_name: str, level: int) -> logging.Logger:
5+
logger = logging.getLogger(file_name)
6+
handler = logging.StreamHandler()
7+
formatter = logging.Formatter(
8+
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
9+
)
10+
handler.setFormatter(formatter)
11+
logger.addHandler(handler)
12+
logger.setLevel(level)
13+
return logger
14+
15+
16+
logger = get_logger(__file__, LOGGING_LEVEL)
17+
18+
19+
def run_cmd(
20+
cmd: Command,
21+
stdin=bytes(),
22+
raise_on_error=True,
23+
) -> Tuple[StdOut, StdErr]:
24+
print(f"Running {cmd}...")
25+
process = subprocess.Popen(
26+
cmd,
27+
stdout=subprocess.PIPE,
28+
stderr=subprocess.PIPE,
29+
stdin=subprocess.PIPE,
30+
shell=True,
31+
)
32+
stdout, stderr = process.communicate(input=stdin)
33+
process.wait()
34+
if raise_on_error and process.returncode != 0:
35+
raise AdocMathException(stdout.decode() + stderr.decode())
36+
else:
37+
return StdOut(stdout.decode()), StdErr(stderr.decode())
38+
39+
40+
def for_each_apply_method(
41+
ps: Iterable[Union[str, plib.Path]], # path strs
42+
method: Callable[[plib.Path], None],
43+
):
44+
"""Calls a method with an argument of all of ps.
45+
If a p is a directory, it searches it recursively.
46+
The idea is that the method is bound to some object (such as set), and this function allows easy updating of that set over a tree of files.
47+
"""
48+
for p_path_or_str in ps:
49+
p = plib.Path(p_path_or_str).resolve(strict=True)
50+
if p.is_dir():
51+
for_each_apply_method(
52+
ps=sorted(p.glob("*")),
53+
method=method,
54+
)
55+
elif p.is_file():
56+
method(p)
57+
else:
58+
raise AdocMathException(DEAD_CODE_MSG)
59+
60+
61+
def log(*args):
62+
"""Useful for debuggin :-P
63+
64+
https://stackoverflow.com/a/2749857/4204961"""
65+
66+
frame = inspect.currentframe()
67+
frame = inspect.getouterframes(frame)[1]
68+
string = inspect.getframeinfo(frame[0]).code_context[0].strip() # type: ignore
69+
params = string[string.find("(") + 1 : -1].split(",")
70+
71+
names = []
72+
for i in params:
73+
if i.find("=") != -1:
74+
names.append(i.split("=")[1].strip())
75+
76+
else:
77+
names.append(i)
78+
79+
for name, val in zip(names, args):
80+
logger.debug(f"\n {name} =\n{' ' * 14}{pprint.pformat(val)}")
81+
82+
83+
def join_with(
84+
it: Iterable[str],
85+
joiner: str,
86+
) -> str:
87+
"""Reverses the arguments of x.join(y)"""
88+
89+
return joiner.join(it)

adoc_math/_setup/__main__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from . import a_install, b_link
2+
3+
4+
def main():
5+
i = a_install.Install()
6+
i.run()
7+
l = b_link.Link()
8+
l.run()
9+
10+
11+
if __name__ == "__main__":
12+
main()

adoc_math/_setup/a_install.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from .._common import *
2+
3+
4+
class Install:
5+
def run():
6+
run_cmd(
7+
cmd=Command("npm i -g mathjax@3"),
8+
)
9+
10+
11+
def main():
12+
i = Install()
13+
i.run()
14+
15+
16+
if __name__ == "__main__":
17+
main()

0 commit comments

Comments
 (0)