hindent-5.2.2: src/main/Main.hs
{-# LANGUAGE Unsafe #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Main entry point to hindent.
--
-- hindent
module Main where
import Control.Applicative
import Control.Exception
import Control.Monad
import qualified Data.ByteString as S
import qualified Data.ByteString.Builder as S
import qualified Data.ByteString.Lazy.Char8 as L8
import Data.Maybe
import Data.Text (Text)
import qualified Data.Text as T
import Data.Version (showVersion)
import qualified Data.Yaml as Y
import Descriptive
import Descriptive.Options
import Foreign.C.Error
import GHC.IO.Exception
import HIndent
import HIndent.Types
import Language.Haskell.Exts hiding (Style, style)
import Path
import qualified Path.Find as Path
import qualified Path.IO as Path
import Paths_hindent (version)
import qualified System.Directory as IO
import System.Environment
import qualified System.IO as IO
import Text.Read
-- | Main entry point.
main :: IO ()
main = do
args <- getArgs
config <- getConfig
case consume (options config) (map T.pack args) of
Succeeded (style, exts, mfilepath) ->
case mfilepath of
Just filepath -> do
text <- S.readFile filepath
case reformat style (Just exts) text of
Left e -> error (filepath ++ ": " ++ e)
Right out -> unless (L8.fromStrict text == S.toLazyByteString out) $ do
tmpDir <- IO.getTemporaryDirectory
(fp, h) <- IO.openTempFile tmpDir "hindent.hs"
L8.hPutStr h (S.toLazyByteString out)
IO.hFlush h
IO.hClose h
let exdev e =
if ioe_errno e == Just ((\(Errno a) -> a) eXDEV)
then IO.copyFile fp filepath >> IO.removeFile fp
else throw e
IO.copyPermissions filepath fp
IO.renameFile fp filepath `catch` exdev
Nothing ->
L8.interact
(either error S.toLazyByteString . reformat style (Just exts) . L8.toStrict)
Failed (Wrap (Stopped Version) _) ->
putStrLn ("hindent " ++ showVersion version)
Failed (Wrap (Stopped Help) _) -> putStrLn (help defaultConfig)
_ -> error (help defaultConfig)
-- | Read config from a config file, or return 'defaultConfig'.
getConfig :: IO Config
getConfig = do
cur <- Path.getCurrentDir
homeDir <- Path.getHomeDir
mfile <-
Path.findFileUp cur ((== ".hindent.yaml") . toFilePath . filename) (Just homeDir)
case mfile of
Nothing -> return defaultConfig
Just file -> do
result <- Y.decodeFileEither (toFilePath file)
case result of
Left e -> error (show e)
Right config -> return config
-- | Help text.
help :: Config -> String
help config =
"hindent " ++
T.unpack (textDescription (describe (options config) [])) ++
"\nVersion " ++
showVersion version ++
"\n" ++
"Default --indent-size is 2. Specify --indent-size 4 if you prefer that.\n" ++
"-X to pass extensions e.g. -XMagicHash etc.\n" ++
"The --style option is now ignored, but preserved for backwards-compatibility.\n" ++
"Johan Tibell is the default and only style."
-- | Options that stop the argument parser.
data Stoppers
= Version
| Help
deriving (Show)
-- | Program options.
options
:: Monad m
=> Config -> Consumer [Text] (Option Stoppers) m (Config, [Extension], Maybe FilePath)
options config = ver *> ((,,) <$> style <*> exts <*> file)
where
ver =
stop (flag "version" "Print the version" Version) *>
stop (flag "help" "Show help" Help)
style =
makeStyle <$>
fmap
(const config)
(optional
(constant "--style" "Style to print with" () *> anyString "STYLE")) <*>
lineLen <*>
indentSpaces <*>
trailingNewline <*>
sortImports
exts = fmap getExtensions (many (prefix "X" "Language extension"))
indentSpaces =
fmap
(>>= (readMaybe . T.unpack))
(optional
(arg "indent-size" "Indentation size in spaces, default: 4" <|>
arg "tab-size" "Same as --indent-size, for compatibility"))
lineLen =
fmap
(>>= (readMaybe . T.unpack))
(optional (arg "line-length" "Desired length of lines"))
trailingNewline =
optional
(constant "--no-force-newline" "Don't force a trailing newline" False)
sortImports =
optional
(constant "--sort-imports" "Sort imports in groups" True <|>
constant "--no-sort-imports" "Don't sort imports" False)
makeStyle s mlen tabs trailing imports =
s
{ configMaxColumns = fromMaybe (configMaxColumns s) mlen
, configIndentSpaces = fromMaybe (configIndentSpaces s) tabs
, configTrailingNewline = fromMaybe (configTrailingNewline s) trailing
, configSortImports = fromMaybe (configSortImports s) imports
}
file = fmap (fmap T.unpack) (optional (anyString "[<filename>]"))