summaryrefslogtreecommitdiffhomepage
path: root/lib/Language/Haskell/Stylish/Step/LanguagePragmas.hs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Language/Haskell/Stylish/Step/LanguagePragmas.hs')
-rw-r--r--lib/Language/Haskell/Stylish/Step/LanguagePragmas.hs168
1 files changed, 168 insertions, 0 deletions
diff --git a/lib/Language/Haskell/Stylish/Step/LanguagePragmas.hs b/lib/Language/Haskell/Stylish/Step/LanguagePragmas.hs
new file mode 100644
index 0000000..0239736
--- /dev/null
+++ b/lib/Language/Haskell/Stylish/Step/LanguagePragmas.hs
@@ -0,0 +1,168 @@
+--------------------------------------------------------------------------------
+module Language.Haskell.Stylish.Step.LanguagePragmas
+ ( Style (..)
+ , step
+
+ -- * Utilities
+ , addLanguagePragma
+ ) where
+
+
+--------------------------------------------------------------------------------
+import qualified Data.Set as S
+import qualified Language.Haskell.Exts.Annotated as H
+
+
+--------------------------------------------------------------------------------
+import Language.Haskell.Stylish.Block
+import Language.Haskell.Stylish.Editor
+import Language.Haskell.Stylish.Step
+import Language.Haskell.Stylish.Util
+
+
+--------------------------------------------------------------------------------
+data Style
+ = Vertical
+ | Compact
+ | CompactLine
+ deriving (Eq, Show)
+
+
+--------------------------------------------------------------------------------
+pragmas :: H.Module l -> [(l, [String])]
+pragmas (H.Module _ _ ps _ _) =
+ [(l, map nameToString names) | H.LanguagePragma l names <- ps]
+pragmas _ = []
+
+
+--------------------------------------------------------------------------------
+-- | The start of the first block
+firstLocation :: [(Block a, [String])] -> Int
+firstLocation = minimum . map (blockStart . fst)
+
+
+--------------------------------------------------------------------------------
+verticalPragmas :: Int -> Bool -> [String] -> Lines
+verticalPragmas longest align pragmas' =
+ [ "{-# LANGUAGE " ++ pad pragma ++ " #-}"
+ | pragma <- pragmas'
+ ]
+ where
+ pad
+ | align = padRight longest
+ | otherwise = id
+
+
+--------------------------------------------------------------------------------
+compactPragmas :: Int -> [String] -> Lines
+compactPragmas columns pragmas' = wrap columns "{-# LANGUAGE" 13 $
+ map (++ ",") (init pragmas') ++ [last pragmas', "#-}"]
+
+
+--------------------------------------------------------------------------------
+compactLinePragmas :: Int -> Bool -> [String] -> Lines
+compactLinePragmas _ _ [] = []
+compactLinePragmas columns align pragmas' = map (wrapLanguage . pad) prags
+ where
+ wrapLanguage ps = "{-# LANGUAGE" ++ ps ++ " #-}"
+
+ maxWidth = columns - 16
+
+ longest = maximum $ map length prags
+
+ pad
+ | align = padRight longest
+ | otherwise = id
+
+ prags = map truncateComma $ wrap maxWidth "" 1 $
+ map (++ ",") (init pragmas') ++ [last pragmas']
+
+
+--------------------------------------------------------------------------------
+truncateComma :: String -> String
+truncateComma "" = ""
+truncateComma xs
+ | last xs == ',' = init xs
+ | otherwise = xs
+
+
+--------------------------------------------------------------------------------
+prettyPragmas :: Int -> Int -> Bool -> Style -> [String] -> Lines
+prettyPragmas _ longest align Vertical = verticalPragmas longest align
+prettyPragmas cols _ _ Compact = compactPragmas cols
+prettyPragmas cols _ align CompactLine = compactLinePragmas cols align
+
+
+--------------------------------------------------------------------------------
+-- | Filter redundant (and duplicate) pragmas out of the groups. As a side
+-- effect, we also sort the pragmas in their group...
+filterRedundant :: (String -> Bool)
+ -> [(l, [String])]
+ -> [(l, [String])]
+filterRedundant isRedundant' = snd . foldr filterRedundant' (S.empty, [])
+ where
+ filterRedundant' (l, xs) (known, zs)
+ | S.null xs' = (known', zs)
+ | otherwise = (known', (l, S.toAscList xs') : zs)
+ where
+ fxs = filter (not . isRedundant') xs
+ xs' = S.fromList fxs `S.difference` known
+ known' = xs' `S.union` known
+
+--------------------------------------------------------------------------------
+step :: Int -> Style -> Bool -> Bool -> Step
+step = (((makeStep "LanguagePragmas" .) .) .) . step'
+
+
+--------------------------------------------------------------------------------
+step' :: Int -> Style -> Bool -> Bool -> Lines -> Module -> Lines
+step' columns style align removeRedundant ls (module', _)
+ | null pragmas' = ls
+ | otherwise = applyChanges changes ls
+ where
+ isRedundant'
+ | removeRedundant = isRedundant module'
+ | otherwise = const False
+
+ pragmas' = pragmas $ fmap linesFromSrcSpan module'
+ longest = maximum $ map length $ snd =<< pragmas'
+ groups = [(b, concat pgs) | (b, pgs) <- groupAdjacent pragmas']
+ changes =
+ [ change b (const $ prettyPragmas columns longest align style pg)
+ | (b, pg) <- filterRedundant isRedundant' groups
+ ]
+
+
+--------------------------------------------------------------------------------
+-- | Add a LANGUAGE pragma to a module if it is not present already.
+addLanguagePragma :: String -> H.Module H.SrcSpanInfo -> [Change String]
+addLanguagePragma prag modu
+ | prag `elem` present = []
+ | otherwise = [insert line ["{-# LANGUAGE " ++ prag ++ " #-}"]]
+ where
+ pragmas' = pragmas (fmap linesFromSrcSpan modu)
+ present = concatMap snd pragmas'
+ line = if null pragmas' then 1 else firstLocation pragmas'
+
+
+--------------------------------------------------------------------------------
+-- | Check if a language pragma is redundant. We can't do this for all pragmas,
+-- but we do a best effort.
+isRedundant :: H.Module H.SrcSpanInfo -> String -> Bool
+isRedundant m "ViewPatterns" = isRedundantViewPatterns m
+isRedundant m "BangPatterns" = isRedundantBangPatterns m
+isRedundant _ _ = False
+
+
+--------------------------------------------------------------------------------
+-- | Check if the ViewPatterns language pragma is redundant.
+isRedundantViewPatterns :: H.Module H.SrcSpanInfo -> Bool
+isRedundantViewPatterns m = null
+ [() | H.PViewPat _ _ _ <- everything m :: [H.Pat H.SrcSpanInfo]]
+
+
+--------------------------------------------------------------------------------
+-- | Check if the BangPatterns language pragma is redundant.
+isRedundantBangPatterns :: H.Module H.SrcSpanInfo -> Bool
+isRedundantBangPatterns m = null
+ [() | H.PBangPat _ _ <- everything m :: [H.Pat H.SrcSpanInfo]]