Safe Haskell | None |
---|---|
Language | Haskell2010 |
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 #