haskell / haskell/haskeline

ANSI Color Support

Open
#63 6 comments 1 reaction 0 assignees View on GitHub
Dominant language
Haskell
Stars
247
Forks
82
PR merge metrics
No merged PRs in 30d

Description

I would like to mix haskeline with the ansi-terminal package to get cross platform colored text support that also has good command line support. However, it seems that the printer function that you obtain from `getExternalPrint` is actually only an action to queue text for later printing, so directly mixing it with the `setSGR` function from ansi-terminal doesn't work at all.

Example:

```haskell
-- base
import Control.Monad (forever)
import Control.Concurrent (threadDelay)
-- haskeline
import System.Console.Haskeline
-- async
import Control.Concurrent.Async
-- transformers
import Control.Monad.IO.Class (liftIO)
-- ansi-terminal
import System.Console.ANSI

counter :: (String -> IO ()) -> IO ()
counter printer = go 0 infColorList where
infColorList = cycle [Red,Green,Yellow,Blue,Magenta,Cyan,White]
go n (c:cs) = do
-- This part should print each message 1 second apart, and also in
-- different colors. That's not what happens though. Instead, there are
-- no colors at all. Also, the printing rate is a bit unsteady, but
-- that's more of a secondary problem.
threadDelay 1000000
setSGR [SetColor Foreground Vivid c]
printer $ show n ++ "\n"
setSGR [Reset]
go (n+1) cs

main = runInputT defaultSettings $ do
printer <- getExternalPrint
outputStrLn "Spawning a counting thread..."
(liftIO . async) (counter printer)
loop
where
loop :: InputT IO ()
loop = do
input <- getInputLine "> "
case input of
Nothing -> return ()
Just "quit" -> return ()
Just line -> do
outputStrLn $ "you typed:" ++ line
loop
```

Now I would ideally like to be able to encode the color changes _into the String_ without using the normal raw escape sequences (which don't work on Windows anyway), ideally in a human-readable way so that users would be able to take advantage of this with their inputs. Then an escape sequence aware printing action can switch colors as it prints out the String. Here's an excerpt of the [full example](https://gist.github.com/Lokathor/b9275c459ad4707025587a96ecee86aa) of what I mean.

```haskell
formatString :: String -> IO ()
formatString [] = setSGR [Reset]
formatString ('@':'@':more) = putChar '@' >> formatString more
formatString ('@':c:more) = do
case c of
'k' -> setSGR [SetColor Foreground Dull Black]
'K' -> setSGR [SetColor Foreground Vivid Black]
'r' -> setSGR [SetColor Foreground Dull Red]
'R' -> setSGR [SetColor Foreground Vivid Red]
-- more colors here, you get the idea
_ -> putChar '@' >> putChar c
formatString more
formatString ('$':'$':more) = putChar '$' >> formatString more
formatString ('$':c:more) = do
case c of
'k' -> setSGR [SetColor Background Dull Black]
'K' -> setSGR [SetColor Background Vivid Black]
'r' -> setSGR [SetColor Background Dull Red]
'R' -> setSGR [SetColor Background Vivid Red]
-- more colors here, you get the idea
_ -> putChar '$' >> putChar c
formatString more
formatString (other:more) = do
putChar other
formatString more
```

This would probably necessitate that the printer that you get from `getExternalPrint` actually do the printing by blocking until the printing happens instead of just queuing it up for later. Or perhaps `getExternalPrint` can stay as it is (non-blocking) with updated docs, and then a new blocking printer function can be made available via a new InputT action.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.