Skip to content

Commit 8addbf1

Browse files
committed
Auto-detect elm version per file
Resolves #561
1 parent 3b2a0fd commit 8addbf1

File tree

12 files changed

+217
-143
lines changed

12 files changed

+217
-143
lines changed

elm-format.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ library
119119
bytestring >= 0.10.8.2 && < 0.11,
120120
containers >= 0.6.0.1 && < 0.7,
121121
directory >= 1.3.3.0 && < 2,
122+
exceptions >= 0.10.1 && < 0.11,
122123
filepath >= 1.4.2.1 && < 2,
123124
free >= 5.1.1 && < 6,
124125
indents >= 0.3.3 && < 0.4,
@@ -180,6 +181,7 @@ test-Suite elm-format-tests
180181
Util.ListTest
181182

182183
build-depends:
184+
filepath >= 1.4.2.1 && < 2,
183185
tasty >= 1.2 && < 2,
184186
tasty-golden >= 2.3.2 && < 3,
185187
tasty-hunit >= 0.10.0.1 && < 0.11,

src/ElmFormat.hs

Lines changed: 73 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Control.Monad.Free
1111
import qualified CommandLine.Helpers as Helpers
1212
import ElmVersion
1313
import ElmFormat.FileStore (FileStore)
14+
import ElmFormat.Filesystem (ElmFile)
1415
import ElmFormat.FileWriter (FileWriter)
1516
import ElmFormat.InputConsole (InputConsole)
1617
import ElmFormat.OutputConsole (OutputConsole)
@@ -19,6 +20,7 @@ import ElmFormat.World
1920
import qualified AST.Json
2021
import qualified AST.Module
2122
import qualified Flags
23+
import qualified Data.Maybe as Maybe
2224
import qualified Data.Text as Text
2325
import qualified ElmFormat.Execute as Execute
2426
import qualified ElmFormat.InputConsole as InputConsole
@@ -34,18 +36,21 @@ import qualified Reporting.Result as Result
3436
import qualified Text.JSON
3537

3638

37-
resolveFile :: FileStore f => FilePath -> Free f (Either InputFileMessage [FilePath])
38-
resolveFile path =
39+
resolveFile :: FileStore f => ElmVersion -> FilePath -> Free f (Either InputFileMessage [ElmFile])
40+
resolveFile defaultElmVersion path =
3941
do
40-
fileType <- FileStore.stat path
42+
upwardElmVersion <- FS.findElmVersion path
43+
let elmFile = FS.ElmFile (Maybe.fromMaybe defaultElmVersion upwardElmVersion) path
4144

45+
fileType <- FileStore.stat path
4246
case fileType of
4347
FileStore.IsFile ->
44-
return $ Right [path]
48+
do
49+
return $ Right [elmFile]
4550

4651
FileStore.IsDirectory ->
4752
do
48-
elmFiles <- FS.findAllElmFiles path
53+
elmFiles <- FS.findAllElmFiles elmFile
4954
case elmFiles of
5055
[] -> return $ Left $ NoElmFiles path
5156
_ -> return $ Right elmFiles
@@ -74,32 +79,32 @@ collectErrors list =
7479
foldl step (Right []) list
7580

7681

77-
resolveFiles :: FileStore f => [FilePath] -> Free f (Either [InputFileMessage] [FilePath])
78-
resolveFiles inputFiles =
82+
resolveFiles :: FileStore f => ElmVersion -> [FilePath] -> Free f (Either [InputFileMessage] [ElmFile])
83+
resolveFiles defaultElmVersion inputFiles =
7984
do
80-
result <- collectErrors <$> mapM resolveFile inputFiles
85+
result <- collectErrors <$> mapM (resolveFile defaultElmVersion) inputFiles
8186
case result of
8287
Left ls ->
8388
return $ Left ls
8489

8590
Right files ->
86-
return $ Right $ concat files
91+
return $ Right $ concat $ files
8792

8893

