Skip to content

Commit 29e933d

Browse files
authoredJan 19, 2025··
Make it possible to search for config without getCurrentDirectory (#483)
Co-Authored-By: Jan Hrček <[email protected]>
1 parent cdff43e commit 29e933d

File tree

8 files changed

+74
-38
lines changed

8 files changed

+74
-38
lines changed
 

‎CHANGELOG

+13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
# CHANGELOG
22

33
- UNRELEASED
4+
* #482 Add `ConfigSearchStrategy` to allow avoiding `getCurrentDirectory`
5+
when loading config (by Jan Hrček)
6+
7+
This is breaking API change that can be fixed like this:
8+
9+
```diff
10+
-format Nothing maybeFile contents
11+
+format SearchFromCurrentDirectory maybeFile contents
12+
13+
-format (Just cfgFile) maybeFile content
14+
+format (UseConfig cfgFile) maybeFile content
15+
```
16+
417
* Bump `Cabal` lower bound to 3.14
518

619
- 0.14.6.0 (2024-01-19)

‎lib/Language/Haskell/Stylish.hs

+11-8
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ module Language.Haskell.Stylish
1919
, module Language.Haskell.Stylish.Verbose
2020
, version
2121
, format
22-
, ConfigPath(..)
22+
, ConfigSearchStrategy(..)
2323
, Lines
2424
, Step
2525
) where
@@ -105,14 +105,17 @@ runSteps ::
105105
runSteps exts mfp steps ls =
106106
foldM (runStep exts mfp) ls steps
107107

108-
newtype ConfigPath = ConfigPath { unConfigPath :: FilePath }
109108

110-
-- |Formats given contents optionally using the config provided as first param.
111-
-- The second file path is the location from which the contents were read.
112-
-- If provided, it's going to be printed out in the error message.
113-
format :: Maybe ConfigPath -> Maybe FilePath -> String -> IO (Either String Lines)
114-
format maybeConfigPath maybeFilePath contents = do
115-
conf <- loadConfig (makeVerbose True) (fmap unConfigPath maybeConfigPath)
109+
-- | Formats given contents.
110+
format ::
111+
ConfigSearchStrategy
112+
-> Maybe FilePath
113+
-- ^ the location from which the contents to format were read.
114+
-- If provided, it's going to be printed out in the error message.
115+
-> String -- ^ the contents to format
116+
-> IO (Either String Lines)
117+
format configSearchStrategy maybeFilePath contents = do
118+
conf <- loadConfig (makeVerbose True) configSearchStrategy
116119
pure $ runSteps (configLanguageExtensions conf) maybeFilePath (configSteps conf) $ lines contents
117120

118121

‎lib/Language/Haskell/Stylish/Config.hs

