-
Notifications
You must be signed in to change notification settings - Fork 148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Auto-detect elm version per file #653
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ import Control.Monad.Free | |
import qualified CommandLine.Helpers as Helpers | ||
import ElmVersion | ||
import ElmFormat.FileStore (FileStore) | ||
import ElmFormat.Filesystem (ElmFile) | ||
import ElmFormat.FileWriter (FileWriter) | ||
import ElmFormat.InputConsole (InputConsole) | ||
import ElmFormat.OutputConsole (OutputConsole) | ||
|
@@ -19,6 +20,7 @@ import ElmFormat.World | |
import qualified AST.Json | ||
import qualified AST.Module | ||
import qualified Flags | ||
import qualified Data.Maybe as Maybe | ||
import qualified Data.Text as Text | ||
import qualified ElmFormat.Execute as Execute | ||
import qualified ElmFormat.InputConsole as InputConsole | ||
|
@@ -34,18 +36,21 @@ import qualified Reporting.Result as Result | |
import qualified Text.JSON | ||
|
||
|
||
resolveFile :: FileStore f => FilePath -> Free f (Either InputFileMessage [FilePath]) | ||
resolveFile path = | ||
resolveFile :: FileStore f => ElmVersion -> FilePath -> Free f (Either InputFileMessage [ElmFile]) | ||
resolveFile defaultElmVersion path = | ||
do | ||
fileType <- FileStore.stat path | ||
upwardElmVersion <- FS.findElmVersion path | ||
let elmFile = FS.ElmFile (Maybe.fromMaybe defaultElmVersion upwardElmVersion) path | ||
|
||
fileType <- FileStore.stat path | ||
case fileType of | ||
FileStore.IsFile -> | ||
return $ Right [path] | ||
do | ||
return $ Right [elmFile] | ||
|
||
FileStore.IsDirectory -> | ||
do | ||
elmFiles <- FS.findAllElmFiles path | ||
elmFiles <- FS.findAllElmFiles elmFile | ||
case elmFiles of | ||
[] -> return $ Left $ NoElmFiles path | ||
_ -> return $ Right elmFiles | ||
|
@@ -74,32 +79,32 @@ collectErrors list = | |
foldl step (Right []) list | ||
|
||
|
||
resolveFiles :: FileStore f => [FilePath] -> Free f (Either [InputFileMessage] [FilePath]) | ||
resolveFiles inputFiles = | ||
resolveFiles :: FileStore f => ElmVersion -> [FilePath] -> Free f (Either [InputFileMessage] [ElmFile]) | ||
resolveFiles defaultElmVersion inputFiles = | ||
do | ||
result <- collectErrors <$> mapM resolveFile inputFiles | ||
result <- collectErrors <$> mapM (resolveFile defaultElmVersion) inputFiles | ||
case result of | ||
Left ls -> | ||
return $ Left ls | ||
|
||
Right files -> | ||
return $ Right $ concat files | ||
return $ Right $ concat $ files | ||
|
||
|
||
data WhatToDo | ||
= FormatToFile FilePath FilePath | ||
| StdinToFile FilePath | ||
| FormatInPlace FilePath [FilePath] | ||
| StdinToStdout | ||
| ValidateStdin | ||
| ValidateFiles FilePath [FilePath] | ||
| FileToJson FilePath | ||
| StdinToJson | ||
= FormatToFile ElmFile FilePath | ||
| StdinToFile ElmVersion FilePath | ||
| FormatInPlace ElmFile [ElmFile] | ||
| StdinToStdout ElmVersion | ||
| ValidateStdin ElmVersion | ||
| ValidateFiles ElmFile [ElmFile] | ||
| FileToJson ElmFile | ||
| StdinToJson ElmVersion | ||
|
||
|
||
data Source | ||
= Stdin | ||
| FromFiles FilePath [FilePath] | ||
= Stdin ElmVersion | ||
| FromFiles ElmFile [ElmFile] | ||
|
||
|
||
data Destination | ||
|
@@ -109,16 +114,33 @@ data Destination | |
| ToJson | ||
|
||
|
||
determineSource :: Bool -> Either [InputFileMessage] [FilePath] -> Either ErrorMessage Source | ||
determineSource stdin inputFiles = | ||
determineSource :: Bool -> Bool -> Maybe ElmVersion -> ElmVersion -> Either [InputFileMessage] [ElmFile] -> Either ErrorMessage Source | ||
determineSource stdin upgrade versionFlag defaultElmVersion inputFiles = | ||
let | ||
determineFile (FS.ElmFile fileDetectedVersion path) = | ||
FS.ElmFile (upgradeVersion upgrade $ Maybe.fromMaybe fileDetectedVersion versionFlag) path | ||
in | ||
case ( stdin, inputFiles ) of | ||
( _, Left fileErrors ) -> Left $ BadInputFiles fileErrors | ||
( True, Right [] ) -> Right Stdin | ||
( True, Right [] ) -> Right $ Stdin $ upgradeVersion upgrade $ Maybe.fromMaybe defaultElmVersion versionFlag | ||
( False, Right [] ) -> Left NoInputs | ||
( False, Right (first:rest) ) -> Right $ FromFiles first rest | ||
( False, Right (first:rest) ) -> Right $ FromFiles (determineFile first) (fmap determineFile rest) | ||
( True, Right (_:_) ) -> Left TooManyInputs | ||
|
||
|
||
upgradeVersion :: Bool -> ElmVersion -> ElmVersion | ||
upgradeVersion upgrade version = | ||
case (upgrade, version) of | ||
(True, Elm_0_18) -> | ||
Elm_0_18_Upgrade | ||
|
||
(True, _) -> | ||
Elm_0_19_Upgrade | ||
|
||
_ -> | ||
version | ||
|
||
|
||
determineDestination :: Maybe FilePath -> Bool -> Bool -> Either ErrorMessage Destination | ||
determineDestination output doValidate json = | ||
case ( output, doValidate, json ) of | ||
|
@@ -133,48 +155,42 @@ determineDestination output doValidate json = | |
determineWhatToDo :: Source -> Destination -> Either ErrorMessage WhatToDo | ||
determineWhatToDo source destination = | ||
case ( source, destination ) of | ||
( Stdin, ValidateOnly ) -> Right $ ValidateStdin | ||
( Stdin version, ValidateOnly ) -> Right $ ValidateStdin version | ||
( FromFiles first rest, ValidateOnly) -> Right $ ValidateFiles first rest | ||
( Stdin, UpdateInPlace ) -> Right StdinToStdout | ||
( Stdin, ToJson ) -> Right StdinToJson | ||
( Stdin, ToFile output ) -> Right $ StdinToFile output | ||
( Stdin version, UpdateInPlace ) -> Right $ StdinToStdout version | ||
( Stdin version, ToJson ) -> Right $ StdinToJson version | ||
( Stdin version, ToFile output ) -> Right $ StdinToFile version output | ||
( FromFiles first [], ToFile output ) -> Right $ FormatToFile first output | ||
( FromFiles first rest, UpdateInPlace ) -> Right $ FormatInPlace first rest | ||
( FromFiles _ _, ToFile _ ) -> Left SingleOutputWithMultipleInputs | ||
( FromFiles first [], ToJson ) -> Right $ FileToJson first | ||
( FromFiles _ _, ToJson ) -> Left SingleOutputWithMultipleInputs | ||
|
||
|
||
determineWhatToDoFromConfig :: Flags.Config -> Either [InputFileMessage] [FilePath] -> Either ErrorMessage WhatToDo | ||
determineWhatToDoFromConfig config resolvedInputFiles = | ||
determineWhatToDoFromConfig :: Flags.Config -> ElmVersion -> Either [InputFileMessage] [ElmFile] -> Either ErrorMessage WhatToDo | ||
determineWhatToDoFromConfig config defaultElmVersion resolvedInputFiles = | ||
do | ||
source <- determineSource (Flags._stdin config) resolvedInputFiles | ||
checkUpgradeVersion (Flags._upgrade config) (Flags._elmVersion config) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In 0.8.2, upgrade can actually use the auto-detected current directory elm version, which seems confusing to me (ie:
This check makes sure that a flag is provided, whatever the result of "current directory elm version" (which might be removed) or "per file version detection" (which is overridden by the flag). I'm not sure if this is the expected behavior. |
||
source <- determineSource (Flags._stdin config) (Flags._upgrade config) (Flags._elmVersion config) defaultElmVersion resolvedInputFiles | ||
destination <- determineDestination (Flags._output config) (Flags._validate config) (Flags._json config) | ||
determineWhatToDo source destination | ||
|
||
|
||
checkUpgradeVersion :: Bool -> Maybe ElmVersion -> Either ErrorMessage () | ||
checkUpgradeVersion upgrade elmVersionFlag = | ||
case (upgrade, elmVersionFlag) of | ||
(True, Just Elm_0_18) -> Right () | ||
(True, Just Elm_0_19) -> Right () | ||
(True, _) -> Left $ MustSpecifyVersionWithUpgrade Elm_0_19_Upgrade | ||
(False, _) -> Right () | ||
|
||
|
||
exitWithError :: World m => ErrorMessage -> m () | ||
exitWithError message = | ||
(putStrLnStderr $ Helpers.r $ message) | ||
>> exitFailure | ||
|
||
|
||
determineVersion :: ElmVersion -> Bool -> Either ErrorMessage ElmVersion | ||
determineVersion elmVersion upgrade = | ||
case (elmVersion, upgrade) of | ||
(Elm_0_18, True) -> | ||
Right Elm_0_18_Upgrade | ||
|
||
(Elm_0_19, True) -> | ||
Right Elm_0_19_Upgrade | ||
|
||
(_, True) -> | ||
Left $ MustSpecifyVersionWithUpgrade Elm_0_19_Upgrade | ||
|
||
(_, False) -> | ||
Right elmVersion | ||
|
||
|
||
elmFormatVersion :: String | ||
elmFormatVersion = | ||
ElmFormat.Version.asString | ||
|
@@ -222,9 +238,11 @@ main'' elmFormatVersion_ experimental_ args = | |
Just config -> | ||
do | ||
let autoYes = Flags._yes config | ||
resolvedInputFiles <- Execute.run (Execute.forHuman autoYes) $ resolveFiles (Flags._input config) | ||
currentDirectoryElmVersion <- Execute.run (Execute.forHuman autoYes) $ FS.findElmVersion "." | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this is useful anymore versus just using 0.19 as the default version. I kept current working directory global version detection to avoid behavior regressions, but I fail to see valid use cases for it. The only one I see is running in a 0.18 project with |
||
let defaultElmVersion = Maybe.fromMaybe Elm_0_19 currentDirectoryElmVersion; | ||
resolvedInputFiles <- Execute.run (Execute.forHuman autoYes) $ resolveFiles defaultElmVersion (Flags._input config) | ||
|
||
case determineWhatToDoFromConfig config resolvedInputFiles of | ||
case determineWhatToDoFromConfig config defaultElmVersion resolvedInputFiles of | ||
Left NoInputs -> | ||
(handleParseResult $ Flags.showHelpText elmFormatVersion_ experimental_) | ||
-- TODO: handleParseResult is exitSuccess, so we never get to exitFailure | ||
|
@@ -233,53 +251,23 @@ main'' elmFormatVersion_ experimental_ args = | |
Left message -> | ||
exitWithError message | ||
|
||
Right whatToDo -> do | ||
elmVersionChoice <- case Flags._elmVersion config of | ||
rlefevre marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Just v -> return $ Right v | ||
Nothing -> autoDetectElmVersion | ||
|
||
case elmVersionChoice of | ||
Left message -> | ||
putStr message *> exitFailure | ||
|
||
Right elmVersionChoice' -> do | ||
let elmVersionResult = determineVersion elmVersionChoice' (Flags._upgrade config) | ||
|
||
case elmVersionResult of | ||
Left message -> | ||
exitWithError message | ||
|
||
Right elmVersion -> | ||
do | ||
let run = case (Flags._validate config) of | ||
True -> Execute.run $ Execute.forMachine elmVersion True | ||
False -> Execute.run $ Execute.forHuman autoYes | ||
result <- run $ doIt elmVersion whatToDo | ||
if result | ||
then exitSuccess | ||
else exitFailure | ||
|
||
|
||
autoDetectElmVersion :: World m => m (Either String ElmVersion) | ||
autoDetectElmVersion = | ||
do | ||
hasElmPackageJson <- doesFileExist "elm-package.json" | ||
if hasElmPackageJson | ||
then | ||
do | ||
hasElmJson <- doesFileExist "elm.json" | ||
if hasElmJson | ||
then return $ Right Elm_0_19 | ||
else return $ Right Elm_0_18 | ||
else return $ Right Elm_0_19 | ||
Right whatToDo -> | ||
do | ||
let run = case (Flags._validate config) of | ||
True -> Execute.run $ Execute.forMachine True | ||
False -> Execute.run $ Execute.forHuman autoYes | ||
result <- run $ doIt whatToDo | ||
if result | ||
then exitSuccess | ||
else exitFailure | ||
|
||
|
||
validate :: ElmVersion -> (FilePath, Text.Text) -> Either InfoMessage () | ||
validate elmVersion (inputFile, inputText) = | ||
case Parse.parse elmVersion inputText of | ||
Result.Result _ (Result.Ok modu) -> | ||
if inputText /= Render.render elmVersion modu then | ||
Left $ FileWouldChange inputFile | ||
Left $ FileWouldChange inputFile elmVersion | ||
else | ||
Right () | ||
|
||
|
@@ -363,35 +351,36 @@ logErrorOr fn result = | |
fn value *> return True | ||
|
||
|
||
doIt :: (InputConsole f, OutputConsole f, InfoFormatter f, FileStore f, FileWriter f) => ElmVersion -> WhatToDo -> Free f Bool | ||
doIt elmVersion whatToDo = | ||
|
||
doIt :: (InputConsole f, OutputConsole f, InfoFormatter f, FileStore f, FileWriter f) => WhatToDo -> Free f Bool | ||
doIt whatToDo = | ||
case whatToDo of | ||
ValidateStdin -> | ||
ValidateStdin elmVersion -> | ||
(validate elmVersion <$> readStdin) >>= logError | ||
|
||
ValidateFiles first rest -> | ||
all id <$> mapM validateFile (first:rest) | ||
where validateFile file = (validate elmVersion <$> ElmFormat.readFile file) >>= logError | ||
where validateFile (FS.ElmFile elmVersion path) = (validate elmVersion <$> ElmFormat.readFile path) >>= logError | ||
|
||
StdinToStdout -> | ||
StdinToStdout elmVersion -> | ||
(fmap getOutputText <$> format elmVersion <$> readStdin) >>= logErrorOr OutputConsole.writeStdout | ||
|
||
StdinToFile outputFile -> | ||
StdinToFile elmVersion outputFile -> | ||
(fmap getOutputText <$> format elmVersion <$> readStdin) >>= logErrorOr (FileWriter.overwriteFile outputFile) | ||
|
||
FormatToFile inputFile outputFile -> | ||
FormatToFile (FS.ElmFile elmVersion inputFile) outputFile -> | ||
(fmap getOutputText <$> format elmVersion <$> ElmFormat.readFile inputFile) >>= logErrorOr (FileWriter.overwriteFile outputFile) | ||
|
||
FormatInPlace first rest -> | ||
do | ||
canOverwrite <- approve $ FilesWillBeOverwritten (first:rest) | ||
canOverwrite <- approve $ FilesWillBeOverwritten $ fmap FS.path (first:rest) | ||
if canOverwrite | ||
then all id <$> mapM formatFile (first:rest) | ||
else return True | ||
where | ||
formatFile file = (format elmVersion <$> ElmFormat.readFile file) >>= logErrorOr ElmFormat.updateFile | ||
formatFile (FS.ElmFile elmVersion path) = (format elmVersion <$> ElmFormat.readFile path) >>= logErrorOr ElmFormat.updateFile | ||
|
||
StdinToJson -> | ||
StdinToJson elmVersion -> | ||
(fmap (Text.pack . Text.JSON.encode . AST.Json.showModule) <$> parseModule elmVersion <$> readStdin) >>= logErrorOr OutputConsole.writeStdout | ||
|
||
-- TODO: this prints "Processing such-and-such-a-file.elm" which makes the JSON output invalid | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checkUpgradeVersion
makes actually impossible to use something else than 0.19.Still, the whole upgrade version code (check and upgrade) seems a little messy, but I'm not sure of the exact behavior we want, nor how to refactor this to avoid splitting the version check and the version upgrade later.