summaryrefslogtreecommitdiffhomepage
path: root/lib/Language/Haskell/Stylish
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Language/Haskell/Stylish')
-rw-r--r--lib/Language/Haskell/Stylish/Config.hs50
-rw-r--r--lib/Language/Haskell/Stylish/Config/Cabal.hs92
-rw-r--r--lib/Language/Haskell/Stylish/Config/Internal.hs15
-rw-r--r--lib/Language/Haskell/Stylish/Step/Imports.hs10
-rw-r--r--lib/Language/Haskell/Stylish/Step/TrailingWhitespace.hs7
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