From 5138754218e4f65c3e9d2ae58f4172fcd3c8b156 Mon Sep 17 00:00:00 2001 From: Charlon Date: Sat, 25 Jan 2025 12:47:39 +0100 Subject: [PATCH 1/6] Improve format command to match elm-format behavior --- extra/Lamdera/CLI/Format.hs | 229 ++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 extra/Lamdera/CLI/Format.hs diff --git a/extra/Lamdera/CLI/Format.hs b/extra/Lamdera/CLI/Format.hs new file mode 100644 index 00000000..83f38177 --- /dev/null +++ b/extra/Lamdera/CLI/Format.hs @@ -0,0 +1,229 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Lamdera.CLI.Format + ( Format(..) + , run + , command + ) where + +{- `lamdera format` functionality -} + +import qualified Data.Text as T +import qualified Data.Text.IO as TIO +import qualified Ext.ElmFormat as ElmFormat +import qualified System.Exit as Exit +import qualified System.IO as IO +import Terminal hiding (args) +import Terminal.Helpers +import qualified Text.PrettyPrint.ANSI.Leijen as P +import qualified Data.List as List +import qualified System.Directory as Dir +import qualified System.FilePath as FP + +data Format = Format + { _yes :: Bool + , _validate :: Bool + , _stdin :: Bool + , _output :: Maybe FilePath + , _help :: Bool + } + +yes_ :: Parser Bool +yes_ = + Parser + { _singular = "yes" + , _plural = "yes" + , _parser = \_ -> Just True + , _suggest = \_ -> return [] + , _examples = \_ -> return [] + } + +validate_ :: Parser Bool +validate_ = + Parser + { _singular = "validate" + , _plural = "validate" + , _parser = \_ -> Just True + , _suggest = \_ -> return [] + , _examples = \_ -> return [] + } + +stdin_ :: Parser Bool +stdin_ = + Parser + { _singular = "stdin" + , _plural = "stdin" + , _parser = \_ -> Just True + , _suggest = \_ -> return [] + , _examples = \_ -> return [] + } + +output_ :: Parser FilePath +output_ = + Parser + { _singular = "output" + , _plural = "outputs" + , _parser = Just + , _suggest = \_ -> return [] + , _examples = \_ -> return ["Main2.elm"] + } + +elmFileOrDir :: Parser FilePath +elmFileOrDir = + Parser + { _singular = "elm file or directory" + , _plural = "elm files or directories" + , _parser = Just -- Accept any path + , _suggest = \_ -> return [] + , _examples = \_ -> return ["Main.elm", "src/Main.elm"] + } + +command :: Terminal.Command +command = + let + summary = + "Format Elm source files." + + details = + "Usage: lamdera format [INPUT] [--output FILE] [--yes] [--validate] [--stdin]\n\n" ++ + " Format Elm source files." + + example = + stack + [ reflow "Examples:" + , P.indent 2 $ P.green "lamdera format Main.elm # formats Main.elm" + , P.indent 2 $ P.green "lamdera format Main.elm --output Main2.elm # formats Main.elm as Main2.elm" + , P.indent 2 $ P.green "lamdera format src/ # format all *.elm files in the src directory" + , "" + , reflow "Full guide to using elm-format at " + ] + + formatFlags = + flags Format + |-- onOff "yes" "Reply 'yes' to all automated prompts." + |-- onOff "validate" "Check if files are formatted without changing them." + |-- onOff "stdin" "Read from stdin, output to stdout." + |-- flag "output" output_ "Write output to FILE instead of overwriting the given source file." + |-- onOff "help" "Show this help text." + in + Terminal.Command "format" (Common summary) details example (zeroOrMore elmFileOrDir) formatFlags run + +run :: [FilePath] -> Format -> IO () +run inputs flags = + if _help flags || null inputs && not (_stdin flags) + then do + TIO.putStrLn "Usage: lamdera format [INPUT] [--output FILE] [--yes] [--validate] [--stdin]\n" + TIO.putStrLn " Format Elm source files.\n" + TIO.putStrLn "Available options:" + TIO.putStrLn " --help Show this help text" + TIO.putStrLn " --output FILE Write output to FILE instead of overwriting the given" + TIO.putStrLn " source file." + TIO.putStrLn " --yes Reply 'yes' to all automated prompts." + TIO.putStrLn " --validate Check if files are formatted without changing them." + TIO.putStrLn " --stdin Read from stdin, output to stdout.\n" + TIO.putStrLn "Note: All flags must use double dashes (--flag). Single dashes (-flag) are not supported.\n" + TIO.putStrLn "Examples:" + TIO.putStrLn " lamdera format Main.elm # formats Main.elm" + TIO.putStrLn " lamdera format Main.elm --output Main2.elm # formats Main.elm as Main2.elm" + TIO.putStrLn " lamdera format src/ # format all *.elm files in the src directory\n" + TIO.putStrLn "Full guide to using elm-format at " + Exit.exitSuccess + else case inputs of + [] | _stdin flags -> + do + input <- TIO.hGetContents IO.stdin + formatText flags input + [] -> + do + TIO.putStrLn "Please specify at least one .elm file to format." + Exit.exitFailure + paths -> + mapM_ (formatFile flags) paths + +formatText :: Format -> T.Text -> IO () +formatText flags input = + case ElmFormat.format input of + Right formatted -> + TIO.putStr formatted + Left err -> + do + TIO.putStrLn $ "Error: " <> err + Exit.exitFailure + +formatFile :: Format -> FilePath -> IO () +formatFile flags path = do + isDir <- Dir.doesDirectoryExist path + if isDir + then do + contents <- Dir.listDirectory path + let elmFiles = filter (FP.isExtensionOf "elm") contents + elmFilePaths <- findElmFiles path + if null elmFilePaths + then return () + else do + TIO.putStrLn "This will overwrite the following files to use Elm's preferred style:\n" + mapM_ (\f -> TIO.putStrLn $ " " <> T.pack f) elmFilePaths + TIO.putStrLn "\nThis cannot be undone! Make sure to back up these files before proceeding.\n" + if _yes flags + then formatFiles flags elmFilePaths + else do + TIO.putStrLn "Are you sure you want to overwrite these files with formatted versions? (y/n) " + answer <- getLine + case answer of + "Y" -> formatFiles flags elmFilePaths + "y" -> formatFiles flags elmFilePaths + "" -> formatFiles flags elmFilePaths + _ -> return () + else formatSingleFile flags path + +findElmFiles :: FilePath -> IO [FilePath] +findElmFiles dir = do + contents <- Dir.listDirectory dir + let baseName = FP.takeFileName dir + if baseName `elem` ["elm-stuff", "node_modules"] + then return [] + else do + paths <- mapM (\f -> do + let path = dir FP. f + isDir <- Dir.doesDirectoryExist path + if isDir + then findElmFiles path + else return [path | FP.isExtensionOf "elm" f] + ) contents + return $ concat paths + +formatFiles :: Format -> [FilePath] -> IO () +formatFiles flags = mapM_ (formatSingleFile flags) + +formatSingleFile :: Format -> FilePath -> IO () +formatSingleFile flags path = do + TIO.putStrLn $ "Processing file " <> T.pack path + input <- TIO.readFile path + case ElmFormat.format input of + Right formatted -> + if _validate flags + then + if input == formatted + then return () + else do + TIO.putStrLn $ "File " <> T.pack path <> " would be reformatted" + Exit.exitFailure + else case _output flags of + Just outputPath -> + TIO.writeFile outputPath formatted + Nothing -> + TIO.writeFile path formatted + Left err -> + do + TIO.putStrLn $ "Unable to parse file " <> T.pack path <> ": " <> err <> " To see a detailed explanation, run elm make on the file." + if _validate flags + then Exit.exitFailure + else return () + +stack :: [P.Doc] -> P.Doc +stack docs = + P.vcat $ List.intersperse "" docs + +reflow :: String -> P.Doc +reflow string = + P.fillSep $ map P.text $ words string \ No newline at end of file From 72e8b880744ab20fae810e15cfcb612cb51b771a Mon Sep 17 00:00:00 2001 From: Charlon Date: Sat, 25 Jan 2025 12:48:37 +0100 Subject: [PATCH 2/6] Improve format command to match elm-format behavior --- elm.cabal | 1 + extra/Lamdera/CLI.hs | 32 ++++++++++++++++++++++++++++++-- terminal/src/Main.hs | 1 + 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/elm.cabal b/elm.cabal index 8e2221d7..b3bafb3f 100644 --- a/elm.cabal +++ b/elm.cabal @@ -237,6 +237,7 @@ Executable lamdera -- CLI Experimental Lamdera.CLI.Annotate Lamdera.CLI.Interpreter + Lamdera.CLI.Format Lamdera.AppConfig Lamdera.Checks diff --git a/extra/Lamdera/CLI.hs b/extra/Lamdera/CLI.hs index dd777f78..ec33c18d 100644 --- a/extra/Lamdera/CLI.hs +++ b/extra/Lamdera/CLI.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} -module Lamdera.CLI (live, login, check, deploy, reset, update, annotate, eval) where +module Lamdera.CLI (live, login, check, deploy, reset, update, annotate, eval, format) where import Text.Read (readMaybe) import qualified Text.PrettyPrint.ANSI.Leijen as P @@ -16,6 +16,7 @@ import qualified Lamdera.CLI.Reset import qualified Lamdera.CLI.Update import qualified Lamdera.CLI.Annotate import qualified Lamdera.CLI.Interpreter +import qualified Lamdera.CLI.Format live :: Terminal.Command @@ -193,6 +194,33 @@ eval = Terminal.Command "eval" (Common summary) details example args noFlags Lamdera.CLI.Interpreter.run +-- FORMAT + + +format :: Terminal.Command +format = + let + summary = + "Format Elm source files." + + details = + "Usage: lamdera format [INPUT] [--output FILE] [--yes] [--validate] [--stdin]\n\n" ++ + " Format Elm source files." + + example = + stack + [ reflow "Examples:" + , P.vcat [ P.indent 2 $ P.green "lamdera format Main.el # formats Main.elm" + , P.indent 2 $ P.green "lamdera format Main.elm --output Main2.elm # formats Main.elm as Main2.elm" + , P.indent 2 $ P.green "lamdera format src/ # format all *.elm files in the src directory" + ] + , "" + , reflow "Full guide to using elm-format at " + ] + in + Lamdera.CLI.Format.command + + -- HELPERS @@ -203,4 +231,4 @@ stack docs = reflow :: String -> P.Doc reflow string = - P.fillSep $ map P.text $ words string + P.fillSep $ map P.text $ words string \ No newline at end of file diff --git a/terminal/src/Main.hs b/terminal/src/Main.hs index c5f48c04..b1c60e26 100644 --- a/terminal/src/Main.hs +++ b/terminal/src/Main.hs @@ -48,6 +48,7 @@ main = , Lamdera.CLI.update , Lamdera.CLI.annotate , Lamdera.CLI.eval + , Lamdera.CLI.format -- , reactor -- , bump -- , diff From c6b6db9cc4dcb7f3720497bb09c9cd44a847061c Mon Sep 17 00:00:00 2001 From: Charlon Date: Sat, 25 Jan 2025 15:47:51 +0100 Subject: [PATCH 3/6] Improve elm format error messages and file handling --- ext-common/Ext/ElmFormat.hs | 33 +++++++++++++++++++-------------- extra/Lamdera/CLI/Format.hs | 32 ++++++++++++-------------------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/ext-common/Ext/ElmFormat.hs b/ext-common/Ext/ElmFormat.hs index 205f372b..34826d49 100644 --- a/ext-common/Ext/ElmFormat.hs +++ b/ext-common/Ext/ElmFormat.hs @@ -9,6 +9,7 @@ module Ext.ElmFormat where import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text as Text +import System.IO (FilePath) import System.IO.Unsafe (unsafePerformIO) import qualified System.Process @@ -20,31 +21,35 @@ import qualified ElmFormat.Cli -- import qualified ElmFormat.Render.Text as Render import ElmVersion import ElmFormat.Messages +import Reporting.Annotation (Located(..), Region(..), Position(..)) +import CommandLine.InfoFormatter (ToConsole(..)) -formatWithEmbedded :: Text -> Either ElmFormat.Messages.InfoMessage Text -formatWithEmbedded inputText = do - ElmFormat.Cli.format ElmVersion.Elm_0_19 ("stdin:nofilepath", inputText) +formatWithEmbedded :: FilePath -> Text -> Either ElmFormat.Messages.InfoMessage Text +formatWithEmbedded filePath inputText = do + ElmFormat.Cli.format ElmVersion.Elm_0_19 (filePath, inputText) -format :: Text -> (Either Text Text) -format text = do - case formatWithEmbedded text of - Left err -> - Left $ Lamdera.show_ err - Right formatted -> - Right formatted +format :: FilePath -> Text -> (Either Text Text) +format filePath text = do + case formatWithEmbedded filePath text of + Left err -> Left $ toConsole err + Right formatted -> Right formatted formatOrPassthrough :: Text -> Text formatOrPassthrough text = do - case format text of + case format "stdin" text of Right formatted -> formatted - Left err -> do - -- let !_ = Lamdera.debug $ "🔥💅 warning: " <> show err - text + Left _ -> text +formatOrPassthroughFile :: FilePath -> Text -> Text +formatOrPassthroughFile filePath text = do + case format filePath text of + Right formatted -> formatted + Left _ -> text + -- Old versions that rely on local elm-format binary diff --git a/extra/Lamdera/CLI/Format.hs b/extra/Lamdera/CLI/Format.hs index 83f38177..f01f7fc8 100644 --- a/extra/Lamdera/CLI/Format.hs +++ b/extra/Lamdera/CLI/Format.hs @@ -142,7 +142,7 @@ run inputs flags = formatText :: Format -> T.Text -> IO () formatText flags input = - case ElmFormat.format input of + case ElmFormat.format "stdin" input of Right formatted -> TIO.putStr formatted Left err -> @@ -199,26 +199,18 @@ formatSingleFile :: Format -> FilePath -> IO () formatSingleFile flags path = do TIO.putStrLn $ "Processing file " <> T.pack path input <- TIO.readFile path - case ElmFormat.format input of - Right formatted -> - if _validate flags - then - if input == formatted - then return () - else do - TIO.putStrLn $ "File " <> T.pack path <> " would be reformatted" - Exit.exitFailure + case ElmFormat.format path input of + Right formatted -> + if _validate flags && formatted /= input + then do + TIO.putStrLn $ "File " <> T.pack path <> " would be reformatted" + Exit.exitFailure else case _output flags of - Just outputPath -> - TIO.writeFile outputPath formatted - Nothing -> - TIO.writeFile path formatted - Left err -> - do - TIO.putStrLn $ "Unable to parse file " <> T.pack path <> ": " <> err <> " To see a detailed explanation, run elm make on the file." - if _validate flags - then Exit.exitFailure - else return () + Just outputPath -> TIO.writeFile outputPath formatted + Nothing -> TIO.writeFile path formatted + Left err -> do + TIO.putStrLn err + Exit.exitFailure stack :: [P.Doc] -> P.Doc stack docs = From bc86e79e2992334ef05ece7e685c9aecfe2d9d8d Mon Sep 17 00:00:00 2001 From: Charlon Date: Sat, 25 Jan 2025 16:14:41 +0100 Subject: [PATCH 4/6] Match elm-format behavior for file formatting confirmation --- extra/Lamdera/CLI.hs | 22 +------------------- extra/Lamdera/CLI/Format.hs | 40 +++++++++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/extra/Lamdera/CLI.hs b/extra/Lamdera/CLI.hs index ec33c18d..b41f94f0 100644 --- a/extra/Lamdera/CLI.hs +++ b/extra/Lamdera/CLI.hs @@ -198,27 +198,7 @@ eval = format :: Terminal.Command -format = - let - summary = - "Format Elm source files." - - details = - "Usage: lamdera format [INPUT] [--output FILE] [--yes] [--validate] [--stdin]\n\n" ++ - " Format Elm source files." - - example = - stack - [ reflow "Examples:" - , P.vcat [ P.indent 2 $ P.green "lamdera format Main.el # formats Main.elm" - , P.indent 2 $ P.green "lamdera format Main.elm --output Main2.elm # formats Main.elm as Main2.elm" - , P.indent 2 $ P.green "lamdera format src/ # format all *.elm files in the src directory" - ] - , "" - , reflow "Full guide to using elm-format at " - ] - in - Lamdera.CLI.Format.command +format = Lamdera.CLI.Format.command -- HELPERS diff --git a/extra/Lamdera/CLI/Format.hs b/extra/Lamdera/CLI/Format.hs index f01f7fc8..00b9dae7 100644 --- a/extra/Lamdera/CLI/Format.hs +++ b/extra/Lamdera/CLI/Format.hs @@ -91,10 +91,10 @@ command = example = stack [ reflow "Examples:" - , P.indent 2 $ P.green "lamdera format Main.elm # formats Main.elm" - , P.indent 2 $ P.green "lamdera format Main.elm --output Main2.elm # formats Main.elm as Main2.elm" - , P.indent 2 $ P.green "lamdera format src/ # format all *.elm files in the src directory" - , "" + , P.vcat [ P.indent 2 $ P.green "lamdera format Main.elm # formats Main.elm" + , P.indent 2 $ P.green "lamdera format Main.elm --output Main2.elm # formats Main.elm as Main2.elm" + , P.indent 2 $ P.green "lamdera format src/ # format all *.elm files in the src directory" + ] , reflow "Full guide to using elm-format at " ] @@ -138,7 +138,31 @@ run inputs flags = TIO.putStrLn "Please specify at least one .elm file to format." Exit.exitFailure paths -> - mapM_ (formatFile flags) paths + do + elmFilePaths <- concat <$> mapM expandPath paths + if null elmFilePaths + then return () + else do + TIO.putStrLn "This will overwrite the following files to use Elm's preferred style:\n" + mapM_ (\f -> TIO.putStrLn $ " " <> T.pack f) elmFilePaths + TIO.putStrLn "\nThis cannot be undone! Make sure to back up these files before proceeding.\n" + if _yes flags + then formatFiles flags elmFilePaths + else do + TIO.putStrLn "Are you sure you want to overwrite these files with formatted versions? (y/n) " + answer <- getLine + case answer of + "Y" -> formatFiles flags elmFilePaths + "y" -> formatFiles flags elmFilePaths + "" -> formatFiles flags elmFilePaths + _ -> return () + +expandPath :: FilePath -> IO [FilePath] +expandPath path = do + isDir <- Dir.doesDirectoryExist path + if isDir + then findElmFiles path + else return [path | FP.isExtensionOf "elm" path] formatText :: Format -> T.Text -> IO () formatText flags input = @@ -155,8 +179,6 @@ formatFile flags path = do isDir <- Dir.doesDirectoryExist path if isDir then do - contents <- Dir.listDirectory path - let elmFiles = filter (FP.isExtensionOf "elm") contents elmFilePaths <- findElmFiles path if null elmFilePaths then return () @@ -210,7 +232,9 @@ formatSingleFile flags path = do Nothing -> TIO.writeFile path formatted Left err -> do TIO.putStrLn err - Exit.exitFailure + if _validate flags + then Exit.exitFailure + else return () stack :: [P.Doc] -> P.Doc stack docs = From 76cdecd530e8e30f4abc7bc5b81bdc38bc30a909 Mon Sep 17 00:00:00 2001 From: Charlon Date: Wed, 9 Apr 2025 20:08:22 +0300 Subject: [PATCH 5/6] Address PR feedback: use stdin:nofilepath and remove redundant note --- ext-common/Ext/ElmFormat.hs | 2 +- extra/Lamdera/CLI/Format.hs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ext-common/Ext/ElmFormat.hs b/ext-common/Ext/ElmFormat.hs index 34826d49..b9e342e8 100644 --- a/ext-common/Ext/ElmFormat.hs +++ b/ext-common/Ext/ElmFormat.hs @@ -39,7 +39,7 @@ format filePath text = do formatOrPassthrough :: Text -> Text formatOrPassthrough text = do - case format "stdin" text of + case format "stdin:nofilepath" text of Right formatted -> formatted Left _ -> text diff --git a/extra/Lamdera/CLI/Format.hs b/extra/Lamdera/CLI/Format.hs index 00b9dae7..caa53088 100644 --- a/extra/Lamdera/CLI/Format.hs +++ b/extra/Lamdera/CLI/Format.hs @@ -121,7 +121,6 @@ run inputs flags = TIO.putStrLn " --yes Reply 'yes' to all automated prompts." TIO.putStrLn " --validate Check if files are formatted without changing them." TIO.putStrLn " --stdin Read from stdin, output to stdout.\n" - TIO.putStrLn "Note: All flags must use double dashes (--flag). Single dashes (-flag) are not supported.\n" TIO.putStrLn "Examples:" TIO.putStrLn " lamdera format Main.elm # formats Main.elm" TIO.putStrLn " lamdera format Main.elm --output Main2.elm # formats Main.elm as Main2.elm" @@ -166,7 +165,7 @@ expandPath path = do formatText :: Format -> T.Text -> IO () formatText flags input = - case ElmFormat.format "stdin" input of + case ElmFormat.format "stdin:nofilepath" input of Right formatted -> TIO.putStr formatted Left err -> From ab00039468e74b5e5f81f18d89ed0af3991ee284 Mon Sep 17 00:00:00 2001 From: Charlon Date: Mon, 30 Jun 2025 11:35:21 +0200 Subject: [PATCH 6/6] Special-case format command to delegate directly to elm-format As suggested in PR review, the format command now bypasses Lamdera's normal CLI processing and delegates directly to elm-format's mainIO. This ensures perfect compatibility with all elm-format features and reduces code duplication. Changes: - Intercept "format" command early in Terminal.hs - Add minimal FormatDirect module that calls elm-format - Remove 244-line Format.hs implementation - Keep stub command for help display consistency --- extra/Lamdera/CLI.hs | 15 ++- extra/Lamdera/CLI/Format.hs | 247 +----------------------------------- terminal/impl/Terminal.hs | 29 +++-- 3 files changed, 36 insertions(+), 255 deletions(-) diff --git a/extra/Lamdera/CLI.hs b/extra/Lamdera/CLI.hs index b41f94f0..d9101d95 100644 --- a/extra/Lamdera/CLI.hs +++ b/extra/Lamdera/CLI.hs @@ -16,7 +16,6 @@ import qualified Lamdera.CLI.Reset import qualified Lamdera.CLI.Update import qualified Lamdera.CLI.Annotate import qualified Lamdera.CLI.Interpreter -import qualified Lamdera.CLI.Format live :: Terminal.Command @@ -195,10 +194,22 @@ eval = -- FORMAT +-- @LAMDERA Stub - intercepted in Terminal.hs format :: Terminal.Command -format = Lamdera.CLI.Format.command +format = + let + summary = + "Format Elm source files." + + details = + "The `format` command is handled directly by elm-format for perfect compatibility." + + example = + reflow "See elm-format documentation at " + in + Terminal.Command "format" (Common summary) details example noArgs noFlags (\_ _ -> return ()) -- HELPERS diff --git a/extra/Lamdera/CLI/Format.hs b/extra/Lamdera/CLI/Format.hs index caa53088..564ebb74 100644 --- a/extra/Lamdera/CLI/Format.hs +++ b/extra/Lamdera/CLI/Format.hs @@ -1,244 +1,9 @@ -{-# LANGUAGE OverloadedStrings #-} - -module Lamdera.CLI.Format - ( Format(..) - , run - , command +module Lamdera.CLI.Format + ( run ) where -{- `lamdera format` functionality -} - -import qualified Data.Text as T -import qualified Data.Text.IO as TIO -import qualified Ext.ElmFormat as ElmFormat -import qualified System.Exit as Exit -import qualified System.IO as IO -import Terminal hiding (args) -import Terminal.Helpers -import qualified Text.PrettyPrint.ANSI.Leijen as P -import qualified Data.List as List -import qualified System.Directory as Dir -import qualified System.FilePath as FP - -data Format = Format - { _yes :: Bool - , _validate :: Bool - , _stdin :: Bool - , _output :: Maybe FilePath - , _help :: Bool - } - -yes_ :: Parser Bool -yes_ = - Parser - { _singular = "yes" - , _plural = "yes" - , _parser = \_ -> Just True - , _suggest = \_ -> return [] - , _examples = \_ -> return [] - } - -validate_ :: Parser Bool -validate_ = - Parser - { _singular = "validate" - , _plural = "validate" - , _parser = \_ -> Just True - , _suggest = \_ -> return [] - , _examples = \_ -> return [] - } - -stdin_ :: Parser Bool -stdin_ = - Parser - { _singular = "stdin" - , _plural = "stdin" - , _parser = \_ -> Just True - , _suggest = \_ -> return [] - , _examples = \_ -> return [] - } - -output_ :: Parser FilePath -output_ = - Parser - { _singular = "output" - , _plural = "outputs" - , _parser = Just - , _suggest = \_ -> return [] - , _examples = \_ -> return ["Main2.elm"] - } - -elmFileOrDir :: Parser FilePath -elmFileOrDir = - Parser - { _singular = "elm file or directory" - , _plural = "elm files or directories" - , _parser = Just -- Accept any path - , _suggest = \_ -> return [] - , _examples = \_ -> return ["Main.elm", "src/Main.elm"] - } - -command :: Terminal.Command -command = - let - summary = - "Format Elm source files." - - details = - "Usage: lamdera format [INPUT] [--output FILE] [--yes] [--validate] [--stdin]\n\n" ++ - " Format Elm source files." - - example = - stack - [ reflow "Examples:" - , P.vcat [ P.indent 2 $ P.green "lamdera format Main.elm # formats Main.elm" - , P.indent 2 $ P.green "lamdera format Main.elm --output Main2.elm # formats Main.elm as Main2.elm" - , P.indent 2 $ P.green "lamdera format src/ # format all *.elm files in the src directory" - ] - , reflow "Full guide to using elm-format at " - ] - - formatFlags = - flags Format - |-- onOff "yes" "Reply 'yes' to all automated prompts." - |-- onOff "validate" "Check if files are formatted without changing them." - |-- onOff "stdin" "Read from stdin, output to stdout." - |-- flag "output" output_ "Write output to FILE instead of overwriting the given source file." - |-- onOff "help" "Show this help text." - in - Terminal.Command "format" (Common summary) details example (zeroOrMore elmFileOrDir) formatFlags run - -run :: [FilePath] -> Format -> IO () -run inputs flags = - if _help flags || null inputs && not (_stdin flags) - then do - TIO.putStrLn "Usage: lamdera format [INPUT] [--output FILE] [--yes] [--validate] [--stdin]\n" - TIO.putStrLn " Format Elm source files.\n" - TIO.putStrLn "Available options:" - TIO.putStrLn " --help Show this help text" - TIO.putStrLn " --output FILE Write output to FILE instead of overwriting the given" - TIO.putStrLn " source file." - TIO.putStrLn " --yes Reply 'yes' to all automated prompts." - TIO.putStrLn " --validate Check if files are formatted without changing them." - TIO.putStrLn " --stdin Read from stdin, output to stdout.\n" - TIO.putStrLn "Examples:" - TIO.putStrLn " lamdera format Main.elm # formats Main.elm" - TIO.putStrLn " lamdera format Main.elm --output Main2.elm # formats Main.elm as Main2.elm" - TIO.putStrLn " lamdera format src/ # format all *.elm files in the src directory\n" - TIO.putStrLn "Full guide to using elm-format at " - Exit.exitSuccess - else case inputs of - [] | _stdin flags -> - do - input <- TIO.hGetContents IO.stdin - formatText flags input - [] -> - do - TIO.putStrLn "Please specify at least one .elm file to format." - Exit.exitFailure - paths -> - do - elmFilePaths <- concat <$> mapM expandPath paths - if null elmFilePaths - then return () - else do - TIO.putStrLn "This will overwrite the following files to use Elm's preferred style:\n" - mapM_ (\f -> TIO.putStrLn $ " " <> T.pack f) elmFilePaths - TIO.putStrLn "\nThis cannot be undone! Make sure to back up these files before proceeding.\n" - if _yes flags - then formatFiles flags elmFilePaths - else do - TIO.putStrLn "Are you sure you want to overwrite these files with formatted versions? (y/n) " - answer <- getLine - case answer of - "Y" -> formatFiles flags elmFilePaths - "y" -> formatFiles flags elmFilePaths - "" -> formatFiles flags elmFilePaths - _ -> return () - -expandPath :: FilePath -> IO [FilePath] -expandPath path = do - isDir <- Dir.doesDirectoryExist path - if isDir - then findElmFiles path - else return [path | FP.isExtensionOf "elm" path] - -formatText :: Format -> T.Text -> IO () -formatText flags input = - case ElmFormat.format "stdin:nofilepath" input of - Right formatted -> - TIO.putStr formatted - Left err -> - do - TIO.putStrLn $ "Error: " <> err - Exit.exitFailure - -formatFile :: Format -> FilePath -> IO () -formatFile flags path = do - isDir <- Dir.doesDirectoryExist path - if isDir - then do - elmFilePaths <- findElmFiles path - if null elmFilePaths - then return () - else do - TIO.putStrLn "This will overwrite the following files to use Elm's preferred style:\n" - mapM_ (\f -> TIO.putStrLn $ " " <> T.pack f) elmFilePaths - TIO.putStrLn "\nThis cannot be undone! Make sure to back up these files before proceeding.\n" - if _yes flags - then formatFiles flags elmFilePaths - else do - TIO.putStrLn "Are you sure you want to overwrite these files with formatted versions? (y/n) " - answer <- getLine - case answer of - "Y" -> formatFiles flags elmFilePaths - "y" -> formatFiles flags elmFilePaths - "" -> formatFiles flags elmFilePaths - _ -> return () - else formatSingleFile flags path - -findElmFiles :: FilePath -> IO [FilePath] -findElmFiles dir = do - contents <- Dir.listDirectory dir - let baseName = FP.takeFileName dir - if baseName `elem` ["elm-stuff", "node_modules"] - then return [] - else do - paths <- mapM (\f -> do - let path = dir FP. f - isDir <- Dir.doesDirectoryExist path - if isDir - then findElmFiles path - else return [path | FP.isExtensionOf "elm" f] - ) contents - return $ concat paths - -formatFiles :: Format -> [FilePath] -> IO () -formatFiles flags = mapM_ (formatSingleFile flags) - -formatSingleFile :: Format -> FilePath -> IO () -formatSingleFile flags path = do - TIO.putStrLn $ "Processing file " <> T.pack path - input <- TIO.readFile path - case ElmFormat.format path input of - Right formatted -> - if _validate flags && formatted /= input - then do - TIO.putStrLn $ "File " <> T.pack path <> " would be reformatted" - Exit.exitFailure - else case _output flags of - Just outputPath -> TIO.writeFile outputPath formatted - Nothing -> TIO.writeFile path formatted - Left err -> do - TIO.putStrLn err - if _validate flags - then Exit.exitFailure - else return () - -stack :: [P.Doc] -> P.Doc -stack docs = - P.vcat $ List.intersperse "" docs +import qualified ElmFormat.Cli -reflow :: String -> P.Doc -reflow string = - P.fillSep $ map P.text $ words string \ No newline at end of file +-- | Delegate to elm-format +run :: [String] -> IO () +run args = ElmFormat.Cli.mainIO args \ No newline at end of file diff --git a/terminal/impl/Terminal.hs b/terminal/impl/Terminal.hs index 27e9e178..695829ab 100644 --- a/terminal/impl/Terminal.hs +++ b/terminal/impl/Terminal.hs @@ -31,6 +31,7 @@ import qualified Terminal.Error as Error import qualified Lamdera.Version +import qualified Lamdera.CLI.Format import qualified Sanity -- COMMAND @@ -86,21 +87,25 @@ app intro outro commands = Exit.exitSuccess command : chunks -> - do case List.find (\cmd -> toName cmd == command) commands of - Nothing -> - Error.exitWithUnknown command (map toName commands) + -- @LAMDERA format intercept + if command == "format" then + do Lamdera.CLI.Format.run chunks + else + do case List.find (\cmd -> toName cmd == command) commands of + Nothing -> + Error.exitWithUnknown command (map toName commands) - Just (Command _ _ details example args_ flags_ callback) -> - if elem "--help" chunks then - Error.exitWithHelp (Just command) details example args_ flags_ + Just (Command _ _ details example args_ flags_ callback) -> + if elem "--help" chunks then + Error.exitWithHelp (Just command) details example args_ flags_ - else - case snd $ Chomp.chomp Nothing chunks args_ flags_ of - Right (argsValue, flagsValue) -> - callback argsValue flagsValue + else + case snd $ Chomp.chomp Nothing chunks args_ flags_ of + Right (argsValue, flagsValue) -> + callback argsValue flagsValue - Left err -> - Error.exitWithError err + Left err -> + Error.exitWithError err