Ormolu.Printer.Combinators

fourmolu-0.17.0.0: A formatter for Haskell source code

Safe Haskell | None
Language | GHC2021

Ormolu.Printer.Combinators

Contents

- The `R` monad
- Combinators
  - Basic
  - Formatting lists
  - Wrapping
  - Literals
  - Stateful markers
  - Placement

Description

Printing combinators. The definitions here are presented in such an order so you can just go through the Haddocks and by the end of the file you should have a pretty good idea how to program rendering logic.

Synopsis

- data R a
- runR :: R () -> SpanStream -> CommentStream -> PrinterOptsTotal -> Set ModuleName -> SourceType -> EnumSet Extension -> ModuleFixityMap -> Bool -> Text
- getEnclosingSpan :: R (Maybe RealSrcSpan)
- getEnclosingSpanWhere :: (RealSrcSpan -> Bool) -> R (Maybe RealSrcSpan)
- getEnclosingComments :: R [LComment]
- isExtensionEnabled :: Extension -> R Bool
- txt :: Text -> R ()
- txtStripIndent :: Text -> R ()
- atom :: Outputable a => a -> R ()
- space :: R ()
- newline :: R ()
- declNewline :: R ()
- multilineCommentNewline :: R ()
- inci :: R () -> R ()
- inciBy :: Int -> R () -> R ()
- inciIf :: Bool -> R () -> R ()
- inciByFrac :: Rational -> R () -> R ()
- askSourceType :: R SourceType
- askModuleFixityMap :: R ModuleFixityMap
- askDebug :: R (Choice "debug")
- located :: HasLoc l => GenLocated l a -> (a -> R ()) -> R ()
- encloseLocated :: HasLoc l => GenLocated l [a] -> ([a] -> R ()) -> R ()
- located' :: HasLoc l => (a -> R ()) -> GenLocated l a -> R ()
- switchLayout :: [SrcSpan] -> R () -> R ()
- switchLayoutNoLimit :: [SrcSpan] -> R () -> R ()
- spansLayout :: [SrcSpan] -> R Layout
- data Layout
  - = SingleLine
  - | MultiLine
- vlayout :: R a -> R a -> R a
- getLayout :: R Layout
- breakpoint :: R ()
- breakpoint' :: R ()
- getPrinterOpt :: (forall (f :: Type -> Type). PrinterOpts f -> f a) -> R a
- getLocalModules :: R (Set ModuleName)
- sep :: R () -> (a -> R ()) -> [a] -> R ()
- sepSemi :: (a -> R ()) -> [a] -> R ()
- canUseBraces :: R Bool
- useBraces :: R () -> R ()
- dontUseBraces :: R () -> R ()
- data BracketStyle
  - = N
  - | S
- sitcc :: R () -> R ()
- backticks :: R () -> R ()
- banana :: BracketStyle -> R () -> R ()
- braces :: BracketStyle -> R () -> R ()
- brackets :: BracketStyle -> R () -> R ()
- parens :: BracketStyle -> R () -> R ()
- parensHash :: BracketStyle -> R () -> R ()
- pragmaBraces :: R () -> R ()
- pragma :: Text -> R () -> R ()
- comma :: R ()
- commaDel :: R ()
- commaDelImportExport :: R ()
- equals :: R ()
- token'Larrowtail :: R ()
- token'Rarrowtail :: R ()
- token'darrow :: R ()
- token'dcolon :: R ()
- token'larrow :: R ()
- token'larrowtail :: R ()
- token'rarrow :: R ()
- token'rarrowtail :: R ()
- token'star :: R ()
- token'forall :: R ()
- token'oparenbar :: R ()
- token'cparenbar :: R ()
- token'openExpQuote :: R ()
- token'closeQuote :: R ()
- token'lolly :: R ()
- data SpanMark
  - = HaddockSpan HaddockStyle RealSrcSpan
  - | CommentSpan RealSrcSpan
  - | StatementSpan RealSrcSpan
- spanMarkSpan :: SpanMark -> RealSrcSpan
- data HaddockStyle
  - = Pipe
  - | Caret
  - | Asterisk Int
  - | Named String
- setSpanMark :: SpanMark -> R ()
- getSpanMark :: R (Maybe SpanMark)
- data Placement
  - = Normal
  - | Hanging
