{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} -- | This module contains the implementation of the @dhall format@ subcommand module Dhall.Format ( -- * Format Format(..) , FormatMode(..) , format ) where import Control.Exception (Exception) import Data.Monoid ((<>)) import Dhall.Pretty (CharacterSet(..), annToAnsiStyle, layoutOpts) import Dhall.Util (Censor, Input(..)) import qualified Data.Text.Prettyprint.Doc as Pretty import qualified Data.Text.Prettyprint.Doc.Render.Terminal as Pretty.Terminal import qualified Data.Text.Prettyprint.Doc.Render.Text as Pretty.Text import qualified Control.Exception import qualified Data.Text.IO import qualified Dhall.Pretty import qualified Dhall.Util import qualified System.Console.ANSI import qualified System.IO data NotFormatted = NotFormatted deriving (Exception) instance Show NotFormatted where show _ = "" -- | Arguments to the `format` subcommand data Format = Format { characterSet :: CharacterSet , censor :: Censor , formatMode :: FormatMode } {-| The `format` subcommand can either `Modify` its input or simply `Check` that the input is already formatted -} data FormatMode = Modify { inplace :: Input } | Check { path :: Input } -- | Implementation of the @dhall format@ subcommand format :: Format -> IO () format (Format {..}) = case formatMode of Modify {..} -> case inplace of File file -> do (header, expr) <- Dhall.Util.getExpressionAndHeader censor (File file) let doc = Pretty.pretty header <> Pretty.unAnnotate (Dhall.Pretty.prettyCharacterSet characterSet expr) <> "\n" System.IO.withFile file System.IO.WriteMode (\handle -> do Pretty.Terminal.renderIO handle (Pretty.layoutSmart layoutOpts doc)) StandardInput -> do (header, expr) <- Dhall.Util.getExpressionAndHeader censor StandardInput let doc = Pretty.pretty header <> Dhall.Pretty.prettyCharacterSet characterSet expr <> "\n" supportsANSI <- System.Console.ANSI.hSupportsANSI System.IO.stdout if supportsANSI then Pretty.Terminal.renderIO System.IO.stdout (fmap annToAnsiStyle (Pretty.layoutSmart layoutOpts doc)) else Pretty.Terminal.renderIO System.IO.stdout (Pretty.layoutSmart layoutOpts (Pretty.unAnnotate doc)) Check {..} -> do originalText <- case path of File file -> Data.Text.IO.readFile file StandardInput -> Data.Text.IO.getContents (header, expr) <- Dhall.Util.getExpressionAndHeader censor path let doc = Pretty.pretty header <> Pretty.unAnnotate (Dhall.Pretty.prettyCharacterSet characterSet expr) <> "\n" let formattedText = Pretty.Text.renderStrict (Pretty.layoutSmart layoutOpts doc) if originalText == formattedText then return () else Control.Exception.throwIO NotFormatted