-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlatexclean.py
executable file
·177 lines (158 loc) · 4.08 KB
/
latexclean.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#!/usr/bin/env python3
"""
Clean LaTeX build files with the option of doing so recursively.
Asks before removing files.
https://gist.github.com/klieret/b3e032095d78269c811f4f8c85f6227b
Kilian Lieret 2020
MIT license
No warranty
"""
import argparse
from pathlib import Path, PurePath
import re
from typing import Iterable, List, Union
import os
latex_aux_extensions = [
"aux",
"bbl",
"blg",
"brf",
"idx",
"ilg",
"ind",
"lof",
"log",
"lol",
"lot",
"out",
"toc",
"synctex.gz",
"nav",
"snm",
"vrb",
"pyg",
"bfc",
"fls",
"glo",
"ist",
"run.xml",
"bcf",
"fdb_latexmk",
"glg",
"gls",
"tdo"
]
def yn_prompt(question: str, yes=None, no=None) -> bool:
"""Ask yes-no question.
Args:
question: Description of the prompt
yes: List of strings interpreted as yes
no: List of strings interpreted as no
Returns:
True if yes, False if no.
"""
if yes is None:
yes = ["yes", "ye", "y"]
if no is None:
no = ["no", "n"]
prompt = question
if not prompt.endswith(" "):
prompt += " "
prompt += "[{} or {}] ".format("/".join(yes), "/".join(no))
print(prompt, end="")
while True:
choice = input().lower().strip()
if choice in yes:
return True
elif choice in no:
return False
else:
print(
"Please respond with '{}' or '{}': ".format(
"/".join(yes), "/".join(no)
),
end="",
)
def build_file_ext_regex(extensions: Iterable[str]):
""" Regular expression that matches file names of any extension.
Args:
extensions: List of extensions without leading '.'
Returns:
Compiled regular expression
"""
return re.compile(".*\\.(" + "|".join(extensions) + ")")
def get_files_with_extension(
working_dir: Union[str, PurePath], extensions: Iterable[str], recursive=False
) -> List[Path]:
""" Get all files with extension in directory.
'.git' directories are ignored.
Args:
working_dir: directory
extensions:
Returns:
list of Path objects with the matches
"""
regex = build_file_ext_regex(extensions)
if not recursive:
return [
path for path in Path(working_dir).iterdir()
if regex.match(path.name)
]
else:
matches = []
for root, dirs, files in os.walk(str(working_dir)):
# Remove .git directories, because they contain .idx files
dirs[:] = [d for d in dirs if d not in [".git"]]
matches += [Path(root) / file for file in files if regex.match(file)]
return matches
def ask_remove(files: List[Union[str, PurePath]], stream=print) -> None:
""" Ask whether to remove files and then do it
Args:
files: List of files as paths or string
stream: alternative print function (e.g. for logging)
Returns:
None
"""
if not files:
stream("No files to remove.")
return
stream("The following files will be removed:")
for file in files:
stream("* " + str(file))
if yn_prompt("Remove?"):
for file in files:
os.remove(str(file))
else:
stream("Abort.")
def cli() -> None:
""" Command line interface
Returns:
None
"""
parser = argparse.ArgumentParser(
"latexclean",
description=__file__.__doc__
)
parser.add_argument(
"-r",
"--recursive",
help="Remove files recursively",
action="store_true",
default=False
)
parser.add_argument(
"-f",
"--force",
help="Do not prompt before removing files.",
action="store_true",
default=False
)
args = parser.parse_args()
files = get_files_with_extension(Path("."), latex_aux_extensions, recursive=args.recursive)
if not args.force:
ask_remove(files)
else:
for file in files:
os.remove(str(file))
if __name__ == "__main__":
cli()