- placeHanging :: Placement -> R () -> R ()

# The `R` monad

data R a #

The `R` monad hosts combinators that allow us to describe how to render AST.

#### Instances

Instances details

Applicative R # |
Instance details Defined in Ormolu.Printer.Internal Methods pure :: a -> R a # (<*>) :: R (a -> b) -> R a -> R b # liftA2 :: (a -> b -> c) -> R a -> R b -> R c # (*>) :: R a -> R b -> R b # (<*) :: R a -> R b -> R a #
Functor R # |
Instance details Defined in Ormolu.Printer.Internal Methods fmap :: (a -> b) -> R a -> R b # (<$) :: a -> R b -> R a #
Monad R # |
Instance details Defined in Ormolu.Printer.Internal Methods (>>=) :: R a -> (a -> R b) -> R b # (>>) :: R a -> R b -> R b # return :: a -> R a #

runR #

Arguments

:: R () | Monad to run
-> SpanStream | Span stream
-> CommentStream | Comment stream
-> PrinterOptsTotal |
-> Set ModuleName |
-> SourceType | Whether the source is a signature or a regular module
-> EnumSet Extension | Enabled extensions
-> ModuleFixityMap | Module fixity map
-> Bool | Resulting rendition
-> Text |

Run `R` monad.

getEnclosingSpan :: R (Maybe RealSrcSpan) #

Get the immediately enclosing `RealSrcSpan`.

getEnclosingSpanWhere #

Arguments

:: (RealSrcSpan -> Bool) | Predicate to use
-> R (Maybe RealSrcSpan) |

Get the first enclosing `RealSrcSpan` that satisfies given predicate.

getEnclosingComments :: R [LComment] #

Get the comments contained in the enclosing span.

isExtensionEnabled :: Extension -> R Bool #

# Combinators

## Basic

txt #

Arguments

:: Text | `Text` to output
-> R () |

Output a fixed `Text` fragment. The argument may not contain any line breaks. `txt` is used to output all sorts of “fixed” bits of syntax like keywords and pipes `|` in functional dependencies.

To separate various bits of syntax with white space use `space` instead of ``txt` " "`. To output `Outputable` Haskell entities like numbers use `atom`.

txtStripIndent :: Text -> R () #

Same as `txt`, except strip leading whitespace equal to the current indentation level. Useful for multiline comments, where the AST contains the full leading whitespace.

atom :: Outputable a => a -> R () #

Output `Outputable` fragment of AST. This can be used to output numeric literals and similar. Everything that doesn't have inner structure but does have an `Outputable` instance.

space :: R () #

This primitive does not necessarily output a space. It just ensures that the next thing that will be printed on the same line will be separated by a single space from the previous output. Using this combinator twice results in at most one space.

In practice this design prevents trailing white space and makes it hard to output more than one delimiting space in a row, which is what we usually want.

newline :: R () #

Output a newline. First time `newline` is used after some non-`newline` output it gets inserted immediately. Second use of `newline` does not output anything but makes sure that the next non-white space output will be prefixed by a newline. Using `newline` more than twice in a row has no effect. Also, using `newline` at the very beginning has no effect, this is to avoid leading whitespace.

Similarly to `space`, this design prevents trailing newlines and makes it hard to output more than one blank newline in a row.

declNewline :: R () #

multilineCommentNewline :: R () #

Add a newline in a multiline comment.

Can't use `newline` because it avoids adding multiple consecutive newlines. Single-line comments don't have this issue because each line has at least "--".

inci :: R () -> R () #

Increase indentation level by one indentation step for the inner computation. `inci` should be used when a part of code must be more indented relative to the parts outside of `inci` in order for the output to be valid Haskell. When layout is single-line there is no obvious effect, but with multi-line layout correct indentation levels matter.

inciBy :: Int -> R () -> R () #

Like `inci`, but indents by exactly the given number of steps.

inciIf #

Arguments

:: Bool | Whether to indent
-> R () | The expression to indent
-> R () |

Indent the inner expression if the first argument is `True`.

inciByFrac :: Rational -> R () -> R () #

Like `inci`, but indents by the given fraction of a full step.

askSourceType :: R SourceType #

Return the source type.

askModuleFixityMap :: R ModuleFixityMap #

Retrieve the module fixity map.

