Enhanced C#
Language of your choice: library documentation
|
A helper type for printer objects. Its primary purposes are to manage indentation and to "revoke" newlines; it also tracks the current line/column number. More...
A helper type for printer objects. Its primary purposes are to manage indentation and to "revoke" newlines; it also tracks the current line/column number.
Be careful not to duplicate this structure.
When pretty-printing any language as text, it's a challenge to decide where to place newlines. You may want to break up long lines into shorter ones, as in
if (ReallyLongIdentifier[Fully.Qualified.Name(multiple, parameters)] > SomeConstant) { return ReallyLongIdentifier[firstThing + secondThing] + thirdThing + fourthThing; }
Conversely, you may want to print something on one line that you would ordinarily print on two:
if (c) break;
Of course, the problem is, you don't know how long the syntax tree will be in text form until after you try to print it.
My first idea to solve this problem was to use a rope tree data structure - inner syntax trees would produce small strings that could be "roped" together to produce a bigger tree. But ropes tend not to use memory efficiently, and there was the challenge, which I didn't see how to solve, of how to keep the tree balanced efficiently (for this particular application perhaps a balanced tree wasn't needed, but as a perfectionist I didn't want to implement a "half-baked" data structure.)
Next I thought of the solution used here, a simpler solution based on an ordinary StringBuilder. My idea was to insert newlines "pessimistically"
To use, call Newline() to write a newline (with indentation). To make a decision about whether to keep or revoke the most recent newline(s), call RevokeOrCommitNewlines(cp, maxLineLength) where cp is a "checkpoint" representing some point before the first newline want to potentially revoke, and maxLineLength is the line length threshold: if the line length after combining lines, starting at the line on which the checkpoint is located, does not exceed maxLineLength, then the newlines are revoked, otherwise ALL newlines are committed (so earlier newlines can no longer be revoked.)
This design allows a potentially long series of newlines to be deleted in the reverse order that they were created, but if any newline is kept then previous ones can no longer be deleted.
For an example of how this is used, see the JSON printer in LLLPG samples or look at the implementation of the LESv3 printer.
Nested classes | |
struct | Checkpoint |
Public fields | |
StringBuilder | S |
int | IndentLevel |
int | LineNo |
string | IndentString |
string | NewlineString |
Properties | |
int | LineStartIndex [get] |
int | IndexInCurrentLine [get] |
int | IndexInCurrentLineAfterIndent [get] |
bool | AtStartOfLine [get] |
int | Length [get] |
Current length of the output string More... | |
Public Member Functions | |
PrinterState (StringBuilder s, string indent="\t", string newline="\n") | |
StringBuilder | Append (string s) |
void | Indent () |
void | Dedent () |
Checkpoint | GetCheckpoint () |
Checkpoint | Newline (int changeIndentLevel=0) |
Writes a newline and the appropriate amount of indentation afterward. More... | |
int | RevokeOrCommitNewlines (Checkpoint cp, int maxLineWidth) |
Revokes or commits newlines added since the specified checkpoint. Recent newlines are revoked if the combined line length after revokation does not exceed maxLineWidth , otherwise ALL newlines are committed permanently. More... | |
int | CommitNewlines () |
int | RevokeNewlinesSince (Checkpoint cp) |
|
inline |
Writes a newline and the appropriate amount of indentation afterward.
changeIndentLevel | Amount by which to change IndentLevel before writing the newline |
Note that "revoking" a newline does NOT restore the original indent level.
|
inline |
Revokes or commits newlines added since the specified checkpoint. Recent newlines are revoked if the combined line length after revokation does not exceed maxLineWidth
, otherwise ALL newlines are committed permanently.
This method does not affect the indent level.
|
get |
Current length of the output string