summaryrefslogtreecommitdiffhomepage
path: root/lib/Language/Haskell/Stylish/Step/SimpleAlign.hs
blob: 5e61123276f978db1c8b730ac79d9125735546c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
--------------------------------------------------------------------------------
module Language.Haskell.Stylish.Step.SimpleAlign
    ( Config (..)
    , defaultConfig
    , step
    ) where


--------------------------------------------------------------------------------
import           Data.Data                       (Data)
import           Data.List                       (foldl')
import           Data.Maybe                      (maybeToList)
import qualified Language.Haskell.Exts           as H


--------------------------------------------------------------------------------
import           Language.Haskell.Stylish.Align
import           Language.Haskell.Stylish.Editor
import           Language.Haskell.Stylish.Step
import           Language.Haskell.Stylish.Util


--------------------------------------------------------------------------------
data Config = Config
    { cCases            :: !Bool
    , cTopLevelPatterns :: !Bool
    , cRecords          :: !Bool
    } deriving (Show)


--------------------------------------------------------------------------------
defaultConfig :: Config
defaultConfig = Config
    { cCases            = True
    , cTopLevelPatterns = True
    , cRecords          = True
    }


--------------------------------------------------------------------------------
cases :: Data l => H.Module l -> [[H.Alt l]]
cases modu = [alts | H.Case _ _ alts <- everything modu]


--------------------------------------------------------------------------------
-- | For this to work well, we require a way to merge annotations.  This merge
-- operation should follow the semigroup laws.
altToAlignable :: (l -> l -> l) -> H.Alt l -> Maybe (Alignable l)
altToAlignable _ (H.Alt _   _   _   (Just _)) = Nothing
altToAlignable _ (H.Alt ann pat rhs@(H.UnGuardedRhs _ _) Nothing) = Just $
    Alignable
        { aContainer = ann
        , aLeft      = H.ann pat
        , aRight     = H.ann rhs
        , aRightLead = length "-> "
        }
altToAlignable
        merge
        (H.Alt ann pat (H.GuardedRhss _ [H.GuardedRhs _ guards rhs]) Nothing) =
    -- We currently only support the case where an alternative has a single
    -- guarded RHS.  If there are more, we would need to return multiple
    -- `Alignable`s from this function, which would be a significant change.
    Just $ Alignable
        { aContainer = ann
        , aLeft      = foldl' merge (H.ann pat) (map H.ann guards)
        , aRight     = H.ann rhs
        , aRightLead = length "-> "
        }
altToAlignable _ _ = Nothing


--------------------------------------------------------------------------------
tlpats :: Data l => H.Module l -> [[H.Match l]]
tlpats modu = [matches | H.FunBind _ matches <- everything modu]


--------------------------------------------------------------------------------
matchToAlignable :: H.Match l -> Maybe (Alignable l)
matchToAlignable (H.InfixMatch _ _ _ _ _ _)           = Nothing
matchToAlignable (H.Match _   _    []   _   _)        = Nothing
matchToAlignable (H.Match _   _    _    _   (Just _)) = Nothing
matchToAlignable (H.Match ann name pats rhs Nothing)  = Just $ Alignable
    { aContainer = ann
    , aLeft      = last (H.ann name : map H.ann pats)
    , aRight     = H.ann rhs
    , aRightLead = length "= "
    }


--------------------------------------------------------------------------------
records :: H.Module l -> [[H.FieldDecl l]]
records modu =
    [ fields
    | H.Module _ _ _ _ decls                     <- [modu]
    , H.DataDecl _ _ _ _ cons _                  <- decls
    , H.QualConDecl _ _ _ (H.RecDecl _ _ fields) <- cons
    ]


--------------------------------------------------------------------------------
fieldDeclToAlignable :: H.FieldDecl a -> Maybe (Alignable a)
fieldDeclToAlignable (H.FieldDecl ann names ty) = Just $ Alignable
    { aContainer = ann
    , aLeft      = H.ann (last names)
    , aRight     = H.ann ty
    , aRightLead = length ":: "
    }


--------------------------------------------------------------------------------
step :: Maybe Int -> Config -> Step
step maxColumns config = makeStep "Cases" $ \ls (module', _) ->
    let module''               = fmap H.srcInfoSpan module'
        changes search toAlign =
            [ change_
            | case_   <- search module''
            , aligns  <- maybeToList (mapM toAlign case_)
            , change_ <- align maxColumns aligns
            ]

        configured = concat $
            [ changes cases (altToAlignable H.mergeSrcSpan)
            | cCases config
            ] ++
            [changes tlpats  matchToAlignable | cTopLevelPatterns config] ++
            [changes records fieldDeclToAlignable | cRecords config]

    in applyChanges configured ls