+14-10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
module Language.Haskell.Stylish.Config
77
( Extensions
88
, Config (..)
9+
, ConfigSearchStrategy (..)
910
, ExitCodeBehavior (..)
1011
, defaultConfigBytes
1112
, configFilePath
@@ -95,14 +96,17 @@ defaultConfigBytes = $(FileEmbed.embedFile "data/stylish-haskell.yaml")
9596

9697

9798
--------------------------------------------------------------------------------
98-
configFilePath :: Verbose -> Maybe FilePath -> IO (Maybe FilePath)
99-
configFilePath _ (Just userSpecified) = return (Just userSpecified)
100-
configFilePath verbose Nothing = do
101-
current <- getCurrentDirectory
99+
configFilePath :: Verbose -> ConfigSearchStrategy -> IO (Maybe FilePath)
100+
configFilePath _ (UseConfig userSpecified) = return (Just userSpecified)
101+
configFilePath verbose (SearchFromDirectory dir) = searchFrom verbose dir
102+
configFilePath verbose SearchFromCurrentDirectory = searchFrom verbose =<< getCurrentDirectory
103+
104+
searchFrom :: Verbose -> FilePath -> IO (Maybe FilePath)
105+
searchFrom verbose startDir = do
102106
configPath <- getXdgDirectory XdgConfig "stylish-haskell"
103-
home <- getHomeDirectory
107+
home <- getHomeDirectory
104108
search verbose $
105-
[d </> configFileName | d <- ancestors current] ++
109+
[d </> configFileName | d <- ancestors startDir] ++
106110
[configPath </> "config.yaml", home </> configFileName]
107111

108112
search :: Verbose -> [FilePath] -> IO (Maybe FilePath)
@@ -114,16 +118,16 @@ search verbose (f : fs) = do
114118
if exists then return (Just f) else search verbose fs
115119

116120
--------------------------------------------------------------------------------
117-
loadConfig :: Verbose -> Maybe FilePath -> IO Config
118-
loadConfig verbose userSpecified = do
119-
mbFp <- configFilePath verbose userSpecified
121+
loadConfig :: Verbose -> ConfigSearchStrategy -> IO Config
122+
loadConfig verbose configSearchStrategy = do
123+
mbFp <- configFilePath verbose configSearchStrategy
120124
verbose $ "Loading configuration at " ++ fromMaybe "<embedded>" mbFp
121125
bytes <- maybe (return defaultConfigBytes) B.readFile mbFp
122126
case decode1Strict bytes of
123127
Left (pos, err) -> error $ prettyPosWithSource pos (fromStrict bytes) ("Language.Haskell.Stylish.Config.loadConfig: " ++ err)
124128
Right config -> do
125129
cabalLanguageExtensions <- if configCabal config
126-
then map toStr <$> Cabal.findLanguageExtensions verbose
130+
then map toStr <$> Cabal.findLanguageExtensions verbose configSearchStrategy
127131
else pure []
128132

129133
return $ config

‎lib/Language/Haskell/Stylish/Config/Cabal.hs

+14-13
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,32 @@ import qualified Distribution.Parsec as Cabal
1616
import qualified Distribution.Simple.Utils as Cabal
1717
import qualified Distribution.Utils.Path as Cabal
1818
import qualified Distribution.Verbosity as Cabal
19+
import GHC.Data.Maybe (mapMaybe)
1920
import qualified Language.Haskell.Extension as Language
21+
import Language.Haskell.Stylish.Config.Internal
2022
import Language.Haskell.Stylish.Verbose
2123
import System.Directory (doesFileExist,
2224
getCurrentDirectory)
2325

2426

2527
--------------------------------------------------------------------------------
26-
import GHC.Data.Maybe (mapMaybe)
27-
import Language.Haskell.Stylish.Config.Internal
28-
29-
30-
--------------------------------------------------------------------------------
31-
findLanguageExtensions :: Verbose -> IO [(Language.KnownExtension, Bool)]
32-
findLanguageExtensions verbose =
33-
findCabalFile verbose >>=
28+
findLanguageExtensions
29+
:: Verbose -> ConfigSearchStrategy -> IO [(Language.KnownExtension, Bool)]
30+
findLanguageExtensions verbose configSearchStrategy =
31+
findCabalFile verbose configSearchStrategy >>=
3432
maybe (pure []) (readDefaultLanguageExtensions verbose)
3533

3634

3735
--------------------------------------------------------------------------------
3836
-- | Find the closest .cabal file, possibly going up the directory structure.
39-
-- TODO: use ConfigSearchStrategy here, too
40-
findCabalFile :: Verbose -> IO (Maybe FilePath)
41-
findCabalFile verbose = do
42-
cwd <- getCurrentDirectory
43-
go [] $ ancestors cwd
37+
findCabalFile :: Verbose -> ConfigSearchStrategy -> IO (Maybe FilePath)
38+
findCabalFile verbose configSearchStrategy = case configSearchStrategy of
39+
-- If the invocation pointed us to a specific config file, it doesn't make
40+
-- much sense to search for cabal files manually (the config file could be
41+
-- somewhere like /etc, not necessarily a Haskell project).
42+
UseConfig _ -> pure Nothing
43+
SearchFromDirectory path -> go [] $ ancestors path
44+
SearchFromCurrentDirectory -> getCurrentDirectory >>= go [] . ancestors
4445
where
4546
go :: [FilePath] -> [FilePath] -> IO (Maybe FilePath)
4647
go searched [] = do

‎lib/Language/Haskell/Stylish/Config/Internal.hs

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
--------------------------------------------------------------------------------
22
module Language.Haskell.Stylish.Config.Internal
3-
( ancestors
3+
( ConfigSearchStrategy (..)
4+
, ancestors
45
) where
56

67

@@ -13,3 +14,15 @@ import System.FilePath (joinPath, splitPath)
1314
-- All ancestors of a dir (including that dir)
1415
ancestors :: FilePath -> [FilePath]
1516
ancestors = map joinPath . reverse . dropWhile null . inits . splitPath
17+
18+
19+
--------------------------------------------------------------------------------
20+
data ConfigSearchStrategy
21+
= -- | Don't try to search, just use given config file
22+
UseConfig FilePath
23+
| -- | Search for @.stylish-haskell.yaml@ starting from given directory.
24+
-- If not found, try all ancestor directories, @$XDG_CONFIG\/stylish-haskell\/config.yaml@ and @$HOME\/.stylish-haskell.yaml@ in order.
25+
-- If no config is found, default built-in config will be used.
26+
SearchFromDirectory FilePath
27+
| -- | Like SearchFromDirectory, but using current working directory as a starting point
28+
SearchFromCurrentDirectory

‎src/Main.hs

+3-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ stylishHaskell sa = do
108108
BC8.putStr defaultConfigBytes
109109

110110
else do
111-
conf <- loadConfig verbose' (saConfig sa)
111+
conf <- loadConfig verbose' $ case saConfig sa of
112+
Nothing -> SearchFromCurrentDirectory
113+
Just fp -> UseConfig fp
112114
filesR <- case (saRecursive sa) of
113115
True -> findHaskellFiles (saVerbose sa) (saFiles sa)
114116
_ -> return $ saFiles sa

‎tests/Language/Haskell/Stylish/Config/Tests.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ createFilesAndGetConfig files = withTestDirTree $ do
9696
setCurrentDirectory "src"
9797
-- from that directory read the config file and extract extensions
9898
-- to make sure the search for .cabal file works
99-
loadConfig (const (pure ())) Nothing
99+
loadConfig (const (pure ())) SearchFromCurrentDirectory
100100

101101

102102
--------------------------------------------------------------------------------

‎tests/Language/Haskell/Stylish/Tests.hs

+4-4
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ tests = testGroup "Language.Haskell.Stylish.Tests"
3535

3636
--------------------------------------------------------------------------------
3737
case01 :: Assertion
38-
case01 = (@?= result) =<< format Nothing Nothing input
38+
case01 = (@?= result) =<< format SearchFromCurrentDirectory Nothing input
3939
where
4040
input = "module Herp where\ndata Foo = Bar | Baz { baz :: Int }"
4141
result = Right $ lines input
@@ -54,7 +54,7 @@ case02 = withTestDirTree $ do
5454
, " via: \"indent 2\""
5555
]
5656

57-
actual <- format (Just $ ConfigPath "test-config.yaml") Nothing input
57+
actual <- format (UseConfig "test-config.yaml") Nothing input
5858
actual @?= result
5959
where
6060
input = "module Herp where\ndata Foo = Bar | Baz { baz :: Int }"
@@ -79,7 +79,7 @@ case03 = withTestDirTree $ do
7979
, " via: \"indent 2\""
8080
]
8181

82-
actual <- format (Just $ ConfigPath "test-config.yaml") Nothing input
82+
actual <- format (UseConfig "test-config.yaml") Nothing input
8383
actual @?= result
8484
where
8585
input = unlines [ "module Herp where"
@@ -98,7 +98,7 @@ case03 = withTestDirTree $ do
9898

9999
--------------------------------------------------------------------------------
100100
case04 :: Assertion
101-
case04 = format Nothing (Just fileLocation) input >>= \case
101+
case04 = format SearchFromCurrentDirectory (Just fileLocation) input >>= \case
102102
Right _ -> assertFailure "expected error"
103103
Left err
104104
| fileLocation `isInfixOf` err

0 commit comments

Comments
 (0)
Please sign in to comment.