8994
data WhatToDo
90-
= FormatToFile FilePath FilePath
91-
| StdinToFile FilePath
92-
| FormatInPlace FilePath [FilePath]
93-
| StdinToStdout
94-
| ValidateStdin
95-
| ValidateFiles FilePath [FilePath]
96-
| FileToJson FilePath
97-
| StdinToJson
95+
= FormatToFile ElmFile FilePath
96+
| StdinToFile ElmVersion FilePath
97+
| FormatInPlace ElmFile [ElmFile]
98+
| StdinToStdout ElmVersion
99+
| ValidateStdin ElmVersion
100+
| ValidateFiles ElmFile [ElmFile]
101+
| FileToJson ElmFile
102+
| StdinToJson ElmVersion
98103

99104

100105
data Source
101-
= Stdin
102-
| FromFiles FilePath [FilePath]
106+
= Stdin ElmVersion
107+
| FromFiles ElmFile [ElmFile]
103108

104109

105110
data Destination
@@ -109,13 +114,17 @@ data Destination
109114
| ToJson
110115

111116

112-
determineSource :: Bool -> Either [InputFileMessage] [FilePath] -> Either ErrorMessage Source
113-
determineSource stdin inputFiles =
117+
determineSource :: Bool -> Bool -> Maybe ElmVersion -> ElmVersion -> Either [InputFileMessage] [ElmFile] -> Either ErrorMessage Source
118+
determineSource stdin upgrade versionFlag defaultElmVersion inputFiles =
119+
let
120+
determineFile (FS.ElmFile fileDetectedVersion path) =
121+
FS.ElmFile (upgradeVersion upgrade $ Maybe.fromMaybe fileDetectedVersion versionFlag) path
122+
in
114123
case ( stdin, inputFiles ) of
115124
( _, Left fileErrors ) -> Left $ BadInputFiles fileErrors
116-
( True, Right [] ) -> Right Stdin
125+
( True, Right [] ) -> Right $ Stdin $ upgradeVersion upgrade $ Maybe.fromMaybe defaultElmVersion versionFlag
117126
( False, Right [] ) -> Left NoInputs
118-
( False, Right (first:rest) ) -> Right $ FromFiles first rest
127+
( False, Right (first:rest) ) -> Right $ FromFiles (determineFile first) (fmap determineFile rest)
119128
( True, Right (_:_) ) -> Left TooManyInputs
120129

121130

@@ -133,22 +142,22 @@ determineDestination output doValidate json =
133142
determineWhatToDo :: Source -> Destination -> Either ErrorMessage WhatToDo
134143
determineWhatToDo source destination =
135144
case ( source, destination ) of
136-
( Stdin, ValidateOnly ) -> Right $ ValidateStdin
145+
( Stdin version, ValidateOnly ) -> Right $ ValidateStdin version
137146
( FromFiles first rest, ValidateOnly) -> Right $ ValidateFiles first rest
138-
( Stdin, UpdateInPlace ) -> Right StdinToStdout
139-
( Stdin, ToJson ) -> Right StdinToJson
140-
( Stdin, ToFile output ) -> Right $ StdinToFile output
147+
( Stdin version, UpdateInPlace ) -> Right $ StdinToStdout version
148+
( Stdin version, ToJson ) -> Right $ StdinToJson version
149+
( Stdin version, ToFile output ) -> Right $ StdinToFile version output
141150
( FromFiles first [], ToFile output ) -> Right $ FormatToFile first output
142151
( FromFiles first rest, UpdateInPlace ) -> Right $ FormatInPlace first rest
143152
( FromFiles _ _, ToFile _ ) -> Left SingleOutputWithMultipleInputs
144153
( FromFiles first [], ToJson ) -> Right $ FileToJson first
145154
( FromFiles _ _, ToJson ) -> Left SingleOutputWithMultipleInputs
146155

147156

