simvx.core.scene_io.edits¶
Prefix-preserving editing primitives for parso trees.
parso stores leading whitespace and comments on each leaf’s prefix string;
the prefix of a non-leaf node is the prefix of its first leaf. Edits that
splice nodes in or out of the tree must transfer prefixes carefully so that
trailing comments stay glued to the right line, blank-line spacing does not
drift, and indent depth is preserved.
The primitives here operate directly on the parso tree (mutating
parent.children lists). They are deliberately small: composition lives
in higher tiers (scene_file, scene_module).
Module Contents¶
Functions¶
Replace |
|
Insert |
|
Insert |
|
Remove |
|
Remove one statement, which a semicolon may have joined to others on its line. |
|
The statements written on one line, which a semicolon can join several of. |
|
Return the value subtree for kwarg |
|
Set kwarg |
|
Replace the |
|
Yield everything a call passes, named or not, in the order it is written. |
|
Is this call item written as |
|
Lay the statement containing |
|
The |
API¶
- simvx.core.scene_io.edits.replace_node(old: parso.tree.NodeOrLeaf, new: parso.tree.NodeOrLeaf, *, preserve_prefix: bool = True) None[source]¶
Replace
oldwithnewin their shared parent’schildrenlist.With
preserve_prefix=True(default), the leading whitespace + comments ofold’s first leaf are transferred ontonew’s first leaf so that same-line trailing comments on the previous statement, leading blank lines, and decorators above remain attached.
- simvx.core.scene_io.edits.insert_after(sibling: parso.tree.NodeOrLeaf, new_node: parso.tree.NodeOrLeaf, *, copy_indent: bool = True) None[source]¶
Insert
new_nodeimmediately aftersiblingin their shared parent.With
copy_indent=True(default), the indent run ofsibling’s first leaf prefix is copied ontonew_nodeso the new statement sits at the same column.new_node’s prefix is overwritten with"\n<indent>": callers wanting custom prefixes should passcopy_indent=Falseand populate the prefix themselves.
- simvx.core.scene_io.edits.insert_before(sibling: parso.tree.NodeOrLeaf, new_node: parso.tree.NodeOrLeaf, *, copy_indent: bool = True) None[source]¶
Insert
new_nodeimmediately beforesiblingin their shared parent.With
copy_indent=True(default),new_nodeinheritssibling’s full prefix (so any leading comments/blank lines stay above the inserted node) andsibling’s prefix is reset to"\n<indent>"so it sits at the same column it did originally.Note: this transfers comments above
siblingto the inserted node. To keep them attached tosibling, passcopy_indent=Falseand manage prefixes manually.
- simvx.core.scene_io.edits.remove_node(node: parso.tree.NodeOrLeaf, *, collapse_blank_lines: bool = True) None[source]¶
Remove
nodefrom its parent’schildrenlist.With
collapse_blank_lines=True(default), surplus blank lines innode’s prefix are collapsed onto the next sibling so deleting statements in sequence does not balloon vertical spacing. The collapse rule is: keep at most one blank line of separation; the indent run on the final line is preserved verbatim.Same-line trailing comments stored in
node’s prefix (which originate on the previous sibling: see module docstring) are re-attached to the next sibling so they stay on their original line.
- simvx.core.scene_io.edits.remove_statement(stmt: parso.tree.NodeOrLeaf, *, collapse_blank_lines: bool = True) None[source]¶
Remove one statement, which a semicolon may have joined to others on its line.
stmtis a statement as parso hands one out: the sole statement of an ordinary line, or one of the several a semicolon-joined line holds. A line down to its last statement goes whole, newline and all (:func:remove_node, whose prefix handling this then inherits); otherwise only the statement goes, together with one of the semicolons beside it, and the line keeps its indent whichever end it lost.
- simvx.core.scene_io.edits.line_statements(line: parso.tree.NodeOrLeaf) list[parso.tree.NodeOrLeaf][source]¶
The statements written on one line, which a semicolon can join several of.
parso folds
self.name = "Root"; self.add_child(Hero())into a singlesimple_stmtwhose children are the two statements, the semicolon between them and the newline, so a reader that looks only at the first child sees half the line. Anything that is not asimple_stmtis already a single statement and comes back on its own.
- simvx.core.scene_io.edits.get_call_kwarg(call_node: parso.tree.NodeOrLeaf, name: str) parso.tree.NodeOrLeaf | None[source]¶
Return the value subtree for kwarg
namein a call, orNone.call_nodemay be either thetrailer(the(...)after a name) or the enclosingatom_expr: both forms are accepted.
- simvx.core.scene_io.edits.set_call_kwarg(call_node: parso.tree.NodeOrLeaf, name: str, value_expr: str) None[source]¶
Set kwarg
nameon a call expression.Overwrites if
namealready exists (preserves the order of other args); appends if not.value_expris parsed with :func:parse_snippet, so callers pass real Python source (e.g."Vec2(0, 0)"or'"hello"').A trailing comma in the original
arglistis preserved when appending.
- simvx.core.scene_io.edits.set_call_positional(call_node: parso.tree.NodeOrLeaf, index: int, value_expr: str) None[source]¶
Replace the
index-th argument a call passes by position.The counterpart of :func:
set_call_kwargfor the arguments a call does not name. Which parameter a position fills is a fact about the callable rather than about the text, so establishing thatindexis the caller’s business; all that happens here is that the expression standing there is swapped forvalue_exprand the statement laid out again.Raises :class:
ValueErrorwhen the call passes no such position.
- simvx.core.scene_io.edits.iter_call_items(trailer: parso.tree.BaseNode) collections.abc.Iterator[parso.tree.NodeOrLeaf][source]¶
Yield everything a call passes, named or not, in the order it is written.
- Func:
_iter_argumentsyields theargumentnodes, which is to say the named ones and the unpackings; this yields those and the bare expressions a call passes by position, so a caller counting positions counts them against the same list the interpreter would.
- simvx.core.scene_io.edits.is_named_argument(item: parso.tree.NodeOrLeaf) bool[source]¶
Is this call item written as
name=value?Falsefor everything else :func:iter_call_itemsyields: an expression passed by position, and an unpacking (*args,**kwargs), which parso also builds anargumentnode for but which names no parameter.
- simvx.core.scene_io.edits.relayout_statement(node: parso.tree.NodeOrLeaf, *, limit: int = LINE_LIMIT) None[source]¶
Lay the statement containing
nodeout to fitlimitcolumns.Called after a call in an existing file has been edited, so the statement the save just rewrote is left in the shape a formatter would leave it: on one line while it fits, and one keyword argument per line once it does not (:mod:
~simvx.core.scene_io.layoutdecides, from the same rule the emitter writes new files by). A statement nothing wrote to is never reached, so a save with no edits in it changes no line’s shape.The author’s own expressions survive it: nothing is reparsed, reprinted or rewritten, and every value is the node it always was, down to its quotes and its digits. What moves is the whitespace the layout owns and only that – after an opening bracket, around a comma, before a closing bracket – and it moves at every depth, so a value the author spaced out inside its own brackets comes back spaced the way a formatter spaces it.
Three statements are left exactly as found, because relaying them out would destroy something this layer cannot put back: one carrying a comment, which belongs to the line it was written on; one holding a value spanning lines of its own (a triple-quoted string, a continuation); and one sharing its line with other statements behind semicolons, whose width is not one statement’s to measure.
- simvx.core.scene_io.edits.enclosing_statement(node: parso.tree.NodeOrLeaf) parso.tree.BaseNode | None[source]¶
The
simple_stmtnodesits in, orNonewhen it sits in none.Worth asking for before an edit that detaches
nodefrom the tree: the answer is what :func:relayout_statementthen has to be given, since the detached node no longer leads anywhere.