ghc-exactprint-0.6.2: ExactPrint for GHC

Safe Haskell | None
Language | Haskell2010

Language.Haskell.GHC.ExactPrint.Delta

Contents

- Configuration

Description

This module converts `ApiAnns` into `Anns` by traversing a structure created by the Annotate module.

## Structure of an Annotation

As a rule of thumb, every located element in the GHC AST will have a corresponding entry in `Anns`. An `Annotation` contains 6 fields which can be modifed to change how the AST is printed.

## Layout Calculation

In order to properly place syntax nodes and comments properly after refactoring them (in such a way that the indentation level changes), their position (encoded in the `addEntryDelta` field) is not expressed as absolute but relative to their context. As further motivation, consider the simple let-into-where-block refactoring, from:

    foo = do
      let bar = do
            x
            -- comment
            y
      bar

to

    foo = do
      bar
     where
      bar = do
        x
        -- comment
        y

Notice how the column of `x`, `y` and the comment change due to this refactoring but certain relative positions (e.g. the comment starting at the same column as `x`) remain unchanged.

Now, what does "context" mean exactly? Here we reference the "indentation level" as used in the haskell report (see chapter 2.7: https://www.haskell.org/onlinereport/haskell2010/haskellch2.html#x7-210002.7): `addEntryDelta` is mostly relative to the current (inner-most) indentation level. But in order to get better results, for the purpose of defining relative positions a the offside-rule is modified slightly: Normally it fires (only) at the first elements after whereletdo/of, introducing a new indentation level. In addition, the rule here fires also at the "`let`" keyword (when it is part of a "`let-in`" construct) and at the "`if`" keyword.

The effect of this additional applications of the offside-rule is that any elements (more or less directly) following the "`let`" ("`if`"") keyword have a position relative to the "`let`" ("`if`") keyword position, even when the regular offside-rule does apply not yet/not anymore. This affects two concrete things: Comments directly following "`let`"/"`if`", and the respective follow-up keywords: "`in`" or "`then`"/"`else`".

Due to this additional indentation level, it is possible to observe/obtain negative delta-positions; consider:

    foo = let x = 1
      in x

Here, the `in` keyword has an `annEntryDelta` of `DP (1, -4)` as it appears one line below the previous elements and 4 columns left relative to the start of the `let` keyword.

In general, the element that defines such an indentation level (i.e. the first element after a whereletdo/of) will have an `annEntryDelta` relative to the previous inner-most indentation level; in other words: a new indentation level becomes relevant only after the construct introducing the element received its `annEntryDelta` position. (Otherwise these elements always would have a zero horizontal position - relative to itself.)

(This affects comments, too: A comment preceding the first element of a layout block will have a position relative to the outer block, not of the newly introduced layout block.)

For example, in the following expression the statement corresponding to `baz` will be given a `annEntryDelta` of `DP (1, 2)` as it appears 1 line and 2 columns after the `do` keyword. On the other hand, `bar` will be given a `annEntryDelta` of `DP (1,0)` as it appears 1 line further than `baz` but in the same column as the start of the layout block.

    foo = do
      baz
      bar

A useful way to think of these rules is that the `DeltaPos` is relative to the further left an expression could have been placed. In the previous example, we could have placed `baz` anywhere on the line as its position determines where the other statements must be. `bar` could have not been placed any further left without resulting in a syntax error which is why the relative column is 0.

### annTrueEntryDelta

A very useful function is `annTrueEntryDelta` which calculates the offset from the last syntactic element (ignoring comments). This is different to `annEntryDelta` which does not ignore comments.

Synopsis

- relativiseApiAnns :: (Data (SrcSpanLess ast), Annotate ast, HasSrcSpan ast) => ast -> ApiAnns -> Anns
- relativiseApiAnnsWithComments :: (Data (SrcSpanLess ast), Annotate ast, HasSrcSpan ast) => [Comment] -> ast -> ApiAnns -> Anns
- relativiseApiAnnsWithOptions :: (Data (SrcSpanLess ast), Annotate ast, HasSrcSpan ast) => DeltaOptions -> [Comment] -> ast -> ApiAnns -> Anns
- data DeltaOptions
- deltaOptions :: Rigidity -> DeltaOptions
- normalLayout :: DeltaOptions

# Documentation

relativiseApiAnns :: (Data (SrcSpanLess ast), Annotate ast, HasSrcSpan ast) => ast -> ApiAnns -> Anns Source #

Transform concrete annotations into relative annotations which are more useful when transforming an AST.

relativiseApiAnnsWithComments :: (Data (SrcSpanLess ast), Annotate ast, HasSrcSpan ast) => [Comment] -> ast -> ApiAnns -> Anns Source #

Exactly the same as `relativiseApiAnns` but with the possibilty to inject comments. This is typically used if the source has been preprocessed by e.g. CPP, and the parts stripped out of the original source are re-added as comments so they are not lost for round tripping.

relativiseApiAnnsWithOptions :: (Data (SrcSpanLess ast), Annotate ast, HasSrcSpan ast) => DeltaOptions -> [Comment] -> ast -> ApiAnns -> Anns Source #

# Configuration

data DeltaOptions Source #

deltaOptions :: Rigidity -> DeltaOptions Source #

normalLayout :: DeltaOptions Source #