askDebug :: R (Choice "debug") #

Retrieve whether we should print out certain debug information while printing.

located #

Arguments

:: HasLoc l |
=> GenLocated l a | Thing to enter
-> (a -> R ()) | How to render inner value
-> R () |

Enter a `GenLocated` entity. This combinator handles outputting comments and sets layout (single-line vs multi-line) for the inner computation. Roughly, the rule for using `located` is that every time there is a `Located` wrapper, it should be “discharged” with a corresponding `located` invocation.

encloseLocated :: HasLoc l => GenLocated l [a] -> ([a] -> R ()) -> R () #

Similar to `located`, but when the "payload" is an empty list, print virtual elements at the start and end of the source span to prevent comments from "floating out".

located' #

Arguments

:: HasLoc l |
=> (a -> R ()) | How to render inner value
-> GenLocated l a | Thing to enter
-> R () |

A version of `located` with arguments flipped.

switchLayout #

Arguments

:: [SrcSpan] | Span that controls layout
-> R () | Computation to run with changed layout
-> R () |

Set layout according to combination of given `SrcSpan`s for a given. Use this only when you need to set layout based on e.g. combined span of several elements when there is no corresponding `Located` wrapper provided by GHC AST. It is relatively rare that this one is needed.

Given empty list this function will set layout to single line.

switchLayoutNoLimit :: [SrcSpan] -> R () -> R () #

Same as `switchLayout`, except disregards the column limit.

It should be used for the argument list in function definitions because the column limit can't be enforced there without changing the AST.

spansLayout :: [SrcSpan] -> R Layout #

Which layout combined spans result in?

data Layout #

`Layout` options.

Constructors

SingleLine | Put everything on single line
MultiLine | Use multiple lines

#### Instances

Instances details

Show Layout # |
Instance details Defined in Ormolu.Printer.Internal Methods showsPrec :: Int -> Layout -> ShowS # show :: Layout -> String # showList :: [Layout] -> ShowS #
Eq Layout # |
Instance details Defined in Ormolu.Printer.Internal Methods (==) :: Layout -> Layout -> Bool # (/=) :: Layout -> Layout -> Bool #

vlayout #

Arguments

:: R a | Single line
-> R a | Multi line
-> R a |

Do one or another thing depending on current `Layout`.

getLayout :: R Layout #

Get current `Layout`.

breakpoint :: R () #

Insert a space if enclosing layout is single-line, or newline if it's multiline.

    breakpoint = vlayout space newline

breakpoint' :: R () #

Similar to `breakpoint` but outputs nothing in case of single-line layout.

    breakpoint' = vlayout (return ()) newline

getPrinterOpt :: (forall (f :: Type -> Type). PrinterOpts f -> f a) -> R a #

Get a particular `PrinterOpts` field from the environment.

getLocalModules :: R (Set ModuleName) #

## Formatting lists

sep #

Arguments

:: R () | Separator
-> (a -> R ()) | How to render an element
-> [a] | Elements to render
-> R () |

Render a collection of elements inserting a separator between them.

sepSemi #

Arguments

:: (a -> R ()) | How to render an element
-> [a] | Elements to render
-> R () |

Render a collection of elements layout-sensitively using given printer, inserting semicolons if necessary and respecting `useBraces` and `dontUseBraces` combinators.

    useBraces $ sepSemi txt ["foo", "bar"]
      == vlayout (txt "{ foo; bar }") (txt "foo\nbar")

    dontUseBraces $ sepSemi txt ["foo", "bar"]
      == vlayout (txt "foo; bar") (txt "foo\nbar")

canUseBraces :: R Bool #

Return `True` if we can use braces in this context.

useBraces :: R () -> R () #

Make the inner computation use braces around single-line layouts.

dontUseBraces :: R () -> R () #

Make the inner computation omit braces around single-line layouts.

## Wrapping

data BracketStyle #

`BracketStyle` controlling how closing bracket is rendered.

Constructors

N | Normal
S | Shifted one level

#### Instances

Instances details

Show BracketStyle # |
Instance details Defined in Ormolu.Printer.Combinators Methods showsPrec :: Int -> BracketStyle -> ShowS # show :: BracketStyle -> String # showList :: [BracketStyle] -> ShowS #
Eq BracketStyle # |
Instance details Defined in Ormolu.Printer.Combinators Methods (==) :: BracketStyle -> BracketStyle -> Bool # (/=) :: BracketStyle -> BracketStyle -> Bool #

