simvx.core.scene_io.layout¶
Where an emitted statement breaks its lines.
A scene file is code the author reads, edits and formats alongside everything
else in their project, so what this layer writes has to look like the rest of
it. A constructor carrying a material, a texture and a transform runs well past
any sane column on one line; broken up by hand it would read one keyword
argument per line, and that is what :func:wrap_statement produces.
The rule implemented here is the one a formatter run at :data:LINE_LIMIT
columns applies, so a file this layer writes and a file the author formatted
are the same file:
a statement that fits on one line stays on one line;
a call that does not puts its arguments on a single indented line of their own, if they fit there;
one that still does not puts each argument on its own line, with a trailing comma, descending into any argument that is itself too long;
a list, tuple or dict that has to break at all breaks all the way: a collection with more than one item never takes the single-line-of-its-own form a call can;
a group whose items already end in a comma stays broken out however short it is, because that comma is how an author says so – unless the comma is what makes the value what it is, as in a one-item tuple.
Width is counted in columns rather than characters throughout
(:func:display_width), which is what a limit is and what the formatter
counts: a construction carrying a Chinese label is written wider than it is
long, and measured by its length would be left whole well past the limit.
An import line is the one statement that departs from the rule, and
- func:
wrap_importis where it does: it never takes the single-line-of-its-own form, because the comma a broken import ends up with is one the author could have typed and therefore means what theirs means.
Everything here is text in, text out: no parse, no tree. The emitter builds the
one-line form it always did and hands it over; :mod:~simvx.core.scene_io.edits
applies the same rule to a call it has just edited in an existing file.
Module Contents¶
Functions¶
The columns |
|
|
|
|
|
|
|
The comma-separated items of a bracket body, in order. |
|
Does this bracket body end in a comma the layout has to keep? |
|
Is this group’s trailing comma part of the value rather than a request? |
|
Does |
|
Is the group |
|
How a bracketed group with these |
Data¶
API¶
- simvx.core.scene_io.layout.__all__¶
[‘LINE_LIMIT’, ‘call_layout’, ‘carries_magic_comma’, ‘display_width’, ‘ends_with_comma’, ‘is_collect…
- simvx.core.scene_io.layout.LINE_LIMIT¶
120
- simvx.core.scene_io.layout.INDENT¶
4
- simvx.core.scene_io.layout.display_width(text: str) int[source]¶
The columns
textoccupies, which is not always how many characters it has.A limit is a count of columns, and East Asian script does not spend one character per column: a label of Chinese measured with :func:
lencomes out half the width it is written at, and a line of it is left alone well past the limit. Every width this module compares against a limit is counted here instead, and so is every width :mod:~simvx.core.scene_io.editsmeasures off a parsed file.A character is two columns wide when its East Asian width is wide or fullwidth, and one otherwise. The few wide characters that attach to the one in front of them – a Japanese voiced-sound mark, an emoji skin tone – are not counted wide, being written over their neighbour rather than beside it. That is the count the project’s formatter makes, for every character the standard library’s Unicode tables know about.
- simvx.core.scene_io.layout.wrap_statement(text: str, *, indent: int = 0, limit: int = LINE_LIMIT, explode: bool = False, suffix: str = '') str[source]¶
textlaid out to fitlimitcolumns, starting at columnindent.textis one statement written on a single line –hero = Sprite2D(...),super().__init__(...),self.add_child(hero). The returned string carries no indent on its first line, because the caller is already placing that statement atindent; every continuation line it contains is indented in full.explodeforces the one-argument-per-line form regardless of width, which is what an existing call’s trailing comma asks for.suffixis what follows the group on its last line without being part of it – the:ofclass Hero(Node2D):– and comes back on the end. Passing it rather than writing it in is what lets the group be found and broken at all, and its width counts againstlimitlike everything else sharing the line.A statement with nothing to break – one ending in no bracketed group, or in an empty one – comes back as it went in, over the limit: reflowing the inside of a value is not this layer’s business, and a line that cannot be broken is better left long than broken wrongly. A group whose one argument is itself too long for any line still gets broken open around it, which is where the argument has the most room, and is what a formatter does with it.
- simvx.core.scene_io.layout.wrap_import(module: str, names: list[str], *, limit: int = LINE_LIMIT, explode: bool = False) str[source]¶
from <module> import ...fornames, laid out to fitlimit.One line for as long as the names fit on one, without the brackets a file may have had round them: a pair no longer holding anything apart is a pair a formatter takes off. Past that the names go inside brackets, one per line, each with a comma after it, and never onto the single shared line of their own that a call’s arguments may take.
That last is the one place an import parts company with a call, and the reason is the comma. A broken group’s trailing comma is how an author asks for a line per item (:func:
carries_magic_comma), and an import may carry one –from x import (a, b,)is legal wherefrom x import a, b,is not – so the comma this layout writes would be read back as that request the next time the line is laid out. Writing the form that request asks for is what keeps a second pass over an import agreeing with the first.explodeis that request, found on an import already in the file.namesare written in the order given, and the statement is written at column zero: an import is a module-level statement.
- simvx.core.scene_io.layout.split_call(text: str) tuple[str, list[str], str] | None[source]¶
(head, items, closer)for the bracketed grouptextends with.Sprite2D(name='Hero', position=Vec2(1.0, 2.0))splits into"Sprite2D(", the two arguments, and")". The group taken is the last one that opens at the top level and closes on the final character, which is what makessuper().__init__(...)split at__init__rather than at the emptysuper()in front of it, andself.add_child(Sprite2D(...))split atadd_childrather than at the construction inside it.Nonewhentextdoes not end in a bracket that opened at the top level: there is then no group to break. Brackets and commas inside string literals are text and are skipped.
- simvx.core.scene_io.layout.split_items(body: str) list[str][source]¶
The comma-separated items of a bracket body, in order.
Commas inside nested brackets and inside string literals do not separate, and neither do the ones between a
lambda’s parameters:lambda a, b: xis one argument, and breaking the line at its comma would leave a fragment that is not an expression at all. A trailing comma yields no empty final item; ask :func:ends_with_commaabout the one that was there.
- simvx.core.scene_io.layout.ends_with_comma(body: str) bool[source]¶
Does this bracket body end in a comma the layout has to keep?
For a one-item tuple the comma is what makes it a tuple, so dropping it while breaking the line would change the value the file loads back.
- simvx.core.scene_io.layout.trailing_comma_is_syntax(opener: str, *, collection: bool, item_count: int) bool[source]¶
Is this group’s trailing comma part of the value rather than a request?
An author’s comma after the last of several arguments is a request to keep them on separate lines. Two commas are not requests at all, because without them the expression means something else: the one in a one-item tuple
(value,), and the one in a one-item subscriptframes[0,], each of which is what makes that expression a tuple. A one-item list, dict or call has no such excuse and is broken out like any other.
- simvx.core.scene_io.layout.carries_magic_comma(text: str) bool[source]¶
Does
texthold, at any depth, a comma asking for the broken-out form?A comma an author left after the last item does not only break the group it is in: everything that group sits inside breaks with it, or the group could not have its own lines. So the question has to be asked of a whole value before a line is chosen for it, not only of the group being laid out.
- simvx.core.scene_io.layout.is_collection_head(head: str) bool[source]¶
Is the group
headopens a collection literal rather than a call?[and{and(mean one thing after a name and another after an=:Vec2(1, 2)andART['icon']are a call and a subscript, while[1, 2]and(1, 2)are a list and a tuple. What decides is the character in front of the bracket, and what turns on it is whether the group may put its items on a single line of their own: a collection that has to break at all breaks one item per line.
- simvx.core.scene_io.layout.call_layout(items: list[str], *, indent: int, prefix_width: int, suffix_width: int = 0, limit: int = LINE_LIMIT, explode: bool = False, collection: bool = False, extra: int = 0) str[source]¶
How a bracketed group with these
itemsshould be laid out."line"keeps the whole group on the line it starts on,"row"puts every item on one indented line of its own,"column"gives each item a line and a trailing comma.prefix_widthis the column the group’s opening bracket sits at plus one – everything already written on that line – andsuffix_widthwhat follows its closing bracket, since both share the line and both count againstlimit.collectionmarks a list, tuple or dict literal, which takes"column"wherever a call would take"row", unless it holds a single item and so has nothing to separate.extrais what the body carries beyond the items themselves: the one column of a retained trailing comma, without which a group would be judged a character narrower than it is going to be written.Shared with :func:
wrap_statementso that a call laid out from text and one laid out by editing a parsed file arrive at the same answer.