148-
determineWhatToDoFromConfig :: Flags.Config -> Either [InputFileMessage] [FilePath] -> Either ErrorMessage WhatToDo
149-
determineWhatToDoFromConfig config resolvedInputFiles =
157+
determineWhatToDoFromConfig :: Flags.Config -> ElmVersion -> Either [InputFileMessage] [ElmFile] -> Either ErrorMessage WhatToDo
158+
determineWhatToDoFromConfig config defaultElmVersion resolvedInputFiles =
150159
do
151-
source <- determineSource (Flags._stdin config) resolvedInputFiles
160+
source <- determineSource (Flags._stdin config) (Flags._upgrade config) (Flags._elmVersion config) defaultElmVersion resolvedInputFiles
152161
destination <- determineDestination (Flags._output config) (Flags._validate config) (Flags._json config)
153162
determineWhatToDo source destination
154163

@@ -159,20 +168,17 @@ exitWithError message =
159168
>> exitFailure
160169

161170

162-
determineVersion :: ElmVersion -> Bool -> Either ErrorMessage ElmVersion
163-
determineVersion elmVersion upgrade =
164-
case (elmVersion, upgrade) of
165-
(Elm_0_18, True) ->
166-
Right Elm_0_18_Upgrade
167-
168-
(Elm_0_19, True) ->
169-
Right Elm_0_19_Upgrade
171+
upgradeVersion :: Bool -> ElmVersion -> ElmVersion
172+
upgradeVersion upgrade version =
173+
case (upgrade, version) of
174+
(True, Elm_0_18) ->
175+
Elm_0_18_Upgrade
170176

171-
(_, True) ->
172-
Left $ MustSpecifyVersionWithUpgrade Elm_0_19_Upgrade
177+
(True, Elm_0_19) ->
178+
Elm_0_19_Upgrade
173179

174-
(_, False) ->
175-
Right elmVersion
180+
_ ->
181+
version
176182

177183

178184
elmFormatVersion :: String
@@ -222,9 +228,11 @@ main'' elmFormatVersion_ experimental_ args =
222228
Just config ->
223229
do
224230
let autoYes = Flags._yes config
225-
resolvedInputFiles <- Execute.run (Execute.forHuman autoYes) $ resolveFiles (Flags._input config)
231+
currentDirectoryElmVersion <- Execute.run (Execute.forHuman autoYes) $ FS.findElmVersion "."
232+
let defaultElmVersion = Maybe.fromMaybe Elm_0_19 currentDirectoryElmVersion;
233+
resolvedInputFiles <- Execute.run (Execute.forHuman autoYes) $ resolveFiles defaultElmVersion (Flags._input config)
226234

227-
case determineWhatToDoFromConfig config resolvedInputFiles of
235+
case determineWhatToDoFromConfig config defaultElmVersion resolvedInputFiles of
228236
Left NoInputs ->
229237
(handleParseResult $ Flags.showHelpText elmFormatVersion_ experimental_)
230238
-- TODO: handleParseResult is exitSuccess, so we never get to exitFailure
@@ -233,53 +241,23 @@ main'' elmFormatVersion_ experimental_ args =
233241
Left message ->
234242
exitWithError message
235243

