stylish-haskell 0.1.1.0 → 0.2.0.0
raw patch · 31 files changed
+1206/−475 lines, 31 filesdep +aesondep +bytestringdep +cmdargs
Dependencies added: aeson, bytestring, cmdargs, containers, directory, filepath, syb, yaml
Files
- .stylish-haskell.yaml +55/−0
- src/Main.hs +43/−26
- src/StylishHaskell.hs +22/−0
- src/StylishHaskell/Block.hs +27/−9
- src/StylishHaskell/Config.hs +175/−0
- src/StylishHaskell/Editor.hs +11/−12
- src/StylishHaskell/Imports.hs +0/−110
- src/StylishHaskell/LanguagePragmas.hs +0/−53
- src/StylishHaskell/Parse.hs +1/−1
- src/StylishHaskell/Step.hs +32/−0
- src/StylishHaskell/Step/Imports.hs +142/−0
- src/StylishHaskell/Step/LanguagePragmas.hs +120/−0
- src/StylishHaskell/Step/Tabs.hs +21/−0
- src/StylishHaskell/Step/TrailingWhitespace.hs +22/−0
- src/StylishHaskell/Step/UnicodeSyntax.hs +135/−0
- src/StylishHaskell/Stylish.hs +0/−23
- src/StylishHaskell/Tabs.hs +0/−21
- src/StylishHaskell/TrailingWhitespace.hs +0/−22
- src/StylishHaskell/Util.hs +44/−1
- stylish-haskell.cabal +36/−12
- tests/StylishHaskell/Imports/Tests.hs +0/−51
- tests/StylishHaskell/LanguagePragmas/Tests.hs +0/−41
- tests/StylishHaskell/Step/Imports/Tests.hs +90/−0
- tests/StylishHaskell/Step/LanguagePragmas/Tests.hs +97/−0
- tests/StylishHaskell/Step/Tabs/Tests.hs +42/−0
- tests/StylishHaskell/Step/TrailingWhitespace/Tests.hs +37/−0
- tests/StylishHaskell/Step/UnicodeSyntax/Tests.hs +38/−0
- tests/StylishHaskell/Tabs/Tests.hs +0/−42
- tests/StylishHaskell/Tests/Util.hs +5/−5
- tests/StylishHaskell/TrailingWhitespace/Tests.hs +0/−37
- tests/TestSuite.hs +11/−9
+ .stylish-haskell.yaml view
@@ -0,0 +1,55 @@+# stylish-haskell configuration file+# ==================================+#+# The stylish-haskell tool is mainly configured by specifying steps. These steps+# are a list, so they have an order, and one specific step may appear more than+# once (if needed). Each file is processed by these steps in the given order.+steps:+ # Convert some ASCII sequences to their Unicode equivalents. This is disabled+ # by default.+ # - unicode_syntax:+ # # In order to make this work, we also need to insert the UnicodeSyntax+ # # language pragma. If this flag is set to true, we insert it when it's+ # # not already present. You may want to disable it if you configure+ # # language extensions using some other method than pragmas. Default:+ # # true.+ # add_language_pragma: true++ # Import cleanup+ - imports:+ # There are different ways we can align names and lists.+ #+ # - global: Align the import names and import list throughout the entire+ # file.+ #+ # - group: Only align the imports per group (a group is formed by adjacent+ # import lines).+ #+ # - none: Do not perform any alignment.+ #+ # Default: global.+ align: global++ # Language pragmas+ - language_pragmas:+ # We can generate different styles of language pragma lists.+ #+ # - vertical: Vertical-spaced language pragmas, one per line.+ #+ # - compact: A more compact style.+ #+ # Default: vertical.+ style: vertical++ # stylish-haskell can detect redundancy of some language pragmas. If this+ # is set to true, it will remove those redundant pragmas. Default: true.+ remove_redundant: true++ # Replace tabs by spaces. This is disabled by default.+ # - tabs:+ # # Number of spaces to use for each tab. Default: 8, as specified by the+ # # Haskell report.+ # spaces: 8++ # Remove trailing whitespace+ - trailing_whitespace: {}
src/Main.hs view
@@ -1,48 +1,65 @@ --------------------------------------------------------------------------------+{-# LANGUAGE DeriveDataTypeable #-} module Main ( main ) where ---------------------------------------------------------------------------------import Control.Applicative ((<$>))-import Data.Maybe (listToMaybe)-import System.Environment (getArgs)+import Control.Monad (forM_)+import Data.List (intercalate)+import Data.Maybe (listToMaybe)+import Data.Version (Version(..))+import System.Console.CmdArgs ---------------------------------------------------------------------------------import qualified StylishHaskell.Imports-import qualified StylishHaskell.LanguagePragmas-import StylishHaskell.Parse-import StylishHaskell.Stylish-import qualified StylishHaskell.TrailingWhitespace+import Paths_stylish_haskell (version)+import StylishHaskell+import StylishHaskell.Config+import StylishHaskell.Step+import StylishHaskell.Verbose ---------------------------------------------------------------------------------runStylish :: Maybe FilePath -> Stylish -> Lines -> Lines-runStylish mfp f ls = case parseModule mfp (unlines ls) of- Left err -> error err -- TODO: maybe return original lines?- Right module' -> f ls module'+data StylishArgs = StylishArgs+ { config :: Maybe FilePath+ , verbose :: Bool+ , defaults :: Bool+ , files :: [FilePath]+ } deriving (Data, Show, Typeable) ---------------------------------------------------------------------------------chainStylish :: Maybe FilePath -> [Stylish] -> Lines -> Lines-chainStylish mfp filters = foldr (.) id filters'+stylishArgs :: StylishArgs+stylishArgs = StylishArgs+ { config = Nothing &= typFile &= help "Configuration file"+ , verbose = False &= help "Run in verbose mode"+ , defaults = False &= help "Dump default config and exit"+ , files = [] &= typFile &= args+ } &= summary ("stylish-haskell-" ++ versionString version) where- filters' :: [Lines -> Lines]- filters' = map (runStylish mfp) filters+ versionString = intercalate "." . map show . versionBranch -------------------------------------------------------------------------------- main :: IO ()-main = do- filePath <- listToMaybe <$> getArgs- contents <- maybe getContents readFile filePath- putStr $ unlines $ chainStylish filePath filters $ lines contents+main = cmdArgs stylishArgs >>= stylishHaskell+++--------------------------------------------------------------------------------+stylishHaskell :: StylishArgs -> IO ()+stylishHaskell sa+ | defaults sa = do+ fileName <- defaultConfigFilePath+ verbose' $ "Dumping config from " ++ fileName+ readFile fileName >>= putStr+ | otherwise = do+ conf <- loadConfig verbose' (config sa)+ let steps = configSteps conf+ forM_ steps $ \s -> verbose' $ "Enabled " ++ stepName s ++ " step"+ contents <- maybe getContents readFile filePath+ putStr $ unlines $ runSteps filePath steps $ lines contents where- filters =- [ StylishHaskell.Imports.stylish- , StylishHaskell.LanguagePragmas.stylish- -- , StylishHaskell.Tabs.stylish- , StylishHaskell.TrailingWhitespace.stylish- ]+ verbose' = makeVerbose (verbose sa)+ filePath = listToMaybe $ files sa
+ src/StylishHaskell.hs view
@@ -0,0 +1,22 @@+--------------------------------------------------------------------------------+module StylishHaskell+ ( runStep+ , runSteps+ ) where+++--------------------------------------------------------------------------------+import StylishHaskell.Parse+import StylishHaskell.Step+++--------------------------------------------------------------------------------+runStep :: Maybe FilePath -> Step -> Lines -> Lines+runStep mfp step ls = case parseModule mfp (unlines ls) of+ Left err -> error err -- TODO: maybe return original lines?+ Right module' -> stepFilter step ls module'+++--------------------------------------------------------------------------------+runSteps :: Maybe FilePath -> [Step] -> Lines -> Lines+runSteps mfp = foldr (flip (.)) id . map (runStep mfp)
src/StylishHaskell/Block.hs view
@@ -1,8 +1,11 @@ -------------------------------------------------------------------------------- module StylishHaskell.Block ( Block (..)+ , LineBlock+ , SpanBlock , blockLength- , fromSrcSpanInfo+ , linesFromSrcSpan+ , spanFromSrcSpan , moveBlock , adjacent , merge@@ -17,43 +20,58 @@ -------------------------------------------------------------------------------- -- | Indicates a line span-data Block = Block+data Block a = Block { blockStart :: Int , blockEnd :: Int } deriving (Eq, Ord, Show) ---------------------------------------------------------------------------------blockLength :: Block -> Int+type LineBlock = Block String+++--------------------------------------------------------------------------------+type SpanBlock = Block Char+++--------------------------------------------------------------------------------+blockLength :: Block a -> Int blockLength (Block start end) = end - start + 1 ---------------------------------------------------------------------------------fromSrcSpanInfo :: H.SrcSpanInfo -> Block-fromSrcSpanInfo = H.srcInfoSpan >>>+linesFromSrcSpan :: H.SrcSpanInfo -> LineBlock+linesFromSrcSpan = H.srcInfoSpan >>> H.srcSpanStartLine &&& H.srcSpanEndLine >>> arr (uncurry Block) ---------------------------------------------------------------------------------moveBlock :: Int -> Block -> Block+spanFromSrcSpan :: H.SrcSpanInfo -> SpanBlock+spanFromSrcSpan = H.srcInfoSpan >>>+ H.srcSpanStartColumn &&& H.srcSpanEndColumn >>>+ arr (uncurry Block)+++--------------------------------------------------------------------------------+moveBlock :: Int -> Block a -> Block a moveBlock offset (Block start end) = Block (start + offset) (end + offset) ---------------------------------------------------------------------------------adjacent :: Block -> Block -> Bool+adjacent :: Block a -> Block a -> Bool adjacent b1 b2 = follows b1 b2 || follows b2 b1 where follows (Block _ e1) (Block s2 _) = e1 + 1 == s2 ---------------------------------------------------------------------------------merge :: Block -> Block -> Block+merge :: Block a -> Block a -> Block a merge (Block s1 e1) (Block s2 e2) = Block (min s1 s2) (max e1 e2) ---------------------------------------------------------------------------------overlapping :: [Block] -> Bool+overlapping :: [Block a] -> Bool overlapping blocks = any (uncurry overlapping') $ zip blocks (drop 1 blocks) where
+ src/StylishHaskell/Config.hs view
@@ -0,0 +1,175 @@+--------------------------------------------------------------------------------+{-# LANGUAGE OverloadedStrings #-}+module StylishHaskell.Config+ ( Config (..)+ , defaultConfigFilePath+ , configFilePath+ , loadConfig+ ) where+++--------------------------------------------------------------------------------+import Control.Applicative ((<$>), (<*>))+import Control.Monad (forM, msum, mzero)+import Data.Aeson (FromJSON(..))+import qualified Data.Aeson as A+import qualified Data.Aeson.Types as A+import qualified Data.ByteString as B+import Data.List (intercalate)+import Data.Map (Map)+import qualified Data.Map as M+import Data.Yaml (decodeEither)+import System.Directory+import System.FilePath ((</>))+++--------------------------------------------------------------------------------+import Paths_stylish_haskell (getDataFileName)+import StylishHaskell.Step+import qualified StylishHaskell.Step.Imports as Imports+import qualified StylishHaskell.Step.LanguagePragmas as LanguagePragmas+import qualified StylishHaskell.Step.Tabs as Tabs+import qualified StylishHaskell.Step.TrailingWhitespace as TrailingWhitespace+import qualified StylishHaskell.Step.UnicodeSyntax as UnicodeSyntax+import StylishHaskell.Verbose+++--------------------------------------------------------------------------------+data Config = Config+ { configSteps :: [Step]+ }+++--------------------------------------------------------------------------------+instance FromJSON Config where+ parseJSON = parseConfig+++--------------------------------------------------------------------------------+emptyConfig :: Config+emptyConfig = Config []+++--------------------------------------------------------------------------------+configFileName :: String+configFileName = ".stylish-haskell.yaml"+++--------------------------------------------------------------------------------+defaultConfigFilePath :: IO FilePath+defaultConfigFilePath = getDataFileName ".stylish-haskell.yaml"+++--------------------------------------------------------------------------------+configFilePath :: Verbose -> Maybe FilePath -> IO (Maybe FilePath)+configFilePath verbose userSpecified = do+ (current, currentE) <- check $ (</> configFileName) <$> getCurrentDirectory+ (home, homeE) <- check $ (</> configFileName) <$> getHomeDirectory+ (def, defE) <- check defaultConfigFilePath+ return $ msum+ [ userSpecified+ , if currentE then Just current else Nothing+ , if homeE then Just home else Nothing+ , if defE then Just def else Nothing+ ]+ where+ check fp = do+ fp' <- fp+ ex <- doesFileExist fp'+ verbose $ fp' ++ if ex then " exists" else " does not exist"+ return (fp', ex)+++--------------------------------------------------------------------------------+loadConfig :: Verbose -> Maybe FilePath -> IO Config+loadConfig verbose mfp = do+ mfp' <- configFilePath verbose mfp+ case mfp' of+ Nothing -> do+ verbose $ "Using empty configuration"+ return emptyConfig+ Just fp -> do+ verbose $ "Loading configuration at " ++ fp+ bs <- B.readFile fp+ case decodeEither bs of+ Left err -> error $+ "StylishHaskell.Config.loadConfig: " ++ err+ Right config -> return config+++--------------------------------------------------------------------------------+parseConfig :: A.Value -> A.Parser Config+parseConfig (A.Object o) = Config+ <$> (o A..: "steps" >>= fmap concat . mapM parseSteps)+parseConfig _ = mzero+++--------------------------------------------------------------------------------+catalog :: Map String (A.Object -> A.Parser Step)+catalog = M.fromList+ [ ("imports", parseImports)+ , ("language_pragmas", parseLanguagePragmas)+ , ("tabs", parseTabs)+ , ("trailing_whitespace", parseTrailingWhitespace)+ , ("unicode_syntax", parseUnicodeSyntax)+ ]+++--------------------------------------------------------------------------------+parseSteps :: A.Value -> A.Parser [Step]+parseSteps val = do+ map' <- parseJSON val :: A.Parser (Map String A.Value)+ forM (M.toList map') $ \(k, v) -> case (M.lookup k catalog, v) of+ (Just parser, A.Object o) -> parser o+ _ -> fail $ "Invalid declaration for " ++ k+++--------------------------------------------------------------------------------+-- | Utility for enum-like options+parseEnum :: [(String, a)] -> a -> Maybe String -> A.Parser a+parseEnum _ def Nothing = return def+parseEnum strs _ (Just k) = case lookup k strs of+ Just v -> return v+ Nothing -> fail $ "Unknown option: " ++ k ++ ", should be one of: " +++ intercalate ", " (map fst strs)+++--------------------------------------------------------------------------------+parseImports :: A.Object -> A.Parser Step+parseImports o = Imports.step+ <$> (o A..:? "align" >>= parseEnum aligns Imports.Global)+ where+ aligns =+ [ ("global", Imports.Global)+ , ("group", Imports.Group)+ , ("none", Imports.None)+ ]+++--------------------------------------------------------------------------------+parseLanguagePragmas :: A.Object -> A.Parser Step+parseLanguagePragmas o = LanguagePragmas.step+ <$> (o A..:? "style" >>= parseEnum styles LanguagePragmas.Vertical)+ <*> o A..:? "remove_redundant" A..!= True+ where+ styles =+ [ ("vertical", LanguagePragmas.Vertical)+ , ("compact", LanguagePragmas.Compact)+ ]+++--------------------------------------------------------------------------------+parseTabs :: A.Object -> A.Parser Step+parseTabs o = Tabs.step+ <$> o A..:? "spaces" A..!= 8+++--------------------------------------------------------------------------------+parseTrailingWhitespace :: A.Object -> A.Parser Step+parseTrailingWhitespace _ = return TrailingWhitespace.step+++--------------------------------------------------------------------------------+parseUnicodeSyntax :: A.Object -> A.Parser Step+parseUnicodeSyntax o = UnicodeSyntax.step+ <$> o A..:? "add_language_pragma" A..!= True
src/StylishHaskell/Editor.hs view
@@ -20,30 +20,29 @@ -------------------------------------------------------------------------------- import StylishHaskell.Block-import StylishHaskell.Stylish -------------------------------------------------------------------------------- -- | Changes the lines indicated by the 'Block' into the given 'Lines'-data Change = Change- { changeBlock :: Block- , changeLines :: Lines+data Change a = Change+ { changeBlock :: Block a+ , changeLines :: [a] } deriving (Eq, Show) ---------------------------------------------------------------------------------moveChange :: Int -> Change -> Change+moveChange :: Int -> Change a -> Change a moveChange offset (Change block ls) = Change (moveBlock offset block) ls -------------------------------------------------------------------------------- -- | Number of additional lines introduced when a change is made.-changeExtraLines :: Change -> Int+changeExtraLines :: Change a -> Int changeExtraLines (Change block ls) = length ls - blockLength block ---------------------------------------------------------------------------------applyChanges :: [Change] -> Lines -> Lines+applyChanges :: [Change a] -> [a] -> [a] applyChanges changes | overlapping blocks = error $ "StylishHaskell.Editor.applyChanges: " ++@@ -77,29 +76,29 @@ -------------------------------------------------------------------------------- -- | Change a block of lines for some other lines-change :: Block -> Lines -> Change+change :: Block a -> [a] -> Change a change = Change -------------------------------------------------------------------------------- -- | Change a single line for some other lines-changeLine :: Int -> Lines -> Change+changeLine :: Int -> [a] -> Change a changeLine start = change (Block start start) -------------------------------------------------------------------------------- -- | Delete a block of lines-delete :: Block -> Change+delete :: Block a -> Change a delete block = Change block [] -------------------------------------------------------------------------------- -- | Delete a single line-deleteLine :: Int -> Change+deleteLine :: Int -> Change a deleteLine start = delete (Block start start) -------------------------------------------------------------------------------- -- | Insert something /before/ the given lines-insert :: Int -> Lines -> Change+insert :: Int -> [a] -> Change a insert start = Change (Block start (start - 1))
− src/StylishHaskell/Imports.hs
@@ -1,110 +0,0 @@----------------------------------------------------------------------------------module StylishHaskell.Imports- ( stylish- ) where------------------------------------------------------------------------------------import Control.Arrow ((&&&))-import Data.Char (isAlpha)-import Data.List (sortBy)-import Data.Maybe (isJust, maybeToList)-import Data.Ord (comparing)-import qualified Language.Haskell.Exts.Annotated as H------------------------------------------------------------------------------------import StylishHaskell.Block-import StylishHaskell.Editor-import StylishHaskell.Stylish-import StylishHaskell.Util------------------------------------------------------------------------------------imports :: H.Module l -> [H.ImportDecl l]-imports (H.Module _ _ _ is _) = is-imports _ = []------------------------------------------------------------------------------------importName :: H.ImportDecl l -> String-importName i = let (H.ModuleName _ n) = H.importModule i in n------------------------------------------------------------------------------------longestImport :: [H.ImportDecl l] -> Int-longestImport = maximum . map (length . importName)-------------------------------------------------------------------------------------- | Groups adjacent imports into larger import blocks-groupAdjacent :: [H.ImportDecl Block] -> [(Block, [H.ImportDecl Block])]-groupAdjacent = foldr step []- where- -- This code is ugly and not optimal, and no fucks were given.- step imp is = case break (adjacent b1 . fst) is of- (_, []) -> (b1, [imp]) : is- (xs, ((b2, imps) : ys)) -> (merge b1 b2, imp : imps) : (xs ++ ys)- where- b1 = H.ann imp-------------------------------------------------------------------------------------- | Compare imports for ordering-compareImports :: H.ImportDecl l -> H.ImportDecl l -> Ordering-compareImports = comparing (importName &&& H.importQualified)-------------------------------------------------------------------------------------- | The implementation is a bit hacky to get proper sorting for input specs:--- constructors first, followed by functions, and then operators.-compareImportSpecs :: H.ImportSpec l -> H.ImportSpec l -> Ordering-compareImportSpecs = comparing key- where- key :: H.ImportSpec l -> (Int, Int, String)- key (H.IVar _ x) = let n = nameToString x in (1, operator n, n)- key (H.IAbs _ x) = (0, 0, nameToString x)- key (H.IThingAll _ x) = (0, 0, nameToString x)- key (H.IThingWith _ x _) = (0, 0, nameToString x)-- operator [] = 0 -- But this should not happen- operator (x : _) = if isAlpha x then 0 else 1-------------------------------------------------------------------------------------- | Sort the input spec list inside an 'H.ImportDecl'-sortImportSpecs :: H.ImportDecl l -> H.ImportDecl l-sortImportSpecs imp = imp {H.importSpecs = fmap sort $ H.importSpecs imp}- where- sort (H.ImportSpecList l h specs) = H.ImportSpecList l h $- sortBy compareImportSpecs specs------------------------------------------------------------------------------------prettyImport :: Int -> H.ImportDecl l -> String-prettyImport longest imp = unwords $ concat- [ ["import"]- , [if H.importQualified imp then "qualified" else " "]- , [(if hasExtras then padRight longest else id) (importName imp)]- , ["as " ++ as | H.ModuleName _ as <- maybeToList $ H.importAs imp]- , [H.prettyPrint specs | specs <- maybeToList $ H.importSpecs imp]- ]- where- hasExtras = isJust (H.importAs imp) || isJust (H.importSpecs imp)------------------------------------------------------------------------------------prettyImportGroup :: Int -> [H.ImportDecl Block] -> Lines-prettyImportGroup longest = map (prettyImport longest) . sortBy compareImports------------------------------------------------------------------------------------stylish :: Stylish-stylish ls (module', _) = flip applyChanges ls- [ change block (prettyImportGroup longest importGroup)- | (block, importGroup) <- groups- ]- where- imps = map sortImportSpecs $ imports $ fmap fromSrcSpanInfo module'- longest = longestImport imps- groups = groupAdjacent imps
− src/StylishHaskell/LanguagePragmas.hs
@@ -1,53 +0,0 @@----------------------------------------------------------------------------------module StylishHaskell.LanguagePragmas- ( stylish- ) where------------------------------------------------------------------------------------import Data.List (nub, sort)-import qualified Language.Haskell.Exts.Annotated as H------------------------------------------------------------------------------------import StylishHaskell.Block-import StylishHaskell.Editor-import StylishHaskell.Stylish-import StylishHaskell.Util------------------------------------------------------------------------------------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, [String])] -> Int-firstLocation = minimum . map (blockStart . fst)-------------------------------------------------------------------------------------- | TODO: multiple lines if longer than 80 columns-prettyPragmas :: [String] -> Lines-prettyPragmas pragmas' =- [ "{-# LANGUAGE " ++ padRight longest pragma ++ " #-}"- | pragma <- pragmas'- ]- where- longest = maximum $ map length pragmas'------------------------------------------------------------------------------------stylish :: Stylish-stylish ls (module', _)- | null pragmas' = ls- | otherwise = applyChanges changes ls- where- pragmas' = pragmas $ fmap fromSrcSpanInfo module'- deletes = map (delete . fst) pragmas'- uniques = nub $ sort $ concatMap snd pragmas'- loc = firstLocation pragmas'- changes = insert loc (prettyPragmas uniques) : deletes
src/StylishHaskell/Parse.hs view
@@ -10,7 +10,7 @@ ---------------------------------------------------------------------------------import StylishHaskell.Stylish+import StylishHaskell.Step --------------------------------------------------------------------------------
+ src/StylishHaskell/Step.hs view
@@ -0,0 +1,32 @@+--------------------------------------------------------------------------------+module StylishHaskell.Step+ ( Lines+ , Module+ , Step (..)+ , makeStep+ ) where+++--------------------------------------------------------------------------------+import qualified Language.Haskell.Exts.Annotated as H+++--------------------------------------------------------------------------------+type Lines = [String]+++--------------------------------------------------------------------------------+-- | Concrete module type+type Module = (H.Module H.SrcSpanInfo, [H.Comment])+++--------------------------------------------------------------------------------+data Step = Step+ { stepName :: String+ , stepFilter :: Lines -> Module -> Lines+ }+++--------------------------------------------------------------------------------+makeStep :: String -> (Lines -> Module -> Lines) -> Step+makeStep = Step
+ src/StylishHaskell/Step/Imports.hs view
@@ -0,0 +1,142 @@+--------------------------------------------------------------------------------+module StylishHaskell.Step.Imports+ ( Align (..)+ , step+ ) where+++--------------------------------------------------------------------------------+import Control.Arrow ((&&&))+import Data.Char (isAlpha)+import Data.List (sortBy)+import Data.Maybe (isJust, maybeToList)+import Data.Ord (comparing)+import qualified Language.Haskell.Exts.Annotated as H+++--------------------------------------------------------------------------------+import StylishHaskell.Block+import StylishHaskell.Editor+import StylishHaskell.Step+import StylishHaskell.Util+++--------------------------------------------------------------------------------+data Align+ = Global+ | Group+ | None+ deriving (Eq, Show)+++--------------------------------------------------------------------------------+imports :: H.Module l -> [H.ImportDecl l]+imports (H.Module _ _ _ is _) = is+imports _ = []+++--------------------------------------------------------------------------------+importName :: H.ImportDecl l -> String+importName i = let (H.ModuleName _ n) = H.importModule i in n+++--------------------------------------------------------------------------------+longestImport :: [H.ImportDecl l] -> Int+longestImport = maximum . map (length . importName)+++--------------------------------------------------------------------------------+-- | Groups adjacent imports into larger import blocks+groupAdjacent :: [H.ImportDecl LineBlock]+ -> [(LineBlock, [H.ImportDecl LineBlock])]+groupAdjacent = foldr go []+ where+ -- This code is ugly and not optimal, and no fucks were given.+ go imp is = case break (adjacent b1 . fst) is of+ (_, []) -> (b1, [imp]) : is+ (xs, ((b2, imps) : ys)) -> (merge b1 b2, imp : imps) : (xs ++ ys)+ where+ b1 = H.ann imp+++--------------------------------------------------------------------------------+-- | Compare imports for ordering+compareImports :: H.ImportDecl l -> H.ImportDecl l -> Ordering+compareImports = comparing (importName &&& H.importQualified)+++--------------------------------------------------------------------------------+-- | The implementation is a bit hacky to get proper sorting for input specs:+-- constructors first, followed by functions, and then operators.+compareImportSpecs :: H.ImportSpec l -> H.ImportSpec l -> Ordering+compareImportSpecs = comparing key+ where+ key :: H.ImportSpec l -> (Int, Int, String)+ key (H.IVar _ x) = let n = nameToString x in (1, operator n, n)+ key (H.IAbs _ x) = (0, 0, nameToString x)+ key (H.IThingAll _ x) = (0, 0, nameToString x)+ key (H.IThingWith _ x _) = (0, 0, nameToString x)++ operator [] = 0 -- But this should not happen+ operator (x : _) = if isAlpha x then 0 else 1+++--------------------------------------------------------------------------------+-- | Sort the input spec list inside an 'H.ImportDecl'+sortImportSpecs :: H.ImportDecl l -> H.ImportDecl l+sortImportSpecs imp = imp {H.importSpecs = fmap sort $ H.importSpecs imp}+ where+ sort (H.ImportSpecList l h specs) = H.ImportSpecList l h $+ sortBy compareImportSpecs specs+++--------------------------------------------------------------------------------+prettyImport :: Bool -> Bool -> Int -> H.ImportDecl l -> String+prettyImport padQualified padName longest imp = unwords $ concat+ [ ["import"]+ , qualified+ , [(if hasExtras && padName then padRight longest else id) (importName imp)]+ , ["as " ++ as | H.ModuleName _ as <- maybeToList $ H.importAs imp]+ , [H.prettyPrint specs | specs <- maybeToList $ H.importSpecs imp]+ ]+ where+ hasExtras = isJust (H.importAs imp) || isJust (H.importSpecs imp)++ qualified+ | H.importQualified imp = ["qualified"]+ | padQualified = [" "]+ | otherwise = []+++--------------------------------------------------------------------------------+prettyImportGroup :: Align -> Int -> [H.ImportDecl LineBlock] -> Lines+prettyImportGroup align longest imps =+ map (prettyImport padQual padName longest') $ sortBy compareImports imps+ where+ longest' = case align of+ Group -> longestImport imps+ _ -> longest++ padName = align /= None++ padQual = case align of+ Global -> True+ Group -> any H.importQualified imps+ None -> False+++--------------------------------------------------------------------------------+step :: Align -> Step+step = makeStep "Imports" . step'+++--------------------------------------------------------------------------------+step' :: Align -> Lines -> Module -> Lines+step' align ls (module', _) = flip applyChanges ls+ [ change block (prettyImportGroup align longest importGroup)+ | (block, importGroup) <- groups+ ]+ where+ imps = map sortImportSpecs $ imports $ fmap linesFromSrcSpan module'+ longest = longestImport imps+ groups = groupAdjacent imps
+ src/StylishHaskell/Step/LanguagePragmas.hs view
@@ -0,0 +1,120 @@+--------------------------------------------------------------------------------+module StylishHaskell.Step.LanguagePragmas+ ( Style (..)+ , step++ -- * Utilities+ , addLanguagePragma+ ) where+++--------------------------------------------------------------------------------+import Data.List (nub, sort)+import qualified Language.Haskell.Exts.Annotated as H+++--------------------------------------------------------------------------------+import StylishHaskell.Block+import StylishHaskell.Editor+import StylishHaskell.Step+import StylishHaskell.Util+++--------------------------------------------------------------------------------+data Style+ = Vertical+ | Compact+ 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)+++--------------------------------------------------------------------------------+-- | TODO: multiple lines if longer than 80 columns+verticalPragmas :: [String] -> Lines+verticalPragmas pragmas' =+ [ "{-# LANGUAGE " ++ padRight longest pragma ++ " #-}"+ | pragma <- pragmas'+ ]+ where+ longest = maximum $ map length pragmas'+++--------------------------------------------------------------------------------+compactPragmas :: [String] -> Lines+compactPragmas pragmas' = wrap 80 "{-# LANGUAGE" 13 $+ map (++ ",") (init pragmas') ++ [last pragmas', "#-}"]+++--------------------------------------------------------------------------------+prettyPragmas :: Style -> [String] -> Lines+prettyPragmas Vertical = verticalPragmas+prettyPragmas Compact = compactPragmas+++--------------------------------------------------------------------------------+step :: Style -> Bool -> Step+step style = makeStep "LanguagePragmas" . step' style+++--------------------------------------------------------------------------------+step' :: Style -> Bool -> Lines -> Module -> Lines+step' style removeRedundant ls (module', _)+ | null pragmas' = ls+ | otherwise = applyChanges changes ls+ where+ filterRedundant+ | removeRedundant = filter (not . isRedundant module')+ | otherwise = id++ pragmas' = pragmas $ fmap linesFromSrcSpan module'+ uniques = filterRedundant $ nub $ sort $ snd =<< pragmas'+ loc = firstLocation pragmas'+ deletes = map (delete . fst) pragmas'+ changes = insert loc (prettyPragmas style uniques) : deletes+++--------------------------------------------------------------------------------+-- | 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]]
+ src/StylishHaskell/Step/Tabs.hs view
@@ -0,0 +1,21 @@+--------------------------------------------------------------------------------+module StylishHaskell.Step.Tabs+ ( step+ ) where+++--------------------------------------------------------------------------------+import StylishHaskell.Step+++--------------------------------------------------------------------------------+removeTabs :: Int -> String -> String+removeTabs spaces = concatMap removeTabs'+ where+ removeTabs' '\t' = replicate spaces ' '+ removeTabs' x = [x]+++--------------------------------------------------------------------------------+step :: Int -> Step+step spaces = makeStep "Tabs" $ \ls _ -> map (removeTabs spaces) ls
+ src/StylishHaskell/Step/TrailingWhitespace.hs view
@@ -0,0 +1,22 @@+--------------------------------------------------------------------------------+module StylishHaskell.Step.TrailingWhitespace+ ( step+ ) where+++--------------------------------------------------------------------------------+import Data.Char (isSpace)+++--------------------------------------------------------------------------------+import StylishHaskell.Step+++--------------------------------------------------------------------------------+dropTrailingWhitespace :: String -> String+dropTrailingWhitespace = reverse . dropWhile isSpace . reverse+++--------------------------------------------------------------------------------+step :: Step+step = makeStep "TrailingWhitespace" $ \ls _ -> map dropTrailingWhitespace ls
+ src/StylishHaskell/Step/UnicodeSyntax.hs view
@@ -0,0 +1,135 @@+--------------------------------------------------------------------------------+{-# LANGUAGE ViewPatterns #-}+module StylishHaskell.Step.UnicodeSyntax+ ( step+ ) where+++--------------------------------------------------------------------------------+import Data.List (isPrefixOf, sort)+import Data.Map (Map)+import qualified Data.Map as M+import Data.Maybe (maybeToList)+import qualified Language.Haskell.Exts.Annotated as H+++--------------------------------------------------------------------------------+import StylishHaskell.Block+import StylishHaskell.Editor+import StylishHaskell.Step+import StylishHaskell.Step.LanguagePragmas (addLanguagePragma)+import StylishHaskell.Util+++--------------------------------------------------------------------------------+unicodeReplacements :: Map String String+unicodeReplacements = M.fromList+ [ ("::", "∷")+ , ("=>", "⇒")+ , ("->", "→")+ ]+++--------------------------------------------------------------------------------+replaceAll :: [(Int, [(Int, String)])] -> Lines -> [Change String]+replaceAll positions ls =+ zipWith changeLine' positions $ selectLines (map fst positions) ls+ where+ changeLine' (r, ns) str = changeLine r $ return $ flip applyChanges str+ [ change (Block c ec) repl+ | (c, needle) <- sort ns+ , let ec = c + length needle - 1+ , repl <- maybeToList $ M.lookup needle unicodeReplacements+ ]++++--------------------------------------------------------------------------------+selectLines :: [Int] -> Lines -> [String]+selectLines = go 1+ where+ go _ [] _ = []+ go _ _ [] = []+ go r (x : xs) (l : ls)+ | r == x = l : go (r + 1) xs ls+ | otherwise = go (r + 1) (x : xs) ls+++--------------------------------------------------------------------------------+groupPerLine :: [((Int, Int), a)] -> [(Int, [(Int, a)])]+groupPerLine = M.toList . M.fromListWith (++) .+ map (\((r, c), x) -> (r, [(c, x)]))+++--------------------------------------------------------------------------------+typeSigs :: H.Module H.SrcSpanInfo -> Lines -> [((Int, Int), String)]+typeSigs module' ls =+ [ (pos, "::")+ | H.TypeSig loc _ _ <- everything module' :: [H.Decl H.SrcSpanInfo]+ , (start, end) <- infoPoints loc+ , pos <- maybeToList $ between start end "::" ls+ ]+++--------------------------------------------------------------------------------+contexts :: H.Module H.SrcSpanInfo -> Lines -> [((Int, Int), String)]+contexts module' ls =+ [ (pos, "=>")+ | context <- everything module' :: [H.Context H.SrcSpanInfo]+ , (start, end) <- infoPoints $ H.ann context+ , pos <- maybeToList $ between start end "=>" ls+ ]+++--------------------------------------------------------------------------------+typeFuns :: H.Module H.SrcSpanInfo -> Lines -> [((Int, Int), String)]+typeFuns module' ls =+ [ (pos, "->")+ | H.TyFun _ t1 t2 <- everything module'+ , let start = H.srcSpanEnd $ H.srcInfoSpan $ H.ann t1+ , let end = H.srcSpanStart $ H.srcInfoSpan $ H.ann t2+ , pos <- maybeToList $ between start end "->" ls+ ]+++--------------------------------------------------------------------------------+-- | Search for a needle in a haystack of lines. Only part the inside (startRow,+-- startCol), (endRow, endCol) is searched. The return value is the position of+-- the needle.+between :: (Int, Int) -> (Int, Int) -> String -> Lines -> Maybe (Int, Int)+between (startRow, startCol) (endRow, endCol) needle =+ search (startRow, startCol) .+ withLast (take endCol) .+ withHead (drop $ startCol - 1) .+ take (endRow - startRow + 1) .+ drop (startRow - 1)+ where+ withHead _ [] = []+ withHead f (x : xs) = (f x) : xs++ withLast f [x] = [f x]+ withLast f (x : xs) = x : withLast f xs+ withLast _ [] = []++ search _ [] = Nothing+ search (r, _) ([] : xs) = search (r + 1, 1) xs+ search (r, c) (x : xs)+ | needle `isPrefixOf` x = Just (r, c)+ | otherwise = search (r, c + 1) (tail x : xs)+++--------------------------------------------------------------------------------+step :: Bool -> Step+step = makeStep "UnicodeSyntax" . step'+++--------------------------------------------------------------------------------+step' :: Bool -> Lines -> Module -> Lines+step' alp ls (module', _) = applyChanges changes ls+ where+ changes = (if alp then addLanguagePragma "UnicodeSyntax" module' else []) +++ replaceAll perLine ls+ perLine = sort $ groupPerLine $+ typeSigs module' ls +++ contexts module' ls +++ typeFuns module' ls
− src/StylishHaskell/Stylish.hs
@@ -1,23 +0,0 @@----------------------------------------------------------------------------------module StylishHaskell.Stylish- ( Lines- , Module- , Stylish- ) where------------------------------------------------------------------------------------import qualified Language.Haskell.Exts.Annotated as H------------------------------------------------------------------------------------type Lines = [String]-------------------------------------------------------------------------------------- | Concrete module type-type Module = (H.Module H.SrcSpanInfo, [H.Comment])------------------------------------------------------------------------------------type Stylish = Lines -> Module -> Lines
− src/StylishHaskell/Tabs.hs
@@ -1,21 +0,0 @@----------------------------------------------------------------------------------module StylishHaskell.Tabs- ( stylish- ) where------------------------------------------------------------------------------------import StylishHaskell.Stylish------------------------------------------------------------------------------------removeTabs :: String -> String-removeTabs = concatMap removeTabs'- where- removeTabs' '\t' = " "- removeTabs' x = [x]------------------------------------------------------------------------------------stylish :: Stylish-stylish ls _ = map removeTabs ls
− src/StylishHaskell/TrailingWhitespace.hs
@@ -1,22 +0,0 @@----------------------------------------------------------------------------------module StylishHaskell.TrailingWhitespace- ( stylish- ) where------------------------------------------------------------------------------------import Data.Char (isSpace)------------------------------------------------------------------------------------import StylishHaskell.Stylish------------------------------------------------------------------------------------dropTrailingWhitespace :: String -> String-dropTrailingWhitespace = reverse . dropWhile isSpace . reverse------------------------------------------------------------------------------------stylish :: Stylish-stylish ls _ = map dropTrailingWhitespace ls
src/StylishHaskell/Util.hs view
@@ -2,15 +2,26 @@ module StylishHaskell.Util ( nameToString , padRight+ , everything+ , infoPoints+ , wrap ) where --------------------------------------------------------------------------------+import Control.Arrow ((&&&), (>>>))+import Data.Data (Data)+import qualified Data.Generics as G+import Data.Maybe (maybeToList)+import Data.Typeable (cast) import qualified Language.Haskell.Exts.Annotated as H ----------------------------------------------------------------------------------- | TODO: put this in utilities?+import StylishHaskell.Step+++-------------------------------------------------------------------------------- nameToString :: H.Name l -> String nameToString (H.Ident _ str) = str nameToString (H.Symbol _ str) = str@@ -19,3 +30,35 @@ -------------------------------------------------------------------------------- padRight :: Int -> String -> String padRight len str = str ++ replicate (len - length str) ' '+++--------------------------------------------------------------------------------+everything :: (Data a, Data b) => a -> [b]+everything = G.everything (++) (maybeToList . cast)+++--------------------------------------------------------------------------------+infoPoints :: H.SrcSpanInfo -> [((Int, Int), (Int, Int))]+infoPoints = H.srcInfoPoints >>> map (H.srcSpanStart &&& H.srcSpanEnd)+++--------------------------------------------------------------------------------+wrap :: Int -- ^ Maximum line width+ -> String -- ^ Leading string+ -> Int -- ^ Indentation+ -> [String] -- ^ Strings to add/wrap+ -> Lines -- ^ Resulting lines+wrap maxWidth leading indent strs =+ let (ls, curr, _) = foldl step ([], leading, length leading) strs+ in ls ++ [curr]+ where+ -- TODO: In order to optimize this, use a difference list instead of a+ -- regular list for 'ls'.+ step (ls, curr, width) str+ | width' > maxWidth = (ls ++ [curr], spaces ++ str, indent + len)+ | otherwise = (ls, curr ++ " " ++ str, width')+ where+ len = length str+ width' = width + 1 + len++ spaces = replicate indent ' '
stylish-haskell.cabal view
@@ -1,5 +1,5 @@ Name: stylish-haskell-Version: 0.1.1.0+Version: 0.2.0.0 Synopsis: Haskell code prettifier Homepage: https://github.com/jaspervdj/stylish-haskell License: BSD3@@ -18,25 +18,40 @@ <https://github.com/jaspervdj/stylish-haskell/blob/master/README.markdown> +Data-files:+ .stylish-haskell.yaml+ Executable stylish-haskell Ghc-options: -Wall Hs-source-dirs: src Main-is: Main.hs Other-modules:+ Paths_stylish_haskell+ StylishHaskell StylishHaskell.Block+ StylishHaskell.Config StylishHaskell.Editor- StylishHaskell.Imports- StylishHaskell.LanguagePragmas StylishHaskell.Parse- StylishHaskell.Stylish- StylishHaskell.Tabs- StylishHaskell.TrailingWhitespace+ StylishHaskell.Step+ StylishHaskell.Step.Imports+ StylishHaskell.Step.LanguagePragmas+ StylishHaskell.Step.Tabs+ StylishHaskell.Step.TrailingWhitespace+ StylishHaskell.Step.UnicodeSyntax StylishHaskell.Util Build-depends:+ aeson >= 0.6 && < 0.7, base >= 4 && < 5,- haskell-src-exts >= 1.13 && < 1.14+ bytestring >= 0.9 && < 0.10,+ cmdargs >= 0.9 && < 0.10,+ containers >= 0.3 && < 0.6,+ directory >= 1.1 && < 1.2,+ filepath >= 1.1 && < 1.4,+ haskell-src-exts >= 1.13 && < 1.14,+ syb >= 0.3 && < 0.4,+ yaml >= 0.7 && < 0.8 Test-suite stylish-haskell-tests Ghc-options: -Wall@@ -45,19 +60,28 @@ Type: exitcode-stdio-1.0 Other-modules:- StylishHaskell.Imports.Tests- StylishHaskell.LanguagePragmas.Tests+ StylishHaskell.Step.Imports.Tests+ StylishHaskell.Step.LanguagePragmas.Tests+ StylishHaskell.Step.Tabs.Tests+ StylishHaskell.Step.TrailingWhitespace.Tests+ StylishHaskell.Step.UnicodeSyntax.Tests StylishHaskell.Tests.Util- StylishHaskell.Tabs.Tests- StylishHaskell.TrailingWhitespace.Tests Build-depends: HUnit >= 1.2 && < 1.3, test-framework >= 0.4 && < 0.7, test-framework-hunit >= 0.2 && < 0.3, -- Copied from regular dependencies...+ aeson >= 0.6 && < 0.7, base >= 4 && < 5,- haskell-src-exts >= 1.13 && < 1.14+ bytestring >= 0.9 && < 0.10,+ cmdargs >= 0.9 && < 0.10,+ containers >= 0.3 && < 0.6,+ directory >= 1.1 && < 1.2,+ filepath >= 1.1 && < 1.4,+ haskell-src-exts >= 1.13 && < 1.14,+ syb >= 0.3 && < 0.4,+ yaml >= 0.7 && < 0.8 Source-repository head Type: git
− tests/StylishHaskell/Imports/Tests.hs
@@ -1,51 +0,0 @@----------------------------------------------------------------------------------module StylishHaskell.Imports.Tests- ( tests- ) where------------------------------------------------------------------------------------import Test.Framework (Test, testGroup)-import Test.Framework.Providers.HUnit (testCase)-import Test.HUnit ((@=?))------------------------------------------------------------------------------------import StylishHaskell.Imports-import StylishHaskell.Tests.Util------------------------------------------------------------------------------------tests :: Test-tests = testGroup "StylishHaskell.Imports.Tests"- [ case01- ]------------------------------------------------------------------------------------case01 :: Test-case01 = testCase "case 01" $ expected @=? testStylish stylish input- where- input = unlines- [ "module Herp where"- , ""- , "import qualified Data.Map as M"- , "import Control.Monad"- , "import Data.Map (lookup, (!), insert, Map)"- , ""- , "import Herp.Derp.Internals"- , ""- , "herp = putStrLn \"import Hello world\""- ]-- expected = unlines- [ "module Herp where"- , ""- , "import Control.Monad"- , "import Data.Map (Map, insert, lookup, (!))"- , "import qualified Data.Map as M"- , ""- , "import Herp.Derp.Internals"- , ""- , "herp = putStrLn \"import Hello world\""- ]
− tests/StylishHaskell/LanguagePragmas/Tests.hs
@@ -1,41 +0,0 @@----------------------------------------------------------------------------------module StylishHaskell.LanguagePragmas.Tests- ( tests- ) where------------------------------------------------------------------------------------import Test.Framework (Test, testGroup)-import Test.Framework.Providers.HUnit (testCase)-import Test.HUnit ((@=?))------------------------------------------------------------------------------------import StylishHaskell.LanguagePragmas-import StylishHaskell.Tests.Util------------------------------------------------------------------------------------tests :: Test-tests = testGroup "StylishHaskell.LanguagePragmas.Tests"- [ case01- ]------------------------------------------------------------------------------------case01 :: Test-case01 = testCase "case 01" $ expected @=? testStylish stylish input- where- input = unlines- [ "{-# LANGUAGE ViewPatterns #-}"- , "{-# LANGUAGE TemplateHaskell, ViewPatterns #-}"- , "{-# LANGUAGE ScopedTypeVariables #-}"- , "module Main where"- ]-- expected = unlines- [ "{-# LANGUAGE ScopedTypeVariables #-}"- , "{-# LANGUAGE TemplateHaskell #-}"- , "{-# LANGUAGE ViewPatterns #-}"- , "module Main where"- ]
+ tests/StylishHaskell/Step/Imports/Tests.hs view
@@ -0,0 +1,90 @@+--------------------------------------------------------------------------------+module StylishHaskell.Step.Imports.Tests+ ( tests+ ) where+++--------------------------------------------------------------------------------+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)+import Test.HUnit ((@=?))+++--------------------------------------------------------------------------------+import StylishHaskell.Step.Imports+import StylishHaskell.Tests.Util+++--------------------------------------------------------------------------------+tests :: Test+tests = testGroup "StylishHaskell.Step.Imports.Tests"+ [ case01+ , case02+ , case03+ ]+++--------------------------------------------------------------------------------+input :: String+input = unlines+ [ "module Herp where"+ , ""+ , "import qualified Data.Map as M"+ , "import Control.Monad"+ , "import Data.Map (lookup, (!), insert, Map)"+ , ""+ , "import Herp.Derp.Internals hiding (foo)"+ , ""+ , "herp = putStrLn \"import Hello world\""+ ]+++--------------------------------------------------------------------------------+case01 :: Test+case01 = testCase "case 01" $ expected @=? testStep (step Global) input+ where+ expected = unlines+ [ "module Herp where"+ , ""+ , "import Control.Monad"+ , "import Data.Map (Map, insert, lookup, (!))"+ , "import qualified Data.Map as M"+ , ""+ , "import Herp.Derp.Internals hiding (foo)"+ , ""+ , "herp = putStrLn \"import Hello world\""+ ]+++--------------------------------------------------------------------------------+case02 :: Test+case02 = testCase "case 02" $ expected @=? testStep (step Group) input+ where+ expected = unlines+ [ "module Herp where"+ , ""+ , "import Control.Monad"+ , "import Data.Map (Map, insert, lookup, (!))"+ , "import qualified Data.Map as M"+ , ""+ , "import Herp.Derp.Internals hiding (foo)"+ , ""+ , "herp = putStrLn \"import Hello world\""+ ]+++--------------------------------------------------------------------------------+case03 :: Test+case03 = testCase "case 03" $ expected @=? testStep (step None) input+ where+ expected = unlines+ [ "module Herp where"+ , ""+ , "import Control.Monad"+ , "import Data.Map (Map, insert, lookup, (!))"+ , "import qualified Data.Map as M"+ , ""+ , "import Herp.Derp.Internals hiding (foo)"+ , ""+ , "herp = putStrLn \"import Hello world\""+ ]
+ tests/StylishHaskell/Step/LanguagePragmas/Tests.hs view
@@ -0,0 +1,97 @@+--------------------------------------------------------------------------------+module StylishHaskell.Step.LanguagePragmas.Tests+ ( tests+ ) where+++--------------------------------------------------------------------------------+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)+import Test.HUnit ((@=?))+++--------------------------------------------------------------------------------+import StylishHaskell.Step.LanguagePragmas+import StylishHaskell.Tests.Util+++--------------------------------------------------------------------------------+tests :: Test+tests = testGroup "StylishHaskell.Step.LanguagePragmas.Tests"+ [ case01+ , case02+ , case03+ , case04+ ]+++--------------------------------------------------------------------------------+case01 :: Test+case01 = testCase "case 01" $+ expected @=? testStep (step Vertical False) input+ where+ input = unlines+ [ "{-# LANGUAGE ViewPatterns #-}"+ , "{-# LANGUAGE TemplateHaskell, ViewPatterns #-}"+ , "{-# LANGUAGE ScopedTypeVariables #-}"+ , "module Main where"+ ]++ expected = unlines+ [ "{-# LANGUAGE ScopedTypeVariables #-}"+ , "{-# LANGUAGE TemplateHaskell #-}"+ , "{-# LANGUAGE ViewPatterns #-}"+ , "module Main where"+ ]+++--------------------------------------------------------------------------------+case02 :: Test+case02 = testCase "case 02" $+ expected @=? testStep (step Vertical True) input+ where+ input = unlines+ [ "{-# LANGUAGE BangPatterns #-}"+ , "{-# LANGUAGE ViewPatterns #-}"+ , "increment ((+ 1) -> x) = x"+ ]++ expected = unlines+ [ "{-# LANGUAGE ViewPatterns #-}"+ , "increment ((+ 1) -> x) = x"+ ]+++--------------------------------------------------------------------------------+case03 :: Test+case03 = testCase "case 03" $+ expected @=? testStep (step Vertical True) input+ where+ input = unlines+ [ "{-# LANGUAGE BangPatterns #-}"+ , "{-# LANGUAGE ViewPatterns #-}"+ , "increment x = case x of !_ -> x + 1"+ ]++ expected = unlines+ [ "{-# LANGUAGE BangPatterns #-}"+ , "increment x = case x of !_ -> x + 1"+ ]+++--------------------------------------------------------------------------------+case04 :: Test+case04 = testCase "case 04" $+ expected @=? testStep (step Compact False) input+ where+ input = unlines+ [ "{-# LANGUAGE TypeOperators, StandaloneDeriving, DeriveDataTypeable,"+ , " TemplateHaskell #-}"+ , "{-# LANGUAGE TemplateHaskell, ViewPatterns #-}"+ ]++ expected = unlines+ [ "{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving, " +++ "TemplateHaskell,"+ , " TypeOperators, ViewPatterns #-}"+ ]
+ tests/StylishHaskell/Step/Tabs/Tests.hs view
@@ -0,0 +1,42 @@+--------------------------------------------------------------------------------+module StylishHaskell.Step.Tabs.Tests+ ( tests+ ) where+++--------------------------------------------------------------------------------+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)+import Test.HUnit ((@=?))+++--------------------------------------------------------------------------------+import StylishHaskell.Step.Tabs+import StylishHaskell.Tests.Util+++--------------------------------------------------------------------------------+tests :: Test+tests = testGroup "StylishHaskell.Step.Tabs.Tests" [case01]+++--------------------------------------------------------------------------------+case01 :: Test+case01 = testCase "case 01" $ expected @=? testStep (step 4) input+ where+ input = unlines+ [ "module Main"+ , "\t\twhere"+ , "data Foo"+ , "\t= Bar"+ , " | Qux"+ ]++ expected = unlines+ [ "module Main"+ , " where"+ , "data Foo"+ , " = Bar"+ , " | Qux"+ ]+
+ tests/StylishHaskell/Step/TrailingWhitespace/Tests.hs view
@@ -0,0 +1,37 @@+--------------------------------------------------------------------------------+module StylishHaskell.Step.TrailingWhitespace.Tests+ ( tests+ ) where+++--------------------------------------------------------------------------------+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)+import Test.HUnit ((@=?))+++--------------------------------------------------------------------------------+import StylishHaskell.Step.TrailingWhitespace+import StylishHaskell.Tests.Util+++--------------------------------------------------------------------------------+tests :: Test+tests = testGroup "StylishHaskell.Step.TrailingWhitespace.Tests" [case01]+++--------------------------------------------------------------------------------+case01 :: Test+case01 = testCase "case 01" $ expected @=? testStep step input+ where+ input = unlines+ [ "module Main where"+ , " "+ , "data Foo = Bar | Qux\t "+ ]++ expected = unlines+ [ "module Main where"+ , ""+ , "data Foo = Bar | Qux"+ ]
+ tests/StylishHaskell/Step/UnicodeSyntax/Tests.hs view
@@ -0,0 +1,38 @@+--------------------------------------------------------------------------------+module StylishHaskell.Step.UnicodeSyntax.Tests+ ( tests+ ) where+++--------------------------------------------------------------------------------+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)+import Test.HUnit ((@=?))+++--------------------------------------------------------------------------------+import StylishHaskell.Step.UnicodeSyntax+import StylishHaskell.Tests.Util+++--------------------------------------------------------------------------------+tests :: Test+tests = testGroup "StylishHaskell.Step.UnicodeSyntax.Tests"+ [ case01+ ]+++--------------------------------------------------------------------------------+case01 :: Test+case01 = testCase "case 01" $ expected @=? testStep (step True) input+ where+ input = unlines+ [ "sort :: Ord a => [a] -> [a]"+ , "sort _ = []"+ ]++ expected = unlines+ [ "{-# LANGUAGE UnicodeSyntax #-}"+ , "sort ∷ Ord a ⇒ [a] → [a]"+ , "sort _ = []"+ ]
− tests/StylishHaskell/Tabs/Tests.hs
@@ -1,42 +0,0 @@----------------------------------------------------------------------------------module StylishHaskell.Tabs.Tests- ( tests- ) where------------------------------------------------------------------------------------import Test.Framework (Test, testGroup)-import Test.Framework.Providers.HUnit (testCase)-import Test.HUnit ((@=?))------------------------------------------------------------------------------------import StylishHaskell.Tabs-import StylishHaskell.Tests.Util------------------------------------------------------------------------------------tests :: Test-tests = testGroup "StylishHaskell.Tabs.Tests" [case01]------------------------------------------------------------------------------------case01 :: Test-case01 = testCase "case 01" $ expected @=? testStylish stylish input- where- input = unlines- [ "module Main"- , "\t\twhere"- , "data Foo"- , "\t= Bar"- , " | Qux"- ]-- expected = unlines- [ "module Main"- , " where"- , "data Foo"- , " = Bar"- , " | Qux"- ]-
tests/StylishHaskell/Tests/Util.hs view
@@ -1,17 +1,17 @@ module StylishHaskell.Tests.Util- ( testStylish+ ( testStep ) where -------------------------------------------------------------------------------- import StylishHaskell.Parse-import StylishHaskell.Stylish+import StylishHaskell.Step ---------------------------------------------------------------------------------testStylish :: Stylish -> String -> String-testStylish stylish str = case parseModule Nothing str of+testStep :: Step -> String -> String+testStep step str = case parseModule Nothing str of Left err -> error err- Right module' -> unlines $ stylish ls module'+ Right module' -> unlines $ stepFilter step ls module' where ls = lines str
− tests/StylishHaskell/TrailingWhitespace/Tests.hs
@@ -1,37 +0,0 @@----------------------------------------------------------------------------------module StylishHaskell.TrailingWhitespace.Tests- ( tests- ) where------------------------------------------------------------------------------------import Test.Framework (Test, testGroup)-import Test.Framework.Providers.HUnit (testCase)-import Test.HUnit ((@=?))------------------------------------------------------------------------------------import StylishHaskell.TrailingWhitespace-import StylishHaskell.Tests.Util------------------------------------------------------------------------------------tests :: Test-tests = testGroup "StylishHaskell.TrailingWhitespace.Tests" [case01]------------------------------------------------------------------------------------case01 :: Test-case01 = testCase "case 01" $ expected @=? testStylish stylish input- where- input = unlines- [ "module Main where"- , " "- , "data Foo = Bar | Qux\t "- ]-- expected = unlines- [ "module Main where"- , ""- , "data Foo = Bar | Qux"- ]
tests/TestSuite.hs view
@@ -5,21 +5,23 @@ ---------------------------------------------------------------------------------import Test.Framework (defaultMain)+import Test.Framework (defaultMain) ---------------------------------------------------------------------------------import qualified StylishHaskell.Imports.Tests-import qualified StylishHaskell.LanguagePragmas.Tests-import qualified StylishHaskell.Tabs.Tests-import qualified StylishHaskell.TrailingWhitespace.Tests+import qualified StylishHaskell.Step.Imports.Tests+import qualified StylishHaskell.Step.LanguagePragmas.Tests+import qualified StylishHaskell.Step.Tabs.Tests+import qualified StylishHaskell.Step.TrailingWhitespace.Tests+import qualified StylishHaskell.Step.UnicodeSyntax.Tests -------------------------------------------------------------------------------- main :: IO () main = defaultMain- [ StylishHaskell.Imports.Tests.tests- , StylishHaskell.LanguagePragmas.Tests.tests- , StylishHaskell.Tabs.Tests.tests- , StylishHaskell.TrailingWhitespace.Tests.tests+ [ StylishHaskell.Step.Imports.Tests.tests+ , StylishHaskell.Step.LanguagePragmas.Tests.tests+ , StylishHaskell.Step.Tabs.Tests.tests+ , StylishHaskell.Step.TrailingWhitespace.Tests.tests+ , StylishHaskell.Step.UnicodeSyntax.Tests.tests ]