System.Console.ANSI

ansi-terminal-0.11.1: Simple ANSI terminal support, with Windows compatibility

Safe Haskell | Safe
Language | Haskell2010

System.Console.ANSI

Contents

- Basic data types
- Cursor movement by character
- Cursor movement by line
- Directly changing cursor position
- Saving, restoring and reporting cursor position
- Clearing parts of the screen
- Scrolling the screen
- Select Graphic Rendition mode: colors and other whizzy stuff
- Cursor visibilty changes
- Changing the title
- Checking if handle supports ANSI (not portable: GHC only)
- Getting the cursor position
- Getting the terminal size

Description

Through this module, this library provides platform-independent support for control character sequences following the 'ANSI' standards (see further below) for terminal software that supports those sequences, running on a Unix-like operating system or Windows.

The sequences of control characters (also referred to as 'escape' sequences or codes) provide a rich range of functionality for terminal control, which includes:

- Colored text output, with control over both foreground and background colors
- Clearing parts of a line or the screen
- Hiding or showing the cursor
- Moving the cursor around
- Reporting the position of the cursor
- Scrolling the screen up or down
- Changing the title of the terminal

A terminal that supports control character sequences acts on them when they are flushed from the output buffer (with a newline character `"\n"` or, for the standard output channel, `hFlush stdout`).

The functions moving the cursor to an absolute position are 0-based (the top-left corner is considered to be at row 0 column 0) (see `setCursorPosition`) and so is `getCursorPosition`. The 'ANSI' standards themselves are 1-based (that is, the top-left corner is considered to be at row 1 column 1) and some functions reporting the position of the cursor are too (see `reportCursorPosition`).

The native terminal software on Windows is 'Command Prompt' or `PowerShell`. Before Windows 10 version 1511 (known as the 'November [2015] Update' or 'Threshold 2') that software did not support such control sequences. For that software, this library also provides support for such sequences by using emulation.

Terminal software other than the native software exists for Windows. One example is the 'mintty' terminal emulator for 'Cygwin', 'MSYS' or 'MSYS2', and dervied projects, and for 'WSL' (Windows Subsystem for Linux).

The 'ANSI' standards refer to (1) standard ECMA-48 `Control Functions for Coded Character Sets' (5th edition, 1991); (2) extensions in ITU-T Recommendation (previously CCITT Recommendation) T.416 (03/93) 'Information Technology – Open Document Architecture (ODA) and Interchange Format: Character Content Architectures` (also published as ISO/IEC International Standard 8613-6); and (3) further extensions used by 'XTerm', a terminal emulator for the X Window System. The escape codes are described in a Wikipedia article at http://en.wikipedia.org/wiki/ANSI_escape_code and those codes supported on current versions of Windows at https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences.

The whole of the 'ANSI' standards are not supported by this library but most (if not all) of the parts that are popular and well-supported by terminal software are supported. Every function exported by this module comes in three variants, namely:

- A variant that has an `IO ()` type and doesn't take a `Handle` (for example, `clearScreen :: IO ()`). This variant just outputs the `ANSI` command directly to the standard output channel (`stdout`) and any terminal corresponding to it. Commands issued like this should work as you expect on both Unix-like operating systems and Windows.
- An '`h`...' variant that has an `IO ()` type but takes a `Handle` (for example, `hClearScreen :: Handle -> IO ()`). This variant outputs the `ANSI` command to the supplied handle and any terminal corresponding to it. Commands issued like this should also work as you expect on both Unix-like operating systems and Windows.
- A '...`Code`' variant that has a `String` type (for example, `clearScreenCode :: String`). This variant outputs the sequence of control characters as a `String`, which can be added to any other bit of text before being output. The use of these codes is generally discouraged because they will not work on legacy versions of Windows where the terminal in use is not ANSI-enabled (see further above). On Windows, where emulation has been necessary, these variants will always output the empty string. That is done so that it is possible to use them portably; for example, coloring console output on the understanding that you will see colors only if you are running on a Unix-like operating system or a version of Windows where emulation has not been necessary. If the control characters are always required, see module System.Console.ANSI.Codes.