236-
Right whatToDo -> do
237-
elmVersionChoice <- case Flags._elmVersion config of
238-
Just v -> return $ Right v
239-
Nothing -> autoDetectElmVersion
240-
241-
case elmVersionChoice of
242-
Left message ->
243-
putStr message *> exitFailure
244-
245-
Right elmVersionChoice' -> do
246-
let elmVersionResult = determineVersion elmVersionChoice' (Flags._upgrade config)
247-
248-
case elmVersionResult of
249-
Left message ->
250-
exitWithError message
251-
252-
Right elmVersion ->
253-
do
254-
let run = case (Flags._validate config) of
255-
True -> Execute.run $ Execute.forMachine elmVersion True
256-
False -> Execute.run $ Execute.forHuman autoYes
257-
result <- run $ doIt elmVersion whatToDo
258-
if result
259-
then exitSuccess
260-
else exitFailure
261-
262-
263-
autoDetectElmVersion :: World m => m (Either String ElmVersion)
264-
autoDetectElmVersion =
265-
do
266-
hasElmPackageJson <- doesFileExist "elm-package.json"
267-
if hasElmPackageJson
268-
then
269-
do
270-
hasElmJson <- doesFileExist "elm.json"
271-
if hasElmJson
272-
then return $ Right Elm_0_19
273-
else return $ Right Elm_0_18
274-
else return $ Right Elm_0_19
244+
Right whatToDo ->
245+
do
246+
let run = case (Flags._validate config) of
247+
True -> Execute.run $ Execute.forMachine True
248+
False -> Execute.run $ Execute.forHuman autoYes
249+
result <- run $ doIt whatToDo
250+
if result
251+
then exitSuccess
252+
else exitFailure
275253

276254

277255
validate :: ElmVersion -> (FilePath, Text.Text) -> Either InfoMessage ()
278256
validate elmVersion (inputFile, inputText) =
279257
case Parse.parse elmVersion inputText of
280258
Result.Result _ (Result.Ok modu) ->
281259
if inputText /= Render.render elmVersion modu then
282-
Left $ FileWouldChange inputFile
260+
Left $ FileWouldChange inputFile elmVersion
283261
else
284262
Right ()
285263

@@ -363,35 +341,36 @@ logErrorOr fn result =
363341
fn value *> return True
364342

365343

366-
doIt :: (InputConsole f, OutputConsole f, InfoFormatter f, FileStore f, FileWriter f) => ElmVersion -> WhatToDo -> Free f Bool
367-
doIt elmVersion whatToDo =
344+
345+
doIt :: (InputConsole f, OutputConsole f, InfoFormatter f, FileStore f, FileWriter f) => WhatToDo -> Free f Bool
346+
doIt whatToDo =
368347
case whatToDo of
369-
ValidateStdin ->
348+
ValidateStdin elmVersion ->
370349
(validate elmVersion <$> readStdin) >>= logError
371350

372351
ValidateFiles first rest ->
373352
all id <$> mapM validateFile (first:rest)
374-
where validateFile file = (validate elmVersion <$> ElmFormat.readFile file) >>= logError
353+
where validateFile (FS.ElmFile elmVersion path) = (validate elmVersion <$> ElmFormat.readFile path) >>= logError
375354

376-
StdinToStdout ->
355+
StdinToStdout elmVersion ->
377356
(fmap getOutputText <$> format elmVersion <$> readStdin) >>= logErrorOr OutputConsole.writeStdout
378357

379-
StdinToFile outputFile ->
358+
StdinToFile elmVersion outputFile ->
380359
(fmap getOutputText <$> format elmVersion <$> readStdin) >>= logErrorOr (FileWriter.overwriteFile outputFile)
381360

382-
FormatToFile inputFile outputFile ->
361+
FormatToFile (FS.ElmFile elmVersion inputFile) outputFile ->
383362
(fmap getOutputText <$> format elmVersion <$> ElmFormat.readFile inputFile) >>= logErrorOr (FileWriter.overwriteFile outputFile)
384363

385364
FormatInPlace first rest ->
386365
do
387-
canOverwrite <- approve $ FilesWillBeOverwritten (first:rest)
366+
canOverwrite <- approve $ FilesWillBeOverwritten $ fmap FS.path (first:rest)
388367
if canOverwrite
389368
then all id <$> mapM formatFile (first:rest)
390369
else return True
391370
where
392-
formatFile file = (format elmVersion <$> ElmFormat.readFile file) >>= logErrorOr ElmFormat.updateFile
371+
formatFile (FS.ElmFile elmVersion path) = (format elmVersion <$> ElmFormat.readFile path) >>= logErrorOr ElmFormat.updateFile
393372

