{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TupleSections #-}
module Language.Haskell.Stylish.Module
(
Module
, Comments (..)
, Lines
, moduleImportGroups
, queryModule
, groupByLine
, canMergeImport
, mergeModuleImport
, importModuleName
, moduleLanguagePragmas
) where
import Data.Char (toLower)
import Data.Function (on)
import Data.Generics (Typeable, everything, mkQ)
import qualified Data.List as L
import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe (fromMaybe, mapMaybe)
import GHC.Hs (ImportDecl (..),
ImportDeclQualifiedStyle (..))
import qualified GHC.Hs as GHC
import GHC.Hs.Extension (GhcPs)
import qualified GHC.Types.PkgQual as GHC
import GHC.Types.SrcLoc (GenLocated (..),
RealSrcSpan (..), unLoc)
import qualified GHC.Types.SrcLoc as GHC
import Language.Haskell.Stylish.GHC
type Lines = [String]
deriving instance Eq GHC.RawPkgQual
type Module = GHC.Located (GHC.HsModule GHC.GhcPs)
importModuleName :: ImportDecl GhcPs -> String
importModuleName :: ImportDecl GhcPs -> [Char]
importModuleName = ModuleName -> [Char]
GHC.moduleNameString forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall l e. GenLocated l e -> e
GHC.unLoc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall pass. ImportDecl pass -> XRec pass ModuleName
GHC.ideclName
canMergeImport :: ImportDecl GhcPs -> ImportDecl GhcPs -> Bool
canMergeImport :: ImportDecl GhcPs -> ImportDecl GhcPs -> Bool
canMergeImport ImportDecl GhcPs
i0 ImportDecl GhcPs
i1 = forall (t :: * -> *). Foldable t => t Bool -> Bool
and forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\ImportDecl GhcPs -> ImportDecl GhcPs -> Bool
f -> ImportDecl GhcPs -> ImportDecl GhcPs -> Bool
f ImportDecl GhcPs
i0 ImportDecl GhcPs
i1)
[ forall a. Eq a => a -> a -> Bool
(==) forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` forall l e. GenLocated l e -> e
unLoc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall pass. ImportDecl pass -> XRec pass ModuleName
ideclName
, forall a. Eq a => a -> a -> Bool
(==) forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` forall pass. ImportDecl pass -> ImportDeclPkgQual pass
ideclPkgQual
, forall a. Eq a => a -> a -> Bool
(==) forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` forall pass. ImportDecl pass -> IsBootInterface
ideclSource
, ImportDeclQualifiedStyle -> ImportDeclQualifiedStyle -> Bool
hasMergableQualified forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` forall pass. ImportDecl pass -> ImportDeclQualifiedStyle
ideclQualified
, forall a. Eq a => a -> a -> Bool
(==) forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall l e. GenLocated l e -> e
unLoc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall pass. ImportDecl pass -> Maybe (XRec pass ModuleName)
ideclAs
, forall a. Eq a => a -> a -> Bool
(==) forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall pass.
ImportDecl pass
-> Maybe (ImportListInterpretation, XRec pass [LIE pass])
ideclImportList
]
where
hasMergableQualified :: ImportDeclQualifiedStyle -> ImportDeclQualifiedStyle -> Bool
hasMergableQualified ImportDeclQualifiedStyle
QualifiedPre ImportDeclQualifiedStyle
QualifiedPost = Bool
True
hasMergableQualified ImportDeclQualifiedStyle
QualifiedPost ImportDeclQualifiedStyle
QualifiedPre = Bool
True
hasMergableQualified ImportDeclQualifiedStyle
q0 ImportDeclQualifiedStyle
q1 = ImportDeclQualifiedStyle
q0 forall a. Eq a => a -> a -> Bool
== ImportDeclQualifiedStyle
q1
newtype = [GHC.RealLocated GHC.EpaComment]
moduleImportGroups :: Module -> [NonEmpty (GHC.LImportDecl GHC.GhcPs)]
moduleImportGroups :: Module -> [NonEmpty (LImportDecl GhcPs)]
moduleImportGroups =
forall a. (a -> RealSrcSpan) -> [a] -> [NonEmpty a]
groupByLine (forall a. a -> Maybe a -> a
fromMaybe forall {a}. a
err forall b c a. (b -> c) -> (a -> b) -> a -> c
. SrcSpan -> Maybe RealSrcSpan
GHC.srcSpanToRealSrcSpan forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a e. GenLocated (SrcSpanAnn' a) e -> SrcSpan
GHC.getLocA) forall b c a. (b -> c) -> (a -> b) -> a -> c
.
forall p. HsModule p -> [LImportDecl p]
GHC.hsmodImports forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall l e. GenLocated l e -> e
GHC.unLoc
where
err :: a
err = forall a. HasCallStack => [Char] -> a
error [Char]
"moduleImportGroups: import without soure span"
groupByLine :: (a -> RealSrcSpan) -> [a] -> [NonEmpty a]
groupByLine :: forall a. (a -> RealSrcSpan) -> [a] -> [NonEmpty a]
groupByLine a -> RealSrcSpan
f = [a] -> Maybe Int -> [a] -> [NonEmpty a]
go [] forall a. Maybe a
Nothing
where
go :: [a] -> Maybe Int -> [a] -> [NonEmpty a]
go [a]
acc Maybe Int
_ [] = forall {a}. [a] -> [NonEmpty a]
ne [a]
acc
go [a]
acc Maybe Int
mbCurrentLine (a
x:[a]
xs) =
let
lStart :: Int
lStart = RealSrcSpan -> Int
GHC.srcSpanStartLine (a -> RealSrcSpan
f a
x)
lEnd :: Int
lEnd = RealSrcSpan -> Int
GHC.srcSpanEndLine (a -> RealSrcSpan
f a
x) in
case Maybe Int
mbCurrentLine of
Just Int
lPrevEnd | Int
lPrevEnd forall a. Num a => a -> a -> a
+ Int
1 forall a. Ord a => a -> a -> Bool
< Int
lStart
-> forall {a}. [a] -> [NonEmpty a]
ne [a]
acc forall a. [a] -> [a] -> [a]
++ [a] -> Maybe Int -> [a] -> [NonEmpty a]
go [a
x] (forall a. a -> Maybe a
Just Int
lEnd) [a]
xs
Maybe Int
_ -> [a] -> Maybe Int -> [a] -> [NonEmpty a]
go ([a]
acc forall a. [a] -> [a] -> [a]
++ [a
x]) (forall a. a -> Maybe a
Just Int
lEnd) [a]
xs
ne :: [a] -> [NonEmpty a]
ne [] = []
ne (a
x : [a]
xs) = [a
x forall a. a -> [a] -> NonEmpty a
:| [a]
xs]
mergeModuleImport
:: GHC.LImportDecl GHC.GhcPs -> GHC.LImportDecl GHC.GhcPs
-> GHC.LImportDecl GHC.GhcPs
mergeModuleImport :: LImportDecl GhcPs -> LImportDecl GhcPs -> LImportDecl GhcPs
mergeModuleImport (L SrcSpanAnn' (EpAnn AnnListItem)
p0 ImportDecl GhcPs
i0) (L SrcSpanAnn' (EpAnn AnnListItem)
_p1 ImportDecl GhcPs
i1) =
forall l e. l -> e -> GenLocated l e
L SrcSpanAnn' (EpAnn AnnListItem)
p0 forall a b. (a -> b) -> a -> b
$ ImportDecl GhcPs
i0 { ideclImportList :: Maybe (ImportListInterpretation, XRec GhcPs [LIE GhcPs])
ideclImportList = Maybe
(ImportListInterpretation, GenLocated SrcSpanAnnL [LIE GhcPs])
newImportNames }
where
newImportNames :: Maybe
(ImportListInterpretation, GenLocated SrcSpanAnnL [LIE GhcPs])
newImportNames =
case (forall pass.
ImportDecl pass
-> Maybe (ImportListInterpretation, XRec pass [LIE pass])
ideclImportList ImportDecl GhcPs
i0, forall pass.
ImportDecl pass
-> Maybe (ImportListInterpretation, XRec pass [LIE pass])
ideclImportList ImportDecl GhcPs
i1) of
(Just (ImportListInterpretation
b, L SrcSpanAnnL
p [LIE GhcPs]
imps0), Just (ImportListInterpretation
_, L SrcSpanAnnL
_ [LIE GhcPs]
imps1)) -> forall a. a -> Maybe a
Just (ImportListInterpretation
b, forall l e. l -> e -> GenLocated l e
L SrcSpanAnnL
p ([LIE GhcPs]
imps0 forall {a}. Outputable a => [a] -> [a] -> [a]
`merge` [LIE GhcPs]
imps1))
(Maybe
(ImportListInterpretation, GenLocated SrcSpanAnnL [LIE GhcPs])
Nothing, Maybe
(ImportListInterpretation, GenLocated SrcSpanAnnL [LIE GhcPs])
Nothing) -> forall a. Maybe a
Nothing
(Just (ImportListInterpretation, GenLocated SrcSpanAnnL [LIE GhcPs])
x, Maybe
(ImportListInterpretation, GenLocated SrcSpanAnnL [LIE GhcPs])
Nothing) -> forall a. a -> Maybe a
Just (ImportListInterpretation, GenLocated SrcSpanAnnL [LIE GhcPs])
x
(Maybe
(ImportListInterpretation, GenLocated SrcSpanAnnL [LIE GhcPs])
Nothing, Just (ImportListInterpretation, GenLocated SrcSpanAnnL [LIE GhcPs])
x) -> forall a. a -> Maybe a
Just (ImportListInterpretation, GenLocated SrcSpanAnnL [LIE GhcPs])
x
merge :: [a] -> [a] -> [a]
merge [a]
xs [a]
ys
= forall a. (a -> a -> Bool) -> [a] -> [a]
L.nubBy (forall a. Eq a => a -> a -> Bool
(==) forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` forall a. Outputable a => a -> [Char]
showOutputable) ([a]
xs forall a. [a] -> [a] -> [a]
++ [a]
ys)
queryModule :: Typeable a => (a -> [b]) -> Module -> [b]
queryModule :: forall a b. Typeable a => (a -> [b]) -> Module -> [b]
queryModule a -> [b]
f = forall r. (r -> r -> r) -> GenericQ r -> GenericQ r
everything forall a. [a] -> [a] -> [a]
(++) (forall a b r. (Typeable a, Typeable b) => r -> (b -> r) -> a -> r
mkQ [] a -> [b]
f)
moduleLanguagePragmas :: Module -> [(RealSrcSpan, NonEmpty String)]
moduleLanguagePragmas :: Module -> [(RealSrcSpan, NonEmpty [Char])]
moduleLanguagePragmas =
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe LEpaComment -> Maybe (RealSrcSpan, NonEmpty [Char])
prag forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. EpAnn a -> [LEpaComment]
epAnnComments forall b c a. (b -> c) -> (a -> b) -> a -> c
. XModulePs -> EpAnn AnnsModule
GHC.hsmodAnn forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall p. HsModule p -> XCModule p
GHC.hsmodExt forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall l e. GenLocated l e -> e
GHC.unLoc
where
prag :: GHC.LEpaComment -> Maybe (GHC.RealSrcSpan, NonEmpty String)
prag :: LEpaComment -> Maybe (RealSrcSpan, NonEmpty [Char])
prag LEpaComment
comment = case EpaComment -> EpaCommentTok
GHC.ac_tok (forall l e. GenLocated l e -> e
GHC.unLoc LEpaComment
comment) of
GHC.EpaBlockComment [Char]
str
| [Char]
lang : [Char]
p1 : [[Char]]
ps <- [Char] -> [[Char]]
tokenize [Char]
str, forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower [Char]
lang forall a. Eq a => a -> a -> Bool
== [Char]
"language" ->
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Anchor -> RealSrcSpan
GHC.anchor (forall l e. GenLocated l e -> l
GHC.getLoc LEpaComment
comment), [Char]
p1 forall a. a -> [a] -> NonEmpty a
:| [[Char]]
ps)
EpaCommentTok
_ -> forall a. Maybe a
Nothing
tokenize :: [Char] -> [[Char]]
tokenize = [Char] -> [[Char]]
words forall b c a. (b -> c) -> (a -> b) -> a -> c
.
forall a b. (a -> b) -> [a] -> [b]
map (\Char
c -> if Char
c forall a. Eq a => a -> a -> Bool
== Char
',' then Char
' ' else Char
c) forall b c a. (b -> c) -> (a -> b) -> a -> c
.
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (forall a. Eq a => a -> a -> Bool
/= Char
'#') forall b c a. (b -> c) -> (a -> b) -> a -> c
.
forall a. Int -> [a] -> [a]
drop Int
1 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
dropWhile (forall a. Eq a => a -> a -> Bool
/= Char
'#')