textmate-grammar-python 0.1.2__py3-none-any.whl → 0.3.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.
- textmate_grammar/__init__.py +1 -0
- textmate_grammar/elements.py +458 -41
- textmate_grammar/grammars/markdown/__init__.py +11 -8
- textmate_grammar/grammars/matlab/__init__.py +16 -14
- textmate_grammar/language.py +75 -27
- textmate_grammar/parser.py +219 -76
- textmate_grammar/py.typed +0 -0
- textmate_grammar/utils/__init__.py +0 -0
- textmate_grammar/utils/cache.py +160 -0
- textmate_grammar/utils/exceptions.py +69 -0
- textmate_grammar/{handler.py → utils/handler.py} +112 -36
- textmate_grammar/{logging.py → utils/logger.py} +49 -24
- textmate_grammar_python-0.3.0.dist-info/METADATA +105 -0
- textmate_grammar_python-0.3.0.dist-info/RECORD +21 -0
- {textmate_grammar_python-0.1.2.dist-info → textmate_grammar_python-0.3.0.dist-info}/WHEEL +1 -1
- textmate_grammar/captures.py +0 -120
- textmate_grammar/exceptions.py +0 -27
- textmate_grammar_python-0.1.2.dist-info/METADATA +0 -105
- textmate_grammar_python-0.1.2.dist-info/RECORD +0 -19
- {textmate_grammar_python-0.1.2.dist-info → textmate_grammar_python-0.3.0.dist-info}/LICENSE +0 -0
textmate_grammar/__init__.py
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|
textmate_grammar/elements.py
CHANGED
|
@@ -1,19 +1,147 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC
|
|
3
4
|
from collections import defaultdict
|
|
4
5
|
from itertools import groupby
|
|
6
|
+
from pprint import pprint
|
|
7
|
+
from typing import TYPE_CHECKING, Generator
|
|
5
8
|
|
|
6
|
-
from .handler import POS
|
|
9
|
+
from .utils.handler import POS, ContentHandler, Match, Pattern
|
|
10
|
+
from .utils.logger import LOGGER
|
|
7
11
|
|
|
8
12
|
if TYPE_CHECKING:
|
|
9
|
-
from .parser import
|
|
13
|
+
from .parser import GrammarParser
|
|
10
14
|
|
|
11
15
|
|
|
12
16
|
TOKEN_DICT = dict[POS, list[str]]
|
|
13
17
|
|
|
14
18
|
|
|
15
|
-
class
|
|
16
|
-
"""
|
|
19
|
+
class Capture:
|
|
20
|
+
"""A captured matching group.
|
|
21
|
+
|
|
22
|
+
After mathing, any pattern can have a number of capture groups for which subsequent parsers can be defined.
|
|
23
|
+
The Capture object stores this subsequent parse to be dispatched at a later moment.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
handler: ContentHandler,
|
|
29
|
+
pattern: Pattern,
|
|
30
|
+
matching: Match,
|
|
31
|
+
parsers: dict[int, GrammarParser],
|
|
32
|
+
starting: tuple[int, int],
|
|
33
|
+
boundary: tuple[int, int],
|
|
34
|
+
key: str = "",
|
|
35
|
+
**kwargs,
|
|
36
|
+
):
|
|
37
|
+
"""
|
|
38
|
+
Initialize a new instance of the Element class.
|
|
39
|
+
|
|
40
|
+
:param handler: The content handler for the element.
|
|
41
|
+
:param pattern: The pattern used for matching.
|
|
42
|
+
:param matching: The match object.
|
|
43
|
+
:param parsers: A dictionary of grammar parsers.
|
|
44
|
+
:param starting: The starting position of the element.
|
|
45
|
+
:param boundary: The boundary position of the element.
|
|
46
|
+
:param key: The key for the element. Defaults to "".
|
|
47
|
+
:param **kwargs: Additional keyword arguments.
|
|
48
|
+
:returns: None
|
|
49
|
+
"""
|
|
50
|
+
self.handler = handler
|
|
51
|
+
self.pattern = pattern
|
|
52
|
+
self.matching = matching
|
|
53
|
+
self.parsers = parsers
|
|
54
|
+
self.starting = starting
|
|
55
|
+
self.boundary = boundary
|
|
56
|
+
self.key = key
|
|
57
|
+
self.kwargs = kwargs
|
|
58
|
+
|
|
59
|
+
def __eq__(self, other: object) -> bool:
|
|
60
|
+
if isinstance(other, Capture):
|
|
61
|
+
return bool(
|
|
62
|
+
self.key == other.key
|
|
63
|
+
and self.starting == other.starting
|
|
64
|
+
and self.matching.group() == other.matching.group()
|
|
65
|
+
)
|
|
66
|
+
else:
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
def __repr__(self) -> str:
|
|
70
|
+
return f"@capture<{self.key}>"
|
|
71
|
+
|
|
72
|
+
def dispatch(self) -> list[Capture | ContentElement]:
|
|
73
|
+
"""Dispatches the remaining parse of the capture group.
|
|
74
|
+
|
|
75
|
+
This method iterates over the defined parsers for the capture group and dispatches the remaining parse
|
|
76
|
+
based on the captured elements. It returns a list of captured elements or captures.
|
|
77
|
+
|
|
78
|
+
:return: A list of Capture or ContentElement objects representing the parsed elements.
|
|
79
|
+
"""
|
|
80
|
+
elements = []
|
|
81
|
+
for group_id, parser in self.parsers.items():
|
|
82
|
+
if group_id > self.pattern.number_of_captures():
|
|
83
|
+
LOGGER.warning(
|
|
84
|
+
f"The capture group {group_id} does not exist in pattern {self.pattern._pattern}"
|
|
85
|
+
)
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
group_span = self.matching.span(group_id)
|
|
89
|
+
|
|
90
|
+
# Empty group
|
|
91
|
+
if group_span[0] == group_span[1]:
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
group_starting = (self.starting[0], group_span[0])
|
|
95
|
+
group_boundary = (self.starting[0], group_span[1])
|
|
96
|
+
|
|
97
|
+
if (
|
|
98
|
+
parser == self
|
|
99
|
+
and group_starting == self.starting
|
|
100
|
+
and group_boundary == self.boundary
|
|
101
|
+
):
|
|
102
|
+
LOGGER.warning("Parser loop detected, continuing...", self, self.starting)
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
# Dispatch the parse
|
|
106
|
+
self.kwargs.pop("greedy", None)
|
|
107
|
+
parsed, captured_elements, _ = parser._parse(
|
|
108
|
+
self.handler,
|
|
109
|
+
starting=group_starting,
|
|
110
|
+
boundary=group_boundary,
|
|
111
|
+
find_one=self.kwargs.pop("find_one", False),
|
|
112
|
+
parent_capture=self,
|
|
113
|
+
**self.kwargs,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if parsed:
|
|
117
|
+
elements.extend(captured_elements)
|
|
118
|
+
|
|
119
|
+
return elements
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _dispatch_list(
|
|
123
|
+
pending_elements: list[Capture | ContentElement], parent: ContentElement | None = None
|
|
124
|
+
) -> list[ContentElement]:
|
|
125
|
+
"""Dispatches all captured parsers in the list."""
|
|
126
|
+
elements = []
|
|
127
|
+
for item in pending_elements:
|
|
128
|
+
if isinstance(item, Capture):
|
|
129
|
+
captured_elements: list[ContentElement] = _dispatch_list(item.dispatch())
|
|
130
|
+
elements.extend(captured_elements)
|
|
131
|
+
elif item != parent:
|
|
132
|
+
elements.append(item)
|
|
133
|
+
return elements
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _str_to_list(input: str | list[str]) -> list[str]:
|
|
137
|
+
if isinstance(input, str):
|
|
138
|
+
return [input] if input else []
|
|
139
|
+
else:
|
|
140
|
+
return input
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class ContentElement:
|
|
144
|
+
"""The parsed grammar element."""
|
|
17
145
|
|
|
18
146
|
def __init__(
|
|
19
147
|
self,
|
|
@@ -21,35 +149,219 @@ class ContentElement(object):
|
|
|
21
149
|
grammar: dict,
|
|
22
150
|
content: str,
|
|
23
151
|
characters: dict[POS, str],
|
|
24
|
-
children:
|
|
152
|
+
children: list[Capture | ContentElement] | None = None,
|
|
25
153
|
) -> None:
|
|
154
|
+
"""
|
|
155
|
+
Initialize a new instance of the Element class.
|
|
156
|
+
|
|
157
|
+
:param token: The token associated with the element.
|
|
158
|
+
:param grammar: The grammar associated with the element.
|
|
159
|
+
:param content: The content associated with the element.
|
|
160
|
+
:param characters: The characters associated with the element.
|
|
161
|
+
:param children: The children associated with the element. Defaults to None.
|
|
162
|
+
"""
|
|
163
|
+
if children is None:
|
|
164
|
+
children = []
|
|
26
165
|
self.token = token
|
|
27
166
|
self.grammar = grammar
|
|
28
167
|
self.content = content
|
|
29
168
|
self.characters = characters
|
|
30
|
-
self.
|
|
169
|
+
self._children_captures = children
|
|
170
|
+
self._dispatched: bool = False
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def _subelements(self) -> list[ContentElement]:
|
|
174
|
+
return self.children
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def children(self) -> list[ContentElement]:
|
|
178
|
+
"""
|
|
179
|
+
Returns a list of children elements.
|
|
180
|
+
|
|
181
|
+
If the elements have not been dispatched yet, this method will dispatch them before returning.
|
|
182
|
+
|
|
183
|
+
:return: A list of ContentElement objects representing the children elements.
|
|
184
|
+
"""
|
|
185
|
+
if not self._dispatched:
|
|
186
|
+
self._dispatch()
|
|
187
|
+
return self._children
|
|
188
|
+
|
|
189
|
+
def _dispatch(self, nested: bool = False):
|
|
190
|
+
"""
|
|
191
|
+
Dispatches the content element and its children.
|
|
192
|
+
|
|
193
|
+
:param nested: Indicates whether the dispatch is nested within another dispatch.
|
|
194
|
+
:type nested: bool
|
|
195
|
+
:return: None
|
|
196
|
+
"""
|
|
197
|
+
if self._dispatched:
|
|
198
|
+
return
|
|
199
|
+
self._dispatched = True
|
|
200
|
+
self._children: list[ContentElement] = _dispatch_list(self._children_captures, parent=self)
|
|
201
|
+
self._children_captures = []
|
|
202
|
+
if nested:
|
|
203
|
+
for child in self._children:
|
|
204
|
+
child._dispatch(True)
|
|
31
205
|
|
|
32
206
|
def __eq__(self, other):
|
|
33
|
-
if
|
|
34
|
-
return True
|
|
35
|
-
else:
|
|
207
|
+
if not isinstance(other, ContentElement):
|
|
36
208
|
return False
|
|
209
|
+
return bool(self.grammar == other.grammar and self.characters == other.characters)
|
|
210
|
+
|
|
211
|
+
def _find(
|
|
212
|
+
self,
|
|
213
|
+
tokens: str | list[str],
|
|
214
|
+
start_tokens: str | list[str] = "",
|
|
215
|
+
hide_tokens: str | list[str] = "",
|
|
216
|
+
stop_tokens: str | list[str] = "",
|
|
217
|
+
depth: int = -1,
|
|
218
|
+
attribute: str = "_subelements",
|
|
219
|
+
stack: list[str] | None = None,
|
|
220
|
+
) -> Generator[tuple[ContentElement, list[str]], None, None]:
|
|
221
|
+
tokens = _str_to_list(tokens)
|
|
222
|
+
start_tokens = _str_to_list(start_tokens)
|
|
223
|
+
hide_tokens = _str_to_list(hide_tokens)
|
|
224
|
+
stop_tokens = _str_to_list(stop_tokens)
|
|
225
|
+
|
|
226
|
+
if not set(tokens).isdisjoint(set(stop_tokens)):
|
|
227
|
+
raise ValueError("Input tokens and stop_tokens must be disjoint")
|
|
228
|
+
|
|
229
|
+
if stack is None:
|
|
230
|
+
stack = []
|
|
231
|
+
stack += [self.token]
|
|
37
232
|
|
|
38
|
-
|
|
39
|
-
|
|
233
|
+
start_found = not start_tokens
|
|
234
|
+
|
|
235
|
+
if depth:
|
|
236
|
+
depth -= 1
|
|
237
|
+
children: list[ContentElement] = getattr(self, attribute, self._subelements)
|
|
238
|
+
for child in children:
|
|
239
|
+
if stop_tokens and (
|
|
240
|
+
child.token in stop_tokens
|
|
241
|
+
or (stop_tokens == ["*"] and child.token not in tokens)
|
|
242
|
+
):
|
|
243
|
+
return None
|
|
244
|
+
|
|
245
|
+
if not start_found and child.token in start_tokens:
|
|
246
|
+
start_found = True
|
|
247
|
+
start_tokens = []
|
|
248
|
+
|
|
249
|
+
if (
|
|
250
|
+
start_found
|
|
251
|
+
and (child.token in tokens or tokens == ["*"])
|
|
252
|
+
and child.token not in hide_tokens
|
|
253
|
+
):
|
|
254
|
+
yield child, [e for e in stack]
|
|
255
|
+
if depth:
|
|
256
|
+
nested_generator = child._find(
|
|
257
|
+
tokens,
|
|
258
|
+
start_tokens=start_tokens,
|
|
259
|
+
hide_tokens=hide_tokens,
|
|
260
|
+
stop_tokens=stop_tokens,
|
|
261
|
+
depth=depth - 1,
|
|
262
|
+
stack=[e for e in stack],
|
|
263
|
+
)
|
|
264
|
+
yield from nested_generator
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
def find(
|
|
268
|
+
self,
|
|
269
|
+
tokens: str | list[str],
|
|
270
|
+
start_tokens: str | list[str] = "",
|
|
271
|
+
hide_tokens: str | list[str] = "",
|
|
272
|
+
stop_tokens: str | list[str] = "",
|
|
273
|
+
depth: int = -1,
|
|
274
|
+
attribute: str = "_subelements",
|
|
275
|
+
) -> Generator[tuple[ContentElement, list[str]], None, None]:
|
|
276
|
+
"""
|
|
277
|
+
Find content elements based on the given criteria.
|
|
278
|
+
|
|
279
|
+
The find method will return a generator that globs though the element-tree, searching for the next
|
|
280
|
+
subelement that matches the given token.
|
|
281
|
+
|
|
282
|
+
:param tokens: The tokens to search for. Can be a single token or a list of tokens.
|
|
283
|
+
:param start_tokens: The tokens that mark the start of the search. Can be a single token or a list of tokens.
|
|
284
|
+
:param hide_tokens: The tokens to hide from the search results. Can be a single token or a list of tokens.
|
|
285
|
+
:param stop_tokens: The tokens that mark the end of the search. Can be a single token or a list of tokens.
|
|
286
|
+
:param depth: The maximum depth to search. Defaults to -1 (unlimited depth).
|
|
287
|
+
:param attribute: The attribute name to access the subelements. Defaults to "_subelements".
|
|
288
|
+
|
|
289
|
+
:yield: A tuple containing the found content element and the stack of tokens encountered.
|
|
290
|
+
|
|
291
|
+
:raises ValueError: If the input tokens and stop_tokens are not disjoint.
|
|
292
|
+
|
|
293
|
+
:return: None if no matching content elements are found.
|
|
294
|
+
"""
|
|
295
|
+
return self._find(
|
|
296
|
+
tokens,
|
|
297
|
+
start_tokens=start_tokens,
|
|
298
|
+
hide_tokens=hide_tokens,
|
|
299
|
+
stop_tokens=stop_tokens,
|
|
300
|
+
depth=depth,
|
|
301
|
+
attribute=attribute,
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
def findall(
|
|
305
|
+
self,
|
|
306
|
+
tokens: str | list[str],
|
|
307
|
+
start_tokens: str | list[str] = "",
|
|
308
|
+
hide_tokens: str | list[str] = "",
|
|
309
|
+
stop_tokens: str | list[str] = "",
|
|
310
|
+
depth: int = -1,
|
|
311
|
+
attribute: str = "_subelements",
|
|
312
|
+
) -> list[tuple[ContentElement, list[str]]]:
|
|
313
|
+
"""
|
|
314
|
+
Find all occurrences of the specified tokens within the content element.
|
|
315
|
+
|
|
316
|
+
:param tokens: The tokens to search for.
|
|
317
|
+
:param start_tokens: The tokens that must appear before the found tokens. Defaults to "".
|
|
318
|
+
:param hide_tokens: The tokens that should be hidden from the search. Defaults to "".
|
|
319
|
+
:param stop_tokens: The tokens that, if found, should stop the search. Defaults to "".
|
|
320
|
+
:param depth: The maximum depth to search. Defaults to -1 (unlimited depth).
|
|
321
|
+
:param attribute: The attribute to search within. Defaults to "_subelements".
|
|
322
|
+
|
|
323
|
+
:return: A list of tuples containing the content element and the found tokens.
|
|
324
|
+
"""
|
|
325
|
+
return list(
|
|
326
|
+
self._find(
|
|
327
|
+
tokens,
|
|
328
|
+
start_tokens=start_tokens,
|
|
329
|
+
hide_tokens=hide_tokens,
|
|
330
|
+
stop_tokens=stop_tokens,
|
|
331
|
+
depth=depth,
|
|
332
|
+
attribute=attribute,
|
|
333
|
+
)
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
def to_dict(self, depth: int = -1, all_content: bool = False, **kwargs) -> dict:
|
|
337
|
+
"""
|
|
338
|
+
Converts the object to a dictionary.
|
|
339
|
+
|
|
340
|
+
:param depth: The depth of the conversion. Defaults to -1.
|
|
341
|
+
:param all_content: Whether to include all content or only the top-level content. Defaults to False.
|
|
342
|
+
|
|
343
|
+
:return: The converted dictionary representation of the object.
|
|
344
|
+
"""
|
|
40
345
|
out_dict = {"token": self.token}
|
|
41
346
|
if all_content or not self.children:
|
|
42
347
|
out_dict["content"] = self.content
|
|
43
348
|
if self.children:
|
|
44
349
|
out_dict["children"] = (
|
|
45
|
-
self._list_property_to_dict("children",
|
|
46
|
-
if
|
|
350
|
+
self._list_property_to_dict("children", depth=depth - 1, all_content=all_content)
|
|
351
|
+
if depth
|
|
47
352
|
else self.children
|
|
48
353
|
)
|
|
49
354
|
return out_dict
|
|
50
355
|
|
|
51
356
|
def flatten(self) -> list[tuple[tuple[int, int], str, list[str]]]:
|
|
52
|
-
"""
|
|
357
|
+
"""
|
|
358
|
+
Converts the object to a flattened array of tokens per index, similarly to vscode-textmate.
|
|
359
|
+
|
|
360
|
+
:return: A list of tuples representing the flattened tokens. Each tuple contains:
|
|
361
|
+
- A tuple representing the starting and ending index of the token.
|
|
362
|
+
- The content of the token.
|
|
363
|
+
- A list of keys associated with the token.
|
|
364
|
+
"""
|
|
53
365
|
token_dict = self._token_by_index(defaultdict(list))
|
|
54
366
|
tokens = []
|
|
55
367
|
for (_, key), group in groupby(sorted(token_dict.items()), lambda x: (x[0][0], x[1])):
|
|
@@ -59,26 +371,55 @@ class ContentElement(object):
|
|
|
59
371
|
for pos, _ in group_tokens:
|
|
60
372
|
content += self.characters[pos]
|
|
61
373
|
if content:
|
|
62
|
-
tokens.append(
|
|
374
|
+
tokens.append((starting, content, key))
|
|
63
375
|
return tokens
|
|
64
376
|
|
|
65
|
-
def print(
|
|
66
|
-
|
|
377
|
+
def print(
|
|
378
|
+
self,
|
|
379
|
+
flatten: bool = False,
|
|
380
|
+
depth: int = -1,
|
|
381
|
+
all_content: bool = False,
|
|
382
|
+
**kwargs,
|
|
383
|
+
) -> None:
|
|
384
|
+
"""
|
|
385
|
+
Prints the current object recursively by converting it to a dictionary or a flattened array.
|
|
386
|
+
|
|
387
|
+
:param flatten: If True, flattens the object before printing. Defaults to False.
|
|
388
|
+
:param depth: The maximum depth to print. Defaults to -1 (unlimited depth).
|
|
389
|
+
:param all_content: If True, includes all content in the printout. Defaults to False.
|
|
390
|
+
:param **kwargs: Additional keyword arguments to be passed to the pprint function.
|
|
391
|
+
|
|
392
|
+
:return: None
|
|
393
|
+
"""
|
|
67
394
|
if flatten:
|
|
68
|
-
|
|
395
|
+
pprint(
|
|
396
|
+
self.flatten(**kwargs),
|
|
397
|
+
sort_dicts=False,
|
|
398
|
+
width=kwargs.pop("width", 150),
|
|
399
|
+
**kwargs,
|
|
400
|
+
)
|
|
69
401
|
else:
|
|
70
|
-
|
|
402
|
+
pprint(
|
|
403
|
+
self.to_dict(depth=depth, all_content=all_content, **kwargs),
|
|
404
|
+
sort_dicts=False,
|
|
405
|
+
width=kwargs.pop("width", 150),
|
|
406
|
+
**kwargs,
|
|
407
|
+
)
|
|
71
408
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
sort_dicts=False,
|
|
75
|
-
width=kwargs.pop("width", 150),
|
|
76
|
-
**kwargs,
|
|
77
|
-
)
|
|
409
|
+
def _token_by_index(self, token_dict: TOKEN_DICT | None = None) -> TOKEN_DICT:
|
|
410
|
+
"""Recursively tokenize every index between start and close.
|
|
78
411
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
412
|
+
This method recursively tokenizes every index between the start and close positions of the element.
|
|
413
|
+
It populates a dictionary, `token_dict`, with the tokens corresponding to each index.
|
|
414
|
+
|
|
415
|
+
:param token_dict: A dictionary to store the tokens. If None, a new dictionary is created.
|
|
416
|
+
:type token_dict: dict | None
|
|
417
|
+
:return: A dictionary containing the tokens for each index.
|
|
418
|
+
:rtype: dict
|
|
419
|
+
"""
|
|
420
|
+
if token_dict is None:
|
|
421
|
+
token_dict = defaultdict(list)
|
|
422
|
+
for pos in self.characters:
|
|
82
423
|
token_dict[pos].append(self.token)
|
|
83
424
|
|
|
84
425
|
# Tokenize child elements
|
|
@@ -89,7 +430,8 @@ class ContentElement(object):
|
|
|
89
430
|
def _list_property_to_dict(self, prop: str, **kwargs):
|
|
90
431
|
"""Makes a dictionary from a property."""
|
|
91
432
|
return [
|
|
92
|
-
item.to_dict(**kwargs) if isinstance(item, ContentElement) else item
|
|
433
|
+
item.to_dict(**kwargs) if isinstance(item, ContentElement) else item
|
|
434
|
+
for item in getattr(self, prop, [])
|
|
93
435
|
]
|
|
94
436
|
|
|
95
437
|
def __repr__(self) -> str:
|
|
@@ -101,29 +443,104 @@ class ContentBlockElement(ContentElement):
|
|
|
101
443
|
"""A parsed element with a begin and a end"""
|
|
102
444
|
|
|
103
445
|
def __init__(
|
|
104
|
-
self,
|
|
446
|
+
self,
|
|
447
|
+
*args,
|
|
448
|
+
begin: list[Capture | ContentElement] | None = None,
|
|
449
|
+
end: list[Capture | ContentElement] | None = None,
|
|
450
|
+
**kwargs,
|
|
105
451
|
) -> None:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
452
|
+
"""
|
|
453
|
+
Initialize a new instance of the Element class.
|
|
454
|
+
|
|
455
|
+
:param begin: A list of Capture or ContentElement objects representing the beginning captures of the element. Defaults to None.
|
|
456
|
+
:param end: A list of Capture or ContentElement objects representing the ending captures of the element. Defaults to None.
|
|
457
|
+
:param **kwargs: Additional keyword arguments to be passed to the parent class constructor.
|
|
458
|
+
|
|
459
|
+
:return: None
|
|
460
|
+
"""
|
|
461
|
+
if end is None:
|
|
462
|
+
end = []
|
|
463
|
+
if begin is None:
|
|
464
|
+
begin = []
|
|
465
|
+
super().__init__(*args, **kwargs)
|
|
466
|
+
self._begin_captures = begin
|
|
467
|
+
self._end_captures = end
|
|
468
|
+
|
|
469
|
+
@property
|
|
470
|
+
def _subelements(self) -> list[ContentElement]:
|
|
471
|
+
return self.begin + self.children + self.end
|
|
109
472
|
|
|
110
|
-
|
|
111
|
-
|
|
473
|
+
@property
|
|
474
|
+
def begin(self) -> list[ContentElement]:
|
|
475
|
+
"""
|
|
476
|
+
Returns the list of begin elements.
|
|
477
|
+
|
|
478
|
+
If the elements have not been dispatched yet, this method will dispatch them before returning.
|
|
479
|
+
|
|
480
|
+
:return: The list of begin elements.
|
|
481
|
+
"""
|
|
482
|
+
if not self._dispatched:
|
|
483
|
+
self._dispatch()
|
|
484
|
+
return self._begin
|
|
485
|
+
|
|
486
|
+
@property
|
|
487
|
+
def end(self) -> list[ContentElement]:
|
|
488
|
+
"""
|
|
489
|
+
Returns the end elements.
|
|
490
|
+
|
|
491
|
+
If the elements have not been dispatched yet, this method will dispatch them before returning.
|
|
492
|
+
|
|
493
|
+
:return: A list of end elements.
|
|
494
|
+
"""
|
|
495
|
+
if not self._dispatched:
|
|
496
|
+
self._dispatch()
|
|
497
|
+
return self._end
|
|
498
|
+
|
|
499
|
+
def _dispatch(self, nested: bool = False):
|
|
500
|
+
if self._dispatched:
|
|
501
|
+
return
|
|
502
|
+
super()._dispatch(nested)
|
|
503
|
+
self._begin: list[ContentElement] = _dispatch_list(self._begin_captures, parent=self)
|
|
504
|
+
self._end: list[ContentElement] = _dispatch_list(self._end_captures, parent=self)
|
|
505
|
+
self._begin_captures, self._end_captures = [], []
|
|
506
|
+
if nested:
|
|
507
|
+
for item in self._begin:
|
|
508
|
+
item._dispatch(True)
|
|
509
|
+
for item in self._end:
|
|
510
|
+
item._dispatch(True)
|
|
511
|
+
|
|
512
|
+
def to_dict(self, depth: int = -1, all_content: bool = False, **kwargs) -> dict:
|
|
513
|
+
"""
|
|
514
|
+
Converts the element to a dictionary representation.
|
|
515
|
+
|
|
516
|
+
:param depth: The depth of the conversion. Defaults to -1.
|
|
517
|
+
:param all_content: Whether to include all content. Defaults to False.
|
|
518
|
+
:param **kwargs: Additional keyword arguments.
|
|
519
|
+
|
|
520
|
+
:return: The dictionary representation of the element.
|
|
521
|
+
"""
|
|
522
|
+
out_dict = super().to_dict(depth=depth, all_content=all_content, **kwargs)
|
|
112
523
|
if self.begin:
|
|
113
524
|
out_dict["begin"] = (
|
|
114
|
-
self._list_property_to_dict("begin",
|
|
525
|
+
self._list_property_to_dict("begin", depth=depth - 1, **kwargs)
|
|
526
|
+
if depth
|
|
527
|
+
else self.begin
|
|
115
528
|
)
|
|
116
529
|
if self.end:
|
|
117
530
|
out_dict["end"] = (
|
|
118
|
-
self._list_property_to_dict("end",
|
|
531
|
+
self._list_property_to_dict("end", depth=depth - 1, **kwargs) if depth else self.end
|
|
119
532
|
)
|
|
120
533
|
|
|
121
|
-
ordered_keys = [
|
|
534
|
+
ordered_keys = [
|
|
535
|
+
key for key in ["token", "begin", "end", "content", "children"] if key in out_dict
|
|
536
|
+
]
|
|
122
537
|
ordered_dict = {key: out_dict[key] for key in ordered_keys}
|
|
123
538
|
return ordered_dict
|
|
124
539
|
|
|
125
|
-
def _token_by_index(self, token_dict: TOKEN_DICT =
|
|
540
|
+
def _token_by_index(self, token_dict: TOKEN_DICT | None = None) -> TOKEN_DICT:
|
|
126
541
|
"""Converts the object to a flattened array of tokens."""
|
|
542
|
+
if token_dict is None:
|
|
543
|
+
token_dict = defaultdict(list)
|
|
127
544
|
super()._token_by_index(token_dict)
|
|
128
545
|
for element in self.begin:
|
|
129
546
|
element._token_by_index(token_dict)
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
1
|
import shutil
|
|
3
|
-
import
|
|
2
|
+
from pathlib import Path
|
|
4
3
|
|
|
4
|
+
import yaml
|
|
5
5
|
|
|
6
|
-
tmLanguageFile =
|
|
6
|
+
tmLanguageFile = (
|
|
7
|
+
Path(__file__).parents[3] / "syntaxes" / "markdown" / "markdown.tmLanguage.base.yaml"
|
|
8
|
+
)
|
|
7
9
|
tmLanguageYAML = Path(__file__).parent / "grammar.yaml"
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
if tmLanguageFile.exists():
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
with open(tmLanguageYAML, "r") as file:
|
|
14
|
-
GRAMMAR = yaml.load(file.read())
|
|
15
|
-
|
|
13
|
+
shutil.copyfile(tmLanguageFile, tmLanguageYAML)
|
|
16
14
|
|
|
15
|
+
with open(tmLanguageYAML) as file:
|
|
16
|
+
try:
|
|
17
|
+
GRAMMAR = yaml.load(file.read(), Loader=yaml.CLoader)
|
|
18
|
+
except ImportError:
|
|
19
|
+
GRAMMAR = yaml.load(file.read(), Loader=yaml.Loader)
|
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
1
|
import plistlib
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
try:
|
|
7
|
-
from yaml import CLoader as Loader, CDumper as Dumper
|
|
8
|
-
except ImportError:
|
|
9
|
-
from yaml import Loader, Dumper
|
|
2
|
+
from pathlib import Path
|
|
10
3
|
|
|
4
|
+
import yaml
|
|
11
5
|
|
|
12
|
-
tmLanguageFile =
|
|
6
|
+
tmLanguageFile = (
|
|
7
|
+
Path(__file__).parents[3]
|
|
8
|
+
/ "syntaxes"
|
|
9
|
+
/ "matlab"
|
|
10
|
+
/ "Matlab.tmbundle"
|
|
11
|
+
/ "Syntaxes"
|
|
12
|
+
/ "MATLAB.tmLanguage"
|
|
13
|
+
)
|
|
13
14
|
tmLanguageYAML = Path(__file__).parent / "grammar.yaml"
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
if tmLanguageFile.exists():
|
|
17
18
|
with open(tmLanguageFile, "rb") as file:
|
|
18
19
|
GRAMMAR = plistlib.load(file, fmt=plistlib.FMT_XML)
|
|
19
|
-
with open(tmLanguageYAML,
|
|
20
|
+
with open(tmLanguageYAML, "w") as f:
|
|
20
21
|
f.write(yaml.dump(GRAMMAR, indent=2))
|
|
21
22
|
else:
|
|
22
|
-
with open(tmLanguageYAML
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
with open(tmLanguageYAML) as file:
|
|
24
|
+
try:
|
|
25
|
+
GRAMMAR = yaml.load(file.read(), Loader=yaml.CLoader)
|
|
26
|
+
except ImportError:
|
|
27
|
+
GRAMMAR = yaml.load(file.read(), Loader=yaml.Loader)
|