stylish-haskell 0.5.14.4 → 0.5.15.0
raw patch · 33 files changed
+1877/−1297 lines, 33 filesdep ~base
Dependency ranges changed: base
Files
- CHANGELOG +3/−0
- data/stylish-haskell.yaml +77/−0
- lib/Language/Haskell/Stylish.hs +92/−0
- lib/Language/Haskell/Stylish/Block.hs +91/−0
- lib/Language/Haskell/Stylish/Config.hs +229/−0
- lib/Language/Haskell/Stylish/Editor.hs +101/−0
- lib/Language/Haskell/Stylish/Parse.hs +74/−0
- lib/Language/Haskell/Stylish/Step.hs +32/−0
- lib/Language/Haskell/Stylish/Step/Imports.hs +275/−0
- lib/Language/Haskell/Stylish/Step/LanguagePragmas.hs +168/−0
- lib/Language/Haskell/Stylish/Step/Records.hs +79/−0
- lib/Language/Haskell/Stylish/Step/Tabs.hs +21/−0
- lib/Language/Haskell/Stylish/Step/TrailingWhitespace.hs +22/−0
- lib/Language/Haskell/Stylish/Step/UnicodeSyntax.hs +115/−0
- lib/Language/Haskell/Stylish/Util.hs +128/−0
- lib/Language/Haskell/Stylish/Verbose.hs +20/−0
- src/Language/Haskell/Stylish.hs +0/−92
- src/Language/Haskell/Stylish/Block.hs +0/−91
- src/Language/Haskell/Stylish/Config.hs +0/−206
- src/Language/Haskell/Stylish/Editor.hs +0/−101
- src/Language/Haskell/Stylish/Parse.hs +0/−74
- src/Language/Haskell/Stylish/Step.hs +0/−32
- src/Language/Haskell/Stylish/Step/Imports.hs +0/−172
- src/Language/Haskell/Stylish/Step/LanguagePragmas.hs +0/−158
- src/Language/Haskell/Stylish/Step/Records.hs +0/−79
- src/Language/Haskell/Stylish/Step/Tabs.hs +0/−21
- src/Language/Haskell/Stylish/Step/TrailingWhitespace.hs +0/−22
- src/Language/Haskell/Stylish/Step/UnicodeSyntax.hs +0/−115
- src/Language/Haskell/Stylish/Util.hs +0/−95
- src/Language/Haskell/Stylish/Verbose.hs +0/−20
- stylish-haskell.cabal +6/−6
- tests/Language/Haskell/Stylish/Step/Imports/Tests.hs +299/−7
- tests/Language/Haskell/Stylish/Step/LanguagePragmas/Tests.hs +45/−6
CHANGELOG view
@@ -1,3 +1,6 @@+- 0.5.15.0+ * Add new options for import list alignment (by Ondřej Janošík)+ - 0.5.14.4 * Bump `stylish-haskell` to 1.17.0
data/stylish-haskell.yaml view
@@ -33,6 +33,73 @@ # Default: global. align: global + # Folowing options affect only import list alignment.+ #+ # List align has following options:+ #+ # - after_alias: Import list is aligned with end of import including+ # 'as' and 'hiding' keywords.+ #+ # > import qualified Data.List as List (concat, foldl, foldr, head,+ # > init, last, length)+ #+ # - with_alias: Import list is aligned with start of alias or hiding.+ #+ # > import qualified Data.List as List (concat, foldl, foldr, head,+ # > init, last, length)+ #+ # - new_line: Import list starts always on new line.+ #+ # > import qualified Data.List as List+ # > (concat, foldl, foldr, head, init, last, length)+ #+ # Default: after alias+ list_align: after_alias++ # Long list align style takes effect when import is too long. This is+ # determined by 'columns' setting.+ #+ # - inline: This option will put as much specs on same line as possible.+ #+ # - new_line: Import list will start on new line.+ #+ # - new_line_multiline: Import list will start on new line when it's+ # short enough to fit to single line. Otherwise it'll be multiline.+ #+ # - multiline: One line per import list entry.+ # Type with contructor list acts like single import.+ #+ # > import qualified Data.Map as M+ # > ( empty+ # > , singleton+ # > , ...+ # > , delete+ # > )+ #+ # Default: inline+ long_list_align: inline++ # List padding determines indentation of import list on lines after import.+ # This option affects 'list_align' and 'long_list_align'.+ list_padding: 4++ # Separate lists option affects formating of import list for type+ # or class. The only difference is single space between type and list+ # of constructors, selectors and class functions.+ #+ # - true: There is single space between Foldable type and list of it's+ # functions.+ #+ # > import Data.Foldable (Foldable (fold, foldl, foldMap))+ #+ # - false: There is no space between Foldable type and list of it's+ # functions.+ #+ # > import Data.Foldable (Foldable(fold, foldl, foldMap))+ #+ # Default: true+ separate_lists: true+ # Language pragmas - language_pragmas: # We can generate different styles of language pragma lists.@@ -46,6 +113,16 @@ # # Default: vertical. style: vertical++ # Align affects alignment of closing pragma brackets.+ #+ # - true: Brackets are aligned in same collumn.+ #+ # - false: Brackets are not aligned together. There is only one space+ # between actual import and closing bracket.+ #+ # Default: true+ align: true # stylish-haskell can detect redundancy of some language pragmas. If this # is set to true, it will remove those redundant pragmas. Default: true.
+ lib/Language/Haskell/Stylish.hs view
@@ -0,0 +1,92 @@+--------------------------------------------------------------------------------+module Language.Haskell.Stylish+ ( -- * Run+ runSteps+ -- * Steps+ , imports+ , languagePragmas+ , records+ , tabs+ , trailingWhitespace+ , unicodeSyntax+ -- ** Data types+ , Imports.Align (..)+ , LanguagePragmas.Style (..)+ -- ** Helpers+ , stepName+ -- * Config+ , module Language.Haskell.Stylish.Config+ -- * Misc+ , module Language.Haskell.Stylish.Verbose+ , version+ , Lines+ , Step+ ) where+++--------------------------------------------------------------------------------+import Control.Monad (foldM)+++--------------------------------------------------------------------------------+import Language.Haskell.Stylish.Config+import Language.Haskell.Stylish.Parse+import Language.Haskell.Stylish.Step+import qualified Language.Haskell.Stylish.Step.Imports as Imports+import qualified Language.Haskell.Stylish.Step.LanguagePragmas as LanguagePragmas+import qualified Language.Haskell.Stylish.Step.Records as Records+import qualified Language.Haskell.Stylish.Step.Tabs as Tabs+import qualified Language.Haskell.Stylish.Step.TrailingWhitespace as TrailingWhitespace+import qualified Language.Haskell.Stylish.Step.UnicodeSyntax as UnicodeSyntax+import Language.Haskell.Stylish.Verbose+import Paths_stylish_haskell (version)+++--------------------------------------------------------------------------------+imports :: Int -- ^ columns+ -> Imports.Align+ -> Step+imports = Imports.step+++--------------------------------------------------------------------------------+languagePragmas :: Int -- ^ columns+ -> LanguagePragmas.Style+ -> Bool -- ^ Pad to same length in vertical mode?+ -> Bool -- ^ remove redundant?+ -> Step+languagePragmas = LanguagePragmas.step+++--------------------------------------------------------------------------------+records :: Step+records = Records.step+++--------------------------------------------------------------------------------+tabs :: Int -- ^ number of spaces+ -> Step+tabs = Tabs.step+++--------------------------------------------------------------------------------+trailingWhitespace :: Step+trailingWhitespace = TrailingWhitespace.step+++--------------------------------------------------------------------------------+unicodeSyntax :: Bool -- ^ add language pragma?+ -> Step+unicodeSyntax = UnicodeSyntax.step+++--------------------------------------------------------------------------------+runStep :: Extensions -> Maybe FilePath -> Lines -> Step -> Either String Lines+runStep exts mfp ls step =+ stepFilter step ls <$> parseModule exts mfp (unlines ls)+++--------------------------------------------------------------------------------+runSteps :: Extensions -> Maybe FilePath -> [Step] -> Lines+ -> Either String Lines+runSteps exts mfp steps ls = foldM (runStep exts mfp) ls steps
+ lib/Language/Haskell/Stylish/Block.hs view
@@ -0,0 +1,91 @@+--------------------------------------------------------------------------------+module Language.Haskell.Stylish.Block+ ( Block (..)+ , LineBlock+ , SpanBlock+ , blockLength+ , linesFromSrcSpan+ , spanFromSrcSpan+ , moveBlock+ , adjacent+ , merge+ , overlapping+ , groupAdjacent+ ) where+++--------------------------------------------------------------------------------+import Control.Arrow (arr, (&&&), (>>>))+import qualified Language.Haskell.Exts.Annotated as H+++--------------------------------------------------------------------------------+-- | Indicates a line span+data Block a = Block+ { blockStart :: Int+ , blockEnd :: Int+ } deriving (Eq, Ord, Show)+++--------------------------------------------------------------------------------+type LineBlock = Block String+++--------------------------------------------------------------------------------+type SpanBlock = Block Char+++--------------------------------------------------------------------------------+blockLength :: Block a -> Int+blockLength (Block start end) = end - start + 1+++--------------------------------------------------------------------------------+linesFromSrcSpan :: H.SrcSpanInfo -> LineBlock+linesFromSrcSpan = H.srcInfoSpan >>>+ H.srcSpanStartLine &&& H.srcSpanEndLine >>>+ arr (uncurry 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 a -> Block a -> Bool+adjacent b1 b2 = follows b1 b2 || follows b2 b1+ where+ follows (Block _ e1) (Block s2 _) = e1 + 1 == s2+++--------------------------------------------------------------------------------+merge :: Block a -> Block a -> Block a+merge (Block s1 e1) (Block s2 e2) = Block (min s1 s2) (max e1 e2)+++--------------------------------------------------------------------------------+overlapping :: [Block a] -> Bool+overlapping blocks =+ any (uncurry overlapping') $ zip blocks (drop 1 blocks)+ where+ overlapping' (Block _ e1) (Block s2 _) = e1 >= s2+++--------------------------------------------------------------------------------+-- | Groups adjacent blocks into larger blocks+groupAdjacent :: [(Block a, b)]+ -> [(Block a, [b])]+groupAdjacent = foldr go []+ where+ -- This code is ugly and not optimal, and no fucks were given.+ go (b1, x) gs = case break (adjacent b1 . fst) gs of+ (_, []) -> (b1, [x]) : gs+ (ys, ((b2, xs) : zs)) -> (merge b1 b2, x : xs) : (ys ++ zs)
+ lib/Language/Haskell/Stylish/Config.hs view
@@ -0,0 +1,229 @@+--------------------------------------------------------------------------------+{-# LANGUAGE OverloadedStrings #-}+module Language.Haskell.Stylish.Config+ ( Extensions+ , Config (..)+ , defaultConfigFilePath+ , configFilePath+ , loadConfig+ ) where+++--------------------------------------------------------------------------------+import Control.Monad (forM, 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 (inits,+ intercalate)+import Data.Map (Map)+import qualified Data.Map as M+import Data.Yaml (decodeEither)+import System.Directory+import System.FilePath (joinPath,+ splitPath,+ (</>))+++--------------------------------------------------------------------------------+import Language.Haskell.Stylish.Step+import qualified Language.Haskell.Stylish.Step.Imports as Imports+import qualified Language.Haskell.Stylish.Step.LanguagePragmas as LanguagePragmas+import qualified Language.Haskell.Stylish.Step.Records as Records+import qualified Language.Haskell.Stylish.Step.Tabs as Tabs+import qualified Language.Haskell.Stylish.Step.TrailingWhitespace as TrailingWhitespace+import qualified Language.Haskell.Stylish.Step.UnicodeSyntax as UnicodeSyntax+import Language.Haskell.Stylish.Verbose+import Paths_stylish_haskell (getDataFileName)+++--------------------------------------------------------------------------------+type Extensions = [String]+++--------------------------------------------------------------------------------+data Config = Config+ { configSteps :: [Step]+ , configColumns :: Int+ , configLanguageExtensions :: [String]+ }+++--------------------------------------------------------------------------------+instance FromJSON Config where+ parseJSON = parseConfig+++--------------------------------------------------------------------------------+emptyConfig :: Config+emptyConfig = Config [] 80 []+++--------------------------------------------------------------------------------+configFileName :: String+configFileName = ".stylish-haskell.yaml"+++--------------------------------------------------------------------------------+defaultConfigFilePath :: IO FilePath+defaultConfigFilePath = getDataFileName "data/stylish-haskell.yaml"+++--------------------------------------------------------------------------------+configFilePath :: Verbose -> Maybe FilePath -> IO (Maybe FilePath)+configFilePath _ (Just userSpecified) = return $ Just userSpecified+configFilePath verbose Nothing = do+ current <- getCurrentDirectory+ home <- getHomeDirectory+ def <- defaultConfigFilePath+ search $+ [d </> configFileName | d <- ancestors current] +++ [home </> configFileName, def]+ where+ -- All ancestors of a dir (including that dir)+ ancestors :: FilePath -> [FilePath]+ ancestors = init . map joinPath . reverse . inits . splitPath++ search :: [FilePath] -> IO (Maybe FilePath)+ search [] = return Nothing+ search (f : fs) = do+ -- TODO Maybe catch an error here, dir might be unreadable+ exists <- doesFileExist f+ verbose $ f ++ if exists then " exists" else " does not exist"+ if exists then return (Just f) else search fs+++--------------------------------------------------------------------------------+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 $+ "Language.Haskell.Stylish.Config.loadConfig: " ++ err+ Right config -> return config+++--------------------------------------------------------------------------------+parseConfig :: A.Value -> A.Parser Config+parseConfig (A.Object o) = do+ -- First load the config without the actual steps+ config <- Config+ <$> pure []+ <*> (o A..:? "columns" A..!= 80)+ <*> (o A..:? "language_extensions" A..!= [])++ -- Then fill in the steps based on the partial config we already have+ stepValues <- o A..: "steps" :: A.Parser [A.Value]+ steps <- mapM (parseSteps config) stepValues+ return config {configSteps = concat steps}+parseConfig _ = mzero+++--------------------------------------------------------------------------------+catalog :: Map String (Config -> A.Object -> A.Parser Step)+catalog = M.fromList+ [ ("imports", parseImports)+ , ("language_pragmas", parseLanguagePragmas)+ , ("records", parseRecords)+ , ("tabs", parseTabs)+ , ("trailing_whitespace", parseTrailingWhitespace)+ , ("unicode_syntax", parseUnicodeSyntax)+ ]+++--------------------------------------------------------------------------------+parseSteps :: Config -> A.Value -> A.Parser [Step]+parseSteps config 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 config 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 :: Config -> A.Object -> A.Parser Step+parseImports config o = Imports.step+ <$> pure (configColumns config)+ <*> (Imports.Align+ <$> (o A..:? "align" >>= parseEnum aligns Imports.Global)+ <*> (o A..:? "list_align" >>= parseEnum listAligns Imports.AfterAlias)+ <*> (o A..:? "long_list_align"+ >>= parseEnum longListAligns Imports.Inline)+ <*> (maybe 4 (max 1) <$> o A..:? "list_padding")+ -- ^ Padding have to be at least 1. Default is 4.+ <*> o A..:? "separate_lists" A..!= True)+ where+ aligns =+ [ ("global", Imports.Global)+ , ("file", Imports.File)+ , ("group", Imports.Group)+ , ("none", Imports.None)+ ]++ listAligns =+ [ ("new_line", Imports.NewLine)+ , ("with_alias", Imports.WithAlias)+ , ("after_alias", Imports.AfterAlias)+ ]++ longListAligns =+ [ ("inline", Imports.Inline)+ , ("new_line", Imports.InlineWithBreak)+ , ("new_line_multiline", Imports.InlineToMultiline)+ , ("multiline", Imports.Multiline)+ ]+++--------------------------------------------------------------------------------+parseLanguagePragmas :: Config -> A.Object -> A.Parser Step+parseLanguagePragmas config o = LanguagePragmas.step+ <$> pure (configColumns config)+ <*> (o A..:? "style" >>= parseEnum styles LanguagePragmas.Vertical)+ <*> o A..:? "align" A..!= True+ <*> o A..:? "remove_redundant" A..!= True+ where+ styles =+ [ ("vertical", LanguagePragmas.Vertical)+ , ("compact", LanguagePragmas.Compact)+ , ("compact_line", LanguagePragmas.CompactLine)+ ]+++--------------------------------------------------------------------------------+parseRecords :: Config -> A.Object -> A.Parser Step+parseRecords _ _ = return Records.step+++--------------------------------------------------------------------------------+parseTabs :: Config -> A.Object -> A.Parser Step+parseTabs _ o = Tabs.step+ <$> o A..:? "spaces" A..!= 8+++--------------------------------------------------------------------------------+parseTrailingWhitespace :: Config -> A.Object -> A.Parser Step+parseTrailingWhitespace _ _ = return TrailingWhitespace.step+++--------------------------------------------------------------------------------+parseUnicodeSyntax :: Config -> A.Object -> A.Parser Step+parseUnicodeSyntax _ o = UnicodeSyntax.step+ <$> o A..:? "add_language_pragma" A..!= True
+ lib/Language/Haskell/Stylish/Editor.hs view
@@ -0,0 +1,101 @@+--------------------------------------------------------------------------------+-- | This module provides you with a line-based editor. It's main feature is+-- that you can specify multiple changes at the same time, e.g.:+--+-- > [deleteLine 3, changeLine 4 ["Foo"]]+--+-- when this is evaluated, we take into account that 4th line will become the+-- 3rd line before it needs changing.+module Language.Haskell.Stylish.Editor+ ( Change+ , applyChanges++ , change+ , changeLine+ , delete+ , deleteLine+ , insert+ ) where+++--------------------------------------------------------------------------------+import Language.Haskell.Stylish.Block+++--------------------------------------------------------------------------------+-- | Changes the lines indicated by the 'Block' into the given 'Lines'+data Change a = Change+ { changeBlock :: Block a+ , changeLines :: ([a] -> [a])+ }+++--------------------------------------------------------------------------------+moveChange :: Int -> Change a -> Change a+moveChange offset (Change block ls) = Change (moveBlock offset block) ls+++--------------------------------------------------------------------------------+applyChanges :: [Change a] -> [a] -> [a]+applyChanges changes+ | overlapping blocks = error $+ "Language.Haskell.Stylish.Editor.applyChanges: " +++ "refusing to make overlapping changes"+ | otherwise = go 1 changes+ where+ blocks = map changeBlock changes++ go _ [] ls = ls+ go n (ch : chs) ls =+ -- Divide the remaining lines into:+ --+ -- > pre+ -- > old (lines that are affected by the change)+ -- > post+ --+ -- And generate:+ --+ -- > pre+ -- > new+ -- > (recurse)+ --+ let block = changeBlock ch+ (pre, ls') = splitAt (blockStart block - n) ls+ (old, post) = splitAt (blockLength block) ls'+ new = changeLines ch old+ extraLines = length new - blockLength block+ chs' = map (moveChange extraLines) chs+ n' = blockStart block + blockLength block + extraLines+ in pre ++ new ++ go n' chs' post+++--------------------------------------------------------------------------------+-- | Change a block of lines for some other lines+change :: Block a -> ([a] -> [a]) -> Change a+change = Change+++--------------------------------------------------------------------------------+-- | Change a single line for some other lines+changeLine :: Int -> (a -> [a]) -> Change a+changeLine start f = change (Block start start) $ \xs -> case xs of+ [] -> []+ (x : _) -> f x+++--------------------------------------------------------------------------------+-- | Delete a block of lines+delete :: Block a -> Change a+delete block = Change block $ const []+++--------------------------------------------------------------------------------+-- | Delete a single line+deleteLine :: Int -> Change a+deleteLine start = delete (Block start start)+++--------------------------------------------------------------------------------+-- | Insert something /before/ the given lines+insert :: Int -> [a] -> Change a+insert start = Change (Block start (start - 1)) . const
+ lib/Language/Haskell/Stylish/Parse.hs view
@@ -0,0 +1,74 @@+--------------------------------------------------------------------------------+module Language.Haskell.Stylish.Parse+ ( parseModule+ ) where+++--------------------------------------------------------------------------------+import Data.Maybe (fromMaybe, listToMaybe)+import qualified Language.Haskell.Exts.Annotated as H+import Data.List (isPrefixOf)+++--------------------------------------------------------------------------------+import Language.Haskell.Stylish.Config+import Language.Haskell.Stylish.Step+++--------------------------------------------------------------------------------+-- | Filter out lines which use CPP macros+unCpp :: String -> String+unCpp = unlines . go False . lines+ where+ go _ [] = []+ go isMultiline (x : xs) =+ let isCpp = isMultiline || listToMaybe x == Just '#'+ nextMultiline = isCpp && not (null x) && last x == '\\'+ in (if isCpp then "" else x) : go nextMultiline xs+++--------------------------------------------------------------------------------+-- | Remove shebang from the first line+unShebang :: String -> String+unShebang str+ | "#!" `isPrefixOf` str = unlines $ ("" :) $ drop 1 $ lines str+ | otherwise = str+++--------------------------------------------------------------------------------+-- | If the given string is prefixed with an UTF-8 Byte Order Mark, drop it+-- because haskell-src-exts can't handle it.+dropBom :: String -> String+dropBom ('\xfeff' : str) = str+dropBom str = str+++--------------------------------------------------------------------------------+-- | Abstraction over HSE's parsing+parseModule :: Extensions -> Maybe FilePath -> String -> Either String Module+parseModule extraExts mfp string = do+ -- Determine the extensions: those specified in the file and the extra ones+ let noBom = dropBom string+ extraExts' = map H.classifyExtension extraExts+ (lang, fileExts) = fromMaybe (Nothing, []) $ H.readExtensions noBom+ exts = fileExts ++ extraExts'++ -- Parsing options...+ fp = fromMaybe "<unknown>" mfp+ mode = H.defaultParseMode+ { H.extensions = exts+ , H.fixities = Nothing+ , H.baseLanguage = case lang of+ Nothing -> H.baseLanguage H.defaultParseMode+ Just l -> l+ }++ -- Preprocessing+ processed = unShebang $+ if H.EnableExtension H.CPP `elem` exts then unCpp noBom else noBom++ case H.parseModuleWithComments mode processed of+ H.ParseOk md -> return md+ err -> Left $+ "Language.Haskell.Stylish.Parse.parseModule: could not parse " +++ fp ++ ": " ++ show err
+ lib/Language/Haskell/Stylish/Step.hs view
@@ -0,0 +1,32 @@+--------------------------------------------------------------------------------+module Language.Haskell.Stylish.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
+ lib/Language/Haskell/Stylish/Step/Imports.hs view
@@ -0,0 +1,275 @@+{-# LANGUAGE RecordWildCards #-}+--------------------------------------------------------------------------------+module Language.Haskell.Stylish.Step.Imports+ ( Align (..)+ , ImportAlign (..)+ , ListAlign (..)+ , LongListAlign (..)+ , step+ ) where+++--------------------------------------------------------------------------------+import Control.Arrow ((&&&))+import Data.Char (toLower)+import Data.List (intercalate, sortBy)+import Data.Maybe (isJust, maybeToList)+import Data.Ord (comparing)+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 Align = Align+ { importAlign :: ImportAlign+ , listAlign :: ListAlign+ , longListAlign :: LongListAlign+ , listPadding :: Int+ , separateLists :: Bool+ }+ deriving (Eq, Show)++data ImportAlign+ = Global+ | File+ | Group+ | None+ deriving (Eq, Show)++data ListAlign+ = NewLine+ | WithAlias+ | AfterAlias+ deriving (Eq, Show)++data LongListAlign+ = Inline+ | InlineWithBreak+ | InlineToMultiline+ | Multiline+ 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)+++--------------------------------------------------------------------------------+-- | Compare imports for ordering+compareImports :: H.ImportDecl l -> H.ImportDecl l -> Ordering+compareImports = comparing (map toLower . 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, Bool, String)+ key (H.IVar _ x) = (1, isOperator x, nameToString x)+ key (H.IAbs _ _ x) = (0, False, nameToString x)+ key (H.IThingAll _ x) = (0, False, nameToString x)+ key (H.IThingWith _ x _) = (0, False, nameToString x)+++--------------------------------------------------------------------------------+-- | Sort the input spec list inside an 'H.ImportDecl'+sortImportSpecs :: H.ImportDecl l -> H.ImportDecl l+sortImportSpecs imp = imp {H.importSpecs = sort' <$> H.importSpecs imp}+ where+ sort' (H.ImportSpecList l h specs) = H.ImportSpecList l h $+ sortBy compareImportSpecs specs+++--------------------------------------------------------------------------------+-- | Order of imports in sublist is:+-- Constructors, accessors/methods, operators.+compareImportSubSpecs :: H.CName l -> H.CName l -> Ordering+compareImportSubSpecs = comparing key+ where+ key :: H.CName l -> (Int, Bool, String)+ key (H.ConName _ x) = (0, False, nameToString x)+ key (H.VarName _ x) = (1, isOperator x, nameToString x)+++--------------------------------------------------------------------------------+-- | By default, haskell-src-exts pretty-prints+--+-- > import Foo (Bar(..))+--+-- but we want+--+-- > import Foo (Bar (..))+--+-- instead.+prettyImportSpec :: (Ord l) => Bool -> H.ImportSpec l -> String+prettyImportSpec separate = prettyImportSpec'+ where+ prettyImportSpec' (H.IThingAll _ n) = H.prettyPrint n ++ sep "(..)"+ prettyImportSpec' (H.IThingWith _ n cns) = H.prettyPrint n+ ++ sep "("+ ++ intercalate ", "+ (map H.prettyPrint $ sortBy compareImportSubSpecs cns)+ ++ ")"+ prettyImportSpec' x = H.prettyPrint x++ sep = if separate then (' ' :) else id+++--------------------------------------------------------------------------------+prettyImport :: (Ord l, Show l) =>+ Int -> Align -> Bool -> Bool -> Int -> H.ImportDecl l -> [String]+prettyImport columns Align{..} padQualified padName longest imp =+ case longListAlign of+ Inline -> inlineWrap+ InlineWithBreak -> longListWrapper inlineWrap inlineWithBreakWrap+ InlineToMultiline -> longListWrapper inlineWrap inlineToMultilineWrap+ Multiline -> longListWrapper inlineWrap multilineWrap+ where+ longListWrapper shortWrap longWrap+ | listAlign == NewLine+ || length shortWrap > 1+ || length (head shortWrap) > columns+ = longWrap+ | otherwise = shortWrap++ inlineWrap = inlineWrapper+ $ mapSpecs+ $ withInit (++ ",")+ . withHead ("(" ++)+ . withLast (++ ")")++ inlineWrapper = case listAlign of+ NewLine -> (paddedNoSpecBase :) . wrapRest columns listPadding+ WithAlias -> wrap columns paddedBase (inlineBaseLength + 1)+ -- Add 1 extra space to ensure same padding as in original code.+ AfterAlias -> withTail (' ' :)+ . wrap columns paddedBase (afterAliasBaseLength + 1)++ inlineWithBreakWrap = paddedNoSpecBase : wrapRest columns listPadding+ ( mapSpecs+ $ withInit (++ ",")+ . withHead ("(" ++)+ . withLast (++ ")"))++ inlineToMultilineWrap+ | length inlineWithBreakWrap > 2+ || any ((> columns) . length) (tail inlineWithBreakWrap)+ = multilineWrap+ | otherwise = inlineWithBreakWrap++ -- 'wrapRest 0' ensures that every item of spec list is on new line.+ multilineWrap = paddedNoSpecBase : wrapRest 0 listPadding+ ( mapSpecs+ ( withHead ("( " ++)+ . withTail (", " ++))+ ++ [")"])++ paddedBase = base $ padImport $ importName imp++ paddedNoSpecBase = base $ padImportNoSpec $ importName imp++ padImport = if hasExtras && padName+ then padRight longest+ else id++ padImportNoSpec = if (isJust (H.importAs imp) || hasHiding) && padName+ then padRight longest+ else id++ base' baseName importAs hasHiding' = unwords $ concat $ filter (not . null)+ [ ["import"]+ , qualified+ , show <$> maybeToList (H.importPkg imp)+ , [baseName]+ , importAs+ , hasHiding'+ ]++ base baseName = base' baseName+ ["as " ++ as | H.ModuleName _ as <- maybeToList $ H.importAs imp]+ ["hiding" | hasHiding]++ inlineBaseLength = length $ base' (padImport $ importName imp) [] []++ afterAliasBaseLength = length $ base' (padImport $ importName imp)+ ["as " ++ as | H.ModuleName _ as <- maybeToList $ H.importAs imp] []++ (hasHiding, importSpecs) = case H.importSpecs imp of+ Just (H.ImportSpecList _ h l) -> (h, Just l)+ _ -> (False, Nothing)++ hasExtras = isJust (H.importAs imp) || isJust (H.importSpecs imp)++ qualified+ | H.importQualified imp = ["qualified"]+ | padQualified = [" "]+ | otherwise = []++ mapSpecs f = case importSpecs of+ Nothing -> [] -- Import everything+ Just [] -> ["()"] -- Instance only imports+ Just is -> f $ map (prettyImportSpec separateLists) is+++--------------------------------------------------------------------------------+prettyImportGroup :: Int -> Align -> Bool -> Int+ -> [H.ImportDecl LineBlock]+ -> Lines+prettyImportGroup columns align fileAlign longest imps =+ concatMap (prettyImport columns align padQual padName longest') $+ sortBy compareImports imps+ where+ align' = importAlign align++ longest' = case align' of+ Group -> longestImport imps+ _ -> longest++ padName = align' /= None++ padQual = case align' of+ Global -> True+ File -> fileAlign+ Group -> any H.importQualified imps+ None -> False+++--------------------------------------------------------------------------------+step :: Int -> Align -> Step+step columns = makeStep "Imports" . step' columns+++--------------------------------------------------------------------------------+step' :: Int -> Align -> Lines -> Module -> Lines+step' columns align ls (module', _) = applyChanges+ [ change block $ const $+ prettyImportGroup columns align fileAlign longest importGroup+ | (block, importGroup) <- groups+ ]+ ls+ where+ imps = map sortImportSpecs $ imports $ fmap linesFromSrcSpan module'+ longest = longestImport imps+ groups = groupAdjacent [(H.ann i, i) | i <- imps]++ fileAlign = case importAlign align of+ File -> any H.importQualified imps+ _ -> False
+ lib/Language/Haskell/Stylish/Step/LanguagePragmas.hs view
@@ -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]]
+ lib/Language/Haskell/Stylish/Step/Records.hs view
@@ -0,0 +1,79 @@+--------------------------------------------------------------------------------+module Language.Haskell.Stylish.Step.Records+ ( step+ ) where+++--------------------------------------------------------------------------------+import Data.Char (isSpace)+import Data.List (nub)+import qualified Language.Haskell.Exts.Annotated as H+++--------------------------------------------------------------------------------+import Language.Haskell.Stylish.Editor+import Language.Haskell.Stylish.Step+import Language.Haskell.Stylish.Util+++--------------------------------------------------------------------------------+records :: H.Module l -> [[Alignable l]]+records modu =+ [ map fieldDeclToAlignable fields+ | H.Module _ _ _ _ decls <- [modu]+ , H.DataDecl _ _ _ _ cons _ <- decls+ , H.QualConDecl _ _ _ (H.RecDecl _ _ fields) <- cons+ ]+++--------------------------------------------------------------------------------+data Alignable a = Alignable+ { aContainer :: !a+ , aLeft :: !a+ , aRight :: !a+ } deriving (Show)+++--------------------------------------------------------------------------------+fieldDeclToAlignable :: H.FieldDecl a -> Alignable a+fieldDeclToAlignable (H.FieldDecl ann names ty) = Alignable+ { aContainer = ann+ , aLeft = H.ann (last names)+ , aRight = H.ann ty+ }+++--------------------------------------------------------------------------------+-- | Align the type of a field+align :: [Alignable H.SrcSpan] -> [Change String]+align alignment = map align' alignment+ where+ longest = maximum $ map (H.srcSpanEndColumn . aLeft) alignment++ align' a = changeLine (H.srcSpanStartLine $ aContainer a) $ \str ->+ let column = H.srcSpanEndColumn $ aLeft a+ (pre, post) = splitAt column str+ in [padRight longest (trimRight pre) ++ trimLeft post]++ trimLeft = dropWhile isSpace+ trimRight = reverse . trimLeft . reverse+++--------------------------------------------------------------------------------+-- | Checks that all no field of the record appears on more than one line,+-- amonst other things+fixable :: [Alignable H.SrcSpan] -> Bool+fixable [] = False+fixable fields = all singleLine containers && nonOverlapping containers+ where+ containers = map aContainer fields+ singleLine s = H.srcSpanStartLine s == H.srcSpanEndLine s+ nonOverlapping ss = length ss == length (nub $ map H.srcSpanStartLine ss)+++--------------------------------------------------------------------------------+step :: Step+step = makeStep "Records" $ \ls (module', _) ->+ let module'' = fmap H.srcInfoSpan module'+ fixableRecords = filter fixable $ records module''+ in applyChanges (fixableRecords >>= align) ls
+ lib/Language/Haskell/Stylish/Step/Tabs.hs view
@@ -0,0 +1,21 @@+--------------------------------------------------------------------------------+module Language.Haskell.Stylish.Step.Tabs+ ( step+ ) where+++--------------------------------------------------------------------------------+import Language.Haskell.Stylish.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
+ lib/Language/Haskell/Stylish/Step/TrailingWhitespace.hs view
@@ -0,0 +1,22 @@+--------------------------------------------------------------------------------+module Language.Haskell.Stylish.Step.TrailingWhitespace+ ( step+ ) where+++--------------------------------------------------------------------------------+import Data.Char (isSpace)+++--------------------------------------------------------------------------------+import Language.Haskell.Stylish.Step+++--------------------------------------------------------------------------------+dropTrailingWhitespace :: String -> String+dropTrailingWhitespace = reverse . dropWhile isSpace . reverse+++--------------------------------------------------------------------------------+step :: Step+step = makeStep "TrailingWhitespace" $ \ls _ -> map dropTrailingWhitespace ls
+ lib/Language/Haskell/Stylish/Step/UnicodeSyntax.hs view
@@ -0,0 +1,115 @@+--------------------------------------------------------------------------------+module Language.Haskell.Stylish.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 Language.Haskell.Stylish.Block+import Language.Haskell.Stylish.Editor+import Language.Haskell.Stylish.Step+import Language.Haskell.Stylish.Step.LanguagePragmas (addLanguagePragma)+import Language.Haskell.Stylish.Util+++--------------------------------------------------------------------------------+unicodeReplacements :: Map String String+unicodeReplacements = M.fromList+ [ ("::", "∷")+ , ("=>", "⇒")+ , ("->", "→")+ ]+++--------------------------------------------------------------------------------+replaceAll :: [(Int, [(Int, String)])] -> [Change String]+replaceAll = map changeLine'+ where+ changeLine' (r, ns) = changeLine r $ \str -> return $+ flip applyChanges str+ [ change (Block c ec) (const repl)+ | (c, needle) <- sort ns+ , let ec = c + length needle - 1+ , repl <- maybeToList $ M.lookup needle unicodeReplacements+ ]+++--------------------------------------------------------------------------------+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+ 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+ perLine = sort $ groupPerLine $+ typeSigs module' ls +++ contexts module' ls +++ typeFuns module' ls
+ lib/Language/Haskell/Stylish/Util.hs view
@@ -0,0 +1,128 @@+--------------------------------------------------------------------------------+module Language.Haskell.Stylish.Util+ ( nameToString+ , isOperator+ , indent+ , padRight+ , everything+ , infoPoints+ , wrap+ , wrapRest++ , withHead+ , withInit+ , withTail+ , withLast+ ) where+++--------------------------------------------------------------------------------+import Control.Arrow ((&&&), (>>>))+import Data.Char (isAlpha)+import Data.Data (Data)+import qualified Data.Generics as G+import Data.Maybe (fromMaybe, listToMaybe, maybeToList)+import Data.Typeable (cast)+import qualified Language.Haskell.Exts.Annotated as H+++--------------------------------------------------------------------------------+import Language.Haskell.Stylish.Step+++--------------------------------------------------------------------------------+nameToString :: H.Name l -> String+nameToString (H.Ident _ str) = str+nameToString (H.Symbol _ str) = str+++--------------------------------------------------------------------------------+isOperator :: H.Name l -> Bool+isOperator = fromMaybe False+ . (fmap (not . isAlpha) . listToMaybe)+ . nameToString++--------------------------------------------------------------------------------+indent :: Int -> String -> String+indent len = (indentPrefix len ++)+++--------------------------------------------------------------------------------+indentPrefix :: Int -> String+indentPrefix = (`replicate` ' ')+++--------------------------------------------------------------------------------+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 ind = wrap' leading+ where+ wrap' ss [] = [ss]+ wrap' ss (str:strs)+ | overflows ss str =+ ss : wrapRest maxWidth ind (str:strs)+ | otherwise = wrap' (ss ++ " " ++ str) strs++ overflows ss str = length ss > maxWidth ||+ ((length ss + length str) >= maxWidth && ind + length str <= maxWidth)+++--------------------------------------------------------------------------------+wrapRest :: Int+ -> Int+ -> [String]+ -> Lines+wrapRest maxWidth ind = reverse . wrapRest' [] ""+ where+ wrapRest' ls ss []+ | null ss = ls+ | otherwise = ss:ls+ wrapRest' ls ss (str:strs)+ | null ss = wrapRest' ls (indent ind str) strs+ | overflows ss str = wrapRest' (ss:ls) "" (str:strs)+ | otherwise = wrapRest' ls (ss ++ " " ++ str) strs++ overflows ss str = (length ss + length str + 1) >= maxWidth+++--------------------------------------------------------------------------------+withHead :: (a -> a) -> [a] -> [a]+withHead _ [] = []+withHead f (x : xs) = f x : xs+++--------------------------------------------------------------------------------+withLast :: (a -> a) -> [a] -> [a]+withLast _ [] = []+withLast f [x] = [f x]+withLast f (x : xs) = x : withLast f xs+++--------------------------------------------------------------------------------+withInit :: (a -> a) -> [a] -> [a]+withInit _ [] = []+withInit _ [x] = [x]+withInit f (x : xs) = f x : withInit f xs++--------------------------------------------------------------------------------+withTail :: (a -> a) -> [a] -> [a]+withTail _ [] = []+withTail f (x : xs) = x : map f xs
+ lib/Language/Haskell/Stylish/Verbose.hs view
@@ -0,0 +1,20 @@+--------------------------------------------------------------------------------+module Language.Haskell.Stylish.Verbose+ ( Verbose+ , makeVerbose+ ) where+++--------------------------------------------------------------------------------+import System.IO (hPutStrLn, stderr)+++--------------------------------------------------------------------------------+type Verbose = String -> IO ()+++--------------------------------------------------------------------------------+makeVerbose :: Bool -> Verbose+makeVerbose verbose+ | verbose = hPutStrLn stderr+ | otherwise = const $ return ()
− src/Language/Haskell/Stylish.hs
@@ -1,92 +0,0 @@----------------------------------------------------------------------------------module Language.Haskell.Stylish- ( -- * Run- runSteps- -- * Steps- , imports- , languagePragmas- , records- , tabs- , trailingWhitespace- , unicodeSyntax- -- ** Data types- , Imports.Align (..)- , LanguagePragmas.Style (..)- -- ** Helpers- , stepName- -- * Config- , module Language.Haskell.Stylish.Config- -- * Misc- , module Language.Haskell.Stylish.Verbose- , version- , Lines- , Step- ) where------------------------------------------------------------------------------------import Control.Applicative ((<$>))-import Control.Monad (foldM)------------------------------------------------------------------------------------import Language.Haskell.Stylish.Config-import Language.Haskell.Stylish.Step-import Language.Haskell.Stylish.Verbose-import Language.Haskell.Stylish.Parse-import Paths_stylish_haskell (version)-import qualified Language.Haskell.Stylish.Step.Imports as Imports-import qualified Language.Haskell.Stylish.Step.LanguagePragmas as LanguagePragmas-import qualified Language.Haskell.Stylish.Step.Records as Records-import qualified Language.Haskell.Stylish.Step.Tabs as Tabs-import qualified Language.Haskell.Stylish.Step.TrailingWhitespace as TrailingWhitespace-import qualified Language.Haskell.Stylish.Step.UnicodeSyntax as UnicodeSyntax------------------------------------------------------------------------------------imports :: Int -- ^ columns- -> Imports.Align- -> Step-imports = Imports.step------------------------------------------------------------------------------------languagePragmas :: Int -- ^ columns- -> LanguagePragmas.Style- -> Bool -- ^ remove redundant?- -> Step-languagePragmas = LanguagePragmas.step------------------------------------------------------------------------------------records :: Step-records = Records.step------------------------------------------------------------------------------------tabs :: Int -- ^ number of spaces- -> Step-tabs = Tabs.step------------------------------------------------------------------------------------trailingWhitespace :: Step-trailingWhitespace = TrailingWhitespace.step------------------------------------------------------------------------------------unicodeSyntax :: Bool -- ^ add language pragma?- -> Step-unicodeSyntax = UnicodeSyntax.step------------------------------------------------------------------------------------runStep :: Extensions -> Maybe FilePath -> Lines -> Step -> Either String Lines-runStep exts mfp ls step =- stepFilter step ls <$> parseModule exts mfp (unlines ls)------------------------------------------------------------------------------------runSteps :: Extensions -> Maybe FilePath -> [Step] -> Lines- -> Either String Lines-runSteps exts mfp steps ls = foldM (runStep exts mfp) ls steps
− src/Language/Haskell/Stylish/Block.hs
@@ -1,91 +0,0 @@----------------------------------------------------------------------------------module Language.Haskell.Stylish.Block- ( Block (..)- , LineBlock- , SpanBlock- , blockLength- , linesFromSrcSpan- , spanFromSrcSpan- , moveBlock- , adjacent- , merge- , overlapping- , groupAdjacent- ) where------------------------------------------------------------------------------------import Control.Arrow (arr, (&&&), (>>>))-import qualified Language.Haskell.Exts.Annotated as H-------------------------------------------------------------------------------------- | Indicates a line span-data Block a = Block- { blockStart :: Int- , blockEnd :: Int- } deriving (Eq, Ord, Show)------------------------------------------------------------------------------------type LineBlock = Block String------------------------------------------------------------------------------------type SpanBlock = Block Char------------------------------------------------------------------------------------blockLength :: Block a -> Int-blockLength (Block start end) = end - start + 1------------------------------------------------------------------------------------linesFromSrcSpan :: H.SrcSpanInfo -> LineBlock-linesFromSrcSpan = H.srcInfoSpan >>>- H.srcSpanStartLine &&& H.srcSpanEndLine >>>- arr (uncurry 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 a -> Block a -> Bool-adjacent b1 b2 = follows b1 b2 || follows b2 b1- where- follows (Block _ e1) (Block s2 _) = e1 + 1 == s2------------------------------------------------------------------------------------merge :: Block a -> Block a -> Block a-merge (Block s1 e1) (Block s2 e2) = Block (min s1 s2) (max e1 e2)------------------------------------------------------------------------------------overlapping :: [Block a] -> Bool-overlapping blocks =- any (uncurry overlapping') $ zip blocks (drop 1 blocks)- where- overlapping' (Block _ e1) (Block s2 _) = e1 >= s2-------------------------------------------------------------------------------------- | Groups adjacent blocks into larger blocks-groupAdjacent :: [(Block a, b)]- -> [(Block a, [b])]-groupAdjacent = foldr go []- where- -- This code is ugly and not optimal, and no fucks were given.- go (b1, x) gs = case break (adjacent b1 . fst) gs of- (_, []) -> (b1, [x]) : gs- (ys, ((b2, xs) : zs)) -> (merge b1 b2, x : xs) : (ys ++ zs)
− src/Language/Haskell/Stylish/Config.hs
@@ -1,206 +0,0 @@----------------------------------------------------------------------------------{-# LANGUAGE OverloadedStrings #-}-module Language.Haskell.Stylish.Config- ( Extensions- , Config (..)- , defaultConfigFilePath- , configFilePath- , loadConfig- ) where------------------------------------------------------------------------------------import Control.Applicative (pure, (<$>), (<*>))-import Control.Monad (forM, 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 (inits, intercalate)-import Data.Map (Map)-import qualified Data.Map as M-import Data.Yaml (decodeEither)-import System.Directory-import System.FilePath (joinPath, splitPath,- (</>))------------------------------------------------------------------------------------import Paths_stylish_haskell (getDataFileName)-import Language.Haskell.Stylish.Step-import qualified Language.Haskell.Stylish.Step.Imports as Imports-import qualified Language.Haskell.Stylish.Step.LanguagePragmas as LanguagePragmas-import qualified Language.Haskell.Stylish.Step.Records as Records-import qualified Language.Haskell.Stylish.Step.Tabs as Tabs-import qualified Language.Haskell.Stylish.Step.TrailingWhitespace as TrailingWhitespace-import qualified Language.Haskell.Stylish.Step.UnicodeSyntax as UnicodeSyntax-import Language.Haskell.Stylish.Verbose------------------------------------------------------------------------------------type Extensions = [String]------------------------------------------------------------------------------------data Config = Config- { configSteps :: [Step]- , configColumns :: Int- , configLanguageExtensions :: [String]- }------------------------------------------------------------------------------------instance FromJSON Config where- parseJSON = parseConfig------------------------------------------------------------------------------------emptyConfig :: Config-emptyConfig = Config [] 80 []------------------------------------------------------------------------------------configFileName :: String-configFileName = ".stylish-haskell.yaml"------------------------------------------------------------------------------------defaultConfigFilePath :: IO FilePath-defaultConfigFilePath = getDataFileName "data/stylish-haskell.yaml"------------------------------------------------------------------------------------configFilePath :: Verbose -> Maybe FilePath -> IO (Maybe FilePath)-configFilePath _ (Just userSpecified) = return $ Just userSpecified-configFilePath verbose Nothing = do- current <- getCurrentDirectory- home <- getHomeDirectory- def <- defaultConfigFilePath- search $- [d </> configFileName | d <- ancestors current] ++- [home </> configFileName, def]- where- -- All ancestors of a dir (including that dir)- ancestors :: FilePath -> [FilePath]- ancestors = init . map joinPath . reverse . inits . splitPath-- search :: [FilePath] -> IO (Maybe FilePath)- search [] = return Nothing- search (f : fs) = do- -- TODO Maybe catch an error here, dir might be unreadable- exists <- doesFileExist f- verbose $ f ++ if exists then " exists" else " does not exist"- if exists then return (Just f) else search fs------------------------------------------------------------------------------------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 $- "Language.Haskell.Stylish.Config.loadConfig: " ++ err- Right config -> return config------------------------------------------------------------------------------------parseConfig :: A.Value -> A.Parser Config-parseConfig (A.Object o) = do- -- First load the config without the actual steps- config <- Config- <$> pure []- <*> (o A..:? "columns" A..!= 80)- <*> (o A..:? "language_extensions" A..!= [])-- -- Then fill in the steps based on the partial config we already have- stepValues <- o A..: "steps" :: A.Parser [A.Value]- steps <- mapM (parseSteps config) stepValues- return config {configSteps = concat steps}-parseConfig _ = mzero------------------------------------------------------------------------------------catalog :: Map String (Config -> A.Object -> A.Parser Step)-catalog = M.fromList- [ ("imports", parseImports)- , ("language_pragmas", parseLanguagePragmas)- , ("records", parseRecords)- , ("tabs", parseTabs)- , ("trailing_whitespace", parseTrailingWhitespace)- , ("unicode_syntax", parseUnicodeSyntax)- ]------------------------------------------------------------------------------------parseSteps :: Config -> A.Value -> A.Parser [Step]-parseSteps config 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 config 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 :: Config -> A.Object -> A.Parser Step-parseImports config o = Imports.step- <$> pure (configColumns config)- <*> (o A..:? "align" >>= parseEnum aligns Imports.Global)- where- aligns =- [ ("global", Imports.Global)- , ("file", Imports.File)- , ("group", Imports.Group)- , ("none", Imports.None)- ]------------------------------------------------------------------------------------parseLanguagePragmas :: Config -> A.Object -> A.Parser Step-parseLanguagePragmas config o = LanguagePragmas.step- <$> pure (configColumns config)- <*> (o A..:? "style" >>= parseEnum styles LanguagePragmas.Vertical)- <*> o A..:? "remove_redundant" A..!= True- where- styles =- [ ("vertical", LanguagePragmas.Vertical)- , ("compact", LanguagePragmas.Compact)- , ("compact_line", LanguagePragmas.CompactLine)]------------------------------------------------------------------------------------parseRecords :: Config -> A.Object -> A.Parser Step-parseRecords _ _ = return Records.step------------------------------------------------------------------------------------parseTabs :: Config -> A.Object -> A.Parser Step-parseTabs _ o = Tabs.step- <$> o A..:? "spaces" A..!= 8------------------------------------------------------------------------------------parseTrailingWhitespace :: Config -> A.Object -> A.Parser Step-parseTrailingWhitespace _ _ = return TrailingWhitespace.step------------------------------------------------------------------------------------parseUnicodeSyntax :: Config -> A.Object -> A.Parser Step-parseUnicodeSyntax _ o = UnicodeSyntax.step- <$> o A..:? "add_language_pragma" A..!= True
− src/Language/Haskell/Stylish/Editor.hs
@@ -1,101 +0,0 @@------------------------------------------------------------------------------------ | This module provides you with a line-based editor. It's main feature is--- that you can specify multiple changes at the same time, e.g.:------ > [deleteLine 3, changeLine 4 ["Foo"]]------ when this is evaluated, we take into account that 4th line will become the--- 3rd line before it needs changing.-module Language.Haskell.Stylish.Editor- ( Change- , applyChanges-- , change- , changeLine- , delete- , deleteLine- , insert- ) where------------------------------------------------------------------------------------import Language.Haskell.Stylish.Block-------------------------------------------------------------------------------------- | Changes the lines indicated by the 'Block' into the given 'Lines'-data Change a = Change- { changeBlock :: Block a- , changeLines :: ([a] -> [a])- }------------------------------------------------------------------------------------moveChange :: Int -> Change a -> Change a-moveChange offset (Change block ls) = Change (moveBlock offset block) ls------------------------------------------------------------------------------------applyChanges :: [Change a] -> [a] -> [a]-applyChanges changes- | overlapping blocks = error $- "Language.Haskell.Stylish.Editor.applyChanges: " ++- "refusing to make overlapping changes"- | otherwise = go 1 changes- where- blocks = map changeBlock changes-- go _ [] ls = ls- go n (ch : chs) ls =- -- Divide the remaining lines into:- --- -- > pre- -- > old (lines that are affected by the change)- -- > post- --- -- And generate:- --- -- > pre- -- > new- -- > (recurse)- --- let block = changeBlock ch- (pre, ls') = splitAt (blockStart block - n) ls- (old, post) = splitAt (blockLength block) ls'- new = changeLines ch old- extraLines = length new - blockLength block- chs' = map (moveChange extraLines) chs- n' = blockStart block + blockLength block + extraLines- in pre ++ new ++ go n' chs' post-------------------------------------------------------------------------------------- | Change a block of lines for some other lines-change :: Block a -> ([a] -> [a]) -> Change a-change = Change-------------------------------------------------------------------------------------- | Change a single line for some other lines-changeLine :: Int -> (a -> [a]) -> Change a-changeLine start f = change (Block start start) $ \xs -> case xs of- [] -> []- (x : _) -> f x-------------------------------------------------------------------------------------- | Delete a block of lines-delete :: Block a -> Change a-delete block = Change block $ const []-------------------------------------------------------------------------------------- | Delete a single line-deleteLine :: Int -> Change a-deleteLine start = delete (Block start start)-------------------------------------------------------------------------------------- | Insert something /before/ the given lines-insert :: Int -> [a] -> Change a-insert start = Change (Block start (start - 1)) . const
− src/Language/Haskell/Stylish/Parse.hs
@@ -1,74 +0,0 @@----------------------------------------------------------------------------------module Language.Haskell.Stylish.Parse- ( parseModule- ) where------------------------------------------------------------------------------------import Data.Maybe (fromMaybe, listToMaybe)-import qualified Language.Haskell.Exts.Annotated as H-import Data.List (isPrefixOf)------------------------------------------------------------------------------------import Language.Haskell.Stylish.Config-import Language.Haskell.Stylish.Step-------------------------------------------------------------------------------------- | Filter out lines which use CPP macros-unCpp :: String -> String-unCpp = unlines . go False . lines- where- go _ [] = []- go isMultiline (x : xs) =- let isCpp = isMultiline || listToMaybe x == Just '#'- nextMultiline = isCpp && not (null x) && last x == '\\'- in (if isCpp then "" else x) : go nextMultiline xs-------------------------------------------------------------------------------------- | Remove shebang from the first line-unShebang :: String -> String-unShebang str- | "#!" `isPrefixOf` str = unlines $ ("" :) $ drop 1 $ lines str- | otherwise = str-------------------------------------------------------------------------------------- | If the given string is prefixed with an UTF-8 Byte Order Mark, drop it--- because haskell-src-exts can't handle it.-dropBom :: String -> String-dropBom ('\xfeff' : str) = str-dropBom str = str-------------------------------------------------------------------------------------- | Abstraction over HSE's parsing-parseModule :: Extensions -> Maybe FilePath -> String -> Either String Module-parseModule extraExts mfp string = do- -- Determine the extensions: those specified in the file and the extra ones- let noBom = dropBom string- extraExts' = map H.classifyExtension extraExts- (lang, fileExts) = fromMaybe (Nothing, []) $ H.readExtensions noBom- exts = fileExts ++ extraExts'-- -- Parsing options...- fp = fromMaybe "<unknown>" mfp- mode = H.defaultParseMode- { H.extensions = exts- , H.fixities = Nothing- , H.baseLanguage = case lang of- Nothing -> H.baseLanguage H.defaultParseMode- Just l -> l- }-- -- Preprocessing- processed = unShebang $- if H.EnableExtension H.CPP `elem` exts then unCpp noBom else noBom-- case H.parseModuleWithComments mode processed of- H.ParseOk md -> return md- err -> Left $- "Language.Haskell.Stylish.Parse.parseModule: could not parse " ++- fp ++ ": " ++ show err
− src/Language/Haskell/Stylish/Step.hs
@@ -1,32 +0,0 @@----------------------------------------------------------------------------------module Language.Haskell.Stylish.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/Language/Haskell/Stylish/Step/Imports.hs
@@ -1,172 +0,0 @@----------------------------------------------------------------------------------module Language.Haskell.Stylish.Step.Imports- ( Align (..)- , step- ) where------------------------------------------------------------------------------------import Control.Arrow ((&&&))-import Data.Char (isAlpha, toLower)-import Data.List (intercalate, sortBy)-import Data.Maybe (isJust, maybeToList)-import Data.Ord (comparing)-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 Align- = Global- | File- | 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)-------------------------------------------------------------------------------------- | Compare imports for ordering-compareImports :: H.ImportDecl l -> H.ImportDecl l -> Ordering-compareImports = comparing (map toLower . 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-------------------------------------------------------------------------------------- | By default, haskell-src-exts pretty-prints------ > import Foo (Bar(..))------ but we want------ > import Foo (Bar (..))------ instead.-prettyImportSpec :: H.ImportSpec l -> String-prettyImportSpec (H.IThingAll _ n) = H.prettyPrint n ++ " (..)"-prettyImportSpec (H.IThingWith _ n cns) = H.prettyPrint n ++ " (" ++- intercalate ", " (map H.prettyPrint cns) ++ ")"-prettyImportSpec x = H.prettyPrint x------------------------------------------------------------------------------------prettyImport :: Int -> Bool -> Bool -> Int -> H.ImportDecl l -> [String]-prettyImport columns padQualified padName longest imp =- wrap columns base (length base + 2) $- (if hiding then ("hiding" :) else id) $- case importSpecs of- Nothing -> [] -- Import everything- Just [] -> ["()"] -- Instance only imports- Just is ->- withInit (++ ",") $- withHead ("(" ++) $- withLast (++ ")") $- map prettyImportSpec $- is- where- base = unwords $ concat- [ ["import"]- , qualified- , (fmap show $ maybeToList $ H.importPkg imp)- , [(if hasExtras && padName then padRight longest else id)- (importName imp)]- , ["as " ++ as | H.ModuleName _ as <- maybeToList $ H.importAs imp]- ]-- (hiding, importSpecs) = case H.importSpecs imp of- Just (H.ImportSpecList _ h l) -> (h, Just l)- _ -> (False, Nothing)-- hasExtras = isJust (H.importAs imp) || isJust (H.importSpecs imp)-- qualified- | H.importQualified imp = ["qualified"]- | padQualified = [" "]- | otherwise = []------------------------------------------------------------------------------------prettyImportGroup :: Int -> Align -> Bool -> Int -> [H.ImportDecl LineBlock]- -> Lines-prettyImportGroup columns align fileAlign longest imps =- concatMap (prettyImport columns padQual padName longest') $- sortBy compareImports imps- where- longest' = case align of- Group -> longestImport imps- _ -> longest-- padName = align /= None-- padQual = case align of- Global -> True- File -> fileAlign- Group -> any H.importQualified imps- None -> False------------------------------------------------------------------------------------step :: Int -> Align -> Step-step columns = makeStep "Imports" . step' columns------------------------------------------------------------------------------------step' :: Int -> Align -> Lines -> Module -> Lines-step' columns align ls (module', _) = flip applyChanges ls- [ change block $ const $- prettyImportGroup columns align fileAlign longest importGroup- | (block, importGroup) <- groups- ]- where- imps = map sortImportSpecs $ imports $ fmap linesFromSrcSpan module'- longest = longestImport imps- groups = groupAdjacent [(H.ann i, i) | i <- imps]-- fileAlign = case align of- File -> any H.importQualified imps- _ -> False
− src/Language/Haskell/Stylish/Step/LanguagePragmas.hs
@@ -1,158 +0,0 @@----------------------------------------------------------------------------------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 -> [String] -> Lines-verticalPragmas longest pragmas' =- [ "{-# LANGUAGE " ++ padRight longest pragma ++ " #-}"- | pragma <- pragmas'- ]------------------------------------------------------------------------------------compactPragmas :: Int -> [String] -> Lines-compactPragmas columns pragmas' = wrap columns "{-# LANGUAGE" 13 $- map (++ ",") (init pragmas') ++ [last pragmas', "#-}"]------------------------------------------------------------------------------------compactLinePragmas :: Int -> [String] -> Lines-compactLinePragmas _ [] = []-compactLinePragmas columns pragmas' =- let maxWidth = columns - 16- longest = maximum $ map length prags- prags = map truncateComma $ wrap maxWidth "" 1 $- map (++ ",") (init pragmas') ++ [last pragmas']- in map (wrapLanguage . padRight longest) prags- where- wrapLanguage ps = "{-# LANGUAGE" ++ ps ++ " #-}"------------------------------------------------------------------------------------truncateComma :: String -> String-truncateComma "" = ""-truncateComma xs- | last xs == ',' = init xs- | otherwise = xs------------------------------------------------------------------------------------prettyPragmas :: Int -> Int -> Style -> [String] -> Lines-prettyPragmas _ longest Vertical = verticalPragmas longest-prettyPragmas columns _ Compact = compactPragmas columns-prettyPragmas columns _ CompactLine = compactLinePragmas columns-------------------------------------------------------------------------------------- | 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 -> Step-step columns style = makeStep "LanguagePragmas" . step' columns style------------------------------------------------------------------------------------step' :: Int -> Style -> Bool -> Lines -> Module -> Lines-step' columns style 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 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]]
− src/Language/Haskell/Stylish/Step/Records.hs
@@ -1,79 +0,0 @@----------------------------------------------------------------------------------module Language.Haskell.Stylish.Step.Records- ( step- ) where------------------------------------------------------------------------------------import Data.Char (isSpace)-import Data.List (nub)-import qualified Language.Haskell.Exts.Annotated as H------------------------------------------------------------------------------------import Language.Haskell.Stylish.Editor-import Language.Haskell.Stylish.Step-import Language.Haskell.Stylish.Util------------------------------------------------------------------------------------records :: H.Module l -> [[Alignable l]]-records modu =- [ map fieldDeclToAlignable fields- | H.Module _ _ _ _ decls <- [modu]- , H.DataDecl _ _ _ _ cons _ <- decls- , H.QualConDecl _ _ _ (H.RecDecl _ _ fields) <- cons- ]------------------------------------------------------------------------------------data Alignable a = Alignable- { aContainer :: !a- , aLeft :: !a- , aRight :: !a- } deriving (Show)------------------------------------------------------------------------------------fieldDeclToAlignable :: H.FieldDecl a -> Alignable a-fieldDeclToAlignable (H.FieldDecl ann names ty) = Alignable- { aContainer = ann- , aLeft = H.ann (last names)- , aRight = H.ann ty- }-------------------------------------------------------------------------------------- | Align the type of a field-align :: [Alignable H.SrcSpan] -> [Change String]-align alignment = map align' alignment- where- longest = maximum $ map (H.srcSpanEndColumn . aLeft) alignment-- align' a = changeLine (H.srcSpanStartLine $ aContainer a) $ \str ->- let column = H.srcSpanEndColumn $ aLeft a- (pre, post) = splitAt column str- in [padRight longest (trimRight pre) ++ trimLeft post]-- trimLeft = dropWhile isSpace- trimRight = reverse . trimLeft . reverse-------------------------------------------------------------------------------------- | Checks that all no field of the record appears on more than one line,--- amonst other things-fixable :: [Alignable H.SrcSpan] -> Bool-fixable [] = False-fixable fields = all singleLine containers && nonOverlapping containers- where- containers = map aContainer fields- singleLine s = H.srcSpanStartLine s == H.srcSpanEndLine s- nonOverlapping ss = length ss == length (nub $ map H.srcSpanStartLine ss)------------------------------------------------------------------------------------step :: Step-step = makeStep "Records" $ \ls (module', _) ->- let module'' = fmap H.srcInfoSpan module'- fixableRecords = filter fixable $ records module''- in applyChanges (fixableRecords >>= align) ls
− src/Language/Haskell/Stylish/Step/Tabs.hs
@@ -1,21 +0,0 @@----------------------------------------------------------------------------------module Language.Haskell.Stylish.Step.Tabs- ( step- ) where------------------------------------------------------------------------------------import Language.Haskell.Stylish.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/Language/Haskell/Stylish/Step/TrailingWhitespace.hs
@@ -1,22 +0,0 @@----------------------------------------------------------------------------------module Language.Haskell.Stylish.Step.TrailingWhitespace- ( step- ) where------------------------------------------------------------------------------------import Data.Char (isSpace)------------------------------------------------------------------------------------import Language.Haskell.Stylish.Step------------------------------------------------------------------------------------dropTrailingWhitespace :: String -> String-dropTrailingWhitespace = reverse . dropWhile isSpace . reverse------------------------------------------------------------------------------------step :: Step-step = makeStep "TrailingWhitespace" $ \ls _ -> map dropTrailingWhitespace ls
− src/Language/Haskell/Stylish/Step/UnicodeSyntax.hs
@@ -1,115 +0,0 @@----------------------------------------------------------------------------------module Language.Haskell.Stylish.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 Language.Haskell.Stylish.Block-import Language.Haskell.Stylish.Editor-import Language.Haskell.Stylish.Step-import Language.Haskell.Stylish.Step.LanguagePragmas (addLanguagePragma)-import Language.Haskell.Stylish.Util------------------------------------------------------------------------------------unicodeReplacements :: Map String String-unicodeReplacements = M.fromList- [ ("::", "∷")- , ("=>", "⇒")- , ("->", "→")- ]------------------------------------------------------------------------------------replaceAll :: [(Int, [(Int, String)])] -> [Change String]-replaceAll = map changeLine'- where- changeLine' (r, ns) = changeLine r $ \str -> return $- flip applyChanges str- [ change (Block c ec) (const repl)- | (c, needle) <- sort ns- , let ec = c + length needle - 1- , repl <- maybeToList $ M.lookup needle unicodeReplacements- ]------------------------------------------------------------------------------------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- 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- perLine = sort $ groupPerLine $- typeSigs module' ls ++- contexts module' ls ++- typeFuns module' ls
− src/Language/Haskell/Stylish/Util.hs
@@ -1,95 +0,0 @@----------------------------------------------------------------------------------module Language.Haskell.Stylish.Util- ( nameToString- , indent- , padRight- , everything- , infoPoints- , wrap-- , withHead- , withLast- , withInit- ) 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------------------------------------------------------------------------------------import Language.Haskell.Stylish.Step------------------------------------------------------------------------------------nameToString :: H.Name l -> String-nameToString (H.Ident _ str) = str-nameToString (H.Symbol _ str) = str------------------------------------------------------------------------------------indent :: Int -> String -> String-indent len str = replicate len ' ' ++ str------------------------------------------------------------------------------------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 ind 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- | nextLine = (ls ++ [curr], indent ind str, ind + len)- | otherwise = (ls, curr ++ " " ++ str, width')- where- -- Put it on the next line if it would make the current line too long,- -- AND if it doesn't make the next line too long.- nextLine = width' > maxWidth && ind + len <= maxWidth- len = length str- width' = width + 1 + len------------------------------------------------------------------------------------withHead :: (a -> a) -> [a] -> [a]-withHead _ [] = []-withHead f (x : xs) = f x : xs------------------------------------------------------------------------------------withLast :: (a -> a) -> [a] -> [a]-withLast _ [] = []-withLast f (x : []) = [f x]-withLast f (x : xs) = x : withLast f xs------------------------------------------------------------------------------------withInit :: (a -> a) -> [a] -> [a]-withInit _ [] = []-withInit _ (x : []) = [x]-withInit f (x : xs) = f x : withInit f xs
− src/Language/Haskell/Stylish/Verbose.hs
@@ -1,20 +0,0 @@----------------------------------------------------------------------------------module Language.Haskell.Stylish.Verbose- ( Verbose- , makeVerbose- ) where------------------------------------------------------------------------------------import System.IO (hPutStrLn, stderr)------------------------------------------------------------------------------------type Verbose = String -> IO ()------------------------------------------------------------------------------------makeVerbose :: Bool -> Verbose-makeVerbose verbose- | verbose = hPutStrLn stderr- | otherwise = const $ return ()
stylish-haskell.cabal view
@@ -1,5 +1,5 @@ Name: stylish-haskell-Version: 0.5.14.4+Version: 0.5.15.0 Synopsis: Haskell code prettifier Homepage: https://github.com/jaspervdj/stylish-haskell License: BSD3@@ -26,7 +26,7 @@ Library Exposed-modules: Language.Haskell.Stylish- Hs-source-dirs: src+ Hs-source-dirs: lib Ghc-options: -Wall Other-modules:@@ -47,7 +47,7 @@ Build-depends: aeson >= 0.6 && < 0.11,- base >= 4 && < 5,+ base >= 4.8 && < 5, bytestring >= 0.9 && < 0.11, containers >= 0.3 && < 0.6, directory >= 1.1 && < 1.3,@@ -68,7 +68,7 @@ cmdargs >= 0.9 && < 0.11, -- Copied from regular dependencies... aeson >= 0.6 && < 0.11,- base >= 4 && < 5,+ base >= 4.8 && < 5, bytestring >= 0.9 && < 0.11, containers >= 0.3 && < 0.6, directory >= 1.1 && < 1.3,@@ -80,7 +80,7 @@ Test-suite stylish-haskell-tests Ghc-options: -Wall- Hs-source-dirs: src tests+ Hs-source-dirs: tests lib Main-is: TestSuite.hs Type: exitcode-stdio-1.0 @@ -100,7 +100,7 @@ test-framework-hunit >= 0.2 && < 0.4, -- Copied from regular dependencies... aeson >= 0.6 && < 0.11,- base >= 4 && < 5,+ base >= 4.8 && < 5, bytestring >= 0.9 && < 0.11, cmdargs >= 0.9 && < 0.11, containers >= 0.3 && < 0.6,
tests/Language/Haskell/Stylish/Step/Imports/Tests.hs view
@@ -16,6 +16,15 @@ --------------------------------------------------------------------------------+defaultAlign :: Align+defaultAlign = Align Global AfterAlias Inline 4 True+++--------------------------------------------------------------------------------+fromImportAlign :: ImportAlign -> Align+fromImportAlign align = defaultAlign { importAlign = align }++-------------------------------------------------------------------------------- tests :: Test tests = testGroup "Language.Haskell.Stylish.Step.Imports.Tests" [ testCase "case 01" case01@@ -25,6 +34,17 @@ , testCase "case 05" case05 , testCase "case 06" case06 , testCase "case 07" case07+ , testCase "case 08" case08+ , testCase "case 09" case09+ , testCase "case 10" case10+ , testCase "case 11" case11+ , testCase "case 12" case12+ , testCase "case 13" case13+ , testCase "case 14" case14+ , testCase "case 15" case15+ , testCase "case 16" case16+ , testCase "case 17" case17+ , testCase "case 18" case18 ] @@ -37,6 +57,8 @@ , "import Control.Monad" , "import Only.Instances()" , "import Data.Map (lookup, (!), insert, Map)"+ , "import Data.List as List (concat, foldl, foldr, head, init, last,\+ \ length, map, null, reverse, tail, (++))" , "" , "import Herp.Derp.Internals hiding (foo)" , "import Foo (Bar (..))"@@ -47,12 +69,15 @@ -------------------------------------------------------------------------------- case01 :: Assertion-case01 = expected @=? testStep (step 80 Global) input+case01 = expected @=? testStep (step 80 $ fromImportAlign Global) input where expected = unlines [ "module Herp where" , "" , "import Control.Monad"+ , "import Data.List as List (concat, foldl, foldr, head, init,"+ , " last, length, map, null, reverse,"+ , " tail, (++))" , "import Data.Map (Map, insert, lookup, (!))" , "import qualified Data.Map as M" , "import Only.Instances ()"@@ -66,12 +91,14 @@ -------------------------------------------------------------------------------- case02 :: Assertion-case02 = expected @=? testStep (step 80 Group) input+case02 = expected @=? testStep (step 80 $ fromImportAlign Group) input where expected = unlines [ "module Herp where" , "" , "import Control.Monad"+ , "import Data.List as List (concat, foldl, foldr, head, init, last,"+ , " length, map, null, reverse, tail, (++))" , "import Data.Map (Map, insert, lookup, (!))" , "import qualified Data.Map as M" , "import Only.Instances ()"@@ -85,12 +112,14 @@ -------------------------------------------------------------------------------- case03 :: Assertion-case03 = expected @=? testStep (step 80 None) input+case03 = expected @=? testStep (step 80 $ fromImportAlign None) input where expected = unlines [ "module Herp where" , "" , "import Control.Monad"+ , "import Data.List as List (concat, foldl, foldr, head, init, last, length, map,"+ , " null, reverse, tail, (++))" , "import Data.Map (Map, insert, lookup, (!))" , "import qualified Data.Map as M" , "import Only.Instances ()"@@ -104,7 +133,7 @@ -------------------------------------------------------------------------------- case04 :: Assertion-case04 = expected @=? testStep (step 80 Global) input'+case04 = expected @=? testStep (step 80 $ fromImportAlign Global) input' where input' = "import Data.Aeson.Types (object, typeMismatch, FromJSON(..)," ++@@ -119,7 +148,7 @@ -------------------------------------------------------------------------------- case05 :: Assertion-case05 = input' @=? testStep (step 80 Group) input'+case05 = input' @=? testStep (step 80 $ fromImportAlign Group) input' where input' = "import Distribution.PackageDescription.Configuration " ++ "(finalizePackageDescription)\n"@@ -127,7 +156,7 @@ -------------------------------------------------------------------------------- case06 :: Assertion-case06 = input' @=? testStep (step 80 File) input'+case06 = input' @=? testStep (step 80 $ fromImportAlign File) input' where input' = unlines [ "import Bar.Qux"@@ -137,7 +166,7 @@ -------------------------------------------------------------------------------- case07 :: Assertion-case07 = expected @=? testStep (step 80 File) input'+case07 = expected @=? testStep (step 80 $ fromImportAlign File) input' where input' = unlines [ "import Bar.Qux"@@ -149,4 +178,267 @@ [ "import Bar.Qux" , "" , "import qualified Foo.Bar"+ ]+++--------------------------------------------------------------------------------+case08 :: Assertion+case08 = expected+ @=? testStep (step 80 $ Align Global WithAlias Inline 4 True) input+ where+ expected = unlines+ [ "module Herp where"+ , ""+ , "import Control.Monad"+ , "import Data.List as List (concat, foldl, foldr, head, init,"+ , " last, length, map, null, reverse, tail,"+ , " (++))"+ , "import Data.Map (Map, insert, lookup, (!))"+ , "import qualified Data.Map as M"+ , "import Only.Instances ()"+ , ""+ , "import Foo (Bar (..))"+ , "import Herp.Derp.Internals hiding (foo)"+ , ""+ , "herp = putStrLn \"import Hello world\""+ ]+++--------------------------------------------------------------------------------+case09 :: Assertion+case09 = expected+ @=? testStep (step 80 $ Align Global WithAlias Multiline 4 True) input+ where+ expected = unlines+ [ "module Herp where"+ , ""+ , "import Control.Monad"+ , "import Data.List as List"+ , " ( concat"+ , " , foldl"+ , " , foldr"+ , " , head"+ , " , init"+ , " , last"+ , " , length"+ , " , map"+ , " , null"+ , " , reverse"+ , " , tail"+ , " , (++)"+ , " )"+ , "import Data.Map (Map, insert, lookup, (!))"+ , "import qualified Data.Map as M"+ , "import Only.Instances ()"+ , ""+ , "import Foo (Bar (..))"+ , "import Herp.Derp.Internals hiding (foo)"+ , ""+ , "herp = putStrLn \"import Hello world\""+ ]+++--------------------------------------------------------------------------------+case10 :: Assertion+case10 = expected+ @=? testStep (step 40 $ Align Group WithAlias Multiline 4 True) input+ where+ expected = unlines+ [ "module Herp where"+ , ""+ , "import Control.Monad"+ , "import Data.List as List"+ , " ( concat"+ , " , foldl"+ , " , foldr"+ , " , head"+ , " , init"+ , " , last"+ , " , length"+ , " , map"+ , " , null"+ , " , reverse"+ , " , tail"+ , " , (++)"+ , " )"+ , "import Data.Map"+ , " ( Map"+ , " , insert"+ , " , lookup"+ , " , (!)"+ , " )"+ , "import qualified Data.Map as M"+ , "import Only.Instances ()"+ , ""+ , "import Foo (Bar (..))"+ , "import Herp.Derp.Internals hiding (foo)"+ , ""+ , "herp = putStrLn \"import Hello world\""+ ]+++--------------------------------------------------------------------------------+case11 :: Assertion+case11 = expected+ @=? testStep (step 80 $ Align Group NewLine Inline 4 True) input+ where+ expected = unlines+ [ "module Herp where"+ , ""+ , "import Control.Monad"+ , "import Data.List as List"+ , " (concat, foldl, foldr, head, init, last, length, map, null, reverse, tail,"+ , " (++))"+ , "import Data.Map"+ , " (Map, insert, lookup, (!))"+ , "import qualified Data.Map as M"+ , "import Only.Instances"+ , " ()"+ , ""+ , "import Foo"+ , " (Bar (..))"+ , "import Herp.Derp.Internals hiding"+ , " (foo)"++ , ""+ , "herp = putStrLn \"import Hello world\""+ ]+++--------------------------------------------------------------------------------+case12 :: Assertion+case12 = expected+ @=? testStep (step 80 $ Align Group NewLine Inline 2 True) input'+ where+ input' = unlines+ [ "import Data.List (map)"+ ]++ expected = unlines+ [ "import Data.List"+ , " (map)"+ ]+++--------------------------------------------------------------------------------+case13 :: Assertion+case13 = expected+ @=? testStep (step 80 $ Align None WithAlias InlineWithBreak 4 True) input'+ where+ input' = unlines+ [ "import qualified Data.List as List (concat, foldl, foldr, head, init,"+ , " last, length, map, null, reverse, tail, (++))"+ ]++ expected = unlines+ [ "import qualified Data.List as List"+ , " (concat, foldl, foldr, head, init, last, length, map, null, reverse, tail,"+ , " (++))"+ ]+++--------------------------------------------------------------------------------+case14 :: Assertion+case14 = expected+ @=? testStep+ (step 80 $ Align None WithAlias InlineWithBreak 10 True) expected+ where+ expected = unlines+ [ "import qualified Data.List as List (concat, map, null, reverse, tail, (++))"+ ]+++--------------------------------------------------------------------------------+case15 :: Assertion+case15 = expected+ @=? testStep (step 80 $ Align None AfterAlias Multiline 4 True) input'+ where+ expected = unlines+ [ "import Data.Acid (AcidState)"+ , "import qualified Data.Acid as Acid"+ , " ( closeAcidState"+ , " , createCheckpoint"+ , " , openLocalStateFrom"+ , " )"+ , "import Data.Default.Class (Default (def))"+ , ""+ , "import qualified Herp.Derp.Internal.Types.Foobar as Internal (bar, foo)"+ ]++ input' = unlines+ [ "import Data.Acid (AcidState)"+ , "import qualified Data.Acid as Acid (closeAcidState, createCheckpoint, openLocalStateFrom)"+ , "import Data.Default.Class (Default (def))"+ , ""+ , "import qualified Herp.Derp.Internal.Types.Foobar as Internal (foo, bar)"+ ]+++--------------------------------------------------------------------------------+case16 :: Assertion+case16 = expected+ @=? testStep (step 80 $ Align None AfterAlias Multiline 4 False) input'+ where+ expected = unlines+ [ "import Data.Acid (AcidState)"+ , "import Data.Default.Class (Default(def))"+ , ""+ , "import Data.Maybe (Maybe(Just, Nothing))"+ , ""+ , "import Data.Foo (Foo(Bar, Foo), Goo(Goo))"+ ]++ input' = unlines+ [ "import Data.Acid (AcidState)"+ , "import Data.Default.Class (Default(def))"+ , ""+ , "import Data.Maybe (Maybe (Just, Nothing))"+ , ""+ , "import Data.Foo (Foo (Foo,Bar), Goo(Goo))"+ ]+++--------------------------------------------------------------------------------+case17 :: Assertion+case17 = expected+ @=? testStep (step 80 $ Align None AfterAlias Multiline 4 True) input'+ where+ expected = unlines+ [ "import Control.Applicative (Applicative (pure, (<*>)))"+ , ""+ , "import Data.Identity (Identity (Identity, runIdentity))"+ ]++ input' = unlines+ [ "import Control.Applicative (Applicative ((<*>),pure))"+ , ""+ , "import Data.Identity (Identity (runIdentity,Identity))"+ ]+++--------------------------------------------------------------------------------+case18 :: Assertion+case18 = expected @=? testStep+ (step 40 $ Align None AfterAlias InlineToMultiline 4 True) input'+ where+ expected = unlines+ ----------------------------------------+ [ "import Data.Foo as Foo (Bar, Baz, Foo)"+ , ""+ , "import Data.Identity"+ , " (Identity (Identity, runIdentity))"+ , ""+ , "import Data.Acid as Acid"+ , " ( closeAcidState"+ , " , createCheckpoint"+ , " , openLocalStateFrom"+ , " )"+ ]++ input' = unlines+ [ "import Data.Foo as Foo (Bar, Baz, Foo)"+ , ""+ , "import Data.Identity (Identity (Identity, runIdentity))"+ , ""+ , "import Data.Acid as Acid (closeAcidState, createCheckpoint, openLocalStateFrom)" ]
tests/Language/Haskell/Stylish/Step/LanguagePragmas/Tests.hs view
@@ -24,12 +24,14 @@ , testCase "case 04" case04 , testCase "case 05" case05 , testCase "case 06" case06+ , testCase "case 07" case07+ , testCase "case 08" case08 ] -------------------------------------------------------------------------------- case01 :: Assertion-case01 = expected @=? testStep (step 80 Vertical False) input+case01 = expected @=? testStep (step 80 Vertical True False) input where input = unlines [ "{-# LANGUAGE ViewPatterns #-}"@@ -48,7 +50,7 @@ -------------------------------------------------------------------------------- case02 :: Assertion-case02 = expected @=? testStep (step 80 Vertical True) input+case02 = expected @=? testStep (step 80 Vertical True True) input where input = unlines [ "{-# LANGUAGE BangPatterns #-}"@@ -64,7 +66,7 @@ -------------------------------------------------------------------------------- case03 :: Assertion-case03 = expected @=? testStep (step 80 Vertical True) input+case03 = expected @=? testStep (step 80 Vertical True True) input where input = unlines [ "{-# LANGUAGE BangPatterns #-}"@@ -80,7 +82,7 @@ -------------------------------------------------------------------------------- case04 :: Assertion-case04 = expected @=? testStep (step 80 Compact False) input+case04 = expected @=? testStep (step 80 Compact True False) input where input = unlines [ "{-# LANGUAGE TypeOperators, StandaloneDeriving, DeriveDataTypeable,"@@ -97,7 +99,7 @@ -------------------------------------------------------------------------------- case05 :: Assertion-case05 = expected @=? testStep (step 80 Vertical False) input+case05 = expected @=? testStep (step 80 Vertical True False) input where input = unlines [ "{-# LANGUAGE CPP #-}"@@ -115,8 +117,10 @@ , "#endif" ] ++-------------------------------------------------------------------------------- case06 :: Assertion-case06 = expected @=? testStep (step 80 CompactLine False) input+case06 = expected @=? testStep (step 80 CompactLine True False) input where input = unlines [ "{-# LANGUAGE TypeOperators, StandaloneDeriving, DeriveDataTypeable,"@@ -127,4 +131,39 @@ [ "{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving, " ++ "TemplateHaskell #-}" , "{-# LANGUAGE TypeOperators, ViewPatterns #-}"+ ]++--------------------------------------------------------------------------------+case07 :: Assertion+case07 = expected @=? testStep (step 80 Vertical False False) input+ where+ input = unlines+ [ "{-# LANGUAGE ViewPatterns #-}"+ , "{-# LANGUAGE TemplateHaskell, ViewPatterns #-}"+ , "{-# LANGUAGE ScopedTypeVariables, NoImplicitPrelude #-}"+ , "module Main where"+ ]++ expected = unlines+ [ "{-# LANGUAGE NoImplicitPrelude #-}"+ , "{-# LANGUAGE ScopedTypeVariables #-}"+ , "{-# LANGUAGE TemplateHaskell #-}"+ , "{-# LANGUAGE ViewPatterns #-}"+ , "module Main where"+ ]+++--------------------------------------------------------------------------------+case08 :: Assertion+case08 = expected @=? testStep (step 80 CompactLine False False) input+ where+ input = unlines+ [ "{-# LANGUAGE TypeOperators, StandaloneDeriving, DeriveDataTypeable,"+ , " TemplateHaskell #-}"+ , "{-# LANGUAGE TemplateHaskell, ViewPatterns #-}"+ ]+ expected = unlines+ [ "{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving, " +++ "TemplateHaskell #-}"+ , "{-# LANGUAGE TypeOperators, ViewPatterns #-}" ]