pyeasyphd 0.4.42__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.
- pyeasyphd/.python-version +1 -0
- pyeasyphd/Main.sublime-menu +43 -0
- pyeasyphd/__init__.py +5 -0
- pyeasyphd/data/templates/csl/apa-no-ampersand.csl +2183 -0
- pyeasyphd/data/templates/csl/apa.csl +2133 -0
- pyeasyphd/data/templates/csl/ieee.csl +512 -0
- pyeasyphd/data/templates/tex/Article.tex +38 -0
- pyeasyphd/data/templates/tex/Article_Header.tex +29 -0
- pyeasyphd/data/templates/tex/Article_Tail.tex +3 -0
- pyeasyphd/data/templates/tex/Beamer_Header.tex +79 -0
- pyeasyphd/data/templates/tex/Beamer_Tail.tex +14 -0
- pyeasyphd/data/templates/tex/Style.tex +240 -0
- pyeasyphd/data/templates/tex/TEVC_Header.tex +52 -0
- pyeasyphd/data/templates/tex/TEVC_Tail.tex +4 -0
- pyeasyphd/data/templates/tex/eisvogel.tex +1064 -0
- pyeasyphd/data/templates/tex/math.tex +201 -0
- pyeasyphd/data/templates/tex/math_commands.tex +677 -0
- pyeasyphd/data/templates/tex/nextaimathmacros.sty +681 -0
- pyeasyphd/main/__init__.py +6 -0
- pyeasyphd/main/basic_input.py +101 -0
- pyeasyphd/main/pandoc_md_to.py +380 -0
- pyeasyphd/main/python_run_md.py +320 -0
- pyeasyphd/main/python_run_tex.py +200 -0
- pyeasyphd/pyeasyphd.py +86 -0
- pyeasyphd/pyeasyphd.sublime-settings +100 -0
- pyeasyphd/pyeasyphd.sublime-syntax +5 -0
- pyeasyphd/scripts/__init__.py +34 -0
- pyeasyphd/scripts/_base.py +65 -0
- pyeasyphd/scripts/run_article_md.py +101 -0
- pyeasyphd/scripts/run_article_tex.py +94 -0
- pyeasyphd/scripts/run_beamer_tex.py +84 -0
- pyeasyphd/scripts/run_compare.py +71 -0
- pyeasyphd/scripts/run_format.py +62 -0
- pyeasyphd/scripts/run_generate.py +211 -0
- pyeasyphd/scripts/run_replace.py +34 -0
- pyeasyphd/scripts/run_search.py +251 -0
- pyeasyphd/tools/__init__.py +12 -0
- pyeasyphd/tools/generate/generate_from_bibs.py +181 -0
- pyeasyphd/tools/generate/generate_html.py +166 -0
- pyeasyphd/tools/generate/generate_library.py +203 -0
- pyeasyphd/tools/generate/generate_links.py +400 -0
- pyeasyphd/tools/py_run_bib_md_tex.py +398 -0
- pyeasyphd/tools/search/data.py +282 -0
- pyeasyphd/tools/search/search_base.py +146 -0
- pyeasyphd/tools/search/search_core.py +400 -0
- pyeasyphd/tools/search/search_keywords.py +229 -0
- pyeasyphd/tools/search/search_writers.py +350 -0
- pyeasyphd/tools/search/utils.py +190 -0
- pyeasyphd/utils/utils.py +99 -0
- pyeasyphd-0.4.42.dist-info/METADATA +33 -0
- pyeasyphd-0.4.42.dist-info/RECORD +53 -0
- pyeasyphd-0.4.42.dist-info/WHEEL +4 -0
- pyeasyphd-0.4.42.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import shutil
|
|
5
|
+
import time
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from pyadvtools import combine_content_in_list, delete_empty_lines_last_occur_add_new_line, read_list, write_list
|
|
9
|
+
from pybibtexer.bib.core import ConvertStrToLibrary
|
|
10
|
+
from pybibtexer.main.python_writers import PythonWriters
|
|
11
|
+
|
|
12
|
+
from .basic_input import BasicInput
|
|
13
|
+
from .pandoc_md_to import PandocMdTo
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def batch_convert_citations(text):
|
|
17
|
+
r"""Process all citations in the text, including multiple citations in one bracket.
|
|
18
|
+
|
|
19
|
+
Example: [@ref1; @ref2] -> <sup>[@ref1](#ref1)</sup><sup>[@ref2](#ref2)</sup>
|
|
20
|
+
"""
|
|
21
|
+
# Match all citation patterns within square brackets
|
|
22
|
+
pattern = r"\[([^]]+)\]"
|
|
23
|
+
|
|
24
|
+
def process_citation(match):
|
|
25
|
+
citations = match.group(1)
|
|
26
|
+
# Split multiple citations (support semicolon or comma separation)
|
|
27
|
+
citation_list = re.split(r"[;,]", citations)
|
|
28
|
+
|
|
29
|
+
result = []
|
|
30
|
+
for citation in citation_list:
|
|
31
|
+
citation = citation.strip()
|
|
32
|
+
if citation.startswith("@"):
|
|
33
|
+
cite_id = citation[1:] # Remove the @ symbol
|
|
34
|
+
result.append(f"[{citation}](#{cite_id.lower()})")
|
|
35
|
+
else:
|
|
36
|
+
# Keep non-citation content in original format
|
|
37
|
+
result.append(f"[{citation}]")
|
|
38
|
+
|
|
39
|
+
return "".join(result)
|
|
40
|
+
|
|
41
|
+
return re.sub(pattern, process_citation, text)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class PythonRunMd(BasicInput):
|
|
45
|
+
r"""Python markdown processing class.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
options (dict[str, Any]): Configuration options.
|
|
49
|
+
|
|
50
|
+
Attributes:
|
|
51
|
+
delete_temp_generate_md (bool): Whether to delete temporary generated markdown files. Defaults to True.
|
|
52
|
+
add_reference_in_md (bool): Whether to add references in markdown. Defaults to True.
|
|
53
|
+
add_bib_in_md (bool): Whether to add bibliography in markdown. Defaults to False.
|
|
54
|
+
replace_cite_to_fullcite_in_md (bool): Whether to replace citations with full citations in markdown. Defaults to False.
|
|
55
|
+
replace_by_basic_beauty_complex_in_md (str): Replace by basic, beauty, or complex format. Defaults to "beauty".
|
|
56
|
+
display_basic_beauty_complex_references_in_md (str): Display basic, beauty, or complex references. Defaults to "beauty".
|
|
57
|
+
add_anchor_in_md (bool): Whether add anchor in markdown. Defaults to False.
|
|
58
|
+
details_to_bib_separator (str): Separator between <details> and bibliography content. Defaults to "\n".
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, options: dict[str, Any]) -> None:
|
|
62
|
+
"""Initialize PythonRunMd with configuration options.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
options (dict[str, Any]): Configuration options.
|
|
66
|
+
"""
|
|
67
|
+
super().__init__(options)
|
|
68
|
+
|
|
69
|
+
# for md
|
|
70
|
+
self.final_output_main_md_name: str = options.get("final_output_main_md_name", "")
|
|
71
|
+
self.delete_temp_generate_md: bool = options.get("delete_temp_generate_md", True)
|
|
72
|
+
self.add_reference_in_md: bool = options.get("add_reference_in_md", True)
|
|
73
|
+
self.add_bib_in_md: bool = options.get("add_bib_in_md", False)
|
|
74
|
+
self.replace_cite_to_fullcite_in_md: bool = options.get("replace_cite_to_fullcite_in_md", False)
|
|
75
|
+
self.replace_by_basic_beauty_complex_in_md: str = options.get("replace_by_basic_beauty_complex_in_md", "beauty")
|
|
76
|
+
self.display_basic_beauty_complex_references_in_md: str = options.get(
|
|
77
|
+
"display_basic_beauty_complex_references_in_md", "beauty"
|
|
78
|
+
)
|
|
79
|
+
self.add_anchor_in_md: bool = options.get("add_anchor_in_md", False)
|
|
80
|
+
self.details_to_bib_separator: str = options.get("details_to_bib_separator", "\n")
|
|
81
|
+
|
|
82
|
+
# for md
|
|
83
|
+
self._pandoc_md_to = PandocMdTo(self.options)
|
|
84
|
+
|
|
85
|
+
_options = {}
|
|
86
|
+
_options["is_standardize_bib"] = False
|
|
87
|
+
_options["is_display_implicit_comments"] = False
|
|
88
|
+
_options.update(options)
|
|
89
|
+
self._generate_library = ConvertStrToLibrary(_options)
|
|
90
|
+
|
|
91
|
+
def special_operate_for_md(
|
|
92
|
+
self,
|
|
93
|
+
path_output: str,
|
|
94
|
+
data_list_md: list[str],
|
|
95
|
+
output_md_name: str,
|
|
96
|
+
full_bib_for_abbr: str,
|
|
97
|
+
full_bib_for_zotero: str,
|
|
98
|
+
template_name: str = "article",
|
|
99
|
+
generate_html: bool = False,
|
|
100
|
+
generate_tex: bool = True,
|
|
101
|
+
) -> tuple[list[str], list[str]]:
|
|
102
|
+
"""Perform special operations on markdown files.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
path_output (str): Path to output directory.
|
|
106
|
+
data_list_md (list[str]): list of markdown content lines.
|
|
107
|
+
output_md_name (str): Name of output markdown file.
|
|
108
|
+
full_bib_for_abbr (str): Path to abbreviated bibliography file.
|
|
109
|
+
full_bib_for_zotero (str): Path to Zotero bibliography file.
|
|
110
|
+
template_name (str): Name of template to use. Defaults to "article".
|
|
111
|
+
generate_html (bool): Whether to generate HTML. Defaults to False.
|
|
112
|
+
generate_tex (bool): Whether to generate LaTeX. Defaults to True.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
tuple[list[str], list[str]]: Tuple containing processed markdown and LaTeX content.
|
|
116
|
+
"""
|
|
117
|
+
path_temp = os.path.join(path_output, "{}".format(time.strftime("%Y_%m_%d_%H_%M_%S")))
|
|
118
|
+
if not os.path.exists(path_temp):
|
|
119
|
+
os.makedirs(path_temp)
|
|
120
|
+
|
|
121
|
+
# write original md content to temp file
|
|
122
|
+
full_temp_md = os.path.join(path_temp, output_md_name)
|
|
123
|
+
write_list(data_list_md, full_temp_md, "w", None, False)
|
|
124
|
+
|
|
125
|
+
# pandoc md to md to update md content
|
|
126
|
+
if read_list(full_bib_for_abbr, "r") and read_list(full_bib_for_zotero, "r"):
|
|
127
|
+
data_list_md = self._special_operate_for_md(
|
|
128
|
+
output_md_name, path_temp, full_bib_for_abbr, full_bib_for_zotero
|
|
129
|
+
)
|
|
130
|
+
elif os.path.exists(full_bib_for_abbr) and os.path.exists(full_bib_for_zotero):
|
|
131
|
+
print(f"The content of bib: {full_bib_for_abbr} or {full_bib_for_zotero} is empty.")
|
|
132
|
+
else:
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
# main name
|
|
136
|
+
main_name = self.final_output_main_md_name
|
|
137
|
+
if len(main_name) == 0:
|
|
138
|
+
main_name = output_md_name.split(".md")[0] + "_" + self.replace_by_basic_beauty_complex_in_md
|
|
139
|
+
main_name = main_name + "_" + self.display_basic_beauty_complex_references_in_md + ".md"
|
|
140
|
+
if main_name.lower() == output_md_name.lower():
|
|
141
|
+
main_name = main_name.split(".md")[0] + "_.md"
|
|
142
|
+
if main_name[-3:] != ".md":
|
|
143
|
+
main_name = main_name + ".md"
|
|
144
|
+
|
|
145
|
+
# write new generated md content to given file
|
|
146
|
+
write_list(data_list_md, main_name, "w", path_output, False)
|
|
147
|
+
|
|
148
|
+
# for generating html file from md file
|
|
149
|
+
if data_list_md and generate_html:
|
|
150
|
+
self._pandoc_md_to.pandoc_md_to_html(
|
|
151
|
+
path_output, path_output, main_name, f"{main_name.split('.md')[0]}.html", True
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# pandoc md to latex
|
|
155
|
+
data_list_tex = []
|
|
156
|
+
if generate_tex:
|
|
157
|
+
n5 = "5_pandoc" + ".tex"
|
|
158
|
+
data_list_tex = self._pandoc_md_to.pandoc_md_to_tex(template_name, path_temp, path_temp, output_md_name, n5)
|
|
159
|
+
|
|
160
|
+
# delete temp path
|
|
161
|
+
if self.delete_temp_generate_md:
|
|
162
|
+
shutil.rmtree(path_temp)
|
|
163
|
+
return data_list_md, data_list_tex
|
|
164
|
+
|
|
165
|
+
def _special_operate_for_md(
|
|
166
|
+
self, output_md_name: str, path_temp: str, full_bib_for_abbr: str, full_bib_for_zotero: str
|
|
167
|
+
) -> list[str]:
|
|
168
|
+
"""Perform special operations for markdown processing.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
output_md_name (str): Name of output markdown file.
|
|
172
|
+
path_temp (str): Path to temporary directory.
|
|
173
|
+
full_bib_for_abbr (str): Path to abbreviated bibliography file.
|
|
174
|
+
full_bib_for_zotero (str): Path to Zotero bibliography file.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
list[str]: list of processed markdown content lines.
|
|
178
|
+
"""
|
|
179
|
+
# pandoc markdown to markdown
|
|
180
|
+
n1 = "1_pandoc" + ".md"
|
|
181
|
+
data_list_md = self._pandoc_md_to.pandoc_md_to_md(full_bib_for_abbr, path_temp, path_temp, output_md_name, n1)
|
|
182
|
+
|
|
183
|
+
# use zotero bib to generate library
|
|
184
|
+
bib_for_zotero = read_list(full_bib_for_zotero, "r")
|
|
185
|
+
library = self._generate_library.generate_library(bib_for_zotero)
|
|
186
|
+
|
|
187
|
+
_options = {}
|
|
188
|
+
_options.update(self.options)
|
|
189
|
+
_options["add_index_to_enties"] = False
|
|
190
|
+
_python_writers = PythonWriters(_options)
|
|
191
|
+
key_url_http_bib_dict = _python_writers.output_key_url_http_bib_dict(library)
|
|
192
|
+
|
|
193
|
+
content_md = []
|
|
194
|
+
if data_list_md and key_url_http_bib_dict:
|
|
195
|
+
key_basic_dict, key_beauty_dict, key_complex_dict = self._pandoc_md_to.generate_key_data_dict(
|
|
196
|
+
data_list_md, key_url_http_bib_dict
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
key_in_md = list(key_url_http_bib_dict.keys())
|
|
200
|
+
|
|
201
|
+
# generate by replacing `- [@citation_key]` to `- [citation_key]`
|
|
202
|
+
content = read_list(output_md_name, "r", path_temp)
|
|
203
|
+
if self.replace_cite_to_fullcite_in_md:
|
|
204
|
+
regex = re.compile(r"(\s*[-\+\*]\s*)\[@({})\]".format("|".join(key_in_md)))
|
|
205
|
+
for i in range(len(content)):
|
|
206
|
+
if not (mch := regex.match(content[i])):
|
|
207
|
+
continue
|
|
208
|
+
|
|
209
|
+
content[i] = content[i].replace(mch.group(), mch.group(1) + "[" + mch.group(2) + "]")
|
|
210
|
+
# add anchor
|
|
211
|
+
if self.add_anchor_in_md:
|
|
212
|
+
content = [batch_convert_citations(line) for line in content]
|
|
213
|
+
n2 = "2_generate" + ".md"
|
|
214
|
+
write_list(content, n2, "w", path_temp)
|
|
215
|
+
|
|
216
|
+
# pandoc markdown to markdown
|
|
217
|
+
n3 = "3_pandoc" + ".md"
|
|
218
|
+
data_list_md = self._pandoc_md_to.pandoc_md_to_md(full_bib_for_abbr, path_temp, path_temp, n2, n3)
|
|
219
|
+
|
|
220
|
+
# generate by replacing `- [citation_key]` to `- reference`
|
|
221
|
+
if self.replace_cite_to_fullcite_in_md:
|
|
222
|
+
regex = re.compile(r"(\s*)([-\+\*])(\s*)[\\]*\[({})[\\]*\]".format("|".join(key_in_md)))
|
|
223
|
+
for i in range(len(data_list_md)):
|
|
224
|
+
if not (mch := regex.search(data_list_md[i])):
|
|
225
|
+
continue
|
|
226
|
+
|
|
227
|
+
space_one, b, space_two, cite_key = mch.groups()
|
|
228
|
+
if self.replace_by_basic_beauty_complex_in_md.lower() == "basic":
|
|
229
|
+
temp_list = copy.deepcopy(key_basic_dict[cite_key])
|
|
230
|
+
elif self.replace_by_basic_beauty_complex_in_md.lower() == "complex":
|
|
231
|
+
temp_list = copy.deepcopy(key_complex_dict[cite_key])
|
|
232
|
+
else:
|
|
233
|
+
temp_list = copy.deepcopy(key_beauty_dict[cite_key])
|
|
234
|
+
|
|
235
|
+
temp = "".join(self._special_format(temp_list, space_one, space_two))
|
|
236
|
+
data_list_md[i] = data_list_md[i].replace(mch.group(), space_one + b + space_two + temp.strip())
|
|
237
|
+
n4 = "4_generate" + ".md"
|
|
238
|
+
write_list(data_list_md, os.path.join(path_temp, n4), "w")
|
|
239
|
+
|
|
240
|
+
# obtain footnote part (in the last part of the contents)
|
|
241
|
+
main_part, last_part = [], []
|
|
242
|
+
main_part_flag, last_part_flag = True, True
|
|
243
|
+
for line_index in range(len(data_list_md)):
|
|
244
|
+
if main_part_flag and re.match(r"#+ [\"']?References[\"']?\s[\[{]", data_list_md[line_index]):
|
|
245
|
+
main_part_flag = False
|
|
246
|
+
main_part = delete_empty_lines_last_occur_add_new_line(data_list_md[:line_index])
|
|
247
|
+
if last_part_flag and re.match(r"^\[\^1]: ", data_list_md[line_index]):
|
|
248
|
+
last_part_flag = False
|
|
249
|
+
last_part = delete_empty_lines_last_occur_add_new_line(data_list_md[line_index:])
|
|
250
|
+
|
|
251
|
+
if main_part_flag:
|
|
252
|
+
main_part = delete_empty_lines_last_occur_add_new_line(data_list_md)
|
|
253
|
+
if self.add_reference_in_md:
|
|
254
|
+
main_part.append("\n## References\n")
|
|
255
|
+
if len(last_part) > 0:
|
|
256
|
+
last_part.insert(0, "[//]: (Footnotes)\n\n")
|
|
257
|
+
last_part.append("\n")
|
|
258
|
+
|
|
259
|
+
# for bib
|
|
260
|
+
bib_in_md = []
|
|
261
|
+
if self.add_bib_in_md:
|
|
262
|
+
temp_c = combine_content_in_list([key_url_http_bib_dict[k][2] for k in key_in_md])
|
|
263
|
+
bib_in_md = combine_content_in_list(
|
|
264
|
+
[
|
|
265
|
+
["## Bibliography\n\n"],
|
|
266
|
+
[f"<details>{self.details_to_bib_separator}"],
|
|
267
|
+
["```\n"],
|
|
268
|
+
temp_c,
|
|
269
|
+
["```\n"],
|
|
270
|
+
["</details>\n"],
|
|
271
|
+
]
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# Generate basic/beauty/complex markdown content
|
|
275
|
+
if dct := eval(f"key_{self.display_basic_beauty_complex_references_in_md}_dict"):
|
|
276
|
+
content_md = self._generate_content_md(dct, key_in_md, main_part, last_part, bib_in_md)
|
|
277
|
+
return content_md
|
|
278
|
+
|
|
279
|
+
@staticmethod
|
|
280
|
+
def _special_format(temp_list: list[str], space_one: str, space_two: str) -> list[str]:
|
|
281
|
+
"""Apply special formatting for alignment.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
temp_list (list[str]): list of strings to format.
|
|
285
|
+
space_one (str): First space string.
|
|
286
|
+
space_two (str): Second space string.
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
list[str]: Formatted list of strings.
|
|
290
|
+
"""
|
|
291
|
+
for j in range(len(temp_list) - 1):
|
|
292
|
+
if temp_list[j][-1] == "\n":
|
|
293
|
+
temp_list[j + 1] = (space_one + " " + space_two) + temp_list[j + 1]
|
|
294
|
+
return temp_list
|
|
295
|
+
|
|
296
|
+
def _generate_content_md(
|
|
297
|
+
self,
|
|
298
|
+
key_basic_beauty_complex_dict: dict[str, list[str]],
|
|
299
|
+
key_in_md_tex: list[str],
|
|
300
|
+
main_part: list[str],
|
|
301
|
+
last_part: list[str],
|
|
302
|
+
bib_in_md: list[str],
|
|
303
|
+
) -> list[str]:
|
|
304
|
+
"""Generate markdown content from various components.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
key_basic_beauty_complex_dict (dict[str, list[str]]): dictionary of formatted references.
|
|
308
|
+
key_in_md_tex (list[str]): list of citation keys in markdown/LaTeX.
|
|
309
|
+
main_part (list[str]): Main content part.
|
|
310
|
+
last_part (list[str]): Last content part.
|
|
311
|
+
bib_in_md (list[str]): Bibliography content for markdown.
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
list[str]: Generated markdown content.
|
|
315
|
+
"""
|
|
316
|
+
temp_b = []
|
|
317
|
+
if self.add_reference_in_md:
|
|
318
|
+
temp_b = combine_content_in_list([key_basic_beauty_complex_dict[k] for k in key_in_md_tex], ["\n"])
|
|
319
|
+
content_md = combine_content_in_list([main_part, ["\n"], temp_b, last_part, bib_in_md])
|
|
320
|
+
return content_md
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pyadvtools import delete_files, insert_list_in_list, read_list, write_list
|
|
8
|
+
|
|
9
|
+
from .basic_input import BasicInput
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PythonRunTex(BasicInput):
|
|
13
|
+
"""Python LaTeX document processing and compilation class.
|
|
14
|
+
|
|
15
|
+
This class extends BasicInput to handle LaTeX-specific operations including
|
|
16
|
+
document compilation, output management, and temporary file cleanup.
|
|
17
|
+
It supports multiple LaTeX engines and provides configurable cleanup options.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
options (dict[str, Any]): Configuration options for LaTeX processing.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
final_output_main_tex_name (str): Name of the main output LaTeX file.
|
|
24
|
+
Defaults to empty string.
|
|
25
|
+
run_latex (bool): Flag indicating whether to execute LaTeX compilation.
|
|
26
|
+
When True, the system will compile the LaTeX document. Defaults to False.
|
|
27
|
+
pdflatex_xelatex (str): LaTeX engine selection. Valid options are
|
|
28
|
+
'pdflatex' for standard PDF compilation or 'xelatex' for extended
|
|
29
|
+
Unicode and font support. Defaults to "xelatex".
|
|
30
|
+
delete_run_latex_cache (bool): Flag controlling cleanup of temporary
|
|
31
|
+
LaTeX files after compilation. When True, auxiliary files are removed.
|
|
32
|
+
Defaults to True.
|
|
33
|
+
latex_clean_file_types (list[str] | None): List of file extensions to
|
|
34
|
+
remove during cleanup. If None, a default set is used. Defaults to None.
|
|
35
|
+
replace_duplicate_output_tex_file (bool): Flag indicating whether to
|
|
36
|
+
overwrite existing output files with the same name. When True,
|
|
37
|
+
duplicates are replaced; when False, new names may be generated.
|
|
38
|
+
Defaults to False.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, options: dict[str, Any]) -> None:
|
|
42
|
+
"""Initialize PythonRunTex with configuration options.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
options (dict[str, Any]): Configuration options.
|
|
46
|
+
"""
|
|
47
|
+
super().__init__(options)
|
|
48
|
+
|
|
49
|
+
# for tex
|
|
50
|
+
self.final_output_main_tex_name: str = options.get("final_output_main_tex_name", "")
|
|
51
|
+
self.run_latex: bool = options.get("run_latex", False)
|
|
52
|
+
self.pdflatex_xelatex: str = options.get("pdflatex_xelatex", "xelatex") # pdflatex, xelatex
|
|
53
|
+
self.delete_run_latex_cache: bool = options.get("delete_run_latex_cache", True)
|
|
54
|
+
self.latex_clean_file_types: list[str] | None = options.get("latex_clean_file_types", None)
|
|
55
|
+
self.replace_duplicate_output_tex_file: bool = options.get("replace_duplicate_output_tex_file", False)
|
|
56
|
+
|
|
57
|
+
def generate_standard_tex_data_list(
|
|
58
|
+
self,
|
|
59
|
+
data_list_body: list[str],
|
|
60
|
+
output_tex_name: str,
|
|
61
|
+
path_output: str,
|
|
62
|
+
figure_folder_name: str = "figs",
|
|
63
|
+
tex_folder_name: str = "tex",
|
|
64
|
+
bib_folder_name: str = "bib",
|
|
65
|
+
bib_name: str = "abbr.bib",
|
|
66
|
+
template_name: str = "article",
|
|
67
|
+
) -> list[str]:
|
|
68
|
+
"""Generate standard LaTeX data list with proper formatting.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
data_list_body (list[str]): list of body content strings.
|
|
72
|
+
output_tex_name (str): Name of output LaTeX file.
|
|
73
|
+
path_output (str): Path to output directory.
|
|
74
|
+
figure_folder_name (str): Name of figures folder. Defaults to "figs".
|
|
75
|
+
tex_folder_name (str): Name of LaTeX folder. Defaults to "tex".
|
|
76
|
+
bib_folder_name (str): Name of bibliography folder. Defaults to "bib".
|
|
77
|
+
bib_name (str): Name of bibliography file. Defaults to "abbr.bib".
|
|
78
|
+
template_name (str): Name of template to use. Defaults to "article".
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
list[str]: list of processed LaTeX content strings.
|
|
82
|
+
"""
|
|
83
|
+
# for figures
|
|
84
|
+
for i in range(len(data_list_body)):
|
|
85
|
+
if re.search(r"\\includegraphics", data_list_body[i]):
|
|
86
|
+
data_list_body[i] = data_list_body[i].replace(
|
|
87
|
+
f".{os.sep}Figures{os.sep}", f".{os.sep}{figure_folder_name}{os.sep}"
|
|
88
|
+
)
|
|
89
|
+
data_list_body[i] = data_list_body[i].replace(f"Figures{os.sep}", f"{figure_folder_name}{os.sep}")
|
|
90
|
+
write_list(data_list_body, output_tex_name, "w", os.path.join(path_output, tex_folder_name), False)
|
|
91
|
+
|
|
92
|
+
self._special_operate_tex(
|
|
93
|
+
data_list_body, template_name, output_tex_name, path_output, tex_folder_name, bib_folder_name, bib_name
|
|
94
|
+
)
|
|
95
|
+
return data_list_body
|
|
96
|
+
|
|
97
|
+
def _special_operate_tex(
|
|
98
|
+
self,
|
|
99
|
+
data_list_body: list[str],
|
|
100
|
+
template_name: str,
|
|
101
|
+
output_tex_name: str,
|
|
102
|
+
path_output: str,
|
|
103
|
+
tex_folder_name: str,
|
|
104
|
+
bib_folder_name: str,
|
|
105
|
+
bib_name: str,
|
|
106
|
+
) -> None:
|
|
107
|
+
"""Perform special operations on LaTeX files.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
data_list_body (list[str]): list of body content strings.
|
|
111
|
+
template_name (str): Name of template to use.
|
|
112
|
+
output_tex_name (str): Name of output LaTeX file.
|
|
113
|
+
path_output (str): Path to output directory.
|
|
114
|
+
tex_folder_name (str): Name of LaTeX folder.
|
|
115
|
+
bib_folder_name (str): Name of bibliography folder.
|
|
116
|
+
bib_name (str): Name of bibliography file.
|
|
117
|
+
"""
|
|
118
|
+
# read template data
|
|
119
|
+
template_h, template_t = [], []
|
|
120
|
+
if template_name.lower() == "paper":
|
|
121
|
+
template_h, template_t = self.article_template_header_tex, self.article_template_tail_tex
|
|
122
|
+
elif template_name.lower() == "beamer":
|
|
123
|
+
template_h, template_t = self.beamer_template_header_tex, self.beamer_template_tail_tex
|
|
124
|
+
|
|
125
|
+
# style
|
|
126
|
+
if usepackages := self.usepackages_tex:
|
|
127
|
+
usepackages.insert(0, "\n")
|
|
128
|
+
|
|
129
|
+
# command
|
|
130
|
+
if math_commands := self.math_commands_tex:
|
|
131
|
+
math_commands.insert(0, "\n")
|
|
132
|
+
|
|
133
|
+
# main name
|
|
134
|
+
main_name = self.final_output_main_tex_name
|
|
135
|
+
if len(main_name) == 0:
|
|
136
|
+
main_name = output_tex_name.split(".text")[0] + "_main.tex"
|
|
137
|
+
if (not self.replace_duplicate_output_tex_file) and (main_name.lower() == output_tex_name.lower()):
|
|
138
|
+
main_name = main_name.split(".tex")[0] + "_.tex"
|
|
139
|
+
if main_name[-4:] != ".tex":
|
|
140
|
+
main_name = main_name + ".tex"
|
|
141
|
+
|
|
142
|
+
data_list = []
|
|
143
|
+
if (len(template_h) != 0) and (len(template_t) != 0):
|
|
144
|
+
# header
|
|
145
|
+
data_list = insert_list_in_list(template_h, usepackages, r"\\documentclass", "after")
|
|
146
|
+
data_list = insert_list_in_list(data_list, math_commands, r"\\documentclass", "after")
|
|
147
|
+
|
|
148
|
+
if template_name.lower() == "beamer":
|
|
149
|
+
data_list = insert_list_in_list(data_list, ["\n\\def\\allfiles{}\n"], r"\\documentclass", "after")
|
|
150
|
+
|
|
151
|
+
if self.pdflatex_xelatex == "xelatex":
|
|
152
|
+
data_list = insert_list_in_list(data_list, ["\n\\def\\cn{}\n"], r"\\documentclass", "after")
|
|
153
|
+
|
|
154
|
+
# for bib
|
|
155
|
+
expected = r'\addbibresource{./bibs/abbr.bib}'
|
|
156
|
+
for i in range(len(data_list)):
|
|
157
|
+
mch = expected == data_list[i].rstrip()
|
|
158
|
+
if not mch:
|
|
159
|
+
continue
|
|
160
|
+
if bib_folder_name:
|
|
161
|
+
data_list[i] = "\\addbibresource{" + f"./{bib_folder_name}/{bib_name}" + "}\n"
|
|
162
|
+
else:
|
|
163
|
+
data_list[i] = "\\addbibresource{" + f"./{bib_name}" + "}\n"
|
|
164
|
+
|
|
165
|
+
# body
|
|
166
|
+
if len(data_list_body) != 0:
|
|
167
|
+
data_list.append("\n")
|
|
168
|
+
data_list.extend(data_list_body)
|
|
169
|
+
data_list.append("\n")
|
|
170
|
+
|
|
171
|
+
# tail
|
|
172
|
+
data_list.extend(template_t)
|
|
173
|
+
|
|
174
|
+
# save file
|
|
175
|
+
write_list(data_list, main_name, "w", path_output, False)
|
|
176
|
+
else:
|
|
177
|
+
data_list = read_list(output_tex_name, "r", os.path.join(path_output, tex_folder_name))
|
|
178
|
+
write_list(data_list, main_name, "w", path_output, False)
|
|
179
|
+
|
|
180
|
+
# run latex
|
|
181
|
+
if self.run_latex:
|
|
182
|
+
if shutil.which("latexmk"):
|
|
183
|
+
os.chdir(path_output)
|
|
184
|
+
cmd = f"latexmk -{self.pdflatex_xelatex} {main_name}"
|
|
185
|
+
try:
|
|
186
|
+
subprocess.run(cmd.split(), check=True, capture_output=True, text=True)
|
|
187
|
+
except subprocess.CalledProcessError as e:
|
|
188
|
+
print("Error in Run LaTex:", e.stderr)
|
|
189
|
+
else:
|
|
190
|
+
print("latexmk not found. Please install Texlive.")
|
|
191
|
+
|
|
192
|
+
# delete cache
|
|
193
|
+
if self.delete_run_latex_cache:
|
|
194
|
+
if self.latex_clean_file_types is not None:
|
|
195
|
+
postfix = self.latex_clean_file_types
|
|
196
|
+
else:
|
|
197
|
+
postfix = [".aux", ".bbl", ".bcf", ".blg", ".fdb_latexmk", ".fls", ".log", ".out", ".run.xml"]
|
|
198
|
+
postfix.extend([".synctex.gz", ".gz", ".nav", ".snm", ".toc", ".xdv"])
|
|
199
|
+
delete_files(path_output, postfix)
|
|
200
|
+
return None
|
pyeasyphd/pyeasyphd.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import sublime
|
|
4
|
+
import sublime_plugin
|
|
5
|
+
from pyadvtools import IterateUpdateDict
|
|
6
|
+
|
|
7
|
+
from pyeasyphd.tools.py_run_bib_md_tex import PyRunBibMdTex
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def delete_files(path_storage: str, extensions) -> None:
|
|
11
|
+
"""Delete files with specified extensions from storage path.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
path_storage (str): Path to the storage directory.
|
|
15
|
+
extensions: list of file extensions to delete.
|
|
16
|
+
"""
|
|
17
|
+
for name in os.listdir(path_storage):
|
|
18
|
+
for ext in extensions:
|
|
19
|
+
if name.endswith(ext) and os.path.isfile(os.path.join(path_storage, name)):
|
|
20
|
+
os.remove(os.path.join(path_storage, name))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PypapersCommand(sublime_plugin.WindowCommand):
|
|
24
|
+
"""Sublime Text command for processing papers with various templates."""
|
|
25
|
+
|
|
26
|
+
def run(self, template="Paper", output_level="next", delete_cache=False):
|
|
27
|
+
"""Run the paper processing command.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
template (str, optional): Template type to use. Defaults to "Paper".
|
|
31
|
+
output_level (str, optional): Output level for processing. Defaults to "next".
|
|
32
|
+
delete_cache (bool, optional): Whether to delete cache files. Defaults to False.
|
|
33
|
+
"""
|
|
34
|
+
vars_dict = self.window.extract_variables()
|
|
35
|
+
|
|
36
|
+
packages_path = vars_dict["packages"]
|
|
37
|
+
|
|
38
|
+
# settings
|
|
39
|
+
options, default_settings, user_settings, project_settings = {}, {}, {}, {}
|
|
40
|
+
file_default_settings = os.path.join(packages_path, "pypapers", "pypapers.sublime-settings")
|
|
41
|
+
if os.path.exists(file_default_settings):
|
|
42
|
+
default_settings = sublime.decode_value(open(file_default_settings).read())
|
|
43
|
+
|
|
44
|
+
file_user_settings = os.path.join(packages_path, "User", "PyPapers.sublime-settings")
|
|
45
|
+
if os.path.exists(file_user_settings):
|
|
46
|
+
user_settings = sublime.decode_value(open(file_user_settings).read())
|
|
47
|
+
|
|
48
|
+
project_settings = self.window.project_data().get("settings", {})
|
|
49
|
+
|
|
50
|
+
iter_update_dict = IterateUpdateDict()
|
|
51
|
+
options = iter_update_dict.dict_update(options, default_settings)
|
|
52
|
+
options = iter_update_dict.dict_update(options, user_settings)
|
|
53
|
+
options = iter_update_dict.dict_update(options, project_settings)
|
|
54
|
+
|
|
55
|
+
# update
|
|
56
|
+
for key in vars_dict:
|
|
57
|
+
if isinstance(vars_dict[key], str):
|
|
58
|
+
os.environ[key] = vars_dict[key]
|
|
59
|
+
|
|
60
|
+
for key in options:
|
|
61
|
+
if isinstance(options[key], str):
|
|
62
|
+
options[key] = os.path.expandvars(os.path.expanduser(options[key]))
|
|
63
|
+
|
|
64
|
+
if delete_cache:
|
|
65
|
+
file_path = vars_dict["file_path"]
|
|
66
|
+
|
|
67
|
+
if latex_clean_file_types := options.get("latex_clean_file_types", []):
|
|
68
|
+
postfix = latex_clean_file_types
|
|
69
|
+
else:
|
|
70
|
+
postfix = [".aux", ".bbl", ".bcf", ".blg", ".fdb_latexmk", ".fls", ".log", ".out", ".run.xml"]
|
|
71
|
+
postfix.extend([".synctex.gz", ".gz", ".nav", ".snm", ".toc", ".xdv"])
|
|
72
|
+
|
|
73
|
+
delete_files(file_path, postfix)
|
|
74
|
+
delete_files(os.path.dirname(file_path), postfix)
|
|
75
|
+
|
|
76
|
+
else:
|
|
77
|
+
# main
|
|
78
|
+
path_output = options.get("path_output", "")
|
|
79
|
+
if len(path_output.strip()) == 0:
|
|
80
|
+
path_output = vars_dict["file_path"]
|
|
81
|
+
|
|
82
|
+
p_r_l_m = PyRunBibMdTex(path_output, vars_dict["file_extension"], template, options)
|
|
83
|
+
p_r_l_m.run_files([vars_dict["file"]], vars_dict["file_base_name"], output_level)
|
|
84
|
+
|
|
85
|
+
# display
|
|
86
|
+
self.window.status_message("Successful.")
|