Example:

    module Main where

    import System.Console.ANSI

    -- Set colors and write some text in those colors.
    main :: IO ()
    main = do
      setSGR [SetColor Foreground Vivid Red]
      setSGR [SetColor Background Vivid Blue]
      putStrLn "Red-On-Blue"
      setSGR [Reset]  -- Reset to default colour scheme
      putStrLn "Default colors."

Another example:

    module Main where

    import System.IO (hFlush, stdout)
    import System.Console.ANSI

    main :: IO ()
    main = do
      setSGR [SetColor Foreground Dull Blue]
      putStr "Enter your name: "
      setSGR [SetColor Foreground Dull Yellow]
      hFlush stdout  -- flush the output buffer before getLine
      name <- getLine
      setSGR [SetColor Foreground Dull Blue]
      putStrLn $ "Hello, " ++ name ++ "!"
      setSGR [Reset]  -- reset to default colour scheme

For many more examples, see the project's extensive Example.hs file.

Synopsis

- module System.Console.ANSI.Types
- cursorUp :: Int -> IO ()
- cursorDown :: Int -> IO ()
- cursorForward :: Int -> IO ()
- cursorBackward :: Int -> IO ()
- hCursorUp :: Handle -> Int -> IO ()
- hCursorDown :: Handle -> Int -> IO ()
- hCursorForward :: Handle -> Int -> IO ()
- hCursorBackward :: Handle -> Int -> IO ()
- cursorUpCode :: Int -> String
- cursorDownCode :: Int -> String
- cursorForwardCode :: Int -> String
- cursorBackwardCode :: Int -> String
- cursorUpLine :: Int -> IO ()
- cursorDownLine :: Int -> IO ()
- hCursorUpLine :: Handle -> Int -> IO ()
- hCursorDownLine :: Handle -> Int -> IO ()
- cursorUpLineCode :: Int -> String
- cursorDownLineCode :: Int -> String
- setCursorColumn :: Int -> IO ()
- hSetCursorColumn :: Handle -> Int -> IO ()
- setCursorColumnCode :: Int -> String
- setCursorPosition :: Int -> Int -> IO ()
- hSetCursorPosition :: Handle -> Int -> Int -> IO ()
- setCursorPositionCode :: Int -> Int -> String
- saveCursor :: IO ()
- hSaveCursor :: Handle -> IO ()
- saveCursorCode :: String
- restoreCursor :: IO ()
- hRestoreCursor :: Handle -> IO ()
- restoreCursorCode :: String
- reportCursorPosition :: IO ()
- hReportCursorPosition :: Handle -> IO ()
- reportCursorPositionCode :: String
- clearFromCursorToScreenEnd :: IO ()
- clearFromCursorToScreenBeginning :: IO ()
- clearScreen :: IO ()
- hClearFromCursorToScreenEnd :: Handle -> IO ()
- hClearFromCursorToScreenBeginning :: Handle -> IO ()
- hClearScreen :: Handle -> IO ()
- clearFromCursorToScreenEndCode :: String
- clearFromCursorToScreenBeginningCode :: String
- clearScreenCode :: String
- clearFromCursorToLineEnd :: IO ()
- clearFromCursorToLineBeginning :: IO ()
- clearLine :: IO ()
- hClearFromCursorToLineEnd :: Handle -> IO ()
- hClearFromCursorToLineBeginning :: Handle -> IO ()
- hClearLine :: Handle -> IO ()
- clearFromCursorToLineEndCode :: String
- clearFromCursorToLineBeginningCode :: String
- clearLineCode :: String
- scrollPageUp :: Int -> IO ()
- scrollPageDown :: Int -> IO ()
- hScrollPageUp :: Handle -> Int -> IO ()
- hScrollPageDown :: Handle -> Int -> IO ()
- scrollPageUpCode :: Int -> String
- scrollPageDownCode :: Int -> String
- setSGR :: [SGR] -> IO ()
- hSetSGR :: Handle -> [SGR] -> IO ()
- setSGRCode :: [SGR] -> String
- hideCursor :: IO ()
- showCursor :: IO ()
- hHideCursor :: Handle -> IO ()
- hShowCursor :: Handle -> IO ()
- hideCursorCode :: String
- showCursorCode :: String
- setTitle :: String -> IO ()
- hSetTitle :: Handle -> String -> IO ()
- setTitleCode :: String -> String
- hSupportsANSI :: Handle -> IO Bool
- hSupportsANSIColor :: Handle -> IO Bool
- hSupportsANSIWithoutEmulation :: Handle -> IO (Maybe Bool)
- getCursorPosition :: IO (Maybe (Int, Int))
- hGetCursorPosition :: Handle -> IO (Maybe (Int, Int))
- getReportedCursorPosition :: IO String
- cursorPosition :: ReadP (Int, Int)
- getTerminalSize :: IO (Maybe (Int, Int))
- hGetTerminalSize :: Handle -> IO (Maybe (Int, Int))

