diff --git a/src/formatitko/nop_processor.py b/src/formatitko/nop_processor.py index bc20a2a..f20ff1b 100644 --- a/src/formatitko/nop_processor.py +++ b/src/formatitko/nop_processor.py @@ -127,13 +127,17 @@ class NOPProcessor: raise FormatitkoRecursiveError(e, self.context) from err def transform_list(self, e: list[Union[Element, ListContainer]]) -> list[Union[Element, ListContainer]]: - for i in range(len(e)): + i = 0 + while i < len(e): # The length of the list can change mid-transformation, so we need to check the length each time e[i] = self.transform(e[i]) + i-=-1 return e def transform_ListContainer(self, e: ListContainer) -> ListContainer: - for i in range(len(e)): + i = 0 + while i < len(e): # The length of the list can change mid-transformation, so we need to check the length each time e[i] = self.transform(e[i]) + i-=-1 return e diff --git a/src/formatitko/output_generator.py b/src/formatitko/output_generator.py index 332bb25..7ba1278 100644 --- a/src/formatitko/output_generator.py +++ b/src/formatitko/output_generator.py @@ -31,7 +31,7 @@ class FormatitkoRecursiveError(Exception): def add_element(self, e: Union[Element, ListContainer, list[Union[Element, ListContainer]]]): self.elements.append(e) - def pretty_print(self): + def pretty_print(self, tracebacklimit: int=0): def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) @@ -49,7 +49,7 @@ class FormatitkoRecursiveError(Exception): eprint('on line: "' + stringify(line).strip() + '"', end="") eprint() eprint("in element: " + str(self.elements[0]).replace("\n", "\\n")) - sys.tracebacklimit = 0 + sys.tracebacklimit = tracebacklimit raise self.__cause__ from None diff --git a/src/formatitko/transform_processor.py b/src/formatitko/transform_processor.py index d327663..4560e0c 100644 --- a/src/formatitko/transform_processor.py +++ b/src/formatitko/transform_processor.py @@ -3,6 +3,7 @@ from panflute import Cite, Code, Emph, Image, LineBreak, Link, Math, Note, Quote from panflute import BlockQuote, BulletList, Citation, CodeBlock, Definition, DefinitionItem, DefinitionList, Div, Figure, Header, HorizontalRule, LineBlock, LineItem, ListItem, MetaBlocks, MetaBool, MetaInlines, MetaList, MetaMap, MetaString, Null, OrderedList, Para, Plain, RawBlock, Table, TableBody, TableFoot, TableHead from panflute import TableRow, TableCell, Caption, Doc from panflute import MetaValue +from panflute.containers import attach from typing import Union, Callable from types import ModuleType @@ -75,8 +76,8 @@ class TransformProcessor(NOPProcessor): self.context = Context(e, self.root_file_path) for module, module_name in self._command_modules: self.context.add_commands_from_module(module, module_name) - e.content = self.transform(e.content) e.content = [BlockGroup(*e.content, context=self.context)] + e.content = self.transform(e.content) return e @@ -136,8 +137,9 @@ class TransformProcessor(NOPProcessor): # Commands can be called multiple ways, this handles the following syntax: # :::{c=commandname} # ::: - e = BlockCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes) - return self.transform(e) + command = BlockCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes) + attach(command, e.parent, e.location, e.index) + return self.transform(command) if "partial" in e.attributes: # `partial` attribute @@ -195,14 +197,16 @@ class TransformProcessor(NOPProcessor): if "c" in e.attributes: # Commands can be called multiple ways, this handles the following syntax: # []{c=commandname} and - e = InlineCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes) - return self.transform(e) + command = InlineCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes) + attach(command, e.parent, e.location, e.index) + return self.transform(command) if len(e.content) == 1 and isinstance(e.content[0], Str): ## Handle special command shorthand [!commandname]{} if re.match(r"^![\w.]+$", e.content[0].text): - e = InlineCommand(identifier=e.identifier, classes=e.classes, attributes={**e.attributes, "c": e.content[0].text[1:]}) - return self.transform(e) + command = InlineCommand(identifier=e.identifier, classes=e.classes, attributes={**e.attributes, "c": e.content[0].text[1:]}) + attach(command, e.parent, e.location, e.index) + return self.transform(command) ## Handle import [#ksp_formatitko as ksp]{}, [#ksp_formatitko]{type=module} or [#path/file.md]{type=md} # Import a python module as commands (type=module, the default) or @@ -255,8 +259,9 @@ class TransformProcessor(NOPProcessor): if not self.context.trusted: return nullify(e) command_output = parse_command(e.text)(BlockCommand(), self.context, self) - e = BlockCommand().replaceSelf(*([] if command_output is None else command_output)) - return self.transform(e) + command = BlockCommand().replaceSelf(*([] if command_output is None else command_output)) + attach(command, e.parent, e.location, e.index) + return self.transform(command) if "python" in e.classes and ("define" in e.attributes or "redefine" in e.attributes): if not self.context.trusted: @@ -264,7 +269,9 @@ class TransformProcessor(NOPProcessor): return handle_command_define(e, self.context) if "c" in e.attributes: - return self.transform(CodeCommand(e.text, identifier=e.identifier, classes=e.classes, attributes=e.attributes)) + command = CodeCommand(e.text, identifier=e.identifier, classes=e.classes, attributes=e.attributes) + attach(command, e.parent, e.location, e.index) + return self.transform(command) # Pass down metadata 'highlight' and 'highlight_style' as attribute to CodeBlocks # OG now has Context so this is not needed per se, but I'm keeping this here for the handling of attribute > context > default value