diff options
author | Hiromi Ishii <konn.jinro@gmail.com> | 2013-04-02 21:29:04 +0900 |
---|---|---|
committer | Hiromi Ishii <konn.jinro@gmail.com> | 2013-04-02 21:29:04 +0900 |
commit | 2f312f0648c71ac4b1312855d5cc6506bdd85fa0 (patch) | |
tree | 7c8919f3e6b10d8e5e6d892789f1730ad14f4a93 /src | |
parent | 56542d7fb0fc99caa4251378be1ce7567a50d822 (diff) | |
parent | 105a1845152876bb7c49acda190e995c64659d01 (diff) | |
download | stylish-haskell-2f312f0648c71ac4b1312855d5cc6506bdd85fa0.tar.gz |
* Added `line' style for pretty language pragma.
Conflicts:
src/Language/Haskell/Stylish/Step/LanguagePragmas.hs
Diffstat (limited to 'src')
-rw-r--r-- | src/Language/Haskell/Stylish/Block.hs | 13 | ||||
-rw-r--r-- | src/Language/Haskell/Stylish/Config.hs | 42 | ||||
-rw-r--r-- | src/Language/Haskell/Stylish/Parse.hs | 27 | ||||
-rw-r--r-- | src/Language/Haskell/Stylish/Step/Imports.hs | 54 | ||||
-rw-r--r-- | src/Language/Haskell/Stylish/Step/LanguagePragmas.hs | 59 | ||||
-rw-r--r-- | src/Language/Haskell/Stylish/Util.hs | 11 |
6 files changed, 117 insertions, 89 deletions
diff --git a/src/Language/Haskell/Stylish/Block.hs b/src/Language/Haskell/Stylish/Block.hs index bc47d18..fd680a8 100644 --- a/src/Language/Haskell/Stylish/Block.hs +++ b/src/Language/Haskell/Stylish/Block.hs @@ -10,6 +10,7 @@ module Language.Haskell.Stylish.Block , adjacent , merge , overlapping + , groupAdjacent ) where @@ -76,3 +77,15 @@ 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) diff --git a/src/Language/Haskell/Stylish/Config.hs b/src/Language/Haskell/Stylish/Config.hs index 07b89e0..32eb55d 100644 --- a/src/Language/Haskell/Stylish/Config.hs +++ b/src/Language/Haskell/Stylish/Config.hs @@ -11,17 +11,18 @@ module Language.Haskell.Stylish.Config -------------------------------------------------------------------------------- import Control.Applicative (pure, (<$>), (<*>)) -import Control.Monad (forM, msum, mzero) +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 (intercalate) +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 ((</>)) +import System.FilePath (joinPath, splitPath, + (</>)) -------------------------------------------------------------------------------- @@ -70,22 +71,26 @@ defaultConfigFilePath = getDataFileName "data/stylish-haskell.yaml" -------------------------------------------------------------------------------- configFilePath :: Verbose -> Maybe FilePath -> IO (Maybe FilePath) -configFilePath verbose userSpecified = do - (current, currentE) <- check $ (</> configFileName) <$> getCurrentDirectory - (home, homeE) <- check $ (</> configFileName) <$> getHomeDirectory - (def, defE) <- check defaultConfigFilePath - return $ msum - [ userSpecified - , if currentE then Just current else Nothing - , if homeE then Just home else Nothing - , if defE then Just def else Nothing - ] +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 - check fp = do - fp' <- fp - ex <- doesFileExist fp' - verbose $ fp' ++ if ex then " exists" else " does not exist" - return (fp', ex) + -- 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 -------------------------------------------------------------------------------- @@ -159,6 +164,7 @@ parseImports config o = Imports.step where aligns = [ ("global", Imports.Global) + , ("file", Imports.File) , ("group", Imports.Group) , ("none", Imports.None) ] diff --git a/src/Language/Haskell/Stylish/Parse.hs b/src/Language/Haskell/Stylish/Parse.hs index 84b47c2..36422d1 100644 --- a/src/Language/Haskell/Stylish/Parse.hs +++ b/src/Language/Haskell/Stylish/Parse.hs @@ -6,7 +6,7 @@ module Language.Haskell.Stylish.Parse -------------------------------------------------------------------------------- import Control.Monad.Error (throwError) -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, listToMaybe) import qualified Language.Haskell.Exts.Annotated as H @@ -18,10 +18,13 @@ import Language.Haskell.Stylish.Step -------------------------------------------------------------------------------- -- | Filter out lines which use CPP macros unCpp :: String -> String -unCpp = unlines . map unCpp' . lines +unCpp = unlines . go False . lines where - unCpp' ('#' : _) = "" - unCpp' xs = xs + 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 -------------------------------------------------------------------------------- @@ -33,21 +36,13 @@ dropBom str = str -------------------------------------------------------------------------------- --- | Read an extension name from a string -parseExtension :: String -> Either String H.Extension -parseExtension str = case reads str of - [(x, "")] -> return x - _ -> throwError $ "Unknown extension: " ++ 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 - extraExts' <- mapM parseExtension extraExts - let fileExts = fromMaybe [] $ H.readExtensions string - exts = fileExts ++ extraExts' + -- Determine the extensions: those specified in the file and the extra ones + let extraExts' = map H.classifyExtension extraExts + fileExts = fromMaybe [] $ H.readExtensions string + exts = fileExts ++ extraExts' -- Parsing options... fp = fromMaybe "<unknown>" mfp diff --git a/src/Language/Haskell/Stylish/Step/Imports.hs b/src/Language/Haskell/Stylish/Step/Imports.hs index 9699627..e27a946 100644 --- a/src/Language/Haskell/Stylish/Step/Imports.hs +++ b/src/Language/Haskell/Stylish/Step/Imports.hs @@ -24,6 +24,7 @@ import Language.Haskell.Stylish.Util -------------------------------------------------------------------------------- data Align = Global + | File | Group | None deriving (Eq, Show) @@ -46,20 +47,6 @@ longestImport = maximum . map (length . importName) -------------------------------------------------------------------------------- --- | Groups adjacent imports into larger import blocks -groupAdjacent :: [H.ImportDecl LineBlock] - -> [(LineBlock, [H.ImportDecl LineBlock])] -groupAdjacent = foldr go [] - where - -- This code is ugly and not optimal, and no fucks were given. - go imp is = case break (adjacent b1 . fst) is of - (_, []) -> (b1, [imp]) : is - (xs, ((b2, imps) : ys)) -> (merge b1 b2, imp : imps) : (xs ++ ys) - where - b1 = H.ann imp - - --------------------------------------------------------------------------------- -- | Compare imports for ordering compareImports :: H.ImportDecl l -> H.ImportDecl l -> Ordering compareImports = comparing (map toLower . importName &&& H.importQualified) @@ -108,28 +95,32 @@ prettyImportSpec x = H.prettyPrint x -------------------------------------------------------------------------------- -prettyImport :: Int -> Bool -> Bool -> Int -> H.ImportDecl l -> String +prettyImport :: Int -> Bool -> Bool -> Int -> H.ImportDecl l -> [String] prettyImport columns padQualified padName longest imp = - intercalate "\n" $ wrap columns base (length base + 2) $ (if hiding then ("hiding" :) else id) $ - withInit (++ ",") $ - withHead ("(" ++) $ - withLast (++ ")") $ - map prettyImportSpec $ - importSpecs + 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, l) - _ -> (False, []) + Just (H.ImportSpecList _ h l) -> (h, Just l) + _ -> (False, Nothing) hasExtras = isJust (H.importAs imp) || isJust (H.importSpecs imp) @@ -140,9 +131,10 @@ prettyImport columns padQualified padName longest imp = -------------------------------------------------------------------------------- -prettyImportGroup :: Int -> Align -> Int -> [H.ImportDecl LineBlock] -> Lines -prettyImportGroup columns align longest imps = - map (prettyImport columns padQual padName longest') $ +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 @@ -153,6 +145,7 @@ prettyImportGroup columns align longest imps = padQual = case align of Global -> True + File -> fileAlign Group -> any H.importQualified imps None -> False @@ -165,10 +158,15 @@ 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 longest importGroup) + [ 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 imps + groups = groupAdjacent [(H.ann i, i) | i <- imps] + + fileAlign = case align of + File -> any H.importQualified imps + _ -> False diff --git a/src/Language/Haskell/Stylish/Step/LanguagePragmas.hs b/src/Language/Haskell/Stylish/Step/LanguagePragmas.hs index 53657e0..c147ade 100644 --- a/src/Language/Haskell/Stylish/Step/LanguagePragmas.hs +++ b/src/Language/Haskell/Stylish/Step/LanguagePragmas.hs @@ -9,15 +9,15 @@ module Language.Haskell.Stylish.Step.LanguagePragmas -------------------------------------------------------------------------------- -import Data.List (nub, sort) +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 +import Language.Haskell.Stylish.Block +import Language.Haskell.Stylish.Editor +import Language.Haskell.Stylish.Step +import Language.Haskell.Stylish.Util -------------------------------------------------------------------------------- @@ -42,13 +42,11 @@ firstLocation = minimum . map (blockStart . fst) -------------------------------------------------------------------------------- -verticalPragmas :: [String] -> Lines -verticalPragmas pragmas' = +verticalPragmas :: Int -> [String] -> Lines +verticalPragmas longest pragmas' = [ "{-# LANGUAGE " ++ padRight longest pragma ++ " #-}" | pragma <- pragmas' ] - where - longest = maximum $ map length pragmas' -------------------------------------------------------------------------------- @@ -61,13 +59,13 @@ compactPragmas columns pragmas' = wrap columns "{-# LANGUAGE" 13 $ linePragmas :: Int -> [String] -> Lines linePragmas _ [] = [] linePragmas columns (p:pragmas') = - let (ls, curr, _) = foldl step ([], p, length p) pragmas' + let (ls, curr, _) = foldl stp ([], p, length p) pragmas' ps = ls ++ [curr] longest = maximum $ map length ps in map (wrapLANGUAGE . padRight longest) ps where maxWidth = columns - 17 - step (ls, curr, width) str + stp (ls, curr, width) str | width' > maxWidth = (ls ++ [curr], str, len) | otherwise = (ls, curr ++ ", " ++ str, width') where @@ -75,13 +73,26 @@ linePragmas columns (p:pragmas') = width' = width + 2 + len wrapLANGUAGE ps = "{-# LANGUAGE " ++ ps ++ " #-}" +prettyPragmas :: Int -> Int -> Style -> [String] -> Lines +prettyPragmas _ longest Vertical = verticalPragmas longest +prettyPragmas columns _ Compact = compactPragmas columns +prettyPragmas columns _ Line = linePragmas columns -------------------------------------------------------------------------------- -prettyPragmas :: Int -> Style -> [String] -> Lines -prettyPragmas _ Vertical = verticalPragmas -prettyPragmas columns Compact = compactPragmas columns -prettyPragmas columns Line = linePragmas 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 @@ -94,15 +105,17 @@ step' columns style removeRedundant ls (module', _) | null pragmas' = ls | otherwise = applyChanges changes ls where - filterRedundant - | removeRedundant = filter (not . isRedundant module') - | otherwise = id + isRedundant' + | removeRedundant = isRedundant module' + | otherwise = const False pragmas' = pragmas $ fmap linesFromSrcSpan module' - uniques = filterRedundant $ nub $ sort $ snd =<< pragmas' - loc = firstLocation pragmas' - deletes = map (delete . fst) pragmas' - changes = insert loc (prettyPragmas columns style uniques) : deletes + 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 + ] -------------------------------------------------------------------------------- diff --git a/src/Language/Haskell/Stylish/Util.hs b/src/Language/Haskell/Stylish/Util.hs index bd0a466..004c3f1 100644 --- a/src/Language/Haskell/Stylish/Util.hs +++ b/src/Language/Haskell/Stylish/Util.hs @@ -65,11 +65,14 @@ wrap maxWidth leading ind strs = -- TODO: In order to optimize this, use a difference list instead of a -- regular list for 'ls'. step (ls, curr, width) str - | width' > maxWidth = (ls ++ [curr], indent ind str, ind + len) - | otherwise = (ls, curr ++ " " ++ str, width') + | nextLine = (ls ++ [curr], indent ind str, ind + len) + | otherwise = (ls, curr ++ " " ++ str, width') where - len = length str - width' = width + 1 + len + -- 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 -------------------------------------------------------------------------------- |