# Basic data types

module System.Console.ANSI.Types

# Cursor movement by character

cursorUp #

Arguments

:: Int | Number of lines or characters to move
-> IO () |

cursorDown #

Arguments

:: Int | Number of lines or characters to move
-> IO () |

cursorForward #

Arguments

:: Int | Number of lines or characters to move
-> IO () |

cursorBackward #

Arguments

:: Int | Number of lines or characters to move
-> IO () |

hCursorUp #

Arguments

:: Handle |
-> Int | Number of lines or characters to move
-> IO () |

hCursorDown #

Arguments

:: Handle |
-> Int | Number of lines or characters to move
-> IO () |

hCursorForward #

Arguments

:: Handle |
-> Int | Number of lines or characters to move
-> IO () |

hCursorBackward #

Arguments

:: Handle |
-> Int | Number of lines or characters to move
-> IO () |

cursorUpCode #

Arguments

:: Int | Number of lines or characters to move
-> String |

cursorDownCode #

Arguments

:: Int | Number of lines or characters to move
-> String |

cursorForwardCode #

Arguments

:: Int | Number of lines or characters to move
-> String |

cursorBackwardCode #

Arguments

:: Int | Number of lines or characters to move
-> String |

# Cursor movement by line

The difference between movements "by character" and "by line" is that `*Line` functions additionally move the cursor to the start of the line, while functions like `cursorUp` and `cursorDown` keep the column the same.

Also keep in mind that `*Line` functions are not as portable. See https://github.com/UnkindPartition/ansi-terminal/issues/10 for the details.

cursorUpLine #

Arguments

:: Int | Number of lines to move
-> IO () |

cursorDownLine #

Arguments

:: Int | Number of lines to move
-> IO () |

hCursorUpLine #

Arguments

:: Handle |
-> Int | Number of lines to move
-> IO () |

hCursorDownLine #

Arguments

:: Handle |
-> Int | Number of lines to move
-> IO () |

cursorUpLineCode #

Arguments

:: Int | Number of lines to move
-> String |

cursorDownLineCode #

Arguments

:: Int | Number of lines to move
-> String |

# Directly changing cursor position

setCursorColumn #

Arguments

:: Int | 0-based column to move to
-> IO () |

Move the cursor to the specified column. The column numbering is 0-based (that is, the left-most column is numbered 0).

hSetCursorColumn #

Arguments

:: Handle |
-> Int | 0-based column to move to
-> IO () |

setCursorColumnCode #

Arguments

:: Int | 0-based column to move to
-> String |

Code to move the cursor to the specified column. The column numbering is 0-based (that is, the left-most column is numbered 0).

setCursorPosition #

Arguments

:: Int | 0-based row to move to
-> Int | 0-based column to move to
-> IO () |

Move the cursor to the specified position (row and column). The position is 0-based (that is, the top-left corner is at row 0 column 0).

hSetCursorPosition #

Arguments

:: Handle |
-> Int | 0-based row to move to
-> Int | 0-based column to move to
-> IO () |

setCursorPositionCode #

Arguments

:: Int | 0-based row to move to
-> Int | 0-based column to move to
-> String |

Code to move the cursor to the specified position (row and column). The position is 0-based (that is, the top-left corner is at row 0 column 0).

# Saving, restoring and reporting cursor position

saveCursor :: IO () #

