diff options
Diffstat (limited to 'lib/Language')
-rw-r--r-- | lib/Language/Haskell/Stylish/Config.hs | 50 | ||||
-rw-r--r-- | lib/Language/Haskell/Stylish/Config/Cabal.hs | 92 | ||||
-rw-r--r-- | lib/Language/Haskell/Stylish/Config/Internal.hs | 15 | ||||
-rw-r--r-- | lib/Language/Haskell/Stylish/Step/Imports.hs | 10 | ||||
-rw-r--r-- | lib/Language/Haskell/Stylish/Step/TrailingWhitespace.hs | 7 |
5 files changed, 148 insertions, 26 deletions
diff --git a/lib/Language/Haskell/Stylish/Config.hs b/lib/Language/Haskell/Stylish/Config.hs index ad30498..8f43131 100644 --- a/lib/Language/Haskell/Stylish/Config.hs +++ b/lib/Language/Haskell/Stylish/Config.hs @@ -17,22 +17,22 @@ import qualified Data.Aeson as A import qualified Data.Aeson.Types as A import qualified Data.ByteString as B import qualified Data.FileEmbed as FileEmbed -import Data.List (inits, - intercalate) +import Data.List (intercalate, + nub) import Data.Map (Map) import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Yaml (decodeEither', prettyPrintParseException) import System.Directory -import System.FilePath (joinPath, - splitPath, - (</>)) +import System.FilePath ((</>)) import qualified System.IO as IO (Newline (..), nativeNewline) -------------------------------------------------------------------------------- +import qualified Language.Haskell.Stylish.Config.Cabal as Cabal +import Language.Haskell.Stylish.Config.Internal import Language.Haskell.Stylish.Step import qualified Language.Haskell.Stylish.Step.Imports as Imports import qualified Language.Haskell.Stylish.Step.LanguagePragmas as LanguagePragmas @@ -54,6 +54,7 @@ data Config = Config , configColumns :: Int , configLanguageExtensions :: [String] , configNewline :: IO.Newline + , configCabal :: Bool } @@ -79,24 +80,19 @@ configFilePath verbose Nothing = do current <- getCurrentDirectory configPath <- getXdgDirectory XdgConfig "stylish-haskell" home <- getHomeDirectory - mbConfig <- search $ + mbConfig <- search verbose $ [d </> configFileName | d <- ancestors current] ++ [configPath </> "config.yaml", home </> configFileName] return mbConfig - 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 +search :: Verbose -> [FilePath] -> IO (Maybe FilePath) +search _ [] = return Nothing +search verbose (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 verbose fs -------------------------------------------------------------------------------- loadConfig :: Verbose -> Maybe FilePath -> IO Config @@ -107,7 +103,15 @@ loadConfig verbose userSpecified = do case decodeEither' bytes of Left err -> error $ "Language.Haskell.Stylish.Config.loadConfig: " ++ prettyPrintParseException err - Right config -> return config + Right config -> do + cabalLanguageExtensions <- if configCabal config + then map show <$> Cabal.findLanguageExtensions verbose + else pure [] + + return $ config + { configLanguageExtensions = nub $ + configLanguageExtensions config ++ cabalLanguageExtensions + } -------------------------------------------------------------------------------- @@ -119,6 +123,7 @@ parseConfig (A.Object o) = do <*> (o A..:? "columns" A..!= 80) <*> (o A..:? "language_extensions" A..!= []) <*> (o A..:? "newline" >>= parseEnum newlines IO.nativeNewline) + <*> (o A..:? "cabal" A..!= True) -- Then fill in the steps based on the partial config we already have stepValues <- o A..: "steps" :: A.Parser [A.Value] @@ -209,9 +214,10 @@ parseImports config o = Imports.step ] listAligns = - [ ("new_line", Imports.NewLine) - , ("with_alias", Imports.WithAlias) - , ("after_alias", Imports.AfterAlias) + [ ("new_line", Imports.NewLine) + , ("with_module_name", Imports.WithModuleName) + , ("with_alias", Imports.WithAlias) + , ("after_alias", Imports.AfterAlias) ] longListAligns = diff --git a/lib/Language/Haskell/Stylish/Config/Cabal.hs b/lib/Language/Haskell/Stylish/Config/Cabal.hs new file mode 100644 index 0000000..0160af4 --- /dev/null +++ b/lib/Language/Haskell/Stylish/Config/Cabal.hs @@ -0,0 +1,92 @@ +-------------------------------------------------------------------------------- +module Language.Haskell.Stylish.Config.Cabal + ( findLanguageExtensions + ) where + + +-------------------------------------------------------------------------------- +import Data.Either (isRight) +import Data.List (nub) +import Data.Maybe (maybeToList) +import qualified Distribution.PackageDescription as Cabal +import qualified Distribution.PackageDescription.Parsec as Cabal +import qualified Distribution.Simple.Utils as Cabal +import qualified Distribution.Types.CondTree as Cabal +import qualified Distribution.Verbosity as Cabal +import qualified Language.Haskell.Extension as Language +import Language.Haskell.Stylish.Verbose +import System.Directory (getCurrentDirectory) + + +-------------------------------------------------------------------------------- +import Language.Haskell.Stylish.Config.Internal + + +-------------------------------------------------------------------------------- +findLanguageExtensions :: Verbose -> IO [Language.KnownExtension] +findLanguageExtensions verbose = + findCabalFile verbose >>= + maybe (pure []) (readDefaultLanguageExtensions verbose) + + +-------------------------------------------------------------------------------- +-- | Find the closest .cabal file, possibly going up the directory structure. +findCabalFile :: Verbose -> IO (Maybe FilePath) +findCabalFile verbose = do + potentialProjectRoots <- ancestors <$> getCurrentDirectory + potentialCabalFile <- filter isRight <$> + traverse Cabal.findPackageDesc potentialProjectRoots + case potentialCabalFile of + [Right cabalFile] -> return (Just cabalFile) + _ -> do + verbose $ ".cabal file not found, directories searched: " <> + show potentialProjectRoots + verbose $ "Stylish Haskell will work basing on LANGUAGE pragmas in source files." + return Nothing + + +-------------------------------------------------------------------------------- +-- | Extract @default-extensions@ fields from a @.cabal@ file +readDefaultLanguageExtensions :: Verbose -> FilePath -> IO [Language.KnownExtension] +readDefaultLanguageExtensions verbose cabalFile = do + verbose $ "Parsing " <> cabalFile <> "..." + packageDescription <- Cabal.readGenericPackageDescription Cabal.silent cabalFile + let library :: [Cabal.Library] + library = maybeToList $ fst . Cabal.ignoreConditions <$> + Cabal.condLibrary packageDescription + + subLibraries :: [Cabal.Library] + subLibraries = fst . Cabal.ignoreConditions . snd <$> + Cabal.condSubLibraries packageDescription + + executables :: [Cabal.Executable] + executables = fst . Cabal.ignoreConditions . snd <$> + Cabal.condExecutables packageDescription + + testSuites :: [Cabal.TestSuite] + testSuites = fst . Cabal.ignoreConditions . snd <$> + Cabal.condTestSuites packageDescription + + benchmarks :: [Cabal.Benchmark] + benchmarks = fst . Cabal.ignoreConditions . snd <$> + Cabal.condBenchmarks packageDescription + + gatherBuildInfos :: [Cabal.BuildInfo] + gatherBuildInfos = map Cabal.libBuildInfo library <> + map Cabal.libBuildInfo subLibraries <> + map Cabal.buildInfo executables <> + map Cabal.testBuildInfo testSuites <> + map Cabal.benchmarkBuildInfo benchmarks + + defaultExtensions :: [Language.KnownExtension] + defaultExtensions = map fromEnabled . filter isEnabled $ + concatMap Cabal.defaultExtensions gatherBuildInfos + where isEnabled (Language.EnableExtension _) = True + isEnabled _ = False + + fromEnabled (Language.EnableExtension x) = x + fromEnabled x = + error $ "Language.Haskell.Stylish.Config.readLanguageExtensions: " <> + "invalid LANGUAGE pragma: " <> show x + verbose $ "Gathered default-extensions: " <> show defaultExtensions + pure $ nub defaultExtensions diff --git a/lib/Language/Haskell/Stylish/Config/Internal.hs b/lib/Language/Haskell/Stylish/Config/Internal.hs new file mode 100644 index 0000000..b6160f9 --- /dev/null +++ b/lib/Language/Haskell/Stylish/Config/Internal.hs @@ -0,0 +1,15 @@ +-------------------------------------------------------------------------------- +module Language.Haskell.Stylish.Config.Internal + ( ancestors + ) where + + +-------------------------------------------------------------------------------- +import Data.List (inits) +import System.FilePath (joinPath, splitPath) + + +-------------------------------------------------------------------------------- +-- All ancestors of a dir (including that dir) +ancestors :: FilePath -> [FilePath] +ancestors = map joinPath . reverse . dropWhile null . inits . splitPath diff --git a/lib/Language/Haskell/Stylish/Step/Imports.hs b/lib/Language/Haskell/Stylish/Step/Imports.hs index fc035a2..4ceb802 100644 --- a/lib/Language/Haskell/Stylish/Step/Imports.hs +++ b/lib/Language/Haskell/Stylish/Step/Imports.hs @@ -72,6 +72,7 @@ data ImportAlign data ListAlign = NewLine + | WithModuleName | WithAlias | AfterAlias deriving (Eq, Show) @@ -291,10 +292,11 @@ prettyImport columns Options{..} padQualified padName longest imp . withLast (++ (maybeSpace ++ ")")) inlineWrapper = case listAlign of - NewLine -> (paddedNoSpecBase :) . wrapRest columns listPadding' - WithAlias -> wrap columns paddedBase (inlineBaseLength + 1) + NewLine -> (paddedNoSpecBase :) . wrapRest columns listPadding' + WithModuleName -> wrap columns paddedBase (withModuleNameBaseLength + 4) + WithAlias -> wrap columns paddedBase (inlineBaseLength + 1) -- Add 1 extra space to ensure same padding as in original code. - AfterAlias -> withTail ((' ' : maybeSpace) ++) + AfterAlias -> withTail ((' ' : maybeSpace) ++) . wrap columns paddedBase (afterAliasBaseLength + 1) inlineWithBreakWrap = paddedNoSpecBase : wrapRest columns listPadding' @@ -349,6 +351,8 @@ prettyImport columns Options{..} padQualified padName longest imp inlineBaseLength = length $ base' (padImport $ compoundImportName imp) [] [] + withModuleNameBaseLength = length $ base' "" [] [] + afterAliasBaseLength = length $ base' (padImport $ compoundImportName imp) ["as " ++ as | H.ModuleName _ as <- maybeToList $ H.importAs imp] [] diff --git a/lib/Language/Haskell/Stylish/Step/TrailingWhitespace.hs b/lib/Language/Haskell/Stylish/Step/TrailingWhitespace.hs index dbc594c..e41bace 100644 --- a/lib/Language/Haskell/Stylish/Step/TrailingWhitespace.hs +++ b/lib/Language/Haskell/Stylish/Step/TrailingWhitespace.hs @@ -19,4 +19,9 @@ dropTrailingWhitespace = reverse . dropWhile isSpace . reverse -------------------------------------------------------------------------------- step :: Step -step = makeStep "TrailingWhitespace" $ \ls _ -> map dropTrailingWhitespace ls +step = makeStep "TrailingWhitespace" $ \ls _ -> map dropTrailingWhitespace' ls + where + dropTrailingWhitespace' l = case l of + -- Preserve page breaks + "\12" -> l + _ -> dropTrailingWhitespace l |