mirror of
https://github.com/NorfairKing/feedback.git
synced 2024-11-27 00:47:13 +03:00
tore out the tui
This commit is contained in:
parent
ab3195cceb
commit
05a2f8fcad
@ -5,7 +5,7 @@
|
|||||||
Working on nix code?
|
Working on nix code?
|
||||||
|
|
||||||
```
|
```
|
||||||
feedback nix-build
|
feedback -- nix-build --no-out-link
|
||||||
```
|
```
|
||||||
|
|
||||||
## Comparison with other tools
|
## Comparison with other tools
|
||||||
|
@ -22,12 +22,6 @@ library
|
|||||||
exposed-modules:
|
exposed-modules:
|
||||||
Feedback
|
Feedback
|
||||||
Feedback.OptParse
|
Feedback.OptParse
|
||||||
Feedback.TUI
|
|
||||||
Feedback.TUI.Draw
|
|
||||||
Feedback.TUI.Env
|
|
||||||
Feedback.TUI.Handle
|
|
||||||
Feedback.TUI.State
|
|
||||||
Feedback.TUI.Worker
|
|
||||||
other-modules:
|
other-modules:
|
||||||
Paths_feedback
|
Paths_feedback
|
||||||
hs-source-dirs:
|
hs-source-dirs:
|
||||||
@ -37,13 +31,11 @@ library
|
|||||||
, autodocodec
|
, autodocodec
|
||||||
, autodocodec-yaml
|
, autodocodec-yaml
|
||||||
, base >=4.7 && <5
|
, base >=4.7 && <5
|
||||||
, brick
|
|
||||||
, bytestring
|
, bytestring
|
||||||
, conduit
|
, conduit
|
||||||
, conduit-extra
|
, conduit-extra
|
||||||
, containers
|
, containers
|
||||||
, cursor
|
, cursor
|
||||||
, cursor-brick
|
|
||||||
, envparse
|
, envparse
|
||||||
, fsnotify
|
, fsnotify
|
||||||
, mtl
|
, mtl
|
||||||
@ -51,11 +43,12 @@ library
|
|||||||
, path
|
, path
|
||||||
, path-io
|
, path-io
|
||||||
, process
|
, process
|
||||||
|
, safe-coloured-text
|
||||||
|
, safe-coloured-text-terminfo
|
||||||
, text
|
, text
|
||||||
, time
|
, time
|
||||||
, typed-process
|
, typed-process
|
||||||
, unliftio
|
, unliftio
|
||||||
, vty
|
|
||||||
, yaml
|
, yaml
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
@ -15,14 +15,11 @@ library:
|
|||||||
- async
|
- async
|
||||||
- autodocodec
|
- autodocodec
|
||||||
- autodocodec-yaml
|
- autodocodec-yaml
|
||||||
- brick
|
|
||||||
- bytestring
|
- bytestring
|
||||||
- conduit
|
- conduit
|
||||||
- conduit-extra
|
- conduit-extra
|
||||||
- conduit-extra
|
|
||||||
- containers
|
- containers
|
||||||
- cursor
|
- cursor
|
||||||
- cursor-brick
|
|
||||||
- envparse
|
- envparse
|
||||||
- fsnotify
|
- fsnotify
|
||||||
- mtl
|
- mtl
|
||||||
@ -30,11 +27,12 @@ library:
|
|||||||
- path
|
- path
|
||||||
- path-io
|
- path-io
|
||||||
- process
|
- process
|
||||||
|
- safe-coloured-text
|
||||||
|
- safe-coloured-text-terminfo
|
||||||
- text
|
- text
|
||||||
- time
|
- time
|
||||||
- typed-process
|
- typed-process
|
||||||
- unliftio
|
- unliftio
|
||||||
- vty
|
|
||||||
- yaml
|
- yaml
|
||||||
|
|
||||||
executables:
|
executables:
|
||||||
|
@ -1,21 +1,29 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
|
||||||
module Feedback where
|
module Feedback where
|
||||||
|
|
||||||
import Brick.BChan
|
import Control.Monad
|
||||||
import Data.List
|
import Data.List
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import Data.Time
|
||||||
import Feedback.OptParse
|
import Feedback.OptParse
|
||||||
import Feedback.TUI
|
|
||||||
import Feedback.TUI.Env
|
|
||||||
import Path
|
import Path
|
||||||
import Path.IO
|
import Path.IO
|
||||||
|
import System.Exit
|
||||||
import System.FSNotify as FS
|
import System.FSNotify as FS
|
||||||
|
import System.Process (showCommandForUser)
|
||||||
|
import System.Process.Typed as Typed
|
||||||
|
import Text.Colour
|
||||||
|
import Text.Colour.Capabilities.FromEnv (getTerminalCapabilitiesFromEnv)
|
||||||
|
import UnliftIO
|
||||||
|
|
||||||
runFeedbackMain :: IO ()
|
runFeedbackMain :: IO ()
|
||||||
runFeedbackMain = do
|
runFeedbackMain = do
|
||||||
Settings {..} <- getSettings
|
Settings {..} <- getSettings
|
||||||
requestChan <- newBChan 1000
|
eventChan <- newChan
|
||||||
|
outputChan <- newChan
|
||||||
here <- getCurrentDir
|
here <- getCurrentDir
|
||||||
-- 0.1 second debouncing, 0.001 was too little
|
-- 0.1 second debouncing, 0.001 was too little
|
||||||
let conf = FS.defaultConfig {confDebounce = Debounce 0.1}
|
let conf = FS.defaultConfig {confDebounce = Debounce 0.1}
|
||||||
@ -26,8 +34,10 @@ runFeedbackMain = do
|
|||||||
(fromAbsDir here) -- Where to watch
|
(fromAbsDir here) -- Where to watch
|
||||||
(eventFilter here)
|
(eventFilter here)
|
||||||
$ \event -> do
|
$ \event -> do
|
||||||
writeBChan requestChan $ ReceiveEvent event
|
writeChan eventChan event
|
||||||
feedbackTUI settingCommand requestChan
|
concurrently_
|
||||||
|
(processWorker settingCommand eventChan outputChan)
|
||||||
|
(outputWorker outputChan)
|
||||||
stopListeningAction
|
stopListeningAction
|
||||||
|
|
||||||
eventFilter :: Path Abs Dir -> FS.Event -> Bool
|
eventFilter :: Path Abs Dir -> FS.Event -> Bool
|
||||||
@ -59,3 +69,98 @@ isHiddenIn curdir ad =
|
|||||||
case stripProperPrefix curdir ad of
|
case stripProperPrefix curdir ad of
|
||||||
Nothing -> False
|
Nothing -> False
|
||||||
Just rp -> "." `isPrefixOf` toFilePath rp
|
Just rp -> "." `isPrefixOf` toFilePath rp
|
||||||
|
|
||||||
|
processWorker :: [String] -> Chan FS.Event -> Chan Output -> IO ()
|
||||||
|
processWorker command eventChan outputChan = do
|
||||||
|
let sendOutput = writeChan outputChan
|
||||||
|
currentProcessVar <- newEmptyMVar
|
||||||
|
let startNewProcess = do
|
||||||
|
-- Start a new process
|
||||||
|
let processConfig =
|
||||||
|
setStdout inherit
|
||||||
|
. setStderr inherit
|
||||||
|
. setStdin closed -- TODO make this configurable?
|
||||||
|
. shell
|
||||||
|
$ unwords command
|
||||||
|
processHandleProcess <- startProcess processConfig
|
||||||
|
processHandleWaiter <- async $ do
|
||||||
|
ec <- waitExitCode processHandleProcess
|
||||||
|
sendOutput $ OutputProcessExited ec
|
||||||
|
putMVar currentProcessVar ProcessHandle {..}
|
||||||
|
sendOutput $ OutputProcessStarted command
|
||||||
|
-- Start one process ahead of time
|
||||||
|
startNewProcess
|
||||||
|
forever $ do
|
||||||
|
-- Output the event that made the rerun happen
|
||||||
|
event <- readChan eventChan
|
||||||
|
sendOutput $ OutputEvent event
|
||||||
|
-- Kill the current process
|
||||||
|
mCurrentProcess <- tryTakeMVar currentProcessVar
|
||||||
|
forM_ mCurrentProcess $ \currentProcess -> do
|
||||||
|
sendOutput OutputKilling
|
||||||
|
stopProcess $ processHandleProcess currentProcess
|
||||||
|
-- No need to cancel the waiter thread.
|
||||||
|
sendOutput OutputKilled
|
||||||
|
startNewProcess
|
||||||
|
|
||||||
|
data ProcessHandle = ProcessHandle
|
||||||
|
{ processHandleProcess :: !P,
|
||||||
|
processHandleWaiter :: Async ()
|
||||||
|
}
|
||||||
|
|
||||||
|
type P = Process () () ()
|
||||||
|
|
||||||
|
data Output
|
||||||
|
= OutputEvent !FS.Event
|
||||||
|
| OutputKilling
|
||||||
|
| OutputKilled
|
||||||
|
| OutputProcessStarted ![String]
|
||||||
|
| OutputProcessExited !ExitCode
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
outputWorker :: Chan Output -> IO ()
|
||||||
|
outputWorker outputChan = do
|
||||||
|
terminalCapabilities <- getTerminalCapabilitiesFromEnv
|
||||||
|
let put chunks = do
|
||||||
|
now <- getCurrentTime
|
||||||
|
let timeChunk = fore yellow $ chunk $ T.pack $ formatTime defaultTimeLocale "%H:%M:%S" now
|
||||||
|
putChunksWith terminalCapabilities $ timeChunk : " " : chunks
|
||||||
|
putStrLn ""
|
||||||
|
forever $ do
|
||||||
|
event <- readChan outputChan
|
||||||
|
case event of
|
||||||
|
OutputEvent fsEvent -> do
|
||||||
|
put
|
||||||
|
[ fore cyan "event: ",
|
||||||
|
case fsEvent of
|
||||||
|
Added {} -> fore green "added "
|
||||||
|
Modified {} -> fore yellow "modified "
|
||||||
|
Removed {} -> fore red "removed "
|
||||||
|
Unknown {} -> "unknown ",
|
||||||
|
chunk $ T.pack $ eventPath fsEvent
|
||||||
|
]
|
||||||
|
OutputKilling -> put [fore cyan "killing"]
|
||||||
|
OutputKilled -> put [fore cyan "killed"]
|
||||||
|
OutputProcessStarted command -> do
|
||||||
|
let commandString = case command of
|
||||||
|
[] -> ""
|
||||||
|
(bin : args) -> showCommandForUser bin args
|
||||||
|
put
|
||||||
|
[ fore cyan "started:",
|
||||||
|
" ",
|
||||||
|
fore blue $ chunk $ T.pack commandString
|
||||||
|
]
|
||||||
|
OutputProcessExited ec ->
|
||||||
|
case ec of
|
||||||
|
ExitSuccess ->
|
||||||
|
put
|
||||||
|
[ fore cyan "exited: ",
|
||||||
|
" ",
|
||||||
|
fore green "success"
|
||||||
|
]
|
||||||
|
ExitFailure c ->
|
||||||
|
put
|
||||||
|
[ fore cyan "exited: ",
|
||||||
|
" ",
|
||||||
|
fore red $ chunk $ T.pack $ "failed: " <> show c
|
||||||
|
]
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
|
||||||
|
|
||||||
module Feedback.TUI where
|
|
||||||
|
|
||||||
import Brick.BChan
|
|
||||||
import Brick.Main
|
|
||||||
import Control.Concurrent
|
|
||||||
import Control.Concurrent.Async
|
|
||||||
import Control.Monad.Reader
|
|
||||||
import Feedback.TUI.Draw
|
|
||||||
import Feedback.TUI.Env
|
|
||||||
import Feedback.TUI.Handle
|
|
||||||
import Feedback.TUI.State
|
|
||||||
import Feedback.TUI.Worker
|
|
||||||
import Graphics.Vty (defaultConfig, mkVty)
|
|
||||||
|
|
||||||
feedbackTUI :: [String] -> BChan Request -> IO ()
|
|
||||||
feedbackTUI command requestChan = do
|
|
||||||
-- Define the tui thread
|
|
||||||
initialState <- buildInitialState command
|
|
||||||
responseChan <- newBChan 1000
|
|
||||||
let vtyBuilder = mkVty defaultConfig
|
|
||||||
firstVty <- vtyBuilder
|
|
||||||
let runTui = customMain firstVty vtyBuilder (Just responseChan) (tuiApp requestChan) initialState
|
|
||||||
-- Define the worker thread
|
|
||||||
envCurrentProcess <- newEmptyMVar
|
|
||||||
let envCommand = command
|
|
||||||
let envRequestChan = requestChan
|
|
||||||
let envResponseChan = responseChan
|
|
||||||
let env = Env {..}
|
|
||||||
let runWorker = runReaderT tuiWorker env
|
|
||||||
-- Left always works because the worker runs forever
|
|
||||||
Left _ <- race runTui runWorker
|
|
||||||
pure ()
|
|
||||||
|
|
||||||
tuiApp :: BChan Request -> App State Response ResourceName
|
|
||||||
tuiApp chan =
|
|
||||||
App
|
|
||||||
{ appDraw = drawTui,
|
|
||||||
appChooseCursor = showFirstCursor,
|
|
||||||
appHandleEvent = handleTuiEvent chan,
|
|
||||||
appStartEvent = pure,
|
|
||||||
appAttrMap = buildAttrMap
|
|
||||||
}
|
|
||||||
|
|
||||||
buildInitialState :: [String] -> IO State
|
|
||||||
buildInitialState stateCommand = do
|
|
||||||
let stateEvents = []
|
|
||||||
let stateCurrentProcess = Nothing
|
|
||||||
let stateOutput = emptyOutput
|
|
||||||
pure State {..}
|
|
@ -1,41 +0,0 @@
|
|||||||
{-# LANGUAGE RecordWildCards #-}
|
|
||||||
|
|
||||||
module Feedback.TUI.Draw where
|
|
||||||
|
|
||||||
import Brick.AttrMap
|
|
||||||
import Brick.Types
|
|
||||||
import Brick.Widgets.Border
|
|
||||||
import Brick.Widgets.Core
|
|
||||||
import qualified Data.ByteString as SB
|
|
||||||
import qualified Data.Map as M
|
|
||||||
import qualified Data.Text.Encoding as TE
|
|
||||||
import qualified Data.Text.Encoding.Error as TE
|
|
||||||
import Feedback.TUI.State
|
|
||||||
import Graphics.Vty.Attributes
|
|
||||||
import System.Process
|
|
||||||
|
|
||||||
buildAttrMap :: State -> AttrMap
|
|
||||||
buildAttrMap = const $ attrMap defAttr []
|
|
||||||
|
|
||||||
drawTui :: State -> [Widget ResourceName]
|
|
||||||
drawTui State {..} =
|
|
||||||
[ vBox
|
|
||||||
[ case stateCommand of
|
|
||||||
[] -> emptyWidget
|
|
||||||
(command : args) -> str $ showCommandForUser command args,
|
|
||||||
hBorder,
|
|
||||||
str $ show stateCurrentProcess,
|
|
||||||
hBorder,
|
|
||||||
vBox $ map (str . show) stateEvents,
|
|
||||||
hBorder,
|
|
||||||
drawOutput stateOutput
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
drawOutput :: Output -> Widget n
|
|
||||||
drawOutput (Output m) =
|
|
||||||
txtWrap
|
|
||||||
. TE.decodeUtf8With TE.lenientDecode
|
|
||||||
. SB.concat
|
|
||||||
. map (snd . snd)
|
|
||||||
$ M.toAscList m
|
|
@ -1,41 +0,0 @@
|
|||||||
module Feedback.TUI.Env where
|
|
||||||
|
|
||||||
import Brick.BChan
|
|
||||||
import Conduit
|
|
||||||
import Control.Monad.Reader
|
|
||||||
import Data.ByteString
|
|
||||||
import System.Exit
|
|
||||||
import System.FSNotify as FS
|
|
||||||
import System.Process.Typed as Typed
|
|
||||||
import UnliftIO
|
|
||||||
|
|
||||||
data Env = Env
|
|
||||||
{ envCommand :: ![String],
|
|
||||||
envCurrentProcess :: !(MVar ProcessHandle),
|
|
||||||
envRequestChan :: !(BChan Request),
|
|
||||||
envResponseChan :: !(BChan Response)
|
|
||||||
}
|
|
||||||
|
|
||||||
data ProcessHandle = ProcessHandle
|
|
||||||
{ processHandleProcess :: !P,
|
|
||||||
processHandleWaiter :: Async (),
|
|
||||||
processHandleStdoutReader :: Async (),
|
|
||||||
processHandleStderrReader :: Async ()
|
|
||||||
}
|
|
||||||
|
|
||||||
type P =
|
|
||||||
Typed.Process
|
|
||||||
()
|
|
||||||
(ConduitT () ByteString W ())
|
|
||||||
(ConduitT () ByteString W ())
|
|
||||||
|
|
||||||
type W = ReaderT Env IO
|
|
||||||
|
|
||||||
data Request = ReceiveEvent !FS.Event
|
|
||||||
|
|
||||||
data Response
|
|
||||||
= ReceivedEvent !FS.Event
|
|
||||||
| ProcessStarted
|
|
||||||
| ProcessExited !ExitCode
|
|
||||||
| StdoutChunk !ByteString
|
|
||||||
| StderrChunk !ByteString
|
|
@ -1,37 +0,0 @@
|
|||||||
module Feedback.TUI.Handle where
|
|
||||||
|
|
||||||
import Brick.BChan
|
|
||||||
import Brick.Main
|
|
||||||
import Brick.Types
|
|
||||||
import Control.Monad.IO.Class
|
|
||||||
import Feedback.TUI.Env
|
|
||||||
import Feedback.TUI.State
|
|
||||||
import GHC.Clock (getMonotonicTimeNSec)
|
|
||||||
import Graphics.Vty.Input.Events
|
|
||||||
|
|
||||||
handleTuiEvent :: BChan Request -> State -> BrickEvent n Response -> EventM n (Next State)
|
|
||||||
handleTuiEvent _requestChan s e =
|
|
||||||
case e of
|
|
||||||
VtyEvent vtye ->
|
|
||||||
case vtye of
|
|
||||||
EvKey (KChar 'q') [] -> halt s
|
|
||||||
_ -> continue s
|
|
||||||
AppEvent resp -> case resp of
|
|
||||||
ReceivedEvent fsEvent ->
|
|
||||||
continue $ s {stateEvents = fsEvent : stateEvents s}
|
|
||||||
ProcessStarted ->
|
|
||||||
continue $
|
|
||||||
s
|
|
||||||
{ stateEvents = [],
|
|
||||||
stateCurrentProcess = Nothing,
|
|
||||||
stateOutput = emptyOutput
|
|
||||||
}
|
|
||||||
ProcessExited ec ->
|
|
||||||
continue $ s {stateCurrentProcess = Just ec}
|
|
||||||
StdoutChunk contents -> do
|
|
||||||
now <- liftIO getMonotonicTimeNSec
|
|
||||||
continue $ s {stateOutput = addOutput Stdout now contents (stateOutput s)}
|
|
||||||
StderrChunk contents -> do
|
|
||||||
now <- liftIO getMonotonicTimeNSec
|
|
||||||
continue $ s {stateOutput = addOutput Stderr now contents (stateOutput s)}
|
|
||||||
_ -> continue s
|
|
@ -1,30 +0,0 @@
|
|||||||
module Feedback.TUI.State where
|
|
||||||
|
|
||||||
import Data.ByteString (ByteString)
|
|
||||||
import Data.Map
|
|
||||||
import qualified Data.Map as M
|
|
||||||
import Data.Word
|
|
||||||
import System.Exit
|
|
||||||
import System.FSNotify as FS
|
|
||||||
|
|
||||||
data State = State
|
|
||||||
{ stateCommand :: [String],
|
|
||||||
stateCurrentProcess :: !(Maybe ExitCode),
|
|
||||||
stateEvents :: [FS.Event],
|
|
||||||
stateOutput :: !Output
|
|
||||||
}
|
|
||||||
|
|
||||||
newtype Output = Output {unOutput :: Map Word64 (OutputStream, ByteString)}
|
|
||||||
deriving (Show)
|
|
||||||
|
|
||||||
data OutputStream = Stdout | Stderr
|
|
||||||
deriving (Show)
|
|
||||||
|
|
||||||
emptyOutput :: Output
|
|
||||||
emptyOutput = Output M.empty
|
|
||||||
|
|
||||||
addOutput :: OutputStream -> Word64 -> ByteString -> Output -> Output
|
|
||||||
addOutput os u bs (Output m) = Output $ M.insert u (os, bs) m
|
|
||||||
|
|
||||||
data ResourceName = ResourceName
|
|
||||||
deriving (Show, Eq, Ord)
|
|
@ -1,88 +0,0 @@
|
|||||||
{-# LANGUAGE RecordWildCards #-}
|
|
||||||
|
|
||||||
module Feedback.TUI.Worker where
|
|
||||||
|
|
||||||
import Brick.BChan
|
|
||||||
import Conduit
|
|
||||||
import Control.Monad
|
|
||||||
import Control.Monad.IO.Class
|
|
||||||
import Control.Monad.Reader
|
|
||||||
import qualified Data.Conduit.Combinators as C
|
|
||||||
import Data.Conduit.Process.Typed
|
|
||||||
import Data.Word
|
|
||||||
import Feedback.TUI.Env
|
|
||||||
import Feedback.TUI.State
|
|
||||||
import GHC.Clock
|
|
||||||
import System.Process.Typed as Typed
|
|
||||||
import UnliftIO
|
|
||||||
|
|
||||||
tuiWorker :: W ()
|
|
||||||
tuiWorker = do
|
|
||||||
doRun -- Start immediately
|
|
||||||
requestChan <- asks envRequestChan
|
|
||||||
forever $ do
|
|
||||||
req <- liftIO $ readBChan requestChan
|
|
||||||
case req of
|
|
||||||
ReceiveEvent event -> do
|
|
||||||
-- Immediately notify of the last-received event.
|
|
||||||
sendResponse $ ReceivedEvent event
|
|
||||||
doRun
|
|
||||||
|
|
||||||
doRun :: W ()
|
|
||||||
doRun = do
|
|
||||||
currentProcessVar <- asks envCurrentProcess
|
|
||||||
-- Stop the current process
|
|
||||||
mCurrentProcess <- tryTakeMVar currentProcessVar
|
|
||||||
mapM_ stopProcessHandle mCurrentProcess
|
|
||||||
-- Start the new process
|
|
||||||
processHandle <- startNewProcess
|
|
||||||
putMVar currentProcessVar processHandle
|
|
||||||
|
|
||||||
stopProcessHandle :: ProcessHandle -> W ()
|
|
||||||
stopProcessHandle ProcessHandle {..} = do
|
|
||||||
stopProcess processHandleProcess
|
|
||||||
-- No need to cancel the waiter, it will finish automatically
|
|
||||||
-- liftIO $ cancel processHandleWaiter
|
|
||||||
-- TODO: figure out if that's true
|
|
||||||
cancel processHandleStdoutReader
|
|
||||||
cancel processHandleStderrReader
|
|
||||||
pure ()
|
|
||||||
|
|
||||||
startNewProcess :: W ProcessHandle
|
|
||||||
startNewProcess = do
|
|
||||||
command <- asks envCommand
|
|
||||||
let processConfig =
|
|
||||||
setStdout createSource
|
|
||||||
. setStderr createSource
|
|
||||||
. setStdin inherit
|
|
||||||
. shell
|
|
||||||
$ unwords command
|
|
||||||
-- Start a new process
|
|
||||||
processHandleProcess <- startProcess processConfig
|
|
||||||
processHandleWaiter <- async $ waiterThread processHandleProcess
|
|
||||||
processHandleStdoutReader <- async $ stdoutThread processHandleProcess
|
|
||||||
processHandleStderrReader <- async $ stderrThread processHandleProcess
|
|
||||||
sendResponse ProcessStarted
|
|
||||||
pure ProcessHandle {..}
|
|
||||||
|
|
||||||
waiterThread :: P -> W ()
|
|
||||||
waiterThread process = do
|
|
||||||
ec <- waitExitCode process
|
|
||||||
sendResponse $ ProcessExited ec
|
|
||||||
|
|
||||||
stdoutThread :: P -> W ()
|
|
||||||
stdoutThread process =
|
|
||||||
runConduit $
|
|
||||||
getStdout process
|
|
||||||
.| C.mapM_ (sendResponse . StdoutChunk)
|
|
||||||
|
|
||||||
stderrThread :: P -> W ()
|
|
||||||
stderrThread process =
|
|
||||||
runConduit $
|
|
||||||
getStdout process
|
|
||||||
.| C.mapM_ (sendResponse . StdoutChunk)
|
|
||||||
|
|
||||||
sendResponse :: Response -> W ()
|
|
||||||
sendResponse response = do
|
|
||||||
responseChan <- asks envResponseChan
|
|
||||||
liftIO $ writeBChan responseChan response
|
|
@ -4,6 +4,11 @@ packages:
|
|||||||
- feedback
|
- feedback
|
||||||
|
|
||||||
extra-deps:
|
extra-deps:
|
||||||
|
- github: NorfairKing/safe-coloured-text
|
||||||
|
commit: f30fe0e4d2a0fa036967ffe17538b60e5f9861f5
|
||||||
|
subdirs:
|
||||||
|
- safe-coloured-text
|
||||||
|
- safe-coloured-text-terminfo
|
||||||
- github: NorfairKing/autodocodec
|
- github: NorfairKing/autodocodec
|
||||||
commit: 9880ec062f1166bdac50b8eb59c6dd010bc9af0c
|
commit: 9880ec062f1166bdac50b8eb59c6dd010bc9af0c
|
||||||
subdirs:
|
subdirs:
|
||||||
|
Loading…
Reference in New Issue
Block a user