Save the cursor position in memory. The only way to access the saved value is with the `restoreCursor` command.

Since: 0.7.1

hSaveCursor :: Handle -> IO () #

saveCursorCode :: String #

Since: 0.7.1

restoreCursor :: IO () #

Restore the cursor position from memory. There will be no value saved in memory until the first use of the `saveCursor` command.

Since: 0.7.1

hRestoreCursor :: Handle -> IO () #

restoreCursorCode :: String #

Since: 0.7.1

reportCursorPosition :: IO () #

Looking for a way to get the cursors position? See `getCursorPosition`.

Emit the cursor position into the console input stream, immediately after being recognised on the output stream, as: `ESC [ <cursor row> ; <cursor column> R`

Note that the information that is emitted is 1-based (the top-left corner is at row 1 column 1) but `setCursorColumn` and `setCursorPosition` are 0-based.

In isolation of `getReportedCursorPosition` or `getCursorPosition`, this function may be of limited use on Windows operating systems because of difficulties in obtaining the data emitted into the console input stream. The function `hGetBufNonBlocking` in module System.IO does not work on Windows. This has been attributed to the lack of non-blocking primatives in the operating system (see the GHC bug report #806 at https://ghc.haskell.org/trac/ghc/ticket/806).

Since: 0.7.1

hReportCursorPosition :: Handle -> IO () #

reportCursorPositionCode :: String #

Code to emit the cursor position into the console input stream, immediately after being recognised on the output stream, as: `ESC [ <cursor row> ; <cursor column> R`

Note that the information that is emitted is 1-based (the top-left corner is at row 1 column 1) but `setCursorPositionCode` is 0-based.

In isolation of `getReportedCursorPosition` or `getCursorPosition`, this function may be of limited use on Windows operating systems because of difficulties in obtaining the data emitted into the console input stream. The function `hGetBufNonBlocking` in module System.IO does not work on Windows. This has been attributed to the lack of non-blocking primatives in the operating system (see the GHC bug report #806 at https://ghc.haskell.org/trac/ghc/ticket/806).

Since: 0.7.1

# Clearing parts of the screen

Note that these functions only clear parts of the screen. They do not move the cursor.

clearFromCursorToScreenEnd :: IO () #

clearFromCursorToScreenBeginning :: IO () #

clearScreen :: IO () #

hClearFromCursorToScreenEnd :: Handle -> IO () #

hClearFromCursorToScreenBeginning :: Handle -> IO () #

hClearScreen :: Handle -> IO () #

clearFromCursorToScreenEndCode :: String #

clearFromCursorToScreenBeginningCode :: String #

clearScreenCode :: String #

clearFromCursorToLineEnd :: IO () #

clearFromCursorToLineBeginning :: IO () #

clearLine :: IO () #

hClearFromCursorToLineEnd :: Handle -> IO () #

hClearFromCursorToLineBeginning :: Handle -> IO () #

hClearLine :: Handle -> IO () #

clearFromCursorToLineEndCode :: String #

clearFromCursorToLineBeginningCode :: String #

clearLineCode :: String #

# Scrolling the screen

scrollPageUp #

Arguments

:: Int | Number of lines to scroll by
-> IO () |

Scroll the displayed information up or down the terminal: not widely supported

scrollPageDown #

Arguments

:: Int | Number of lines to scroll by
-> IO () |

Scroll the displayed information up or down the terminal: not widely supported

hScrollPageUp #

Arguments

:: Handle |
-> Int | Number of lines to scroll by
-> IO () |

Scroll the displayed information up or down the terminal: not widely supported

hScrollPageDown #

Arguments

:: Handle |
-> Int | Number of lines to scroll by
-> IO () |

Scroll the displayed information up or down the terminal: not widely supported

scrollPageUpCode #

Arguments

:: Int | Number of lines to scroll by
-> String |

scrollPageDownCode #

Arguments

:: Int | Number of lines to scroll by
-> String |

# Select Graphic Rendition mode: colors and other whizzy stuff

setSGR #

Arguments

:: [SGR] | Commands: these will typically be applied on top of the current console SGR mode. An empty list of commands is equivalent to the list `[Reset]`. Commands are applied left to right.
-> IO () |

Set the Select Graphic Rendition mode

hSetSGR #

Arguments

:: Handle |
-> [SGR] | Commands: these will typically be applied on top of the current console SGR mode. An empty list of commands is equivalent to the list `[Reset]`. Commands are applied left to right.
-> IO () |

Set the Select Graphic Rendition mode

setSGRCode #

Arguments

:: [SGR] | Commands: these will typically be applied on top of the current console SGR mode. An empty list of commands is equivalent to the list `[Reset]`. Commands are applied left to right.
-> String |

# Cursor visibilty changes

hideCursor :: IO () #

showCursor :: IO () #

hHideCursor :: Handle -> IO () #

hShowCursor :: Handle -> IO () #

hideCursorCode :: String #

showCursorCode :: String #

# Changing the title

setTitle #

Arguments

:: String | New title
-> IO () |

Set the terminal window title

hSetTitle #

Arguments

:: Handle |
-> String | New title
-> IO () |

Set the terminal window title

setTitleCode #

Arguments

:: String | New Icon Name and Window Title
-> String |

XTerm control sequence to set the Icon Name and Window Title.

# Checking if handle supports ANSI (not portable: GHC only)

hSupportsANSI :: Handle -> IO Bool #

Use heuristics to determine whether the functions defined in this package will work with a given handle. This function assumes that the handle is writable (that is, it manages output - see `hIsWritable`).

For Unix-like operating systems, the current implementation checks that: (1) the handle is a terminal; and (2) a `TERM` environment variable is not set to `dumb` (which is what the GNU Emacs text editor sets for its integrated terminal).

For Windows, the current implementation performs the same checks as for Unix-like operating systems and, as an alternative, checks whether the handle is connected to a 'mintty' terminal. (That is because the function `hIsTerminalDevice` is used to check if the handle is a terminal. However, where a non-native Windows terminal (such as 'mintty') is implemented using redirection, that function will not identify a handle to the terminal as a terminal.) On Windows 10, if the handle is identified as connected to a native terminal, this function does not enable the processing of 'ANSI' control characters in output (see `hSupportsANSIWithoutEmulation`).

Since: 0.6.2

hSupportsANSIColor :: Handle -> IO Bool #

Some terminals (e.g. Emacs) are not fully ANSI compliant but can support ANSI colors. This can be used in such cases, if colors are all that is needed.

Since: 0.9

hSupportsANSIWithoutEmulation :: Handle -> IO (Maybe Bool) #

Use heuristics to determine whether a given handle will support 'ANSI' control characters in output. (On Windows versions before Windows 10, that means 'support without emulation'.)

If the handle is not writable (that is, it cannot manage output - see `hIsWritable`), then `return (Just False)` is returned.

On Unix-like operating systems, with one exception, the function is consistent with `hSupportsANSI`. The exception is if the handle is not writable.

On Windows, what is returned will depend on what the handle is connected to and the version of the operating system. If the handle is identified as connected to a 'mintty' terminal, `return (Just True)` is returned. If it is identifed as connected to a native terminal, then, on Windows 10, the processing of 'ANSI' control characters will be enabled and `return (Just True)` returned; and, on versions of Windows before Windows 10, `return (Just False)` is returned. Otherwise, if a `TERM` environment variable is set to `dumb`, `return (Just False)` is returned. In all other cases of a writable handle, `return Nothing` is returned; this indicates that the heuristics cannot assist - the handle may be connected to a file or to another type of terminal.

Since: 0.8.1

# Getting the cursor position

getCursorPosition :: IO (Maybe (Int, Int)) #

Attempts to get the reported cursor position, combining the functions `reportCursorPosition`, `getReportedCursorPosition` and `cursorPosition`. Any position `(row, column)` is translated to be 0-based (that is, the top-left corner is at `(0, 0)`), consistent with `setCursorColumn` and `setCursorPosition`. (Note that the information emitted into the console input stream by `reportCursorPosition` is 1-based.) Returns `Nothing` if any data emitted by `reportCursorPosition`, obtained by `getReportedCursorPosition`, cannot be parsed by `cursorPosition`. Uses `stdout`. If `stdout` will be redirected, see `hGetCursorPosition` for a more general function.

On Windows operating systems, the function is not supported on consoles, such as mintty, that are not based on the Windows' Console API. (Command Prompt and PowerShell are based on the Console API.)

Since: 0.10.3

hGetCursorPosition :: Handle -> IO (Maybe (Int, Int)) #

Attempts to get the reported cursor position, combining the functions `hReportCursorPosition` (with the specified handle), `getReportedCursorPosition` and `cursorPosition`. Any position `(row, column)` is translated to be 0-based (that is, the top-left corner is at `(0, 0)`), consistent with `hSetCursorColumn` and `hSetCursorPosition`. (Note that the information emitted into the console input stream by `hReportCursorPosition` is 1-based.) Returns `Nothing` if any data emitted by `hReportCursorPosition`, obtained by `getReportedCursorPosition`, cannot be parsed by `cursorPosition`.

On Windows operating systems, the function is not supported on consoles, such as mintty, that are not based on the Windows' Console API. (Command Prompt and PowerShell are based on the Console API.)

Since: 0.10.1

getReportedCursorPosition :: IO String #

Attempts to get the reported cursor position data from the console input stream. The function is intended to be called immediately after `reportCursorPosition` (or related functions) have caused characters to be emitted into the stream.

For example, on a Unix-like operating system:

    -- set no buffering (if 'no buffering' is not already set, the contents of
    -- the buffer will be discarded, so this needs to be done before the cursor
    -- positon is emitted)
    hSetBuffering stdin NoBuffering
    -- ensure that echoing is off
    input <- bracket (hGetEcho stdin) (hSetEcho stdin) $ \_ -> do
      hSetEcho stdin False
      reportCursorPosition
      hFlush stdout -- ensure the report cursor position code is sent to the
                    -- operating system
      getReportedCursorPosition

On Windows operating systems, the function is not supported on consoles, such as mintty, that are not based on the Windows' Console API. (Command Prompt and PowerShell are based on the Console API.)

Since: 0.7.1

cursorPosition :: ReadP (Int, Int) #

Parses the characters emitted by `reportCursorPosition` into the console input stream. Returns the cursor row and column as a tuple.

For example, if the characters emitted by `reportCursorPosition` are in `String` `input` then the parser could be applied like this:

    let result = readP_to_S cursorPosition input
    case result of
        [] -> putStrLn $ "Error: could not parse " ++ show input
        [((row, column), _)] -> putStrLn $ "The cursor was at row " ++ show row
            ++ " and column" ++ show column ++ "."
        (_:_) -> putStrLn $ "Error: parse not unique"

Since: 0.7.1

# Getting the terminal size

getTerminalSize :: IO (Maybe (Int, Int)) #

Attempts to get the current terminal size (height in rows, width in columns).

There is no 'ANSI' control character sequence that reports the terminal size. So, it attempts to set the cursor position beyond the bottom right corner of the terminal and then use `getCursorPosition` to query the console input stream. It works only on terminals that support each step. Uses `stdout`. If `stdout` will be redirected, see `hGetTerminalSize` for a more general function.

On Windows operating systems, the function is not supported on consoles, such as mintty, that are not based on Windows' Console API. (Command Prompt and PowerShell are based on the Console API.)

For a different approach, one that does not use control character sequences, see the terminal-size package.

Since: 0.9

hGetTerminalSize :: Handle -> IO (Maybe (Int, Int)) #

Attempts to get the current terminal size (height in rows, width in columns), by writing control character sequences to the specified handle (which will typically be `stdout` or `stderr`).

There is no 'ANSI' control character sequence that reports the terminal size. So, it attempts to set the cursor position beyond the bottom right corner of the terminal and then use `hGetCursorPosition` to query the console input stream. It works only on terminals that support each step.

On Windows operating systems, the function is not supported on consoles, such as mintty, that are not based on the Windows' Console API. (Command Prompt and PowerShell are based on the Console API.)

For a different approach, one that does not use control character sequences, see the terminal-size package.

Since: 0.10.1