on d82d607 help sign in
ormolu/app/Main.hs
152 lines 4.1 kB history raw
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE RecordWildCards #-}
4{-# LANGUAGE TemplateHaskell #-}
6module Main (main) where
8import Control.Monad
9import Data.List (intercalate)
10import Data.Version (showVersion)
11import Development.GitRev
12import Options.Applicative
13import Ormolu
14import Paths_ormolu (version)
15import System.Exit (ExitCode (..), exitWith)
16import System.IO (hPutStrLn, stderr)
17import qualified Data.Text.IO as TIO
18import qualified Data.Yaml as Yaml
20-- | Entry point of the program.
22main :: IO ()
23main = withPrettyOrmoluExceptions $ do
24 Opts {..} <- execParser optsParserInfo
25 config <- case optConfigFile of
26 Nothing -> return optConfig
27 Just path -> do
28 config <- Yaml.decodeFileThrow path
29 return (config <> optConfig)
30 r <- case optInputFile of
31 "-" -> ormoluStdin config
32 inputFile -> ormoluFile config inputFile
33 let notForStdin = do
34 when (optInputFile == "-") $ do
35 hPutStrLn
36 stderr
37 "This feature is not supported when input comes from stdin."
38 -- 101 is different from all the other exit codes we already use.
39 exitWith (ExitFailure 101)
40 case optMode of
41 Stdout ->
42 TIO.putStr r
43 InPlace -> do
44 notForStdin
45 TIO.writeFile optInputFile r
46 Check -> do
47 notForStdin
48 r' <- TIO.readFile optInputFile
49 when (r /= r') $
50 -- 100 is different to all the other exit code that are emitted
51 -- either from an 'OrmoluException' or from 'error' and
52 -- 'notImplemented'.
53 exitWith (ExitFailure 100)
55----------------------------------------------------------------------------
56-- Command line options parsing.
58data Opts = Opts
59 { optMode :: !Mode
60 -- ^ Mode of operation
61 , optConfigFile :: !(Maybe FilePath)
62 -- ^ Location of configuration file (optional)
63 , optConfig :: !Config
64 -- ^ Ormolu 'Config'
65 , optInputFile :: !FilePath
66 -- ^ Input source file or stdin ("-")
67 }
69-- | Mode of operation.
71data Mode
72 = Stdout -- ^ Output formatted source code to stdout
73 | InPlace -- ^ Overwrite original file
74 | Check -- ^ Exit with non-zero status code if
75 -- source is not already formatted
76 deriving (Eq, Show)
78optsParserInfo :: ParserInfo Opts
79optsParserInfo = info (helper <*> ver <*> optsParser) . mconcat $
80 [ fullDesc
81 , progDesc ""
82 , header ""
83 ]
84 where
85 ver :: Parser (a -> a)
86 ver = infoOption verStr . mconcat $
87 [ long "version"
88 , short 'v'
89 , help "Print version of the program"
90 ]
91 verStr = intercalate "\n"
92 [ unwords
93 [ "ormolu"
94 , showVersion version
95 , $gitBranch
96 , $gitHash
97 ]
98 , "using ghc " ++ VERSION_ghc
99 ]
101optsParser :: Parser Opts
102optsParser = Opts
103 <$> (option parseMode . mconcat)
104 [ long "mode"
105 , short 'm'
106 , metavar "MODE"
107 , value Stdout
108 , help "Mode of operation: 'stdout', 'inplace', or 'check'"
109 ]
110 <*> (optional . strOption . mconcat)
111 [ long "config"
112 , short 'c'
113 , metavar "CONFIG"
114 , help "Location of configuration file"
115 ]
116 <*> configParser
117 <*> (strArgument . mconcat)
118 [ metavar "FILE"
119 , value "-"
120 , help "Haskell source file to format or stdin (default)"
121 ]
123configParser :: Parser Config
124configParser = Config
125 <$> (fmap (fmap DynOption) . many . strOption . mconcat)
126 [ long "ghc-opt"
127 , short 'o'
128 , metavar "OPT"
129 , help "GHC options to enable (e.g. language extensions)"
130 ]
131 <*> (switch . mconcat)
132 [ long "unsafe"
133 , short 'u'
134 , help "Do formatting faster but without automatic detection of defects"
135 ]
136 <*> (switch . mconcat)
137 [ long "debug"
138 , short 'd'
139 , help "Output information useful for debugging"
140 ]
142----------------------------------------------------------------------------
143-- Helpers
145-- | Parse 'Mode'.
147parseMode :: ReadM Mode
148parseMode = eitherReader $ \case
149 "stdout" -> Right Stdout
150 "inplace" -> Right InPlace
151 "check" -> Right Check
152 s -> Left $ "unknown mode: " ++ s