Enhanced C#
Language of your choice: library documentation
Nested classes | Public fields | Properties | Public Member Functions | List of all members
Loyc.Syntax.Impl.PrinterState Struct Reference

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...


Source file:

Remarks

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)
 

Member Function Documentation

◆ Newline()

Checkpoint Loyc.Syntax.Impl.PrinterState.Newline ( int  changeIndentLevel = 0)
inline

Writes a newline and the appropriate amount of indentation afterward.

Parameters
changeIndentLevelAmount by which to change IndentLevel before writing the newline
Returns
A Checkpoint that can be used to revoke the newline

Note that "revoking" a newline does NOT restore the original indent level.

◆ RevokeOrCommitNewlines()

int Loyc.Syntax.Impl.PrinterState.RevokeOrCommitNewlines ( Checkpoint  cp,
int  maxLineWidth 
)
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.

Returns
0 if the method had no effect, -N if N newlines were revoked, and +N if N newlines were committed.

This method does not affect the indent level.

Property Documentation

◆ Length

int Loyc.Syntax.Impl.PrinterState.Length
get

Current length of the output string