concurrent-output-1.3.0: Ungarble output from several threads

Copyright2015 Joey Hess <id@joeyh.name>
LicenseBSD-2-clause
Safe HaskellNone
LanguageHaskell98

System.Console.Regions

Contents

Description

Console regions are displayed near the bottom of the console, and can be updated concurrently by threads. Any other output displayed using outputConcurrent and createProcessConcurrent will scroll up above the open console regions.

For example, this program:

 import Control.Concurrent.Async
 import Control.Concurrent
 import System.Console.Concurrent
 import System.Console.Regions
 import System.Process
 
 main = displayConsoleRegions $ do
 	mapConcurrently download [1..5]
		`concurrently` mapM_ message [1..10]
		`concurrently` createProcessConcurrent (proc "echo" ["hello world"])
 
 message :: Int -> IO ()
 message n = do
 	threadDelay 500000
 	outputConcurrent ("Message " ++ show n ++ "\n")
 
 download :: Int -> IO ()
 download n = withConsoleRegion Linear $ \r -> do
 	setConsoleRegion r basemsg
 	go n r
   where
 	basemsg = "Download " ++ show n
	go c r
		| c < 1 = finishConsoleRegion r (basemsg ++ " done!")
 		| otherwise = do
 			threadDelay 1000000
 			appendConsoleRegion r " ... "
 			go (c-1) r

Will display like this:

Message 1
hello world
Message 2
Download 1 ...
Download 2 ...
Download 3 ...

Once the 1st download has finished, and another message has displayed, the console will update like this:

Message 1
hello world
Message 2
Download 1 done!
Message 3
Download 2 ... ...
Download 3 ... ...

Synopsis

Initialization

displayConsoleRegions :: (MonadIO m, MonadMask m) => m a -> m a Source

Handles all display for the other functions in this module.

Note that this uses lockOutput, so it takes over all output to the console while the passed IO action is running. As well as displaying the console regions, this handles display of anything buffered by outputConcurrent and createProcessConcurrent.

When standard output is not an ANSI capable terminal, console regions are not displayed.

data RegionLayout Source

Controls how a region is laid out in the console.

Here's an annotated example of how the console layout works.

scrolling......
scrolling......
scrolling......
aaaaaa......... -- Linear
bbbbbbbbbbbbbbb -- Linear
bbb............       (expanded to multiple lines)
ccccccccc...... -- Linear
ddddeeeefffffff -- [InLine]
fffffggggg.....       (expanded to multiple lines)

withConsoleRegion :: (MonadIO m, MonadMask m) => RegionLayout -> (ConsoleRegionHandle -> m a) -> m a Source

Runs the action with a new console region, closing the region when the action finishes or on exception.

openConsoleRegion :: RegionLayout -> IO ConsoleRegionHandle Source

Opens a new console region for output.

closeConsoleRegion :: ConsoleRegionHandle -> IO () Source

Closes a console region. Once closed, the region is removed from the display.

Region display

class Displayable v where Source

Values that can be displayed in a region.

Instances

Displayable String Source 
Displayable Text Source 
Displayable (STM Text) Source

Makes a STM action be run to get the content of a region.

Any change to the values that action reads will result in an immediate refresh of the display.

setConsoleRegion :: Displayable v => ConsoleRegionHandle -> v -> IO () Source

Sets the value to display within a console region.

It's fine for the value to be longer than the terminal is wide, or to include newlines ('\n'). Regions expand to multiple lines as necessary.

The value can include ANSI SGR escape sequences for changing the colors etc of all or part of a region.

Other ANSI escape sequences, especially those doing cursor movement, will mess up the layouts of regions. Caveat emptor.

appendConsoleRegion :: Outputable v => ConsoleRegionHandle -> v -> IO () Source

Appends the value to whatever was already on display within a console region.

finishConsoleRegion :: Outputable v => ConsoleRegionHandle -> v -> IO () Source

Closes the console region and displays the passed value in the scrolling area above the active console regions.

STM interface

These actions can be composed into a STM transaction; once the transaction completes the console will be updated a single time to reflect all the changes made.

openConsoleRegionSTM :: Displayable v => RegionLayout -> v -> STM ConsoleRegionHandle Source

STM version of openConsoleRegion. Allows atomically opening multiple regions at the same time, which guarantees they are adjacent.

 [r1, r2, r3] <- atomically $
	replicateM 3 (openConsoleRegionSTM Linear T.empty)

newConsoleRegionSTM :: Displayable v => RegionLayout -> v -> STM ConsoleRegionHandle Source

Makes a new region, but does not add it to the display.

STM regions

The Displayable instance for STM text can be used to make regions that automatically update whenever there's a change to any of the STM values that they use.

For example, a region that displays the screen size, and automatically refreshes it:

import System.Console.Terminal.Size
import qualified Data.Text as T
 r <- openConsoleRegion Linear s
 setConsoleRegion r $ do
 	sz <- readTVar consoleSize
 	return $ T.pack $ unwords
 		[ "size:"
		, show (width sz)
 		, "x"
		, show (height sz)
 		]

readRegionContent :: ConsoleRegionHandle -> STM Text Source

Reads the content of a region.

consoleSize :: TVar (Window Int) Source

On unix systems, this TVar is automatically updated when the terminal is resized.

regionList :: TMVar [ConsoleRegionHandle] Source

All the regions that are currently displayed on the screen.

The list is ordered from the bottom of the screen up. Reordering it will change the order in which regions are displayed. It's also fine to remove, duplicate, or add new regions to the list.