Skip to content

Commit c09e060

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

File tree

12 files changed

+231
-147
lines changed

12 files changed

+231
-147
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: 87 additions & 98 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,16 +114,33 @@ 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

131+
upgradeVersion :: Bool -> ElmVersion -> ElmVersion
132+
upgradeVersion upgrade version =
133+
case (upgrade, version) of
134+
(True, Elm_0_18) ->
135+
Elm_0_18_Upgrade
136+
137+
(True, _) ->
138+
Elm_0_19_Upgrade
139+
140+
_ ->
141+
version
142+
143+
122144
determineDestination :: Maybe FilePath -> Bool -> Bool -> Either ErrorMessage Destination
123145
determineDestination output doValidate json =
124146
case ( output, doValidate, json ) of
@@ -133,48 +155,42 @@ determineDestination output doValidate json =
133155
determineWhatToDo :: Source -> Destination -> Either ErrorMessage WhatToDo
134156
determineWhatToDo source destination =
135157
case ( source, destination ) of
136-
( Stdin, ValidateOnly ) -> Right $ ValidateStdin
158+
( Stdin version, ValidateOnly ) -> Right $ ValidateStdin version
137159
( 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
160+
( Stdin version, UpdateInPlace ) -> Right $ StdinToStdout version
161+
( Stdin version, ToJson ) -> Right $ StdinToJson version
162+
( Stdin version, ToFile output ) -> Right $ StdinToFile version output
141163
( FromFiles first [], ToFile output ) -> Right $ FormatToFile first output
142164
( FromFiles first rest, UpdateInPlace ) -> Right $ FormatInPlace first rest
143165
( FromFiles _ _, ToFile _ ) -> Left SingleOutputWithMultipleInputs
144166
( FromFiles first [], ToJson ) -> Right $ FileToJson first
145167
( FromFiles _ _, ToJson ) -> Left SingleOutputWithMultipleInputs
146168

147169

148-
determineWhatToDoFromConfig :: Flags.Config -> Either [InputFileMessage] [FilePath] -> Either ErrorMessage WhatToDo
149-
determineWhatToDoFromConfig config resolvedInputFiles =
170+
determineWhatToDoFromConfig :: Flags.Config -> ElmVersion -> Either [InputFileMessage] [ElmFile] -> Either ErrorMessage WhatToDo
171+
determineWhatToDoFromConfig config defaultElmVersion resolvedInputFiles =
150172
do
151-
source <- determineSource (Flags._stdin config) resolvedInputFiles
173+
checkUpgradeVersion (Flags._upgrade config) (Flags._elmVersion config)
174+
source <- determineSource (Flags._stdin config) (Flags._upgrade config) (Flags._elmVersion config) defaultElmVersion resolvedInputFiles
152175
destination <- determineDestination (Flags._output config) (Flags._validate config) (Flags._json config)
153176
determineWhatToDo source destination
154177

155178

179+
checkUpgradeVersion :: Bool -> Maybe ElmVersion -> Either ErrorMessage ()
180+
checkUpgradeVersion upgrade elmVersionFlag =
181+
case (upgrade, elmVersionFlag) of
182+
(True, Just Elm_0_18) -> Right ()
183+
(True, Just Elm_0_19) -> Right ()
184+
(True, _) -> Left $ MustSpecifyVersionWithUpgrade Elm_0_19_Upgrade
185+
(False, _) -> Right ()
186+
187+
156188
exitWithError :: World m => ErrorMessage -> m ()
157189
exitWithError message =
158190
(putStrLnStderr $ Helpers.r $ message)
159191
>> exitFailure
160192

161193

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
170-
171-
(_, True) ->
172-
Left $ MustSpecifyVersionWithUpgrade Elm_0_19_Upgrade
173-
174-
(_, False) ->
175-
Right elmVersion
176-
177-
178194
elmFormatVersion :: String
179195
elmFormatVersion =
180196
ElmFormat.Version.asString
@@ -222,9 +238,11 @@ main'' elmFormatVersion_ experimental_ args =
222238
Just config ->
223239
do
224240
let autoYes = Flags._yes config
225-
resolvedInputFiles <- Execute.run (Execute.forHuman autoYes) $ resolveFiles (Flags._input config)
241+
currentDirectoryElmVersion <- Execute.run (Execute.forHuman autoYes) $ FS.findElmVersion "."
242+
let defaultElmVersion = Maybe.fromMaybe Elm_0_19 currentDirectoryElmVersion;
243+
resolvedInputFiles <- Execute.run (Execute.forHuman autoYes) $ resolveFiles defaultElmVersion (Flags._input config)
226244

227-
case determineWhatToDoFromConfig config resolvedInputFiles of
245+
case determineWhatToDoFromConfig config defaultElmVersion resolvedInputFiles of
228246
Left NoInputs ->
229247
(handleParseResult $ Flags.showHelpText elmFormatVersion_ experimental_)
230248
-- TODO: handleParseResult is exitSuccess, so we never get to exitFailure
@@ -233,53 +251,23 @@ main'' elmFormatVersion_ experimental_ args =
233251
Left message ->
234252
exitWithError message
235253

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
254+
Right whatToDo ->
255+
do
256+
let run = case (Flags._validate config) of
257+
True -> Execute.run $ Execute.forMachine True
258+
False -> Execute.run $ Execute.forHuman autoYes
259+
result <- run $ doIt whatToDo
260+
if result
261+
then exitSuccess
262+
else exitFailure
275263

276264

277265
validate :: ElmVersion -> (FilePath, Text.Text) -> Either InfoMessage ()
278266
validate elmVersion (inputFile, inputText) =
279267
case Parse.parse elmVersion inputText of
280268
Result.Result _ (Result.Ok modu) ->
281269
if inputText /= Render.render elmVersion modu then
282-
Left $ FileWouldChange inputFile
270+
Left $ FileWouldChange inputFile elmVersion
283271
else
284272
Right ()
285273

@@ -363,35 +351,36 @@ logErrorOr fn result =
363351
fn value *> return True
364352

365353

366-
doIt :: (InputConsole f, OutputConsole f, InfoFormatter f, FileStore f, FileWriter f) => ElmVersion -> WhatToDo -> Free f Bool
367-
doIt elmVersion whatToDo =
354+
355+
doIt :: (InputConsole f, OutputConsole f, InfoFormatter f, FileStore f, FileWriter f) => WhatToDo -> Free f Bool
356+
doIt whatToDo =
368357
case whatToDo of
369-
ValidateStdin ->
358+
ValidateStdin elmVersion ->
370359
(validate elmVersion <$> readStdin) >>= logError
371360

372361
ValidateFiles first rest ->
373362
all id <$> mapM validateFile (first:rest)
374-
where validateFile file = (validate elmVersion <$> ElmFormat.readFile file) >>= logError
363+
where validateFile (FS.ElmFile elmVersion path) = (validate elmVersion <$> ElmFormat.readFile path) >>= logError
375364

376-
StdinToStdout ->
365+
StdinToStdout elmVersion ->
377366
(fmap getOutputText <$> format elmVersion <$> readStdin) >>= logErrorOr OutputConsole.writeStdout
378367

379-
StdinToFile outputFile ->
368+
StdinToFile elmVersion outputFile ->
380369
(fmap getOutputText <$> format elmVersion <$> readStdin) >>= logErrorOr (FileWriter.overwriteFile outputFile)
381370

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

385374
FormatInPlace first rest ->
386375
do
387-
canOverwrite <- approve $ FilesWillBeOverwritten (first:rest)
376+
canOverwrite <- approve $ FilesWillBeOverwritten $ fmap FS.path (first:rest)
388377
if canOverwrite
389378
then all id <$> mapM formatFile (first:rest)
390379
else return True
391380
where
392-
formatFile file = (format elmVersion <$> ElmFormat.readFile file) >>= logErrorOr ElmFormat.updateFile
381+
formatFile (FS.ElmFile elmVersion path) = (format elmVersion <$> ElmFormat.readFile path) >>= logErrorOr ElmFormat.updateFile
393382

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

397386
-- 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

0 commit comments

Comments
 (0)