packages feed

concurrent-output 1.3.0 → 1.4.0

raw patch · 6 files changed

+207/−181 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- System.Console.Regions: appendConsoleRegionSTM :: Outputable v => ConsoleRegionHandle -> v -> STM ()
- System.Console.Regions: class Displayable v
- System.Console.Regions: closeConsoleRegionSTM :: ConsoleRegionHandle -> STM ()
- System.Console.Regions: data ConsoleRegionHandle
- System.Console.Regions: instance GHC.Classes.Eq System.Console.Regions.ConsoleRegionHandle
- System.Console.Regions: instance System.Console.Regions.Displayable (GHC.Conc.Sync.STM Data.Text.Internal.Text)
- System.Console.Regions: instance System.Console.Regions.Displayable Data.Text.Internal.Text
- System.Console.Regions: instance System.Console.Regions.Displayable GHC.Base.String
- System.Console.Regions: newConsoleRegionSTM :: Displayable v => RegionLayout -> v -> STM ConsoleRegionHandle
- System.Console.Regions: openConsoleRegionSTM :: Displayable v => RegionLayout -> v -> STM ConsoleRegionHandle
- System.Console.Regions: readRegionContent :: ConsoleRegionHandle -> STM Text
- System.Console.Regions: setConsoleRegionSTM :: Displayable v => ConsoleRegionHandle -> v -> STM ()
- System.Console.Regions: type Width = Int
+ System.Console.Regions: class LiftRegion m
+ System.Console.Regions: class ToRegionContent v
+ System.Console.Regions: consoleHeight :: STM Int
+ System.Console.Regions: data ConsoleRegion
+ System.Console.Regions: instance GHC.Classes.Eq System.Console.Regions.ConsoleRegion
+ System.Console.Regions: instance System.Console.Regions.LiftRegion GHC.Conc.Sync.STM
+ System.Console.Regions: instance System.Console.Regions.LiftRegion GHC.Types.IO
+ System.Console.Regions: instance System.Console.Regions.ToRegionContent (GHC.Conc.Sync.STM Data.Text.Internal.Text)
+ System.Console.Regions: instance System.Console.Regions.ToRegionContent Data.Text.Internal.Text
+ System.Console.Regions: instance System.Console.Regions.ToRegionContent GHC.Base.String
+ System.Console.Regions: liftRegion :: LiftRegion m => STM a -> m a
+ System.Console.Regions: newConsoleRegion :: ToRegionContent v => RegionLayout -> v -> STM ConsoleRegion
+ System.Console.Regions: tuneDisplay :: LiftRegion m => ConsoleRegion -> (Text -> STM Text) -> m ()
- System.Console.Regions: InLine :: ConsoleRegionHandle -> RegionLayout
+ System.Console.Regions: InLine :: ConsoleRegion -> RegionLayout
- System.Console.Regions: appendConsoleRegion :: Outputable v => ConsoleRegionHandle -> v -> IO ()
+ System.Console.Regions: appendConsoleRegion :: (Outputable v, LiftRegion m) => ConsoleRegion -> v -> m ()
- System.Console.Regions: closeConsoleRegion :: ConsoleRegionHandle -> IO ()
+ System.Console.Regions: closeConsoleRegion :: LiftRegion m => ConsoleRegion -> m ()
- System.Console.Regions: consoleWidth :: STM Width
+ System.Console.Regions: consoleWidth :: STM Int
- System.Console.Regions: finishConsoleRegion :: Outputable v => ConsoleRegionHandle -> v -> IO ()
+ System.Console.Regions: finishConsoleRegion :: (Outputable v, LiftRegion m) => ConsoleRegion -> v -> m ()
- System.Console.Regions: openConsoleRegion :: RegionLayout -> IO ConsoleRegionHandle
+ System.Console.Regions: openConsoleRegion :: LiftRegion m => RegionLayout -> m ConsoleRegion
- System.Console.Regions: regionList :: TMVar [ConsoleRegionHandle]
+ System.Console.Regions: regionList :: TMVar [ConsoleRegion]
- System.Console.Regions: setConsoleRegion :: Displayable v => ConsoleRegionHandle -> v -> IO ()
+ System.Console.Regions: setConsoleRegion :: (ToRegionContent v, LiftRegion m) => ConsoleRegion -> v -> m ()
- System.Console.Regions: toRegionContent :: Displayable v => v -> STM RegionContent
+ System.Console.Regions: toRegionContent :: ToRegionContent v => v -> STM RegionContent
- System.Console.Regions: withConsoleRegion :: (MonadIO m, MonadMask m) => RegionLayout -> (ConsoleRegionHandle -> m a) -> m a
+ System.Console.Regions: withConsoleRegion :: (LiftRegion m, MonadIO m, MonadMask m) => RegionLayout -> (ConsoleRegion -> m a) -> m a

