{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedStrings #-}
module Ide.Plugin.Ormolu
( descriptor
, provider
, LogEvent
)
where
import Control.Exception (Handler (..), IOException,
SomeException (..), catches,
handle)
import Control.Monad.Except (runExceptT, throwError)
import Control.Monad.Extra
import Control.Monad.Trans
import Control.Monad.Trans.Except (ExceptT (..), mapExceptT)
import Data.Functor ((<&>))
import Data.List (intercalate)
import Data.Maybe (catMaybes)
import Data.Text (Text)
import qualified Data.Text as T
import Development.IDE hiding (pluginHandlers)
import Development.IDE.GHC.Compat (hsc_dflags, moduleNameString)
import qualified Development.IDE.GHC.Compat as D
import qualified Development.IDE.GHC.Compat.Util as S
import GHC.LanguageExtensions.Type
import Ide.Plugin.Error (PluginError (PluginInternalError))
import Ide.Plugin.Properties
import Ide.PluginUtils
import Ide.Types hiding (Config)
import qualified Ide.Types as Types
import Language.LSP.Protocol.Types
import Language.LSP.Server hiding (defaultConfig)
import Ormolu
import System.Exit
import System.FilePath
import System.Process.Run (cwd, proc)
import System.Process.Text (readCreateProcessWithExitCode)
import Text.Read (readMaybe)
descriptor :: Recorder (WithPriority LogEvent) -> PluginId -> PluginDescriptor IdeState
descriptor :: Recorder (WithPriority LogEvent)
-> PluginId -> PluginDescriptor IdeState
descriptor Recorder (WithPriority LogEvent)
recorder PluginId
plId =
(PluginId -> Text -> PluginDescriptor IdeState
forall ideState. PluginId -> Text -> PluginDescriptor ideState
defaultPluginDescriptor PluginId
plId Text
desc)
{ pluginHandlers = mkFormattingHandlers $ provider recorder plId,
pluginConfigDescriptor = defaultConfigDescriptor {configCustomConfig = mkCustomConfig properties}
}
where
desc :: Text
desc = Text
"Provides formatting of Haskell files via ormolu. Built with ormolu-" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> VERSION_ormolu
properties :: Properties '[ 'PropertyKey "external" 'TBoolean]
properties :: Properties '[ 'PropertyKey "external" 'TBoolean]
properties =
Properties '[]
emptyProperties
Properties '[]
-> (Properties '[]
-> Properties '[ 'PropertyKey "external" 'TBoolean])
-> Properties '[ 'PropertyKey "external" 'TBoolean]
forall a b. a -> (a -> b) -> b
& KeyNameProxy "external"
-> Text
-> Bool
-> Properties '[]
-> Properties '[ 'PropertyKey "external" 'TBoolean]
forall (s :: Symbol) (r :: [PropertyKey]).
(KnownSymbol s, NotElem s r) =>
KeyNameProxy s
-> Text
-> Bool
-> Properties r
-> Properties ('PropertyKey s 'TBoolean : r)
defineBooleanProperty
#external
Text
"Call out to an external \"ormolu\" executable, rather than using the bundled library"
Bool
False
provider :: Recorder (WithPriority LogEvent) -> PluginId -> FormattingHandler IdeState
provider :: Recorder (WithPriority LogEvent)
-> PluginId -> FormattingHandler IdeState
provider Recorder (WithPriority LogEvent)
recorder PluginId
plId IdeState
ideState Maybe ProgressToken
token FormattingType
typ Text
contents NormalizedFilePath
fp FormattingOptions
_ = HandlerM Config (Either PluginError ([TextEdit] |? Null))
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT (HandlerM Config (Either PluginError ([TextEdit] |? Null))
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null))
-> HandlerM Config (Either PluginError ([TextEdit] |? Null))
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
forall a b. (a -> b) -> a -> b
$ Text
-> Maybe ProgressToken
-> ProgressCancellable
-> ((Text -> HandlerM Config ())
-> HandlerM Config (Either PluginError ([TextEdit] |? Null)))
-> HandlerM Config (Either PluginError ([TextEdit] |? Null))
forall config a.
Text
-> Maybe ProgressToken
-> ProgressCancellable
-> ((Text -> HandlerM config ()) -> HandlerM config a)
-> HandlerM config a
pluginWithIndefiniteProgress Text
title Maybe ProgressToken
token ProgressCancellable
Cancellable (((Text -> HandlerM Config ())
-> HandlerM Config (Either PluginError ([TextEdit] |? Null)))
-> HandlerM Config (Either PluginError ([TextEdit] |? Null)))
-> ((Text -> HandlerM Config ())
-> HandlerM Config (Either PluginError ([TextEdit] |? Null)))
-> HandlerM Config (Either PluginError ([TextEdit] |? Null))
forall a b. (a -> b) -> a -> b
$ \Text -> HandlerM Config ()
_updater -> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
-> HandlerM Config (Either PluginError ([TextEdit] |? Null))
forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
-> HandlerM Config (Either PluginError ([TextEdit] |? Null)))
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
-> HandlerM Config (Either PluginError ([TextEdit] |? Null))
forall a b. (a -> b) -> a -> b
$ do
[[Char]]
fileOpts <-
[[Char]] -> (HscEnvEq -> [[Char]]) -> Maybe HscEnvEq -> [[Char]]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (DynFlags -> [[Char]]
fromDyn (DynFlags -> [[Char]])
-> (HscEnvEq -> DynFlags) -> HscEnvEq -> [[Char]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HscEnv -> DynFlags
hsc_dflags (HscEnv -> DynFlags)
-> (HscEnvEq -> HscEnv) -> HscEnvEq -> DynFlags
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HscEnvEq -> HscEnv
hscEnv)
(Maybe HscEnvEq -> [[Char]])
-> ExceptT PluginError (HandlerM Config) (Maybe HscEnvEq)
-> ExceptT PluginError (HandlerM Config) [[Char]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO (Maybe HscEnvEq)
-> ExceptT PluginError (HandlerM Config) (Maybe HscEnvEq)
forall a. IO a -> ExceptT PluginError (HandlerM Config) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO ([Char]
-> IdeState -> Action (Maybe HscEnvEq) -> IO (Maybe HscEnvEq)
forall a. [Char] -> IdeState -> Action a -> IO a
runAction [Char]
"Ormolu" IdeState
ideState (Action (Maybe HscEnvEq) -> IO (Maybe HscEnvEq))
-> Action (Maybe HscEnvEq) -> IO (Maybe HscEnvEq)
forall a b. (a -> b) -> a -> b
$ GhcSession -> NormalizedFilePath -> Action (Maybe HscEnvEq)
forall k v.
IdeRule k v =>
k -> NormalizedFilePath -> Action (Maybe v)
use GhcSession
GhcSession NormalizedFilePath
fp)
Bool
useCLI <- IO Bool -> ExceptT PluginError (HandlerM Config) Bool
forall a. IO a -> ExceptT PluginError (HandlerM Config) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Bool -> ExceptT PluginError (HandlerM Config) Bool)
-> IO Bool -> ExceptT PluginError (HandlerM Config) Bool
forall a b. (a -> b) -> a -> b
$ [Char] -> IdeState -> Action Bool -> IO Bool
forall a. [Char] -> IdeState -> Action a -> IO a
runAction [Char]
"Ormolu" IdeState
ideState (Action Bool -> IO Bool) -> Action Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$ KeyNameProxy "external"
-> PluginId
-> Properties '[ 'PropertyKey "external" 'TBoolean]
-> Action
(ToHsType
(FindByKeyName "external" '[ 'PropertyKey "external" 'TBoolean]))
forall (s :: Symbol) (k :: PropertyKey) (t :: PropertyType)
(r :: [PropertyKey]).
HasProperty s k t r =>
KeyNameProxy s -> PluginId -> Properties r -> Action (ToHsType t)
usePropertyAction KeyNameProxy "external"
#external PluginId
plId Properties '[ 'PropertyKey "external" 'TBoolean]
properties
if Bool
useCLI
then (IO (Either PluginError ([TextEdit] |? Null))
-> HandlerM Config (Either PluginError ([TextEdit] |? Null)))
-> ExceptT PluginError IO ([TextEdit] |? Null)
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
forall (m :: * -> *) e a (n :: * -> *) e' b.
(m (Either e a) -> n (Either e' b))
-> ExceptT e m a -> ExceptT e' n b
mapExceptT IO (Either PluginError ([TextEdit] |? Null))
-> HandlerM Config (Either PluginError ([TextEdit] |? Null))
forall a. IO a -> HandlerM Config a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (ExceptT PluginError IO ([TextEdit] |? Null)
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null))
-> ExceptT PluginError IO ([TextEdit] |? Null)
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
forall a b. (a -> b) -> a -> b
$ IO (Either PluginError ([TextEdit] |? Null))
-> ExceptT PluginError IO ([TextEdit] |? Null)
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT
(IO (Either PluginError ([TextEdit] |? Null))
-> ExceptT PluginError IO ([TextEdit] |? Null))
-> IO (Either PluginError ([TextEdit] |? Null))
-> ExceptT PluginError IO ([TextEdit] |? Null)
forall a b. (a -> b) -> a -> b
$ forall e a. Exception e => (e -> IO a) -> IO a -> IO a
handle @IOException
(Either PluginError ([TextEdit] |? Null)
-> IO (Either PluginError ([TextEdit] |? Null))
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either PluginError ([TextEdit] |? Null)
-> IO (Either PluginError ([TextEdit] |? Null)))
-> (IOException -> Either PluginError ([TextEdit] |? Null))
-> IOException
-> IO (Either PluginError ([TextEdit] |? Null))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PluginError -> Either PluginError ([TextEdit] |? Null)
forall a b. a -> Either a b
Left (PluginError -> Either PluginError ([TextEdit] |? Null))
-> (IOException -> PluginError)
-> IOException
-> Either PluginError ([TextEdit] |? Null)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> PluginError
PluginInternalError (Text -> PluginError)
-> (IOException -> Text) -> IOException -> PluginError
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> Text
T.pack ([Char] -> Text) -> (IOException -> [Char]) -> IOException -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IOException -> [Char]
forall a. Show a => a -> [Char]
show)
(IO (Either PluginError ([TextEdit] |? Null))
-> IO (Either PluginError ([TextEdit] |? Null)))
-> IO (Either PluginError ([TextEdit] |? Null))
-> IO (Either PluginError ([TextEdit] |? Null))
forall a b. (a -> b) -> a -> b
$ ExceptT PluginError IO ([TextEdit] |? Null)
-> IO (Either PluginError ([TextEdit] |? Null))
forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (ExceptT PluginError IO ([TextEdit] |? Null)
-> IO (Either PluginError ([TextEdit] |? Null)))
-> ExceptT PluginError IO ([TextEdit] |? Null)
-> IO (Either PluginError ([TextEdit] |? Null))
forall a b. (a -> b) -> a -> b
$ [[Char]] -> ExceptT PluginError IO ([TextEdit] |? Null)
cliHandler [[Char]]
fileOpts
else do
Recorder (WithPriority LogEvent)
-> Priority -> LogEvent -> ExceptT PluginError (HandlerM Config) ()
forall (m :: * -> *) msg.
(HasCallStack, MonadIO m) =>
Recorder (WithPriority msg) -> Priority -> msg -> m ()
logWith Recorder (WithPriority LogEvent)
recorder Priority
Debug (LogEvent -> ExceptT PluginError (HandlerM Config) ())
-> LogEvent -> ExceptT PluginError (HandlerM Config) ()
forall a b. (a -> b) -> a -> b
$ [Char] -> LogEvent
LogCompiledInVersion VERSION_ormolu
let
fmt :: T.Text -> Config RegionIndices -> IO (Either SomeException T.Text)
fmt :: Text -> Config RegionIndices -> IO (Either SomeException Text)
fmt Text
cont Config RegionIndices
conf = (IO (Either SomeException Text)
-> [Handler (Either SomeException Text)]
-> IO (Either SomeException Text))
-> [Handler (Either SomeException Text)]
-> IO (Either SomeException Text)
-> IO (Either SomeException Text)
forall a b c. (a -> b -> c) -> b -> a -> c
flip IO (Either SomeException Text)
-> [Handler (Either SomeException Text)]
-> IO (Either SomeException Text)
forall a. IO a -> [Handler a] -> IO a
catches [Handler (Either SomeException Text)]
forall {b}. [Handler (Either SomeException b)]
handlers (IO (Either SomeException Text) -> IO (Either SomeException Text))
-> IO (Either SomeException Text) -> IO (Either SomeException Text)
forall a b. (a -> b) -> a -> b
$ do
#if MIN_VERSION_ormolu(0,5,3)
Maybe CabalInfo
cabalInfo <- [Char] -> IO CabalSearchResult
forall (m :: * -> *). MonadIO m => [Char] -> m CabalSearchResult
getCabalInfoForSourceFile [Char]
fp' IO CabalSearchResult
-> (CabalSearchResult -> Maybe CabalInfo) -> IO (Maybe CabalInfo)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \case
CabalSearchResult
CabalNotFound -> Maybe CabalInfo
forall a. Maybe a
Nothing
CabalDidNotMention CabalInfo
cabalInfo -> CabalInfo -> Maybe CabalInfo
forall a. a -> Maybe a
Just CabalInfo
cabalInfo
CabalFound CabalInfo
cabalInfo -> CabalInfo -> Maybe CabalInfo
forall a. a -> Maybe a
Just CabalInfo
cabalInfo
#if MIN_VERSION_ormolu(0,7,0)
(FixityOverrides
fixityOverrides, ModuleReexports
moduleReexports) <- [Char] -> IO (FixityOverrides, ModuleReexports)
forall (m :: * -> *).
MonadIO m =>
[Char] -> m (FixityOverrides, ModuleReexports)
getDotOrmoluForSourceFile [Char]
fp'
let conf' :: Config RegionIndices
conf' = SourceType
-> Maybe CabalInfo
-> Maybe FixityOverrides
-> Maybe ModuleReexports
-> Config RegionIndices
-> Config RegionIndices
forall region.
SourceType
-> Maybe CabalInfo
-> Maybe FixityOverrides
-> Maybe ModuleReexports
-> Config region
-> Config region
refineConfig SourceType
ModuleSource Maybe CabalInfo
cabalInfo (FixityOverrides -> Maybe FixityOverrides
forall a. a -> Maybe a
Just FixityOverrides
fixityOverrides) (ModuleReexports -> Maybe ModuleReexports
forall a. a -> Maybe a
Just ModuleReexports
moduleReexports) Config RegionIndices
conf
#else
fixityOverrides <- traverse getFixityOverridesForSourceFile cabalInfo
let conf' = refineConfig ModuleSource cabalInfo fixityOverrides conf
#endif
let cont' :: Text
cont' = Text
cont
#else
let conf' = conf
cont' = T.unpack cont
#endif
Text -> Either SomeException Text
forall a b. b -> Either a b
Right (Text -> Either SomeException Text)
-> IO Text -> IO (Either SomeException Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Config RegionIndices -> [Char] -> Text -> IO Text
forall (m :: * -> *).
MonadIO m =>
Config RegionIndices -> [Char] -> Text -> m Text
ormolu Config RegionIndices
conf' [Char]
fp' Text
cont'
handlers :: [Handler (Either SomeException b)]
handlers =
[ (OrmoluException -> IO (Either SomeException b))
-> Handler (Either SomeException b)
forall a e. Exception e => (e -> IO a) -> Handler a
Handler ((OrmoluException -> IO (Either SomeException b))
-> Handler (Either SomeException b))
-> (OrmoluException -> IO (Either SomeException b))
-> Handler (Either SomeException b)
forall a b. (a -> b) -> a -> b
$ Either SomeException b -> IO (Either SomeException b)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either SomeException b -> IO (Either SomeException b))
-> (OrmoluException -> Either SomeException b)
-> OrmoluException
-> IO (Either SomeException b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SomeException -> Either SomeException b
forall a b. a -> Either a b
Left (SomeException -> Either SomeException b)
-> (OrmoluException -> SomeException)
-> OrmoluException
-> Either SomeException b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall e. Exception e => e -> SomeException
SomeException @OrmoluException
, (IOException -> IO (Either SomeException b))
-> Handler (Either SomeException b)
forall a e. Exception e => (e -> IO a) -> Handler a
Handler ((IOException -> IO (Either SomeException b))
-> Handler (Either SomeException b))
-> (IOException -> IO (Either SomeException b))
-> Handler (Either SomeException b)
forall a b. (a -> b) -> a -> b
$ Either SomeException b -> IO (Either SomeException b)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either SomeException b -> IO (Either SomeException b))
-> (IOException -> Either SomeException b)
-> IOException
-> IO (Either SomeException b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SomeException -> Either SomeException b
forall a b. a -> Either a b
Left (SomeException -> Either SomeException b)
-> (IOException -> SomeException)
-> IOException
-> Either SomeException b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall e. Exception e => e -> SomeException
SomeException @IOException
]
Either SomeException Text
res <- IO (Either SomeException Text)
-> ExceptT
PluginError (HandlerM Config) (Either SomeException Text)
forall a. IO a -> ExceptT PluginError (HandlerM Config) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either SomeException Text)
-> ExceptT
PluginError (HandlerM Config) (Either SomeException Text))
-> IO (Either SomeException Text)
-> ExceptT
PluginError (HandlerM Config) (Either SomeException Text)
forall a b. (a -> b) -> a -> b
$ Text -> Config RegionIndices -> IO (Either SomeException Text)
fmt Text
contents Config RegionIndices
defaultConfig { cfgDynOptions = map DynOption fileOpts, cfgRegion = region }
Either SomeException Text
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
ret Either SomeException Text
res
where
fp' :: [Char]
fp' = NormalizedFilePath -> [Char]
fromNormalizedFilePath NormalizedFilePath
fp
region :: RegionIndices
region :: RegionIndices
region = case FormattingType
typ of
FormattingType
FormatText ->
Maybe Int -> Maybe Int -> RegionIndices
RegionIndices Maybe Int
forall a. Maybe a
Nothing Maybe Int
forall a. Maybe a
Nothing
FormatRange (Range (Position UInt
sl UInt
_) (Position UInt
el UInt
_)) ->
Maybe Int -> Maybe Int -> RegionIndices
RegionIndices (Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> Int -> Maybe Int
forall a b. (a -> b) -> a -> b
$ UInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (UInt -> Int) -> UInt -> Int
forall a b. (a -> b) -> a -> b
$ UInt
sl UInt -> UInt -> UInt
forall a. Num a => a -> a -> a
+ UInt
1) (Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> Int -> Maybe Int
forall a b. (a -> b) -> a -> b
$ UInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (UInt -> Int) -> UInt -> Int
forall a b. (a -> b) -> a -> b
$ UInt
el UInt -> UInt -> UInt
forall a. Num a => a -> a -> a
+ UInt
1)
title :: Text
title = [Char] -> Text
T.pack ([Char] -> Text) -> [Char] -> Text
forall a b. (a -> b) -> a -> b
$ [Char]
"Formatting " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char] -> [Char]
takeFileName (NormalizedFilePath -> [Char]
fromNormalizedFilePath NormalizedFilePath
fp)
ret :: Either SomeException T.Text -> ExceptT PluginError (HandlerM Types.Config) ([TextEdit] |? Null)
ret :: Either SomeException Text
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
ret (Left SomeException
err) = PluginError
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
forall a. PluginError -> ExceptT PluginError (HandlerM Config) a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (PluginError
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null))
-> PluginError
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
forall a b. (a -> b) -> a -> b
$ Text -> PluginError
PluginInternalError (Text -> PluginError) -> ([Char] -> Text) -> [Char] -> PluginError
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> Text
T.pack ([Char] -> PluginError) -> [Char] -> PluginError
forall a b. (a -> b) -> a -> b
$ [Char]
"ormoluCmd: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ SomeException -> [Char]
forall a. Show a => a -> [Char]
show SomeException
err
ret (Right Text
new) = ([TextEdit] |? Null)
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
forall a. a -> ExceptT PluginError (HandlerM Config) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (([TextEdit] |? Null)
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null))
-> ([TextEdit] |? Null)
-> ExceptT PluginError (HandlerM Config) ([TextEdit] |? Null)
forall a b. (a -> b) -> a -> b
$ [TextEdit] -> [TextEdit] |? Null
forall a b. a -> a |? b
InL ([TextEdit] -> [TextEdit] |? Null)
-> [TextEdit] -> [TextEdit] |? Null
forall a b. (a -> b) -> a -> b
$ Text -> Text -> [TextEdit]
makeDiffTextEdit Text
contents Text
new
fromDyn :: D.DynFlags -> [String]
fromDyn :: DynFlags -> [[Char]]
fromDyn DynFlags
df =
let
pp :: [[Char]]
pp =
let p :: [Char]
p = Settings -> [Char]
D.sPgm_F (Settings -> [Char]) -> Settings -> [Char]
forall a b. (a -> b) -> a -> b
$ DynFlags -> Settings
D.settings DynFlags
df
in [[Char]
"-pgmF=" [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
p | Bool -> Bool
not ([Char] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Char]
p)]
pm :: [[Char]]
pm = ([Char]
"-fplugin=" <>) ([Char] -> [Char])
-> (ModuleName -> [Char]) -> ModuleName -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ModuleName -> [Char]
moduleNameString (ModuleName -> [Char]) -> [ModuleName] -> [[Char]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> DynFlags -> [ModuleName]
D.pluginModNames DynFlags
df
ex :: [[Char]]
ex = Extension -> [Char]
showExtension (Extension -> [Char]) -> [Extension] -> [[Char]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> EnumSet Extension -> [Extension]
forall a. Enum a => EnumSet a -> [a]
S.toList (DynFlags -> EnumSet Extension
D.extensionFlags DynFlags
df)
in [[Char]]
pp [[Char]] -> [[Char]] -> [[Char]]
forall a. Semigroup a => a -> a -> a
<> [[Char]]
pm [[Char]] -> [[Char]] -> [[Char]]
forall a. Semigroup a => a -> a -> a
<> [[Char]]
ex
cliHandler :: [String] -> ExceptT PluginError IO ([TextEdit] |? Null)
cliHandler :: [[Char]] -> ExceptT PluginError IO ([TextEdit] |? Null)
cliHandler [[Char]]
fileOpts = do
CLIVersionInfo{Bool
noCabal :: Bool
noCabal :: CLIVersionInfo -> Bool
noCabal} <- do
(ExitCode
exitCode, Text
out, Text
_err) <- IO (ExitCode, Text, Text)
-> ExceptT PluginError IO (ExitCode, Text, Text)
forall a. IO a -> ExceptT PluginError IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (ExitCode, Text, Text)
-> ExceptT PluginError IO (ExitCode, Text, Text))
-> IO (ExitCode, Text, Text)
-> ExceptT PluginError IO (ExitCode, Text, Text)
forall a b. (a -> b) -> a -> b
$ CreateProcess -> Text -> IO (ExitCode, Text, Text)
readCreateProcessWithExitCode ( [Char] -> [[Char]] -> CreateProcess
proc [Char]
"ormolu" [[Char]
"--version"] ) Text
""
let version :: Maybe [Int]
version = do
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ ExitCode
exitCode ExitCode -> ExitCode -> Bool
forall a. Eq a => a -> a -> Bool
== ExitCode
ExitSuccess
Text
"ormolu" : Text
v : [Text]
_ <- [Text] -> Maybe [Text]
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Text] -> Maybe [Text]) -> [Text] -> Maybe [Text]
forall a b. (a -> b) -> a -> b
$ Text -> [Text]
T.words Text
out
(Text -> Maybe Int) -> [Text] -> Maybe [Int]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse (forall a. Read a => [Char] -> Maybe a
readMaybe @Int ([Char] -> Maybe Int) -> (Text -> [Char]) -> Text -> Maybe Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Char]
T.unpack) ([Text] -> Maybe [Int]) -> [Text] -> Maybe [Int]
forall a b. (a -> b) -> a -> b
$ HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
T.splitOn Text
"." Text
v
case Maybe [Int]
version of
Just [Int]
v -> do
Recorder (WithPriority LogEvent)
-> Priority -> LogEvent -> ExceptT PluginError IO ()
forall (m :: * -> *) msg.
(HasCallStack, MonadIO m) =>
Recorder (WithPriority msg) -> Priority -> msg -> m ()
logWith Recorder (WithPriority LogEvent)
recorder Priority
Debug (LogEvent -> ExceptT PluginError IO ())
-> LogEvent -> ExceptT PluginError IO ()
forall a b. (a -> b) -> a -> b
$ [Int] -> LogEvent
LogExternalVersion [Int]
v
CLIVersionInfo -> ExceptT PluginError IO CLIVersionInfo
forall a. a -> ExceptT PluginError IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure CLIVersionInfo
{ noCabal :: Bool
noCabal = [Int]
v [Int] -> [Int] -> Bool
forall a. Ord a => a -> a -> Bool
>= [Int
0, Int
7]
}
Maybe [Int]
Nothing -> do
Recorder (WithPriority LogEvent)
-> Priority -> LogEvent -> ExceptT PluginError IO ()
forall (m :: * -> *) msg.
(HasCallStack, MonadIO m) =>
Recorder (WithPriority msg) -> Priority -> msg -> m ()
logWith Recorder (WithPriority LogEvent)
recorder Priority
Debug (LogEvent -> ExceptT PluginError IO ())
-> LogEvent -> ExceptT PluginError IO ()
forall a b. (a -> b) -> a -> b
$ [Int] -> LogEvent
LogExternalVersion []
Recorder (WithPriority LogEvent)
-> Priority -> LogEvent -> ExceptT PluginError IO ()
forall (m :: * -> *) msg.
(HasCallStack, MonadIO m) =>
Recorder (WithPriority msg) -> Priority -> msg -> m ()
logWith Recorder (WithPriority LogEvent)
recorder Priority
Warning (LogEvent -> ExceptT PluginError IO ())
-> LogEvent -> ExceptT PluginError IO ()
forall a b. (a -> b) -> a -> b
$ Text -> LogEvent
NoVersion Text
out
CLIVersionInfo -> ExceptT PluginError IO CLIVersionInfo
forall a. a -> ExceptT PluginError IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure CLIVersionInfo
{ noCabal :: Bool
noCabal = Bool
True
}
(ExitCode
exitCode, Text
out, Text
err) <- do
let commandArgs :: [[Char]]
commandArgs = ([Char] -> [Char]) -> [[Char]] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map ([Char]
"-o" <>) [[Char]]
fileOpts
[[Char]] -> [[Char]] -> [[Char]]
forall a. Semigroup a => a -> a -> a
<> (if Bool
noCabal then [[Char]
"--no-cabal"] else [[Char]
"--stdin-input-file", [Char]
fp'])
[[Char]] -> [[Char]] -> [[Char]]
forall a. Semigroup a => a -> a -> a
<> [Maybe [Char]] -> [[Char]]
forall a. [Maybe a] -> [a]
catMaybes
[ ([Char]
"--start-line=" <>) ([Char] -> [Char]) -> (Int -> [Char]) -> Int -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> [Char]
forall a. Show a => a -> [Char]
show (Int -> [Char]) -> Maybe Int -> Maybe [Char]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> RegionIndices -> Maybe Int
regionStartLine RegionIndices
region
, ([Char]
"--end-line=" <>) ([Char] -> [Char]) -> (Int -> [Char]) -> Int -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> [Char]
forall a. Show a => a -> [Char]
show (Int -> [Char]) -> Maybe Int -> Maybe [Char]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> RegionIndices -> Maybe Int
regionEndLine RegionIndices
region
]
cwd :: [Char]
cwd = [Char] -> [Char]
takeDirectory [Char]
fp'
Recorder (WithPriority LogEvent)
-> Priority -> LogEvent -> ExceptT PluginError IO ()
forall (m :: * -> *) msg.
(HasCallStack, MonadIO m) =>
Recorder (WithPriority msg) -> Priority -> msg -> m ()
logWith Recorder (WithPriority LogEvent)
recorder Priority
Debug (LogEvent -> ExceptT PluginError IO ())
-> LogEvent -> ExceptT PluginError IO ()
forall a b. (a -> b) -> a -> b
$ [[Char]] -> [Char] -> LogEvent
LogOrmoluCommand [[Char]]
commandArgs [Char]
cwd
IO (ExitCode, Text, Text)
-> ExceptT PluginError IO (ExitCode, Text, Text)
forall a. IO a -> ExceptT PluginError IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (ExitCode, Text, Text)
-> ExceptT PluginError IO (ExitCode, Text, Text))
-> IO (ExitCode, Text, Text)
-> ExceptT PluginError IO (ExitCode, Text, Text)
forall a b. (a -> b) -> a -> b
$ CreateProcess -> Text -> IO (ExitCode, Text, Text)
readCreateProcessWithExitCode ([Char] -> [[Char]] -> CreateProcess
proc [Char]
"ormolu" [[Char]]
commandArgs) {cwd = Just cwd} Text
contents
case ExitCode
exitCode of
ExitCode
ExitSuccess -> do
Bool -> ExceptT PluginError IO () -> ExceptT PluginError IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Text -> Bool
T.null Text
err) (ExceptT PluginError IO () -> ExceptT PluginError IO ())
-> ExceptT PluginError IO () -> ExceptT PluginError IO ()
forall a b. (a -> b) -> a -> b
$ Recorder (WithPriority LogEvent)
-> Priority -> LogEvent -> ExceptT PluginError IO ()
forall (m :: * -> *) msg.
(HasCallStack, MonadIO m) =>
Recorder (WithPriority msg) -> Priority -> msg -> m ()
logWith Recorder (WithPriority LogEvent)
recorder Priority
Debug (LogEvent -> ExceptT PluginError IO ())
-> LogEvent -> ExceptT PluginError IO ()
forall a b. (a -> b) -> a -> b
$ Text -> LogEvent
StdErr Text
err
([TextEdit] |? Null) -> ExceptT PluginError IO ([TextEdit] |? Null)
forall a. a -> ExceptT PluginError IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (([TextEdit] |? Null)
-> ExceptT PluginError IO ([TextEdit] |? Null))
-> ([TextEdit] |? Null)
-> ExceptT PluginError IO ([TextEdit] |? Null)
forall a b. (a -> b) -> a -> b
$ [TextEdit] -> [TextEdit] |? Null
forall a b. a -> a |? b
InL ([TextEdit] -> [TextEdit] |? Null)
-> [TextEdit] -> [TextEdit] |? Null
forall a b. (a -> b) -> a -> b
$ Text -> Text -> [TextEdit]
makeDiffTextEdit Text
contents Text
out
ExitFailure Int
n -> do
Recorder (WithPriority LogEvent)
-> Priority -> LogEvent -> ExceptT PluginError IO ()
forall (m :: * -> *) msg.
(HasCallStack, MonadIO m) =>
Recorder (WithPriority msg) -> Priority -> msg -> m ()
logWith Recorder (WithPriority LogEvent)
recorder Priority
Info (LogEvent -> ExceptT PluginError IO ())
-> LogEvent -> ExceptT PluginError IO ()
forall a b. (a -> b) -> a -> b
$ Text -> LogEvent
StdErr Text
err
PluginError -> ExceptT PluginError IO ([TextEdit] |? Null)
forall a. PluginError -> ExceptT PluginError IO a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (PluginError -> ExceptT PluginError IO ([TextEdit] |? Null))
-> PluginError -> ExceptT PluginError IO ([TextEdit] |? Null)
forall a b. (a -> b) -> a -> b
$ Text -> PluginError
PluginInternalError (Text -> PluginError) -> Text -> PluginError
forall a b. (a -> b) -> a -> b
$ Text
"Ormolu failed with exit code " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [Char] -> Text
T.pack (Int -> [Char]
forall a. Show a => a -> [Char]
show Int
n)
newtype CLIVersionInfo = CLIVersionInfo
{ CLIVersionInfo -> Bool
noCabal :: Bool
}
data LogEvent
= NoVersion Text
| StdErr Text
| LogCompiledInVersion String
| LogExternalVersion [Int]
| LogOrmoluCommand [String] FilePath
deriving (Int -> LogEvent -> [Char] -> [Char]
[LogEvent] -> [Char] -> [Char]
LogEvent -> [Char]
(Int -> LogEvent -> [Char] -> [Char])
-> (LogEvent -> [Char])
-> ([LogEvent] -> [Char] -> [Char])
-> Show LogEvent
forall a.
(Int -> a -> [Char] -> [Char])
-> (a -> [Char]) -> ([a] -> [Char] -> [Char]) -> Show a
$cshowsPrec :: Int -> LogEvent -> [Char] -> [Char]
showsPrec :: Int -> LogEvent -> [Char] -> [Char]
$cshow :: LogEvent -> [Char]
show :: LogEvent -> [Char]
$cshowList :: [LogEvent] -> [Char] -> [Char]
showList :: [LogEvent] -> [Char] -> [Char]
Show)
instance Pretty LogEvent where
pretty :: forall ann. LogEvent -> Doc ann
pretty = \case
NoVersion Text
t -> Doc ann
"Couldn't get Ormolu version:" Doc ann -> Doc ann -> Doc ann
forall a. Semigroup a => a -> a -> a
<> Doc ann
forall ann. Doc ann
line Doc ann -> Doc ann -> Doc ann
forall a. Semigroup a => a -> a -> a
<> Int -> Doc ann -> Doc ann
forall ann. Int -> Doc ann -> Doc ann
indent Int
2 (Text -> Doc ann
forall ann. Text -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty Text
t)
StdErr Text
t -> Doc ann
"Ormolu stderr:" Doc ann -> Doc ann -> Doc ann
forall a. Semigroup a => a -> a -> a
<> Doc ann
forall ann. Doc ann
line Doc ann -> Doc ann -> Doc ann
forall a. Semigroup a => a -> a -> a
<> Int -> Doc ann -> Doc ann
forall ann. Int -> Doc ann -> Doc ann
indent Int
2 (Text -> Doc ann
forall ann. Text -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty Text
t)
LogCompiledInVersion [Char]
v -> Doc ann
"Using compiled in ormolu-" Doc ann -> Doc ann -> Doc ann
forall a. Semigroup a => a -> a -> a
<> [Char] -> Doc ann
forall ann. [Char] -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty [Char]
v
LogExternalVersion [Int]
v ->
Doc ann
"Using external ormolu"
Doc ann -> Doc ann -> Doc ann
forall a. Semigroup a => a -> a -> a
<> if [Int] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Int]
v then Doc ann
"" else Doc ann
"-"
Doc ann -> Doc ann -> Doc ann
forall a. Semigroup a => a -> a -> a
<> [Char] -> Doc ann
forall ann. [Char] -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty ([Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"." ([[Char]] -> [Char]) -> [[Char]] -> [Char]
forall a b. (a -> b) -> a -> b
$ (Int -> [Char]) -> [Int] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map Int -> [Char]
forall a. Show a => a -> [Char]
show [Int]
v)
LogOrmoluCommand [[Char]]
commandArgs [Char]
cwd -> Doc ann
"Running: `ormolu " Doc ann -> Doc ann -> Doc ann
forall a. Semigroup a => a -> a -> a
<> [Char] -> Doc ann
forall ann. [Char] -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty ([[Char]] -> [Char]
unwords [[Char]]
commandArgs) Doc ann -> Doc ann -> Doc ann
forall a. Semigroup a => a -> a -> a
<> Doc ann
"` in directory " Doc ann -> Doc ann -> Doc ann
forall a. Semigroup a => a -> a -> a
<> [Char] -> Doc ann
forall ann. [Char] -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty [Char]
cwd
showExtension :: Extension -> String
showExtension :: Extension -> [Char]
showExtension Extension
Cpp = [Char]
"-XCPP"
showExtension Extension
other = [Char]
"-X" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Extension -> [Char]
forall a. Show a => a -> [Char]
show Extension
other