1
- module NOM.IO (interact , processTextStream , StreamParser , Stream ) where
1
+ module NOM.IO (interact , processTextStream , StreamParser , Stream , Window , Output ) where
2
2
3
3
import Control.Concurrent (threadDelay )
4
- import Control.Concurrent.Async (concurrently_ , race_ )
5
- import Control.Concurrent.STM (check , swapTVar )
4
+ import Control.Concurrent.Async (Concurrently (Concurrently , runConcurrently ))
5
+ import Control.Concurrent.STM (check , swapTVar , writeTMVar )
6
+ import Control.Exception (bracket )
6
7
import Data.ByteString qualified as ByteString
7
8
import Data.ByteString.Builder qualified as Builder
8
9
import Data.ByteString.Char8 qualified as ByteString
@@ -11,6 +12,7 @@ import Data.Time (ZonedTime, getZonedTime)
11
12
import NOM.Error (NOMError )
12
13
import NOM.Print (Config (.. ))
13
14
import NOM.Print.Table as Table (bold , displayWidth , displayWidthBS , markup , red , truncate )
15
+ import NOM.State (PrintNameStyle (.. ), PrintState (.. ), initPrintState )
14
16
import NOM.Update.Monad (UpdateMonad , getNow )
15
17
import Relude
16
18
import Streamly.Data.Fold qualified as Fold
@@ -28,7 +30,7 @@ type Output = Text
28
30
29
31
type UpdateFunc update state = forall m . (UpdateMonad m ) = > update -> StateT state m ([NOMError ], ByteString , Bool )
30
32
31
- type OutputFunc state = state -> Maybe Window -> (ZonedTime , Double ) -> Output
33
+ type OutputFunc state = state -> PrintState -> Maybe Window -> (ZonedTime , Double ) -> Output
32
34
33
35
type Finalizer state = forall m . (UpdateMonad m ) = > StateT state m ()
34
36
@@ -59,13 +61,14 @@ writeStateToScreen ::
59
61
Bool ->
60
62
TVar Int ->
61
63
TMVar state ->
64
+ TMVar PrintState ->
62
65
TVar [ByteString ] ->
63
66
TVar Bool ->
64
67
(Double -> state -> state ) ->
65
68
OutputFunc state ->
66
69
Handle ->
67
70
IO ()
68
- writeStateToScreen pad printed_lines_var nom_state_var nix_output_buffer_var refresh_display_var maintenance printer output_handle = do
71
+ writeStateToScreen pad printed_lines_var nom_state_var print_state_var nix_output_buffer_var refresh_display_var maintenance printer output_handle = do
69
72
nowClock <- getZonedTime
70
73
now <- getNow
71
74
terminalSize <-
@@ -88,11 +91,10 @@ writeStateToScreen pad printed_lines_var nom_state_var nix_output_buffer_var ref
88
91
nix_output_raw <- swapTVar nix_output_buffer_var []
89
92
pure (nom_state, nix_output_raw)
90
93
-- ====
91
-
94
+ print_state <- atomically $ readTMVar print_state_var
92
95
let nix_output = ByteString. lines $ ByteString. concat $ reverse nix_output_raw
93
96
nix_output_length = length nix_output
94
-
95
- nom_output = ByteString. lines $ encodeUtf8 $ truncateOutput terminalSize (printer nom_state terminalSize (nowClock, now))
97
+ nom_output = ByteString. lines $ encodeUtf8 $ truncateOutput terminalSize (printer nom_state print_state terminalSize (nowClock, now))
96
98
nom_output_length = length nom_output
97
99
98
100
-- We will try to calculate how many lines we can draw without reaching the end
@@ -214,6 +216,14 @@ minFrameDuration =
214
216
-- feel to sluggish for the eye, for me.
215
217
60_000 -- ~17 times per second
216
218
219
+ getKey :: IO [Char ]
220
+ getKey = reverse <$> getKey' " "
221
+ where
222
+ getKey' chars = do
223
+ char <- System.IO. getChar
224
+ more <- System.IO. hReady stdin
225
+ (if more then getKey' else return ) (char : chars)
226
+
217
227
processTextStream ::
218
228
forall update state .
219
229
Config ->
@@ -227,6 +237,8 @@ processTextStream ::
227
237
IO state
228
238
processTextStream config parser updater maintenance printerMay finalize initialState inputStream = do
229
239
state_var <- newTMVarIO initialState
240
+ print_state_var <- newTMVarIO initPrintState
241
+ input_received <- newEmptyTMVarIO
230
242
output_builder_var <- newTVarIO []
231
243
refresh_display_var <- newTVarIO False
232
244
let keepProcessing :: IO ()
@@ -240,13 +252,47 @@ processTextStream config parser updater maintenance printerMay finalize initialS
240
252
waitForInput = atomically $ check =<< readTVar refresh_display_var
241
253
printerMay & maybe keepProcessing \ (printer, output_handle) -> do
242
254
linesVar <- newTVarIO 0
243
- let writeToScreen :: IO ()
244
- writeToScreen = writeStateToScreen (not config. silent) linesVar state_var output_builder_var refresh_display_var maintenance printer output_handle
255
+ let keepProcessingStdin :: IO ()
256
+ keepProcessingStdin = bracket setBuffering restoreBuffering $ const processStdinForeverLoop
257
+ where
258
+ setBuffering = do
259
+ buff <- System.IO. hGetBuffering stdin
260
+ setEcho <- System.IO. hGetEcho stdin
261
+ System.IO. hSetBuffering stdin NoBuffering
262
+ System.IO. hSetEcho stdin False
263
+ pure (buff, setEcho)
264
+ restoreBuffering (buff, setEcho) = do
265
+ System.IO. hSetBuffering stdin buff
266
+ System.IO. hSetEcho stdin setEcho
267
+ processStdinForeverLoop :: IO ()
268
+ processStdinForeverLoop = forever $ do
269
+ key <- getKey
270
+ case key of
271
+ " n" -> do
272
+ atomically $ do
273
+ print_state <- readTMVar print_state_var
274
+ let print_state_style = if print_state. printName == PrintName then PrintDerivationPath else PrintName
275
+ writeTMVar print_state_var $ print_state{printName = print_state_style}
276
+ writeTMVar input_received ()
277
+ " ?" -> do
278
+ atomically $ do
279
+ print_state <- takeTMVar print_state_var
280
+ putTMVar print_state_var $ print_state{printHelp = True }
281
+ writeTMVar input_received ()
282
+ _ -> pure ()
283
+ writeToScreen :: IO ()
284
+ writeToScreen = writeStateToScreen (not config. silent) linesVar state_var print_state_var output_builder_var refresh_display_var maintenance printer output_handle
245
285
keepPrinting :: IO ()
246
286
keepPrinting = forever do
247
- race_ (concurrently_ (threadDelay minFrameDuration) waitForInput) (threadDelay maxFrameDuration)
287
+ runConcurrently
288
+ $ (Concurrently (threadDelay minFrameDuration) *> Concurrently waitForInput)
289
+ <|> Concurrently (threadDelay maxFrameDuration)
290
+ <|> Concurrently (atomically $ takeTMVar input_received)
248
291
writeToScreen
249
- race_ keepProcessing keepPrinting
292
+ runConcurrently
293
+ $ Concurrently keepProcessing
294
+ <|> Concurrently keepProcessingStdin
295
+ <|> Concurrently keepPrinting
250
296
atomically (takeTMVar state_var) >>= execStateT finalize >>= atomically . putTMVar state_var
251
297
writeToScreen
252
298
(if isNothing printerMay then (>>= execStateT finalize) else id ) $ atomically $ takeTMVar state_var
0 commit comments