Files

CHANGELOG view
@@ -1,3 +1,12 @@+concurrent-output (1.4.0) unstable; urgency=medium++  * Renamed many of the functions and types.+  * Added tuneDisplay, which makes it easy to size-limit regions,+    right-justify regions, or otherwise transform how their values are+    displayed.++ -- Joey Hess <id@joeyh.name>  Wed, 04 Nov 2015 00:32:38 -0400+ concurrent-output (1.3.0) unstable; urgency=medium    * The contents of a Region can now be set to a STM Text
System/Console/Regions.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE BangPatterns, TupleSections, TypeSynonymInstances, FlexibleInstances #-}+{-# LANGUAGE BangPatterns, TupleSections, TypeSynonymInstances #-}+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}  -- |  -- Copyright: 2015 Joey Hess <id@joeyh.name>@@ -61,31 +62,25 @@ -- > Download 3 ... ...  module System.Console.Regions (+	-- * Types+	ConsoleRegion,+	RegionLayout(..),+	ToRegionContent(..),+	LiftRegion(..), 	-- * Initialization 	displayConsoleRegions,-	ConsoleRegionHandle,-	RegionLayout(..), 	withConsoleRegion, 	openConsoleRegion,+	newConsoleRegion, 	closeConsoleRegion,-	-- * Region display-	Displayable(..),+	-- * Region content and display 	setConsoleRegion, 	appendConsoleRegion, 	finishConsoleRegion,-	-- * 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,-	newConsoleRegionSTM,-	closeConsoleRegionSTM,-	setConsoleRegionSTM,-	appendConsoleRegionSTM,-	-- * STM regions+	tuneDisplay,+	-- * STM region contents 	---	-- | The `Displayable` instance for STM text can be used to+	-- | The `ToRegionContent` 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. 	--@@ -106,19 +101,18 @@ 	-- > 		] 	-- > 	RegionContent(..),-	readRegionContent, 	consoleSize,-	Width, 	consoleWidth,+	consoleHeight, 	regionList, ) where  import Data.Monoid-import Data.Maybe import Data.String import Data.Char import qualified Data.Text as T import qualified Data.Text.IO as T+import Data.Text (Text) import Control.Monad import Control.Applicative import Control.Monad.IO.Class (liftIO, MonadIO)@@ -152,22 +146,24 @@ -- > ddddeeeefffffff -- [InLine] -- > fffffggggg.....       (expanded to multiple lines) -- > -data RegionLayout = Linear | InLine ConsoleRegionHandle+data RegionLayout = Linear | InLine ConsoleRegion 	deriving (Eq) -newtype ConsoleRegionHandle = ConsoleRegionHandle (TVar Region)+-- | A handle allowing access to a region of the console.+newtype ConsoleRegion = ConsoleRegion (TVar R) 	deriving (Eq) -data Region = Region+data R = R 	{ regionContent :: RegionContent-	, regionLines :: TVar [T.Text] -- ^ cache of regionContent+	, regionRender :: (Text -> STM Text)+	, regionLines :: TVar [Text] -- cache 	, regionLayout :: RegionLayout-	, regionChildren :: Maybe [ConsoleRegionHandle]+	, regionChildren :: TVar [ConsoleRegion] 	}  data RegionContent-	= RegionContent (TVar T.Text) -	| RegionContentSTM (STM T.Text)+	= RegionContent (TVar Text) +	| RegionContentSTM (STM Text)  -- | All the regions that are currently displayed on the screen. --@@ -175,7 +171,7 @@ -- it will change the order in which regions are displayed. -- It's also fine to remove, duplicate, or add new regions to the list. {-# NOINLINE regionList #-}-regionList :: TMVar [ConsoleRegionHandle]+regionList :: TMVar [ConsoleRegion] regionList = unsafePerformIO newEmptyTMVarIO  -- | On unix systems, this TVar is automatically updated when the@@ -187,23 +183,39 @@  type Width = Int --- Get the width from the `consoleSize`-consoleWidth :: STM Width+-- | Get the width from the `consoleSize`+consoleWidth :: STM Int consoleWidth = Console.width <$> readTVar consoleSize +-- | Get the height from the `consoleSize`+consoleHeight :: STM Int+consoleHeight = Console.width <$> readTVar consoleSize+ -- | The RegionList TMVar is left empty when `displayConsoleRegions` -- is not running. regionDisplayEnabled :: IO Bool regionDisplayEnabled = atomically $ not <$> isEmptyTMVar regionList +-- | Many actions in this module can be run in either the IO monad+-- or the STM monad. Using STM allows making several changes to the+-- displayed regions atomically, with the display updated a single time.+class LiftRegion m where+	liftRegion :: STM a -> m a++instance LiftRegion STM where+	liftRegion = id++instance LiftRegion IO where+	liftRegion = atomically+ -- | Values that can be displayed in a region.-class Displayable v where+class ToRegionContent v where 	toRegionContent :: v -> STM RegionContent -instance Displayable String where+instance ToRegionContent String where 	toRegionContent = fromOutput -instance Displayable T.Text where+instance ToRegionContent Text where 	toRegionContent = fromOutput  fromOutput :: Outputable v => v -> STM RegionContent@@ -213,10 +225,11 @@ -- -- Any change to the values that action reads will result in an immediate -- refresh of the display.-instance Displayable (STM T.Text) where+instance ToRegionContent (STM Text) where 	toRegionContent = pure . RegionContentSTM --- | Sets the value to display within a console region.+-- | Sets the value of a console region. This will cause the+-- console to be updated to display the new value. -- -- 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@@ -227,118 +240,89 @@ --  -- Other ANSI escape sequences, especially those doing cursor -- movement, will mess up the layouts of regions. Caveat emptor.-setConsoleRegion :: Displayable v => ConsoleRegionHandle -> v -> IO ()-setConsoleRegion h = atomically . setConsoleRegionSTM h---- | STM version of `setConsoleRegion`-setConsoleRegionSTM :: Displayable v => ConsoleRegionHandle -> v -> STM ()-setConsoleRegionSTM (ConsoleRegionHandle tv) v = do-	r <- readTVar tv-	width <- consoleWidth-	r' <- modifyRegion r width $ const $ toRegionContent v-	writeTVar tv r'-	case regionLayout r of-		Linear -> return ()-		InLine p -> refreshParent p---- | Appends the value to whatever was already on display within a console--- region.-appendConsoleRegion :: Outputable v => ConsoleRegionHandle -> v -> IO ()-appendConsoleRegion h = atomically . appendConsoleRegionSTM h+setConsoleRegion :: (ToRegionContent v, LiftRegion m) => ConsoleRegion -> v -> m ()+setConsoleRegion r v = liftRegion $+	modifyRegion r $ const $ toRegionContent v --- | STM version of `appendConsoleRegion`-appendConsoleRegionSTM :: Outputable v => ConsoleRegionHandle -> v -> STM ()-appendConsoleRegionSTM (ConsoleRegionHandle tv) v = do-	r <- readTVar tv-	width <- consoleWidth-	r' <- modifyRegion r width $ \rc -> case rc of+-- | Appends a value to the current value of a console region.+--+-- > appendConsoleRegion progress "." -- add another dot to progress display+appendConsoleRegion :: (Outputable v, LiftRegion m) => ConsoleRegion -> v -> m ()+appendConsoleRegion r v = liftRegion $+	modifyRegion r $ \rc -> case rc of 		RegionContent cv -> do 			modifyTVar' cv (<> toOutput v) 			return rc 		RegionContentSTM a -> return $ RegionContentSTM $ do 			t <- a 			return (t <> toOutput v)-	writeTVar tv r'-	case regionLayout r of-		Linear -> return ()-		InLine p -> refreshParent p -modifyRegion :: Region -> Width -> (RegionContent -> STM RegionContent) -> STM Region-modifyRegion r width f = do+modifyRegion :: ConsoleRegion -> (RegionContent -> STM RegionContent) -> STM ()+modifyRegion (ConsoleRegion tv) f = do+	r <- readTVar tv 	rc <- f (regionContent r)-	t <- readRegionContent' rc-	writeTVar (regionLines r) (calcRegionLines t width)-	return $ r { regionContent = rc }---- | Reads the content of a region.-readRegionContent :: ConsoleRegionHandle -> STM T.Text-readRegionContent (ConsoleRegionHandle tv) =-	readRegionContent' . regionContent =<< readTVar tv+	t <- readRegionContent rc+	width <- consoleWidth+	writeTVar (regionLines r) =<< calcRegionLines r t width+	case regionLayout r of+		Linear -> return ()+		InLine p -> refreshRegion p+	let r' = r { regionContent = rc }+	writeTVar tv r' -readRegionContent' :: RegionContent -> STM T.Text-readRegionContent' (RegionContent t) = readTVar t-readRegionContent' (RegionContentSTM a) = a+readRegionContent :: RegionContent -> STM Text+readRegionContent (RegionContent t) = readTVar t+readRegionContent (RegionContentSTM a) = a -resizeRegion :: Width -> ConsoleRegionHandle -> STM (Region, [T.Text])-resizeRegion width (ConsoleRegionHandle tv) = do+resizeRegion :: Width -> ConsoleRegion -> STM (R, [Text])+resizeRegion width (ConsoleRegion tv) = do 	r <- readTVar tv-	t <- readRegionContent' (regionContent r)-	let ls = calcRegionLines t width+	t <- readRegionContent (regionContent r)+	ls <- calcRegionLines r t width 	writeTVar (regionLines r) ls 	return (r, ls)  -- | Runs the action with a new console region, closing the region when -- the action finishes or on exception.-withConsoleRegion :: (MonadIO m, MonadMask m) => RegionLayout -> (ConsoleRegionHandle -> m a) -> m a-withConsoleRegion l = bracketIO (openConsoleRegion l) closeConsoleRegion---- | Opens a new console region for output.-openConsoleRegion :: RegionLayout -> IO ConsoleRegionHandle-openConsoleRegion ly = atomically $ openConsoleRegionSTM ly T.empty+withConsoleRegion :: (LiftRegion m, MonadIO m, MonadMask m) => RegionLayout -> (ConsoleRegion -> m a) -> m a+withConsoleRegion ly = bracketIO (openConsoleRegion ly) (closeConsoleRegion) --- | 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)-openConsoleRegionSTM :: Displayable v => RegionLayout -> v -> STM ConsoleRegionHandle-openConsoleRegionSTM ly v = do-	h <- newConsoleRegionSTM ly T.empty+-- | Opens a new console region.+openConsoleRegion :: LiftRegion m => RegionLayout -> m ConsoleRegion+openConsoleRegion ly = liftRegion $ do+	h <- newConsoleRegion ly T.empty 	case ly of 		Linear -> do-			v <- tryTakeTMVar regionList-			case v of-				Just l -> do putTMVar regionList (h:l)+			ml <- tryTakeTMVar regionList+			case ml of+				Just l -> putTMVar regionList (h:l) 				-- displayConsoleRegions is not active, so 				-- it's not put on any list, and won't display 				Nothing -> return () 		InLine parent -> addChild h parent-	setConsoleRegionSTM h v 	return h  -- | Makes a new region, but does not add it to the display.-newConsoleRegionSTM :: Displayable v => RegionLayout -> v -> STM ConsoleRegionHandle-newConsoleRegionSTM ly v = do-	width <- consoleWidth+newConsoleRegion :: ToRegionContent v => RegionLayout -> v -> STM ConsoleRegion+newConsoleRegion ly v = do 	rc <- newTVar mempty-	ls <- newTVar $ calcRegionLines mempty width-	let r = Region+	rl <- newTVar mempty+	rs <- newTVar mempty+	let r = R 		{ regionContent = RegionContent rc-		, regionLines = ls+		, regionRender = pure+		, regionLines = rl 		, regionLayout = ly-		, regionChildren = Nothing+		, regionChildren = rs 		}-	h <- ConsoleRegionHandle <$> newTVar r-	setConsoleRegionSTM h v+	h <- ConsoleRegion <$> newTVar r+	setConsoleRegion h v 	return h  -- | Closes a console region. Once closed, the region is removed from the -- display.-closeConsoleRegion :: ConsoleRegionHandle -> IO ()-closeConsoleRegion = atomically . closeConsoleRegionSTM--closeConsoleRegionSTM :: ConsoleRegionHandle -> STM ()-closeConsoleRegionSTM h@(ConsoleRegionHandle tv) = do+closeConsoleRegion :: LiftRegion m => ConsoleRegion -> m ()+closeConsoleRegion h@(ConsoleRegion tv) = liftRegion $ do 	v <- tryTakeTMVar regionList 	case v of 		Just l ->@@ -352,43 +336,62 @@  -- | Closes the console region and displays the passed value in the -- scrolling area above the active console regions.-finishConsoleRegion :: Outputable v => ConsoleRegionHandle -> v -> IO ()-finishConsoleRegion h = atomically . finishConsoleRegionSTM h--finishConsoleRegionSTM :: Outputable v => ConsoleRegionHandle -> v -> STM ()-finishConsoleRegionSTM h v = do-	closeConsoleRegionSTM h+finishConsoleRegion :: (Outputable v, LiftRegion m) => ConsoleRegion -> v -> m ()+finishConsoleRegion h v = liftRegion $ do+	closeConsoleRegion h 	bufferOutputSTM StdOut (toOutput v <> fromString "\n") -removeChild :: ConsoleRegionHandle -> ConsoleRegionHandle -> STM ()-removeChild child parent@(ConsoleRegionHandle pv) = do-	modifyTVar' pv $ \p -> case regionChildren p of-		Nothing -> p-		Just l -> p { regionChildren = Just $ filter (/= child) l }-	refreshParent parent+-- | Changes how a console region displays.+--+-- Each time the region's value changes, the STM action is provided+-- with the current value of the region, and returns the value to display.+--+-- For example, this will prevent a region from ever displaying more+-- than 10 characters wide, and will make it display text reversed:+-- +-- > tuneDisplay myregion $ pure . T.take 10+-- > tuneDisplay myregion $ pure . T.reverse+--+-- Note that repeated calls to tuneDisplay are cumulative.+tuneDisplay :: LiftRegion m => ConsoleRegion -> (Text -> STM Text) -> m ()+tuneDisplay cr@(ConsoleRegion tv) renderer = liftRegion $ do+	r <- readTVar tv+	let rr = \t -> renderer =<< regionRender r t+	let r' = r { regionRender = rr }+	writeTVar tv r'+	refreshRegion cr+	+refreshRegion :: ConsoleRegion -> STM ()+refreshRegion (ConsoleRegion tv) = do+	r <- readTVar tv+	width <- consoleWidth+	t <- readRegionContent (regionContent r)+	writeTVar (regionLines r) =<< calcRegionLines r t width -addChild :: ConsoleRegionHandle -> ConsoleRegionHandle -> STM ()-addChild child parent@(ConsoleRegionHandle pv) = do-	modifyTVar' pv $ \p -> p-		{ regionChildren = Just $ child : filter (/= child) (fromMaybe [] (regionChildren p)) }-	refreshParent parent+displayChildren :: ConsoleRegion -> STM ()+displayChildren p@(ConsoleRegion tv) = tuneDisplay p $ \t -> do+	children <- readTVar . regionChildren =<< readTVar tv+	ct <- T.concat <$> mapM getc children+	return $ t <> ct+  where+	getc (ConsoleRegion cv) = do+		c <- readTVar cv+		regionRender c =<< readRegionContent (regionContent c) -refreshParent :: ConsoleRegionHandle -> STM ()-refreshParent (ConsoleRegionHandle pv) = do-	p <- readTVar pv-	width <- consoleWidth-	case regionChildren p of-		Nothing -> return ()-		Just l -> do-			cs <- forM l $ \child -> do-				refreshParent child-				readRegionContent child-			let !c = mconcat cs-			rc <- newTVar c-			let p' = p { regionContent = RegionContent rc }-			writeTVar pv p'-			writeTVar (regionLines p) (calcRegionLines c width)+addChild :: ConsoleRegion -> ConsoleRegion -> STM ()+addChild child parent@(ConsoleRegion pv) = do+	cv <- regionChildren <$> readTVar pv+	children <- readTVar cv+	let !children' = filter (/= child) children ++ [child]+	writeTVar cv children'+	when (null children) $+		displayChildren parent +removeChild :: ConsoleRegion -> ConsoleRegion -> STM ()+removeChild child _parent@(ConsoleRegion pv) = do+	cv <- regionChildren <$> readTVar pv+	modifyTVar' cv (filter (/= child))+ -- | Handles all display for the other functions in this module. -- -- Note that this uses `lockOutput`, so it takes over all output to the@@ -434,7 +437,7 @@ 	| TerminalResize Width 	| EndSignal () -type RegionSnapshot = ([ConsoleRegionHandle], [Region], [[T.Text]])+type RegionSnapshot = ([ConsoleRegion], [R], [[Text]])  displayThread :: Bool -> TSem -> IO () displayThread isterm endsignal = do@@ -481,10 +484,10 @@ 				go (orighandles, newregions, newlines) newwidth 			EndSignal () -> return () -readRegions :: [ConsoleRegionHandle] -> STM [Region]-readRegions = mapM (\(ConsoleRegionHandle h) -> readTVar h)+readRegions :: [ConsoleRegion] -> STM [R]+readRegions = mapM (\(ConsoleRegion h) -> readTVar h) --- Wait for any changes to the region list, eg adding or removing a handle.+-- | Wait for any changes to the region list, eg adding or removing a region. regionListWaiter :: RegionSnapshot -> STM RegionSnapshot regionListWaiter (orighandles, _origregions, origlines) = do 	handles <- readTMVar regionList@@ -494,7 +497,8 @@ 			rs <- readRegions handles 			return (handles, rs, origlines) --- Wait for any changes to any of the regions currently in the region list.+-- Wait for any changes to any of the contents of regions currently in the+-- region list. regionWaiter :: RegionSnapshot -> Width -> STM RegionSnapshot regionWaiter (orighandles, _origregions, origlines) width = do 	rs <- readRegions orighandles@@ -507,14 +511,14 @@ 		RegionContent _ -> reverse <$> readTVar (regionLines r) 		RegionContentSTM a -> do 			c <- a-			let ls = reverse $ calcRegionLines c width+			ls <- reverse <$> calcRegionLines r c width 			when (ls /= ols) $ 				writeTVar (regionLines r) ls 			return ls  -- This is not an optimal screen update like curses can do, but it's -- pretty efficient, most of the time!-changedLines :: [T.Text] -> [T.Text] -> IO ()+changedLines :: [Text] -> [Text] -> IO () changedLines origlines newlines 	| delta == 0 = do 		-- The total number of lines is unchanged, so update@@ -540,7 +544,7 @@   where 	delta = length newlines - length origlines -diffUpdate :: [T.Text] -> [T.Text] -> IO ()+diffUpdate :: [Text] -> [Text] -> IO () diffUpdate old new = updateLines (zip (zip new changed) old)   where 	changed = map (uncurry (/=)) (zip new old) ++ repeat True@@ -554,7 +558,7 @@ -- Displays lines that are paired with True, and skips over the rest. -- Cursor is assumed to be just below the first line at the -- beginning, and is put back there at the end.-updateLines :: [((T.Text, Bool), T.Text)] -> IO ()+updateLines :: [((Text, Bool), Text)] -> IO () updateLines l 	| null l' = noop 	| otherwise = do@@ -571,7 +575,7 @@ -- Move cursor up before the lines, performs some output there, -- which will scroll down and overwrite the lines, so  -- redraws all the lines below.-inAreaAbove :: Bool -> Int -> [T.Text] -> IO () -> IO ()+inAreaAbove :: Bool -> Int -> [Text] -> IO () -> IO () inAreaAbove isterm numlines ls outputter = do 	when isterm $ do 		unless (numlines < 1) $@@ -583,7 +587,7 @@ 		displayLines (reverse ls) 	hFlush stdout -displayLines :: [T.Text] -> IO ()+displayLines :: [Text] -> IO () displayLines = mapM_ $ \l -> do 	T.hPutStr stdout l 	putChar '\n'@@ -592,6 +596,11 @@ installResizeHandler h = void $ 	installHandler windowChange (maybe Default Catch h) Nothing +calcRegionLines :: R -> Text -> Width -> STM [Text]+calcRegionLines r content width = do+	t <- regionRender r content+	return $ calcLines t width+ -- | Splits a Text into the lines it would display using when output onto -- a console with a given width, starting from the first column. --@@ -599,34 +608,34 @@ -- work despite the lines being split up, and the lines can be output -- indepedently. For example, "foooREDbar bazRESET" when split into lines -- becomes ["fooREDbarRESET", "RED bazRESET"]-calcRegionLines :: T.Text -> Width -> [T.Text]-calcRegionLines t width+calcLines :: Text -> Width -> [Text]+calcLines t width 	| width < 1 || T.null t = [t] -- even an empty text is 1 line high-	| otherwise = calcRegionLines' width [] [] 0 1 (T.length t) t+	| otherwise = calcLines' width [] [] 0 1 (T.length t) t -calcRegionLines' :: Int -> [T.Text] -> [T.Text] -> Int -> Int -> Int -> T.Text -> [T.Text]-calcRegionLines' width collectedlines collectedSGR i displaysize len t+calcLines' :: Int -> [Text] -> [Text] -> Int -> Int -> Int -> Text -> [Text]+calcLines' width collectedlines collectedSGR i displaysize len t 	| i >= len = if i > 0 		then reverse (finishline t) 		else reverse collectedlines-	| t1 == '\n' = calcRegionLines' width (finishline $ T.init currline)+	| t1 == '\n' = calcLines' width (finishline $ T.init currline) 		[] 0 1 (T.length rest) (contSGR rest) 	-- ANSI escape sequences do not take up space on screen. 	| t1 == '\ESC' && i+1 < len = case T.index t (i+1) of 		'[' -> skipansi endCSI True 		']' -> skipansi endOSC False-		_ -> calcRegionLines' width collectedlines collectedSGR (i+1) displaysize len t+		_ -> calcLines' width collectedlines collectedSGR (i+1) displaysize len t 	-- Control characters do not take up space on screen.-	| isControl t1 = calcRegionLines' width collectedlines collectedSGR (i+1) displaysize len t-	| displaysize >= width = calcRegionLines' width (finishline currline)+	| isControl t1 = calcLines' width collectedlines collectedSGR (i+1) displaysize len t+	| displaysize >= width = calcLines' width (finishline currline) 		[] 0 1 (T.length rest) (contSGR rest)-	| otherwise = calcRegionLines' width collectedlines collectedSGR (i+1) (displaysize+1) len t+	| otherwise = calcLines' width collectedlines collectedSGR (i+1) (displaysize+1) len t   where 	t1 = T.index t i 	(currline, rest) = T.splitAt (i+1) t  	skipansi toend isCSI = case T.findIndex toend (T.drop (i+2) t) of-		Just csiend -> calcRegionLines' width collectedlines +		Just csiend -> calcLines' width collectedlines  			(addSGR (csiend+2)) (i+2+csiend) (displaysize-1) len t 		Nothing -> reverse (finishline t) 	  where@@ -646,7 +655,7 @@ 	-- Continue any open SGR codes from previous line 	contSGR l = mconcat (reverse collectedSGR) <> l -resetSGR :: T.Text+resetSGR :: Text resetSGR = T.pack (setSGRCode [Reset])  endCSI :: Char -> Bool@@ -684,7 +693,7 @@ -- -- Also note above that the sequence has to clear the rest of the line, -- since the new line is shorter than the old.-calcLineUpdate :: T.Text -> T.Text -> [LineUpdate]+calcLineUpdate :: Text -> Text -> [LineUpdate] calcLineUpdate old new =  	reverse $ go 		(advanceLine old [] [])@@ -711,7 +720,7 @@ -- -- resetSGR is handled specially; it causes all SGRs to be removed from -- invis, It's still prepended to past.-advanceLine :: T.Text -> Past -> Invis -> (Maybe Char, T.Text, Past, Invis)+advanceLine :: Text -> Past -> Invis -> (Maybe Char, Text, Past, Invis) advanceLine t past invis 	| T.null t = (Nothing, T.empty, past, invis) 	| otherwise = case T.head t of@@ -735,14 +744,14 @@ 		| sgrt == resetSGR = filter (not . isSGR) l 	addsgr s l = s:l -data LineUpdate = Display T.Text | Skip [Char] | SGR T.Text | ClearToEnd+data LineUpdate = Display Text | Skip [Char] | SGR Text | ClearToEnd 	deriving (Eq, Show)  isSGR :: LineUpdate -> Bool isSGR (SGR _) = True isSGR _ = False -genLineUpdate :: [LineUpdate] -> T.Text+genLineUpdate :: [LineUpdate] -> Text genLineUpdate l = T.concat $ map tot (optimiseLineUpdate l)   where 	tot (Display t) = t@@ -772,7 +781,7 @@ 	tryharder c l = go [] (reverse c ++ l)  -- Parse and combine 2 ANSI SGR sequences into one.-combineSGR :: T.Text -> T.Text -> T.Text+combineSGR :: Text -> Text -> Text combineSGR a b = case combineSGRCodes (codes a) (codes b) of 	Nothing -> a <> b 	Just cs -> T.pack $ "\ESC[" ++ intercalate ";" (map show cs) ++ "m"
aptdemo.hs view
@@ -17,7 +17,7 @@ 	threadDelay 500000 	outputConcurrent ("Updated blah blah #" ++ show n ++ "\n") -downline cs = withConsoleRegion Linear $ \r -> +downline cs = withConsoleRegion Linear $ \r -> 	mapConcurrently (\a -> a r) (reverse cs)  dl c parent = withConsoleRegion (InLine parent) (go 0)@@ -34,7 +34,7 @@ growingdots parent = withConsoleRegion (InLine parent) (go 0)   where 	go n r-		| n <= 250 = do+		| n <= 300 = do 			setConsoleRegion r ("[" ++ setSGRCode [SetColor Foreground Vivid Blue] ++ replicate n '.' ++ setSGRCode [Reset] ++ "] ") 			threadDelay (100000) 			go (n+1) r
concurrent-output.cabal view
@@ -1,5 +1,5 @@ Name: concurrent-output-Version: 1.3.0+Version: 1.4.0 Cabal-Version: >= 1.8 License: BSD2 Maintainer: Joey Hess <id@joeyh.name>
demo2.hs view
@@ -21,7 +21,7 @@   where 	withtitle n t s = t ++ take n s -spinner :: Int -> Int -> String -> (ConsoleRegionHandle -> String -> IO ()) -> [s] -> (String -> [s] -> String) -> IO ()+spinner :: Int -> Int -> String -> (ConsoleRegion -> String -> IO ()) -> [s] -> (String -> [s] -> String) -> IO () spinner cycles delay title updater source f = 	withConsoleRegion Linear $ \r -> do 		setConsoleRegion r title'
stmdemo.hs view
@@ -13,6 +13,7 @@ main = void $ displayConsoleRegions $ mapConcurrently id 	[ infoRegion 	, clockRegion+	, growingDots 	]  infoRegion :: IO ()@@ -36,14 +37,21 @@ clockRegion :: IO () clockRegion = do 	tv <- atomically . newTVar =<< getCurrentTime-	void $ atomically $ openConsoleRegionSTM Linear . rightAlign-		=<< newConsoleRegionSTM Linear (timeDisplay tv)+	void $ atomically $ do+		r <- openConsoleRegion Linear+		setConsoleRegion r (timeDisplay tv)+		rightAlign r 	forever $ do 		threadDelay 1000000 -- 1 sec 		atomically . (writeTVar tv) =<< getCurrentTime -rightAlign :: ConsoleRegionHandle -> STM T.Text-rightAlign r = do-        t <- readRegionContent r+rightAlign :: ConsoleRegion -> STM ()+rightAlign r = tuneDisplay r $ \t -> do         w <- consoleWidth         return (T.replicate (w - T.length t) (T.singleton ' ') <> t)++growingDots = withConsoleRegion Linear $ \r -> do+	atomically $ rightAlign r+	forever $ do+		appendConsoleRegion r "."+		threadDelay (100000)