sitcc :: R () -> R () #

Set indentation level for the inner computation equal to current column. This makes sure that the entire inner block is uniformly "shifted" to the right.

backticks :: R () -> R () #

Surround given entity by backticks.

banana :: BracketStyle -> R () -> R () #

Surround given entity by banana brackets (i.e., from arrow notation.)

braces :: BracketStyle -> R () -> R () #

Surround given entity by curly braces `{` and `}`.

brackets :: BracketStyle -> R () -> R () #

Surround given entity by square brackets `[` and `]`.

parens :: BracketStyle -> R () -> R () #

Surround given entity by parentheses `(` and `)`.

parensHash :: BracketStyle -> R () -> R () #

Surround given entity by `(#` and `#)`.

pragmaBraces :: R () -> R () #

Braces as used for pragmas: `{-#` and `#-}`.

pragma #

Arguments

:: Text | Pragma text
-> R () | Pragma body
-> R () |

Surround the body with a pragma name and `pragmaBraces`.

## Literals

comma :: R () #

Print `,`.

commaDel :: R () #

Delimiting combination with `comma`. To be used with `sep`.

commaDelImportExport :: R () #

Delimiting combination with `comma` for import-export lists. To be used with `sep`.

equals :: R () #

Print `=`. Do not use ``txt` "="`.

token'Larrowtail :: R () #

Print `⤛` or `-<<` as appropriate.

token'Rarrowtail :: R () #

Print `⤜` or `>>-` as appropriate.

token'darrow :: R () #

Print `⇒` or `=>` as appropriate.

token'dcolon :: R () #

Print `∷` or `::` as appropriate.

token'larrow :: R () #

Print `←` or `<-` as appropriate.

token'larrowtail :: R () #

Print `⤙` or `-<` as appropriate.

token'rarrow :: R () #

Print `→` or `->` as appropriate.

token'rarrowtail :: R () #

Print `⤚` or `>-` as appropriate.

token'star :: R () #

Print `★` or `*` as appropriate.

token'forall :: R () #

Print `∀` or `forall` as appropriate.

token'oparenbar :: R () #

Print `⦇` or `(|` as appropriate.

token'cparenbar :: R () #

Print `⦈` or `|)` as appropriate.

token'openExpQuote :: R () #

Print `⟦` or `[|` as appropriate.

token'closeQuote :: R () #

Print `⟧` or `|]` as appropriate.

token'lolly :: R () #

Print `⊸` or `%1 ->` as appropriate.

## Stateful markers

data SpanMark #

An auxiliary marker for keeping track of last output element.

Constructors

HaddockSpan HaddockStyle RealSrcSpan | Haddock comment
CommentSpan RealSrcSpan | Non-haddock comment
StatementSpan RealSrcSpan | A statement in a do-block and such span

spanMarkSpan :: SpanMark -> RealSrcSpan #

Project `RealSrcSpan` from `SpanMark`.

data HaddockStyle #

Haddock string style.

Constructors

Pipe |
    -- |
Caret |
    -- ^
Asterisk Int |
    -- *
Named String |
    -- $

setSpanMark #

Arguments

:: SpanMark | Span mark to set
-> R () |

Set span of last output comment.

getSpanMark :: R (Maybe SpanMark) #

Get span of last output comment.

## Placement

data Placement #

Expression placement. This marks the places where expressions that implement handing forms may use them.

Constructors

Normal | Multi-line layout should cause insertion of a newline and indentation bump
Hanging | Expressions that have hanging form should use it and avoid bumping one level of indentation

#### Instances

Instances details

Show Placement # |
Instance details Defined in Ormolu.Printer.Combinators Methods showsPrec :: Int -> Placement -> ShowS # show :: Placement -> String # showList :: [Placement] -> ShowS #
Eq Placement # |
Instance details Defined in Ormolu.Printer.Combinators Methods (==) :: Placement -> Placement -> Bool # (/=) :: Placement -> Placement -> Bool #

placeHanging :: Placement -> R () -> R () #

Place a thing that may have a hanging form. This function handles how to separate it from preceding expressions and whether to bump indentation depending on what sort of expression we have.