phoityne-vscode-0.0.1.0: app/Phoityne/IO/CUI/GHCiControl.hs
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE BinaryLiterals #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Phoityne.IO.CUI.GHCiControl where
-- モジュール
import Phoityne.Constant
import Phoityne.IO.Utility
-- システム
import System.Process
import System.IO
import System.Exit
import System.Log.Logger
import qualified Control.Exception as E
import qualified Data.List as L
-- |
--
--
data ExternalCommandData = ExternalCommandData
{
inExternalCommandData :: Maybe Handle
, outExternalCommandData :: Maybe Handle
, errExternalCommandData :: Maybe Handle
, procExternalCommandData :: Maybe ProcessHandle
}
-- |
--
--
defaultExternalCommandData :: ExternalCommandData
defaultExternalCommandData = ExternalCommandData Nothing Nothing Nothing Nothing
-- |
--
--
run :: String -> [String] -> Maybe FilePath -> IO ExternalCommandData
run cmd opts curDir = flip E.catches handlers $ do
debugM _LOG_NAME "run external command."
(fromHaskellHandle, toExternalHandle) <- createPipe
(fromExternalHandle, toHaskellHandle) <- createPipe
osEnc <- getReadHandleEncoding
hSetBuffering toHaskellHandle NoBuffering
hSetEncoding toHaskellHandle osEnc
hSetBuffering fromHaskellHandle NoBuffering
hSetEncoding fromHaskellHandle utf8
hSetBuffering toExternalHandle NoBuffering
hSetEncoding toExternalHandle utf8
hSetBuffering fromExternalHandle NoBuffering
hSetEncoding fromExternalHandle osEnc
debugM _LOG_NAME $ "external command : " ++ cmd ++ " " ++ (L.intercalate " " opts)
ghciProc <- runProcess cmd opts curDir Nothing (Just fromHaskellHandle) (Just toHaskellHandle) (Just toHaskellHandle)
return $ ExternalCommandData (Just toExternalHandle) (Just fromExternalHandle) (Just fromExternalHandle) (Just ghciProc)
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
criticalM _LOG_NAME ("run:" ++ show e)
return $ ExternalCommandData Nothing Nothing Nothing Nothing
-- |
--
--
waitExit :: ExternalCommandData -> IO ExitCode
waitExit (ExternalCommandData _ _ _ (Just ghciProc)) = flip E.catches handlers $ do
waitForProcess ghciProc
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
criticalM _LOG_NAME ("waitExit:" ++ show e)
return $ ExitFailure 2
waitExit _ = do
criticalM _LOG_NAME "waitExit"
return $ ExitFailure 2
-- |
--
--
writeLine :: ExternalCommandData -> String -> IO ()
writeLine (ExternalCommandData (Just ghciIn) _ _ _) cmd = flip E.catches handlers $ hIsOpen ghciIn >>= \case
False -> criticalM _LOG_NAME "[writeLine] handle not open."
True -> debugM _LOG_NAME ("[writeLine]" ++ cmd) >> hPutStrLn ghciIn cmd
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = criticalM _LOG_NAME ("writeLine:" ++ show e)
writeLine _ _ = criticalM _LOG_NAME "[writeLine] handle is nothing."
-- |
--
--
readWhile :: ExternalCommandData -> (String -> Bool) -> IO String
readWhile (ExternalCommandData _ (Just ghciOut) _ _) proc = flip E.catches handlers $ go []
where
go acc = hIsEOF ghciOut >>= \case
True -> return acc
False -> do
c <- hGetChar ghciOut
let acc' = acc ++ [c]
if proc acc' then go acc'
else return acc'
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = criticalM _LOG_NAME ("readWhile:" ++ show e) >> return ""
readWhile _ _ = criticalM _LOG_NAME "readWhile" >> return ""
-- |
--
--
readLineWhileIO :: ExternalCommandData -> ([String] -> IO Bool) -> IO [String]
readLineWhileIO (ExternalCommandData _ (Just ghciOut) _ _) proc = flip E.catches handlers $ go []
where
go acc = hIsEOF ghciOut >>= \case
True -> return acc
False -> do
l <- hGetLine ghciOut
let acc' = acc ++ [l]
proc acc' >>= \case
True -> go acc'
False -> return acc'
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = criticalM _LOG_NAME ("readLineWhileIO:" ++ show e) >> return []
readLineWhileIO _ _ = criticalM _LOG_NAME "readLineWhileIO" >> return []