Karya, built on Mon Jul 24 11:39:07 PDT 2017 (patch 33511aca01257b76b88de7c7a2763b7a965c084e)

Util.Format

Description

This is a library to lay out text with line wrapping and indenting.

The basic theory is that you concatenate text with BreakTypes. In addition, you can increment the indent level with withIndent. When render wraps the text, it will break on the lowest indent level, or as soon as the indent level decreases.

A further wrinkle is that you can mark alternate layouts with shortForm.

Synopsis

# Documentation

data Doc Source #

Constructors

 Text !Text Use text instead of this constructor to get newlines right. Doc :+ Doc infixr 9 ShortForm Doc Doc Use shortForm. Indent !Indent Change the indent by the given number of steps. The new indent level only takes effect after the first Break. Break !BreakType Line break.

Instances

 # Methods(==) :: Doc -> Doc -> Bool #(/=) :: Doc -> Doc -> Bool # # MethodsshowsPrec :: Int -> Doc -> ShowS #showList :: [Doc] -> ShowS # # Methods # Methodsmappend :: Doc -> Doc -> Doc #mconcat :: [Doc] -> Doc # # MethodsformatList :: [Doc] -> Doc Source # # MethodsformatList :: [Value Doc] -> Doc Source # # MethodsformatList :: [Record Doc] -> Doc Source #

The first Doc is the short form, which will be used if it doesn't have to wrap. So you can give a compact form which will render if it can fit without breaking, and then a form with more fancy layout that will be used if it does have to break. For example, [1, 2, 3] for short lists, and [1\n, 2\n, 3\n] for long ones.

Prepending text to a shortForm will distribute over both short and long forms. Otherwise, if you write "prefix " <> x, and x happens to be a shortForm, the long form loses the prefix.

Appending two shortForms will make you lose the long form of the second one. So don't do that. TODO I'd rather both short and long forms be appended, but haven't figured out how to do that yet.

(</>) :: Doc -> Doc -> Doc infixr 5 Source #

Soft break with no space.

(<+/>) :: Doc -> Doc -> Doc infixr 5 Source #

Soft break with a space.

(<//>) :: Doc -> Doc -> Doc infixr 4 Source #

Hard break with a single newline.

(<+>) :: Doc -> Doc -> Doc infixr 6 Source #

Join two docs with a space.

Insert a number of newlines.

Consecutive breaks are merged together, and a hard break always wins. Also, multiple hard breaks are merged into one, and ones with greater newlines win other those with fewer. The rationale is that if you are formatting a list of sub-Docs, and you want to put each on its own line, you need a hard break after each one, but if one of them does the same thing, you wind up with two breaks in a row.

unlines :: [Doc] -> Doc Source #

Analogous to unlines, terminate each Doc with a newline.

paragraphs :: [Doc] -> Doc Source #

This is just like unlines, but separate docs with two newlines, and terminate the last with one.

wrap :: [Doc] -> Doc Source #

Increase the indent level for the given Doc. The indent change only takes effect after the first break, so if you want it to take effect immediately, use one of indent, indent_, or indentLine.

The reason indent is delayed is that this way you can do a hanging indent, where the current line is unindented, but it will be indented if it wraps. Otherwise you don't know where to put the indent, since you don't know where the break will happen.

Change the indent level and add a no-space break so it takes effect immediately.

Change the indent level and add a spaced break so it takes effect immediately.

Change the indent level and add a hard break so it takes effect immediately.

type Width = Int Source #

Width of monospace text, in characters.

Render a Doc, wrapping after the given Width.

Render the Doc all on one line, with no newlines.

Merge Texts so the Doc is easier to read.

Reduce the Doc to a flattened easier to read version.

data Doc Source #

Constructors

 Text !Text Use text instead of this constructor to get newlines right. Doc :+ Doc infixr 9 ShortForm Doc Doc Use shortForm. Indent !Indent Change the indent by the given number of steps. The new indent level only takes effect after the first Break. Break !BreakType Line break.

Instances

 # Methods(==) :: Doc -> Doc -> Bool #(/=) :: Doc -> Doc -> Bool # # MethodsshowsPrec :: Int -> Doc -> ShowS #showList :: [Doc] -> ShowS # # Methods # Methodsmappend :: Doc -> Doc -> Doc #mconcat :: [Doc] -> Doc # # MethodsformatList :: [Doc] -> Doc Source # # MethodsformatList :: [Value Doc] -> Doc Source # # MethodsformatList :: [Record Doc] -> Doc Source #

data BreakType Source #

Space becomes a space when it doesn't break, NoSpace doesn't. Hard breaks can insert >=1 newlines.

Constructors

 NoSpace Space Hard !Int

Instances

 # Methods # Methods # MethodsshowList :: [BreakType] -> ShowS # # Hard breaks with more newlines win over those with fewer. Methodsmconcat :: [BreakType] -> BreakType #

data Section Source #

Constructors

 Section FieldssectionStartIndent :: !IndentThis is the indent in effect when the section starts, and will be the physical indent of the section if it gets wrapped.sectionEndIndent :: !IndentThe indent of this section for breaking, see [NOTE end-indent].sectionB :: !BText of the section.sectionSubs :: ![Section]If present, the B is a short version. If it doesn't fit on a line of its own, then flatten the subs. Normally I put down Sections until I have to break. A short layout stands in for a list of Sections. When I see one, I try to place it, and if it doesn't fit, use the sub-Sections.sectionBreak :: !BreakType

Instances

 # MethodsshowList :: [Section] -> ShowS #

data B Source #

A Builder.Builder that keeps track of its length.

Constructors

 B FieldsbBuilder :: !Builder.Builder bWidth :: !Width

Instances

 # MethodsshowsPrec :: Int -> B -> ShowS #showList :: [B] -> ShowS # # Methodsmempty :: B #mappend :: B -> B -> B #mconcat :: [B] -> B #

Arguments

 :: Width -> Width -> [Section] -> (Text, Bool, [Section], [Section]) (log, break, pre_break, post_break). If break is False, then pre_break can be emitted as-is. If break is True, then the line went over the maxWidth and must be broken. pre_break will then have one Section past the break point. This is so findBreak can know what the next indent break is, so it can know if it's ok to break there.

Collect a line's worth of Sections. TODO findBreak should probably be integrated into this.

findBreak :: [Section] -> ([Section], [Section]) Source #

Given a list of Sections that I know need to be broken, find the best place to break. Split before the last lowest indent.

renderText :: Text -> Width -> [Section] -> Lazy.Text Source #

Take sections until they go over the width, or I see a hard newline, or run out. If they went over, then find a break in the collected, emit before the break, and try again.