394-
StdinToJson ->
373+
StdinToJson elmVersion ->
395374
(fmap (Text.pack . Text.JSON.encode . AST.Json.showModule) <$> parseModule elmVersion <$> readStdin) >>= logErrorOr OutputConsole.writeStdout
396375

397376
-- TODO: this prints "Processing such-and-such-a-file.elm" which makes the JSON output invalid

src/ElmFormat/Execute.hs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import Control.Monad.State
1111
import Control.Monad.Free
1212
import ElmFormat.Operation
1313
import ElmFormat.World
14-
import ElmVersion
1514

1615
import qualified ElmFormat.FileStore as FileStore
1716
import qualified ElmFormat.FileWriter as FileWriter
@@ -59,14 +58,14 @@ forHuman autoYes =
5958

6059

6160
{-| Execute Operations in a fashion appropriate for use by automated scripts. -}
62-
forMachine :: World m => ElmVersion -> Bool -> Program m OperationF Bool
63-
forMachine elmVersion autoYes =
61+
forMachine :: World m => Bool -> Program m OperationF Bool
62+
forMachine autoYes =
6463
Program
6564
{ init = Json.init
6665
, step = \operation ->
6766
case operation of
6867
InFileStore op -> lift $ FileStore.execute op
69-
InInfoFormatter op -> Json.format elmVersion autoYes op
68+
InInfoFormatter op -> Json.format autoYes op
7069
InInputConsole op -> lift $ InputConsole.execute op
7170
InOutputConsole op -> lift $ OutputConsole.execute op
7271
InFileWriter op -> lift $ FileWriter.execute op

src/ElmFormat/FileStore.hs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
module ElmFormat.FileStore (FileStore, FileStoreF(..), FileType(..), readFile, stat, listDirectory, execute) where
1+
module ElmFormat.FileStore (FileStore, FileStoreF(..), FileType(..), readFile, stat, listDirectory, makeAbsolute, execute) where
22

33
import Prelude hiding (readFile, writeFile)
44
import Control.Monad.Free
55
import Data.Text (Text)
6-
import ElmFormat.World hiding (readFile, listDirectory)
6+
import ElmFormat.World hiding (readFile, listDirectory, makeAbsolute)
77
import qualified ElmFormat.World as World
88

99

@@ -17,30 +17,35 @@ class Functor f => FileStore f where
1717
readFile :: FilePath -> f Text
1818
stat :: FilePath -> f FileType
1919
listDirectory :: FilePath -> f [FilePath]
20+
makeAbsolute :: FilePath -> f FilePath
2021

2122

2223
data FileStoreF a
2324
= ReadFile FilePath (Text -> a)
2425
| Stat FilePath (FileType -> a)
2526
| ListDirectory FilePath ([FilePath] -> a)
27+
| MakeAbsolute FilePath (FilePath -> a)
2628

2729

2830
instance Functor FileStoreF where
2931
fmap f (ReadFile path a) = ReadFile path (f . a)
3032
fmap f (Stat path a) = Stat path (f . a)
3133
fmap f (ListDirectory path a) = ListDirectory path (f . a)
34+
fmap f (MakeAbsolute path a) = MakeAbsolute path (f . a)
3235

3336

3437
instance FileStore FileStoreF where
3538
readFile path = ReadFile path id
3639
stat path = Stat path id
3740
listDirectory path = ListDirectory path id
41+
makeAbsolute path = MakeAbsolute path id
3842

3943

4044
instance FileStore f => FileStore (Free f) where
4145
readFile path = liftF (readFile path)
4246
stat path = liftF (stat path)
4347
listDirectory path = liftF (listDirectory path)
48+
makeAbsolute path = liftF (makeAbsolute path)
4449

4550

4651
execute :: World m => FileStoreF a -> m a
@@ -60,3 +65,6 @@ execute operation =
6065

6166
ListDirectory path next ->
6267
next <$> World.listDirectory path
68+
69+
MakeAbsolute path next ->
70+
next <$> World.makeAbsolute path

0 commit comments

Comments
 (0)