1047 lines
42 KiB
Haskell
1047 lines
42 KiB
Haskell
{- ----------------------------------------------------------------------------
|
||
Copyright (c) 2021, Daan Leijen
|
||
This is free software; you can redistribute it and/or modify it
|
||
under the terms of the MIT License. A copy of the license can be
|
||
found in the "LICENSE" file at the root of this distribution.
|
||
---------------------------------------------------------------------------- -}
|
||
{-|
|
||
Description : Binding to the Isocline library, a portable alternative to GNU Readline
|
||
Copyright : (c) 2021, Daan Leijen
|
||
License : MIT
|
||
Maintainer : daan@effp.org
|
||
Stability : Experimental
|
||
|
||
![logo](https://raw.githubusercontent.com/daanx/isocline/main/doc/isocline-inline.svg)
|
||
A Haskell wrapper around the [Isocline C library](https://github.com/daanx/isocline#readme)
|
||
which can provide an alternative to GNU Readline.
|
||
(The Isocline library is included whole and there are no runtime dependencies).
|
||
|
||
Isocline works across Unix, Windows, and macOS, and relies on a minimal subset of ANSI escape sequences.
|
||
It has a good multi-line editing mode (use shift/ctrl-enter) which is nice for inputting small functions etc.
|
||
Other features include support for colors, history, completion, unicode, undo/redo,
|
||
incremental history search, inline hints, brace matching, syntax highlighting, rich text using bbcode
|
||
formatting, etc.
|
||
|
||
Minimal example with history:
|
||
|
||
@
|
||
import System.Console.Isocline
|
||
|
||
main :: IO ()
|
||
main = do putStrLn \"Welcome\"
|
||
`setHistory` \"history.txt\" 200
|
||
input \<- `readline` \"myprompt\" -- full prompt becomes \"myprompt> \"
|
||
`putFmtLn` (\"[gray]You wrote:[\/gray]\\n\" ++ input)
|
||
@
|
||
|
||
Or using custom completions with an interactive loop:
|
||
|
||
@
|
||
import System.Console.Isocline
|
||
import Data.Char( toLower )
|
||
|
||
main :: IO ()
|
||
main
|
||
= do `styleDef' "ic-prompt" "ansi-maroon"
|
||
`setHistory` "history.txt" 200
|
||
`enableAutoTab` `True`
|
||
interaction
|
||
|
||
interaction :: IO ()
|
||
interaction
|
||
= do s <- `readlineEx` \"hαskell\" (Just completer) Nothing
|
||
putStrLn (\"You wrote:\\n\" ++ s)
|
||
if (s == \"\" || s == \"exit\") then return () else interaction
|
||
|
||
completer :: `CompletionEnv` -> String -> IO ()
|
||
completer cenv input
|
||
= do `completeFileName` cenv input Nothing [\".\",\"\/usr\/local\"] [\".hs\"] -- use [] for any extension
|
||
`completeWord` cenv input Nothing wcompleter
|
||
|
||
wcompleter :: String -> [`Completion`]
|
||
wcompleter input
|
||
= `completionsFor` (map toLower input)
|
||
[\"print\",\"println\",\"prints\",\"printsln\",\"prompt\"]
|
||
@
|
||
|
||
See a larger [example](https://github.com/daanx/isocline/blob/main/test/Example.hs)
|
||
with syntax highlighting and more extenstive custom completion
|
||
in the [Github repository](https://github.com/daanx/isocline).
|
||
|
||
Enjoy,
|
||
-- Daan
|
||
-}
|
||
module System.Console.Isocline(
|
||
-- * Readline
|
||
readline,
|
||
readlineEx,
|
||
|
||
-- * History
|
||
setHistory,
|
||
historyClear,
|
||
historyRemoveLast,
|
||
historyAdd,
|
||
|
||
-- * Completion
|
||
CompletionEnv,
|
||
completeFileName,
|
||
completeWord,
|
||
completeQuotedWord,
|
||
completeQuotedWordEx,
|
||
|
||
Completion(..),
|
||
completion,
|
||
isPrefix,
|
||
completionsFor,
|
||
wordCompleter,
|
||
|
||
-- * Syntax Highlighting
|
||
highlightFmt,
|
||
|
||
-- * Rich text
|
||
Style, Fmt,
|
||
style,
|
||
plain,
|
||
pre,
|
||
|
||
putFmt,
|
||
putFmtLn,
|
||
|
||
styleDef,
|
||
styleOpen,
|
||
styleClose,
|
||
withStyle,
|
||
|
||
-- * Configuration
|
||
setPromptMarker,
|
||
enableAutoTab,
|
||
enableColor,
|
||
enableBeep,
|
||
enableMultiline,
|
||
enableHistoryDuplicates,
|
||
enableCompletionPreview,
|
||
enableMultilineIndent,
|
||
enableHighlight,
|
||
enableInlineHelp,
|
||
enableHint,
|
||
setHintDelay,
|
||
enableBraceMatching,
|
||
enableBraceInsertion,
|
||
setMatchingBraces,
|
||
setInsertionBraces,
|
||
|
||
-- * Advanced
|
||
setDefaultCompleter,
|
||
addCompletion,
|
||
addCompletionPrim,
|
||
addCompletions,
|
||
completeWordPrim,
|
||
completeQuotedWordPrim,
|
||
completeQuotedWordPrimEx,
|
||
|
||
readlineMaybe,
|
||
readlineExMaybe,
|
||
readlinePrim,
|
||
readlinePrimMaybe,
|
||
|
||
getPromptMarker,
|
||
getContinuationPromptMarker,
|
||
stopCompleting,
|
||
hasCompletions,
|
||
|
||
asyncStop,
|
||
|
||
-- * Low-level highlighting
|
||
HighlightEnv,
|
||
setDefaultHighlighter,
|
||
setDefaultFmtHighlighter,
|
||
|
||
-- * Low-level Terminal
|
||
termInit,
|
||
termDone,
|
||
withTerm,
|
||
termFlush,
|
||
termWrite,
|
||
termWriteLn,
|
||
termColor,
|
||
termBgColor,
|
||
termColorAnsi,
|
||
termBgColorAnsi,
|
||
termUnderline,
|
||
termReverse,
|
||
termReset
|
||
|
||
) where
|
||
|
||
|
||
import Data.List( intersperse, isPrefixOf )
|
||
import Control.Monad( when, foldM )
|
||
import Control.Exception( bracket )
|
||
import Foreign.C.String( CString, peekCString, peekCStringLen, withCString, castCharToCChar )
|
||
import Foreign.Ptr
|
||
import Foreign.C.Types
|
||
|
||
-- the following are used for utf8 encoding.
|
||
import qualified Data.ByteString as B ( useAsCString, packCString )
|
||
import qualified Data.Text as T ( pack, unpack )
|
||
import Data.Text.Encoding as TE ( decodeUtf8With, encodeUtf8)
|
||
import Data.Text.Encoding.Error ( lenientDecode )
|
||
|
||
|
||
----------------------------------------------------------------------------
|
||
-- C Types
|
||
----------------------------------------------------------------------------
|
||
|
||
data IcCompletionEnv
|
||
|
||
-- | Abstract list of current completions.
|
||
newtype CompletionEnv = CompletionEnv (Ptr IcCompletionEnv)
|
||
|
||
type CCompleterFun = Ptr IcCompletionEnv -> CString -> IO ()
|
||
type CompleterFun = CompletionEnv -> String -> IO ()
|
||
|
||
|
||
data IcHighlightEnv
|
||
|
||
-- | Abstract highlight environment
|
||
newtype HighlightEnv = HighlightEnv (Ptr IcHighlightEnv)
|
||
|
||
type CHighlightFun = Ptr IcHighlightEnv -> CString -> Ptr () -> IO ()
|
||
type HighlightFun = HighlightEnv -> String -> IO ()
|
||
|
||
|
||
|
||
----------------------------------------------------------------------------
|
||
-- Basic readline
|
||
----------------------------------------------------------------------------
|
||
|
||
foreign import ccall ic_free :: (Ptr a) -> IO ()
|
||
foreign import ccall ic_malloc :: CSize -> IO (Ptr a)
|
||
foreign import ccall ic_strdup :: CString -> IO CString
|
||
foreign import ccall ic_readline :: CString -> IO CString
|
||
foreign import ccall ic_readline_ex :: CString -> FunPtr CCompleterFun -> (Ptr ()) -> FunPtr CHighlightFun -> (Ptr ()) -> IO CString
|
||
foreign import ccall ic_async_stop :: IO CCBool
|
||
|
||
unmaybe :: IO (Maybe String) -> IO String
|
||
unmaybe action
|
||
= do mb <- action
|
||
case mb of
|
||
Nothing -> return ""
|
||
Just s -> return s
|
||
|
||
-- | @readline prompt@: Read (multi-line) input from the user with rich editing abilities.
|
||
-- Takes the prompt text as an argument. The full prompt is the combination
|
||
-- of the given prompt and the prompt marker (@\"> \"@ by default) .
|
||
-- See also 'readlineEx', 'readlineMaybe', 'enableMultiline', and 'setPromptMarker'.
|
||
readline :: String -> IO String
|
||
readline prompt
|
||
= unmaybe $ readlineMaybe prompt
|
||
|
||
-- | As 'readline' but returns 'Nothing' on end-of-file or other errors (ctrl-C/ctrl-D).
|
||
readlineMaybe:: String -> IO (Maybe String)
|
||
readlineMaybe prompt
|
||
= withUTF8String prompt $ \cprompt ->
|
||
do cres <- ic_readline cprompt
|
||
res <- peekUTF8StringMaybe cres
|
||
ic_free cres
|
||
return res
|
||
|
||
-- | @readlineEx prompt mbCompleter mbHighlighter@: as 'readline' but
|
||
-- uses the given @mbCompleter@ function to complete words on @tab@ (instead of the default completer).
|
||
-- and the given @mbHighlighter@ function to highlight the input (instead of the default highlighter).
|
||
-- See also 'readline' and 'readlineExMaybe'.
|
||
readlineEx :: String -> Maybe (CompletionEnv -> String -> IO ()) -> Maybe (String -> Fmt) -> IO String
|
||
readlineEx prompt completer highlighter
|
||
= unmaybe $ readlineExMaybe prompt completer highlighter
|
||
|
||
-- | As 'readlineEx' but returns 'Nothing' on end-of-file or other errors (ctrl-C/ctrl-D).
|
||
-- See also 'readlineMaybe'.
|
||
readlineExMaybe :: String -> Maybe (CompletionEnv -> String -> IO ()) -> Maybe (String -> Fmt) -> IO (Maybe String)
|
||
readlineExMaybe prompt completer mbhighlighter
|
||
= readlinePrimMaybe prompt completer (case mbhighlighter of
|
||
Nothing -> Nothing
|
||
Just hl -> Just (highlightFmt hl))
|
||
|
||
-- | @readlinePrim prompt mbCompleter mbHighlighter@: as 'readline' but
|
||
-- uses the given @mbCompleter@ function to complete words on @tab@ (instead of the default completer).
|
||
-- and the given @mbHighlighter@ function to highlight the input (instead of the default highlighter).
|
||
-- See also 'readlineEx' and 'readlinePrimMaybe'.
|
||
readlinePrim :: String -> Maybe (CompletionEnv -> String -> IO ()) -> Maybe (HighlightEnv -> String -> IO ()) -> IO String
|
||
readlinePrim prompt completer highlighter
|
||
= unmaybe $ readlinePrimMaybe prompt completer highlighter
|
||
|
||
-- | As 'readlinePrim' but returns 'Nothing' on end-of-file or other errors (ctrl-C/ctrl-D).
|
||
-- See also 'readlineMaybe'.
|
||
readlinePrimMaybe :: String -> Maybe (CompletionEnv -> String -> IO ()) -> Maybe (HighlightEnv -> String -> IO ()) -> IO (Maybe String)
|
||
readlinePrimMaybe prompt completer highlighter
|
||
= withUTF8String prompt $ \cprompt ->
|
||
do ccompleter <- makeCCompleter completer
|
||
chighlighter <- makeCHighlighter highlighter
|
||
cres <- ic_readline_ex cprompt ccompleter nullPtr chighlighter nullPtr
|
||
res <- peekUTF8StringMaybe cres
|
||
ic_free cres
|
||
when (ccompleter /= nullFunPtr) $ freeHaskellFunPtr ccompleter
|
||
when (chighlighter /= nullFunPtr) $ freeHaskellFunPtr chighlighter
|
||
return res
|
||
|
||
-- | Thread safe call to asynchronously send a stop event to a 'readline'
|
||
-- which behaves as if the user pressed @ctrl-C@,
|
||
-- which will return with 'Nothing' (or @\"\"@).
|
||
-- Returns 'True' if the event was successfully delivered.
|
||
asyncStop :: IO Bool
|
||
asyncStop
|
||
= uncbool $ ic_async_stop
|
||
|
||
----------------------------------------------------------------------------
|
||
-- History
|
||
----------------------------------------------------------------------------
|
||
|
||
foreign import ccall ic_set_history :: CString -> CInt -> IO ()
|
||
foreign import ccall ic_history_remove_last :: IO ()
|
||
foreign import ccall ic_history_clear :: IO ()
|
||
foreign import ccall ic_history_add :: CString -> IO ()
|
||
|
||
-- | @setHistory filename maxEntries@:
|
||
-- Enable history that is persisted to the given file path with a given maximum number of entries.
|
||
-- Use -1 for the default entries (200).
|
||
-- See also 'enableHistoryDuplicates'.
|
||
setHistory :: FilePath -> Int -> IO ()
|
||
setHistory fname maxEntries
|
||
= withUTF8String0 fname $ \cfname ->
|
||
do ic_set_history cfname (toEnum maxEntries)
|
||
|
||
-- | Isocline automatically adds input of more than 1 character to the history.
|
||
-- This command removes the last entry.
|
||
historyRemoveLast :: IO ()
|
||
historyRemoveLast
|
||
= ic_history_remove_last
|
||
|
||
-- | Clear the history.
|
||
historyClear :: IO ()
|
||
historyClear
|
||
= ic_history_clear
|
||
|
||
-- | @historyAdd entry@: add @entry@ to the history.
|
||
historyAdd :: String -> IO ()
|
||
historyAdd entry
|
||
= withUTF8String0 entry $ \centry ->
|
||
do ic_history_add centry
|
||
|
||
|
||
----------------------------------------------------------------------------
|
||
-- Completion
|
||
----------------------------------------------------------------------------
|
||
-- use our own CBool for compatibility with an older base
|
||
type CCBool = CInt
|
||
|
||
type CCharClassFun = CString -> CLong -> IO CCBool
|
||
type CharClassFun = Char -> Bool
|
||
|
||
foreign import ccall ic_set_default_completer :: FunPtr CCompleterFun -> IO ()
|
||
foreign import ccall "wrapper" ic_make_completer :: CCompleterFun -> IO (FunPtr CCompleterFun)
|
||
foreign import ccall "wrapper" ic_make_charclassfun :: CCharClassFun -> IO (FunPtr CCharClassFun)
|
||
|
||
foreign import ccall ic_add_completion_ex :: Ptr IcCompletionEnv -> CString -> CString -> CString -> IO CCBool
|
||
foreign import ccall ic_add_completion_prim :: Ptr IcCompletionEnv -> CString -> CString -> CString -> CInt -> CInt -> IO CCBool
|
||
foreign import ccall ic_complete_filename :: Ptr IcCompletionEnv -> CString -> CChar -> CString -> CString -> IO ()
|
||
foreign import ccall ic_complete_word :: Ptr IcCompletionEnv -> CString -> FunPtr CCompleterFun -> FunPtr CCharClassFun -> IO ()
|
||
foreign import ccall ic_complete_qword :: Ptr IcCompletionEnv -> CString -> FunPtr CCompleterFun -> FunPtr CCharClassFun -> IO ()
|
||
foreign import ccall ic_complete_qword_ex :: Ptr IcCompletionEnv -> CString -> FunPtr CCompleterFun -> FunPtr CCharClassFun -> CChar -> CString -> IO ()
|
||
|
||
foreign import ccall ic_has_completions :: Ptr IcCompletionEnv -> IO CCBool
|
||
foreign import ccall ic_stop_completing :: Ptr IcCompletionEnv -> IO CCBool
|
||
|
||
-- | A completion entry
|
||
data Completion = Completion {
|
||
replacement :: String, -- ^ actual replacement
|
||
display :: String, -- ^ display of the completion in the completion menu
|
||
help :: String -- ^ help message
|
||
} deriving (Eq, Show)
|
||
|
||
-- | Create a completion with just a replacement
|
||
completion :: String -> Completion
|
||
completion replacement
|
||
= Completion replacement "" ""
|
||
|
||
-- | @completionFull replacement display help@: Create a completion with a separate display and help string.
|
||
completionFull :: String -> String -> String -> Completion
|
||
completionFull replacement display help
|
||
= Completion replacement display help
|
||
|
||
|
||
-- | Is the given input a prefix of the completion replacement?
|
||
isPrefix :: String -> Completion -> Bool
|
||
isPrefix input compl
|
||
= isPrefixOf input (replacement compl)
|
||
|
||
-- | @completionsFor input replacements@: Filter those @replacements@ that
|
||
-- start with the given @input@, and return them as completions.
|
||
completionsFor :: String -> [String] -> [Completion]
|
||
completionsFor input rs
|
||
= map completion (filter (isPrefixOf input) rs)
|
||
|
||
-- | Convenience: creates a completer function directly from a list
|
||
-- of candidate completion strings. Uses `completionsFor` to filter the
|
||
-- input and `completeWord` to find the word boundary.
|
||
-- For example: @'readlineEx' \"myprompt\" (Just ('wordCompleter' completer)) Nothing@.
|
||
wordCompleter :: [String] -> (CompletionEnv -> String -> IO ())
|
||
wordCompleter completions
|
||
= (\cenv input -> completeWord cenv input Nothing (\input -> completionsFor input completions))
|
||
|
||
-- | @setDefaultCompleter completer@: Set a new tab-completion function @completer@
|
||
-- that is called by Isocline automatically.
|
||
-- The callback is called with a 'CompletionEnv' context and the current user
|
||
-- input up to the cursor.
|
||
-- By default the 'completeFileName' completer is used.
|
||
-- This overwrites any previously set completer.
|
||
setDefaultCompleter :: (CompletionEnv -> String -> IO ()) -> IO ()
|
||
setDefaultCompleter completer
|
||
= do ccompleter <- makeCCompleter (Just completer)
|
||
ic_set_default_completer ccompleter
|
||
|
||
withCCompleter :: Maybe CompleterFun -> (FunPtr CCompleterFun -> IO a) -> IO a
|
||
withCCompleter completer action
|
||
= bracket (makeCCompleter completer) (\cfun -> when (nullFunPtr /= cfun) (freeHaskellFunPtr cfun)) action
|
||
|
||
makeCCompleter :: Maybe CompleterFun -> IO (FunPtr CCompleterFun)
|
||
makeCCompleter Nothing = return nullFunPtr
|
||
makeCCompleter (Just completer)
|
||
= ic_make_completer wrapper
|
||
where
|
||
wrapper :: Ptr IcCompletionEnv -> CString -> IO ()
|
||
wrapper rpcomp cprefx
|
||
= do prefx <- peekUTF8String0 cprefx
|
||
completer (CompletionEnv rpcomp) prefx
|
||
|
||
|
||
-- | @addCompletion compl completion@: Inside a completer callback, add a new completion.
|
||
-- If 'addCompletion' returns 'True' keep adding completions,
|
||
-- but if it returns 'False' an effort should be made to return from the completer
|
||
-- callback without adding more completions.
|
||
addCompletion :: CompletionEnv -> Completion -> IO Bool
|
||
addCompletion (CompletionEnv rpc) (Completion replacement display help)
|
||
= withUTF8String replacement $ \crepl ->
|
||
withUTF8String0 display $ \cdisplay ->
|
||
withUTF8String0 help $ \chelp ->
|
||
do cbool <- ic_add_completion_ex rpc crepl cdisplay chelp
|
||
return (fromEnum cbool /= 0)
|
||
|
||
-- | @addCompletionPrim compl completion deleteBefore deleteAfter@:
|
||
-- Primitive add completion, use with care and call only directly inside a completer callback.
|
||
-- If 'addCompletion' returns 'True' keep adding completions,
|
||
-- but if it returns 'False' an effort should be made to return from the completer
|
||
-- callback without adding more completions.
|
||
addCompletionPrim :: CompletionEnv -> Completion -> Int -> Int -> IO Bool
|
||
addCompletionPrim (CompletionEnv rpc) (Completion replacement display help) deleteBefore deleteAfter
|
||
= withUTF8String replacement $ \crepl ->
|
||
withUTF8String0 display $ \cdisplay ->
|
||
withUTF8String0 help $ \chelp ->
|
||
do cbool <- ic_add_completion_prim rpc crepl cdisplay chelp (toEnum deleteBefore) (toEnum deleteAfter)
|
||
return (fromEnum cbool /= 0)
|
||
|
||
|
||
-- | @addCompletions compl completions@: add multiple completions at once.
|
||
-- If 'addCompletions' returns 'True' keep adding completions,
|
||
-- but if it returns 'False' an effort should be made to return from the completer
|
||
-- callback without adding more completions.
|
||
addCompletions :: CompletionEnv -> [Completion] -> IO Bool
|
||
addCompletions compl [] = return True
|
||
addCompletions compl (c:cs)
|
||
= do continue <- addCompletion compl c
|
||
if (continue)
|
||
then addCompletions compl cs
|
||
else return False
|
||
|
||
-- | @completeFileName compls input dirSep roots extensions@:
|
||
-- Complete filenames with the given @input@, a possible directory separator @dirSep@,
|
||
-- a list of root folders @roots@ to search from
|
||
-- (by default @["."]@), and a list of extensions to match (use @[]@ to match any extension).
|
||
-- The directory separator is used when completing directory names.
|
||
-- For example, using g @\'/\'@ as a directory separator, we get:
|
||
--
|
||
-- > /ho --> /home/
|
||
-- > /home/.ba --> /home/.bashrc
|
||
--
|
||
completeFileName :: CompletionEnv -> String -> Maybe Char -> [FilePath] -> [String] -> IO ()
|
||
completeFileName (CompletionEnv rpc) prefx dirSep roots extensions
|
||
= withUTF8String prefx $ \cprefx ->
|
||
withUTF8String0 (concat (intersperse ";" roots)) $ \croots ->
|
||
withUTF8String0 (concat (intersperse ";" extensions)) $ \cextensions ->
|
||
do let cdirSep = case dirSep of
|
||
Nothing -> toEnum 0
|
||
Just c -> castCharToCChar c
|
||
ic_complete_filename rpc cprefx cdirSep croots cextensions
|
||
|
||
-- | @completeWord compl input isWordChar completer@:
|
||
-- Complete a /word/ (or /token/) and calls the user @completer@ function with just the current word
|
||
-- (instead of the whole input)
|
||
-- Takes the 'CompletionEnv' environment @compl@, the current @input@, an possible
|
||
-- @isWordChar@ function, and a user defined
|
||
-- @completer@ function that is called with adjusted input which
|
||
-- is limited to the /word/ just before the cursor.
|
||
-- Pass 'Nothing' to @isWordChar@ for the default @not . separator@
|
||
-- where @separator = \c -> c `elem` \" \\t\\r\\n,.;:/\\\\(){}[]\"@.
|
||
completeWord :: CompletionEnv -> String -> Maybe (Char -> Bool) -> (String -> [Completion]) -> IO ()
|
||
completeWord cenv input isWordChar completer
|
||
= completeWordPrim cenv input isWordChar cenvCompleter
|
||
where
|
||
cenvCompleter cenv input
|
||
= do addCompletions cenv (completer input)
|
||
return ()
|
||
|
||
-- | @completeQuotedWord compl input isWordChar completer@:
|
||
-- Complete a /word/ taking care of automatically quoting and escaping characters.
|
||
-- Takes the 'CompletionEnv' environment @compl@, the current @input@, and a user defined
|
||
-- @completer@ function that is called with adjusted input which is unquoted, unescaped,
|
||
-- and limited to the /word/ just before the cursor.
|
||
-- For example, with a @hello world@ completion, we get:
|
||
--
|
||
-- > hel --> hello\ world
|
||
-- > hello\ w --> hello\ world
|
||
-- > hello w --> # no completion, the word is just 'w'>
|
||
-- > "hel --> "hello world"
|
||
-- > "hello w --> "hello world"
|
||
--
|
||
-- The call @('completeWord' compl prefx isWordChar fun)@ is a short hand for
|
||
-- @('completeQuotedWord' compl prefx isWordChar \'\\\\\' \"\'\\\"\" fun)@.
|
||
-- Pass 'Nothing' to @isWordChar@ for the default @not . separator@
|
||
-- where @separator = \c -> c `elem` \" \\t\\r\\n,.;:/\\\\(){}[]\"@.
|
||
completeQuotedWord :: CompletionEnv -> String -> Maybe (Char -> Bool) -> (String -> [Completion]) -> IO ()
|
||
completeQuotedWord cenv input isWordChar completer
|
||
= completeWordPrim cenv input isWordChar cenvCompleter
|
||
where
|
||
cenvCompleter cenv input
|
||
= do addCompletions cenv (completer input)
|
||
return ()
|
||
|
||
-- | @completeQuotedWordEx compl input isWordChar escapeChar quoteChars completer@:
|
||
-- Complete a /word/ taking care of automatically quoting and escaping characters.
|
||
-- Takes the 'CompletionEnv' environment @compl@, the current @input@, and a user defined
|
||
-- @completer@ function that is called with adjusted input which is unquoted, unescaped,
|
||
-- and limited to the /word/ just before the cursor.
|
||
-- Unlike 'completeQuotedWord', this function can specify
|
||
-- the /escape/ character and the /quote/ characters.
|
||
-- See also 'completeWord'.
|
||
completeQuotedWordEx :: CompletionEnv -> String -> Maybe (Char -> Bool) -> Maybe Char -> String -> (String -> [Completion]) -> IO ()
|
||
completeQuotedWordEx cenv input isWordChar escapeChar quoteChars completer
|
||
= completeQuotedWordPrimEx cenv input isWordChar escapeChar quoteChars cenvCompleter
|
||
where
|
||
cenvCompleter cenv input
|
||
= do addCompletions cenv (completer input)
|
||
return ()
|
||
|
||
|
||
-- | @completeWord compl input isWordChar completer@:
|
||
-- Complete a /word/,/token/ and calls the user @completer@ function with just the current word
|
||
-- (instead of the whole input)
|
||
-- Takes the 'CompletionEnv' environment @compl@, the current @input@, an possible
|
||
-- @isWordChar@ function, and a user defined
|
||
-- @completer@ function that is called with adjusted input which
|
||
-- is limited to the /word/ just before the cursor.
|
||
-- Pass 'Nothing' to @isWordChar@ for the default @not . separator@
|
||
-- where @separator = \c -> c `elem` \" \\t\\r\\n,.;:/\\\\(){}[]\"@.
|
||
completeWordPrim :: CompletionEnv -> String -> Maybe (Char -> Bool) -> (CompletionEnv -> String -> IO ()) -> IO ()
|
||
completeWordPrim (CompletionEnv rpc) prefx isWordChar completer
|
||
= withUTF8String prefx $ \cprefx ->
|
||
withCharClassFun isWordChar $ \cisWordChar ->
|
||
withCCompleter (Just completer) $ \ccompleter ->
|
||
do ic_complete_word rpc cprefx ccompleter cisWordChar
|
||
|
||
|
||
-- | @completeWordPrim compl input isWordChar completer@:
|
||
-- Complete a /word/ taking care of automatically quoting and escaping characters.
|
||
-- Takes the 'CompletionEnv' environment @compl@, the current @input@, and a user defined
|
||
-- @completer@ function that is called with adjusted input which is unquoted, unescaped,
|
||
-- and limited to the /word/ just before the cursor.
|
||
-- For example, with a @hello world@ completion, we get:
|
||
--
|
||
-- > hel --> hello\ world
|
||
-- > hello\ w --> hello\ world
|
||
-- > hello w --> # no completion, the word is just 'w'>
|
||
-- > "hel --> "hello world"
|
||
-- > "hello w --> "hello world"
|
||
--
|
||
-- The call @('completeWordPrim' compl prefx isWordChar fun)@ is a short hand for
|
||
-- @('completeQuotedWordPrim' compl prefx isWordChar \'\\\\\' \"\'\\\"\" fun)@.
|
||
-- Pass 'Nothing' to @isWordChar@ for the default @not . separator@
|
||
-- where @separator = \c -> c `elem` \" \\t\\r\\n,.;:/\\\\(){}[]\"@.
|
||
completeQuotedWordPrim :: CompletionEnv -> String -> Maybe (Char -> Bool) -> (CompletionEnv -> String -> IO ()) -> IO ()
|
||
completeQuotedWordPrim (CompletionEnv rpc) prefx isWordChar completer
|
||
= withUTF8String prefx $ \cprefx ->
|
||
withCharClassFun isWordChar $ \cisWordChar ->
|
||
withCCompleter (Just completer) $ \ccompleter ->
|
||
do ic_complete_qword rpc cprefx ccompleter cisWordChar
|
||
|
||
|
||
-- | @completeQuotedWordPrim compl input isWordChar escapeChar quoteChars completer@:
|
||
-- Complete a /word/ taking care of automatically quoting and escaping characters.
|
||
-- Takes the 'CompletionEnv' environment @compl@, the current @input@, and a user defined
|
||
-- @completer@ function that is called with adjusted input which is unquoted, unescaped,
|
||
-- and limited to the /word/ just before the cursor.
|
||
-- Unlike 'completeWord', this function takes an explicit function to determine /word/ characters,
|
||
-- the /escape/ character, and a string of /quote/ characters.
|
||
-- See also 'completeWord'.
|
||
completeQuotedWordPrimEx :: CompletionEnv -> String -> Maybe (Char -> Bool) -> Maybe Char -> String -> (CompletionEnv -> String -> IO ()) -> IO ()
|
||
completeQuotedWordPrimEx (CompletionEnv rpc) prefx isWordChar escapeChar quoteChars completer
|
||
= withUTF8String prefx $ \cprefx ->
|
||
withUTF8String0 quoteChars $ \cquoteChars ->
|
||
withCharClassFun isWordChar $ \cisWordChar ->
|
||
withCCompleter (Just completer) $ \ccompleter ->
|
||
do let cescapeChar = case escapeChar of
|
||
Nothing -> toEnum 0
|
||
Just c -> castCharToCChar c
|
||
ic_complete_qword_ex rpc cprefx ccompleter cisWordChar cescapeChar cquoteChars
|
||
|
||
|
||
withCharClassFun :: Maybe (Char -> Bool) -> (FunPtr CCharClassFun -> IO a) -> IO a
|
||
withCharClassFun isInClass action
|
||
= bracket (makeCharClassFun isInClass) (\cfun -> when (nullFunPtr /= cfun) (freeHaskellFunPtr cfun)) action
|
||
|
||
makeCharClassFun :: Maybe (Char -> Bool) -> IO (FunPtr CCharClassFun)
|
||
makeCharClassFun Nothing = return nullFunPtr
|
||
makeCharClassFun (Just isInClass)
|
||
= let charClassFun :: CString -> CLong -> IO CCBool
|
||
charClassFun cstr clen
|
||
= let len = (fromIntegral clen :: Int)
|
||
in if (len <= 0) then return (cbool False)
|
||
else do s <- peekCStringLen (cstr,len)
|
||
return (if null s then (cbool False) else cbool (isInClass (head s)))
|
||
in do ic_make_charclassfun charClassFun
|
||
|
||
|
||
-- | If this returns 'True' an effort should be made to stop completing and return from the callback.
|
||
stopCompleting :: CompletionEnv -> IO Bool
|
||
stopCompleting (CompletionEnv rpc)
|
||
= uncbool $ ic_stop_completing rpc
|
||
|
||
-- | Have any completions be generated so far?
|
||
hasCompletions :: CompletionEnv -> IO Bool
|
||
hasCompletions (CompletionEnv rpc)
|
||
= uncbool $ ic_has_completions rpc
|
||
|
||
|
||
|
||
----------------------------------------------------------------------------
|
||
-- Syntax highlighting
|
||
----------------------------------------------------------------------------
|
||
|
||
foreign import ccall ic_set_default_highlighter :: FunPtr CHighlightFun -> Ptr () -> IO ()
|
||
foreign import ccall "wrapper" ic_make_highlight_fun:: CHighlightFun -> IO (FunPtr CHighlightFun)
|
||
foreign import ccall ic_highlight :: Ptr IcHighlightEnv -> CLong -> CLong -> CString -> IO ()
|
||
foreign import ccall ic_highlight_formatted :: Ptr IcHighlightEnv -> CString -> CString -> IO ()
|
||
|
||
|
||
-- | Set a syntax highlighter.
|
||
-- There can only be one highlight function, setting it again disables the previous one.
|
||
setDefaultHighlighter :: (HighlightEnv -> String -> IO ()) -> IO ()
|
||
setDefaultHighlighter highlighter
|
||
= do chighlighter <- makeCHighlighter (Just highlighter)
|
||
ic_set_default_highlighter chighlighter nullPtr
|
||
|
||
makeCHighlighter :: Maybe (HighlightEnv -> String -> IO ()) -> IO (FunPtr CHighlightFun)
|
||
makeCHighlighter Nothing = return nullFunPtr
|
||
makeCHighlighter (Just highlighter)
|
||
= ic_make_highlight_fun wrapper
|
||
where
|
||
wrapper :: Ptr IcHighlightEnv -> CString -> Ptr () -> IO ()
|
||
wrapper henv cinput carg
|
||
= do input <- peekUTF8String0 cinput
|
||
highlighter (HighlightEnv henv) input
|
||
|
||
|
||
-- | @highlight henv pos len style@: Set the style of @len@ characters
|
||
-- starting at position @pos@ in the input
|
||
highlight :: HighlightEnv -> Int -> Int -> String -> IO ()
|
||
highlight (HighlightEnv henv) pos len style
|
||
= withUTF8String0 style $ \cstyle ->
|
||
do ic_highlight henv (clong (-pos)) (clong (-len)) cstyle
|
||
|
||
|
||
-- | A style for formatted strings ('Fmt').
|
||
-- For example, a style can be @"red"@ or @"b #7B3050"@.
|
||
-- See the full list of valid [properties](https://github.com/daanx/isocline#bbcode-format)
|
||
type Style = String
|
||
|
||
-- | A string with [bbcode](https://github.com/daanx/isocline#bbcode-format) formatting.
|
||
-- For example @"[red]this is red[\/]"@.n
|
||
type Fmt = String
|
||
|
||
-- | Use an rich text formatted highlighter from inside a highlighter callback.
|
||
highlightFmt :: (String -> Fmt) -> (HighlightEnv -> String -> IO ())
|
||
highlightFmt highlight (HighlightEnv henv) input
|
||
= withUTF8String0 input $ \cinput ->
|
||
withUTF8String0 (highlight input) $ \cfmt ->
|
||
do ic_highlight_formatted henv cinput cfmt
|
||
|
||
|
||
-- | Style a string, e.g. @style "b red" "bold and red"@ (which is equivalent to @"[b red]bold and red[\/]"@).
|
||
-- See the repo for a full description of all [styles](https://github.com/daanx/isocline#bbcode-format).
|
||
style :: Style -> Fmt -> Fmt
|
||
style st s
|
||
= if null st then s else ("[" ++ st ++ "]" ++ s ++ "[/]")
|
||
|
||
-- | Escape a string so no tags are interpreted as formatting.
|
||
plain :: String -> Fmt
|
||
plain s
|
||
= if (any (\c -> (c == '[' || c == ']')) s) then "[!pre]" ++ s ++ "[/pre]" else s
|
||
|
||
-- | Style a string that is printed as is without interpreting markup inside it (using `plain`).
|
||
pre :: Style -> String -> Fmt
|
||
pre st s
|
||
= style st (plain s)
|
||
|
||
-- | Set a syntax highlighter that uses a pure function that returns a bbcode
|
||
-- formatted string (using 'style', 'plain' etc). See 'highlightFmt' for more information.
|
||
-- There can only be one highlight function, setting it again disables the previous one.
|
||
setDefaultFmtHighlighter :: (String -> Fmt) -> IO ()
|
||
setDefaultFmtHighlighter highlight
|
||
= setDefaultHighlighter (highlightFmt highlight)
|
||
|
||
|
||
|
||
|
||
|
||
----------------------------------------------------------------------------
|
||
-- Print rich text
|
||
----------------------------------------------------------------------------
|
||
|
||
foreign import ccall ic_print :: CString -> IO ()
|
||
foreign import ccall ic_println :: CString -> IO ()
|
||
foreign import ccall ic_style_def :: CString -> CString -> IO ()
|
||
foreign import ccall ic_style_open :: CString -> IO ()
|
||
foreign import ccall ic_style_close :: IO ()
|
||
|
||
-- | Output rich formatted text containing [bbcode](https://github.com/daanx/isocline#bbcode-format).
|
||
-- For example: @putFmt \"[b]bold [red]and red[\/][\/]\"@
|
||
-- All unclosed tags are automatically closed (but see also 'styleOpen').
|
||
-- See the repo for more information about [formatted output](https://github.com/daanx/isocline#formatted-output).
|
||
putFmt :: Fmt -> IO ()
|
||
putFmt s
|
||
= withUTF8String0 s $ \cs ->
|
||
do ic_print cs
|
||
|
||
-- | Output rich formatted text containing bbcode's ending with a newline.
|
||
putFmtLn :: Fmt -> IO ()
|
||
putFmtLn s
|
||
= withUTF8String0 s $ \cs ->
|
||
do ic_println cs
|
||
|
||
-- | Define (or redefine) a style.
|
||
-- For example @styleDef "warning" "crimon underline"@,
|
||
-- and then use it as @'putFmtLn' "[warning]this is a warning[/]"@.
|
||
-- This can be very useful for theming your application with semantic styles.
|
||
-- See also [formatted output](https://github.com/daanx/isocline#formatted-output)
|
||
styleDef :: String -> Style -> IO ()
|
||
styleDef name style
|
||
= withUTF8String0 name $ \cname ->
|
||
withUTF8String0 style $ \cstyle ->
|
||
do ic_style_def cname cstyle
|
||
|
||
-- | Open a style that is active for all 'putFmt' and 'putFmtLn' until it is closed again (`styleClose`).
|
||
styleOpen :: Style -> IO ()
|
||
styleOpen style
|
||
= withUTF8String0 style $ \cstyle ->
|
||
do ic_style_open cstyle
|
||
|
||
-- | Close a previously opened style.
|
||
styleClose :: IO ()
|
||
styleClose
|
||
= ic_style_close
|
||
|
||
-- | Use a style over an action.
|
||
withStyle :: Style -> IO a -> IO a
|
||
withStyle style action
|
||
= bracket (styleOpen style) (\() -> styleClose) (\() -> action)
|
||
|
||
|
||
----------------------------------------------------------------------------
|
||
-- Terminal
|
||
----------------------------------------------------------------------------
|
||
|
||
foreign import ccall ic_term_init :: IO ()
|
||
foreign import ccall ic_term_done :: IO ()
|
||
foreign import ccall ic_term_flush :: IO ()
|
||
foreign import ccall ic_term_write :: CString -> IO ()
|
||
foreign import ccall ic_term_writeln :: CString -> IO ()
|
||
foreign import ccall ic_term_underline :: CCBool -> IO ()
|
||
foreign import ccall ic_term_reverse :: CCBool -> IO ()
|
||
foreign import ccall ic_term_color_ansi :: CCBool -> CInt -> IO ()
|
||
foreign import ccall ic_term_color_rgb :: CCBool -> CInt -> IO ()
|
||
foreign import ccall ic_term_style :: CString -> IO ()
|
||
foreign import ccall ic_term_reset :: IO ()
|
||
|
||
-- | Initialize the terminal for the @term@ functions.
|
||
-- Does nothing on most platforms but on windows enables UTF8 output
|
||
-- and potentially enables virtual terminal processing.
|
||
-- See also 'withTerm'.
|
||
termInit :: IO ()
|
||
termInit
|
||
= ic_term_init
|
||
|
||
-- | Done using @term@ functions.
|
||
-- See also 'withTerm'.
|
||
termDone :: IO ()
|
||
termDone
|
||
= ic_term_done
|
||
|
||
-- | Use the @term@ functions (brackets 'termInit' and 'termDone').
|
||
withTerm :: IO a -> IO a
|
||
withTerm action
|
||
= bracket termInit (\() -> termDone) (\() -> action)
|
||
|
||
-- | Flush terminal output. Happens automatically on newline (@'\\n'@) characters as well.
|
||
termFlush :: IO ()
|
||
termFlush
|
||
= ic_term_flush
|
||
|
||
-- | Write output to the terminal where ANSI CSI sequences are
|
||
-- handled portably across platforms (including Windows).
|
||
termWrite :: String -> IO ()
|
||
termWrite s
|
||
= withUTF8String0 s $ \cs -> ic_term_write cs
|
||
|
||
-- | Write output with a ending newline to the terminal where
|
||
-- ANSI CSI sequences are handled portably across platforms (including Windows).
|
||
termWriteLn :: String -> IO ()
|
||
termWriteLn s
|
||
= withUTF8String0 s $ \cs -> ic_term_writeln cs
|
||
|
||
-- | Set the terminal text color as a hexadecimal number @0x@rrggbb.
|
||
-- The color is auto adjusted for terminals with less colors.
|
||
termColor :: Int -> IO ()
|
||
termColor color
|
||
= ic_term_color_rgb (cbool True) (toEnum color)
|
||
|
||
-- | Set the terminal text background color. The color is auto adjusted for terminals with less colors.
|
||
termBgColor :: Int -> IO ()
|
||
termBgColor color
|
||
= ic_term_color_rgb (cbool False) (toEnum color)
|
||
|
||
-- | Set the terminal text color as an ANSI palette color (between @0@ and @255@). Use 256 for the default.
|
||
-- The color is auto adjusted for terminals with less colors.
|
||
termColorAnsi :: Int -> IO ()
|
||
termColorAnsi color
|
||
= ic_term_color_ansi (cbool True) (toEnum color)
|
||
|
||
-- | Set the terminal text background color as an ANSI palette color (between @0@ and @255@). Use 256 for the default.
|
||
-- The color is auto adjusted for terminals with less colors.
|
||
termBgColorAnsi :: Int -> IO ()
|
||
termBgColorAnsi color
|
||
= ic_term_color_ansi (cbool False) (toEnum color)
|
||
|
||
-- | Set the terminal attributes from a style
|
||
termStyle :: Style -> IO ()
|
||
termStyle style
|
||
= withUTF8String0 style $ \cstyle ->
|
||
do ic_term_style cstyle
|
||
|
||
-- | Set the terminal text underline mode.
|
||
termUnderline :: Bool -> IO ()
|
||
termUnderline enable
|
||
= ic_term_underline (cbool enable)
|
||
|
||
-- | Set the terminal text reverse video mode.
|
||
termReverse :: Bool -> IO ()
|
||
termReverse enable
|
||
= ic_term_reverse (cbool enable)
|
||
|
||
-- | Reset the terminal text mode to defaults
|
||
termReset :: IO ()
|
||
termReset
|
||
= ic_term_reset
|
||
|
||
|
||
----------------------------------------------------------------------------
|
||
-- Configuration
|
||
----------------------------------------------------------------------------
|
||
foreign import ccall ic_set_prompt_marker :: CString -> CString -> IO ()
|
||
foreign import ccall ic_get_prompt_marker :: IO CString
|
||
foreign import ccall ic_get_continuation_prompt_marker :: IO CString
|
||
foreign import ccall ic_enable_multiline :: CCBool -> IO CCBool
|
||
foreign import ccall ic_enable_beep :: CCBool -> IO CCBool
|
||
foreign import ccall ic_enable_color :: CCBool -> IO CCBool
|
||
foreign import ccall ic_enable_auto_tab :: CCBool -> IO CCBool
|
||
foreign import ccall ic_enable_inline_help:: CCBool -> IO CCBool
|
||
foreign import ccall ic_enable_hint :: CCBool -> IO CCBool
|
||
foreign import ccall ic_set_hint_delay :: CLong -> IO CLong
|
||
foreign import ccall ic_enable_highlight :: CCBool -> IO CCBool
|
||
foreign import ccall ic_enable_history_duplicates :: CCBool -> IO CCBool
|
||
foreign import ccall ic_enable_completion_preview :: CCBool -> IO CCBool
|
||
foreign import ccall ic_enable_multiline_indent :: CCBool -> IO CCBool
|
||
foreign import ccall ic_enable_brace_matching :: CCBool -> IO CCBool
|
||
foreign import ccall ic_enable_brace_insertion :: CCBool -> IO CCBool
|
||
foreign import ccall ic_set_matching_braces :: CString -> IO ()
|
||
foreign import ccall ic_set_insertion_braces :: CString -> IO ()
|
||
|
||
cbool :: Bool -> CCBool
|
||
cbool True = toEnum 1
|
||
cbool False = toEnum 0
|
||
|
||
uncbool :: IO CCBool -> IO Bool
|
||
uncbool action
|
||
= do i <- action
|
||
return (i /= toEnum 0)
|
||
|
||
clong :: Int -> CLong
|
||
clong l = toEnum l
|
||
|
||
|
||
-- | @setPromptMarker marker multiline_marker@: Set the prompt @marker@ (by default @\"> \"@).
|
||
-- and a possible different continuation prompt marker @multiline_marker@ for multiline
|
||
-- input (defaults to @marker@).
|
||
setPromptMarker :: String -> String -> IO ()
|
||
setPromptMarker marker multiline_marker
|
||
= withUTF8String0 marker $ \cmarker ->
|
||
withUTF8String0 multiline_marker $ \cmultiline_marker ->
|
||
do ic_set_prompt_marker cmarker cmultiline_marker
|
||
|
||
|
||
-- | Get the current prompt marker.
|
||
getPromptMarker :: IO String
|
||
getPromptMarker
|
||
= do cstr <- ic_get_prompt_marker
|
||
if (nullPtr == cstr)
|
||
then return ""
|
||
else do cstr2 <- ic_strdup cstr
|
||
peekUTF8String0 cstr2
|
||
|
||
-- | Get the current prompt continuation marker for multi-line input.
|
||
getContinuationPromptMarker :: IO String
|
||
getContinuationPromptMarker
|
||
= do cstr <- ic_get_continuation_prompt_marker
|
||
if (nullPtr == cstr)
|
||
then return ""
|
||
else do cstr2 <- ic_strdup cstr
|
||
peekUTF8String0 cstr2
|
||
|
||
|
||
-- | Disable or enable multi-line input (enabled by default).
|
||
-- Returns the previous value.
|
||
enableMultiline :: Bool -> IO Bool
|
||
enableMultiline enable
|
||
= do uncbool $ ic_enable_multiline (cbool enable)
|
||
|
||
-- | Disable or enable sound (enabled by default).
|
||
-- | A beep is used when tab cannot find any completion for example.
|
||
-- Returns the previous value.
|
||
enableBeep :: Bool -> IO Bool
|
||
enableBeep enable
|
||
= do uncbool $ ic_enable_beep (cbool enable)
|
||
|
||
-- | Disable or enable color output (enabled by default).
|
||
-- Returns the previous value.
|
||
enableColor :: Bool -> IO Bool
|
||
enableColor enable
|
||
= do uncbool $ ic_enable_color (cbool enable)
|
||
|
||
-- | Disable or enable duplicate entries in the history (duplicate entries are not allowed by default).
|
||
-- Returns the previous value.
|
||
enableHistoryDuplicates :: Bool -> IO Bool
|
||
enableHistoryDuplicates enable
|
||
= do uncbool $ ic_enable_history_duplicates (cbool enable)
|
||
|
||
|
||
-- | Disable or enable automatic tab completion after a completion
|
||
-- to expand as far as possible if the completions are unique. (disabled by default).
|
||
-- Returns the previous value.
|
||
enableAutoTab :: Bool -> IO Bool
|
||
enableAutoTab enable
|
||
= do uncbool $ ic_enable_auto_tab (cbool enable)
|
||
|
||
|
||
-- | Disable or enable short inline help message (for history search etc.) (enabled by default).
|
||
-- Pressing F1 always shows full help regardless of this setting.
|
||
-- Returns the previous value.
|
||
enableInlineHelp :: Bool -> IO Bool
|
||
enableInlineHelp enable
|
||
= do uncbool $ ic_enable_inline_help (cbool enable)
|
||
|
||
-- | Disable or enable preview of a completion selection (enabled by default)
|
||
-- Returns the previous value.
|
||
enableCompletionPreview :: Bool -> IO Bool
|
||
enableCompletionPreview enable
|
||
= do uncbool $ ic_enable_completion_preview (cbool enable)
|
||
|
||
|
||
-- | Disable or enable brace matching (enabled by default)
|
||
-- Returns the previous value.
|
||
enableBraceMatching :: Bool -> IO Bool
|
||
enableBraceMatching enable
|
||
= do uncbool $ ic_enable_brace_matching (cbool enable)
|
||
|
||
-- | Disable or enable automatic close brace insertion (enabled by default)
|
||
-- Returns the previous value.
|
||
enableBraceInsertion :: Bool -> IO Bool
|
||
enableBraceInsertion enable
|
||
= do uncbool $ ic_enable_brace_insertion (cbool enable)
|
||
|
||
-- | Set pairs of matching braces, by default @\"(){}[]\"@.
|
||
setMatchingBraces :: String -> IO ()
|
||
setMatchingBraces bracePairs
|
||
= withUTF8String0 bracePairs $ \cbracePairs ->
|
||
do ic_set_matching_braces cbracePairs
|
||
|
||
-- | Set pairs of auto insertion braces, by default @\"(){}[]\\\"\\\"\'\'\"@.
|
||
setInsertionBraces :: String -> IO ()
|
||
setInsertionBraces bracePairs
|
||
= withUTF8String0 bracePairs $ \cbracePairs ->
|
||
do ic_set_insertion_braces cbracePairs
|
||
|
||
|
||
-- | Disable or enable automatic indentation to line up the
|
||
-- multiline prompt marker with the initial prompt marker (enabled by default).
|
||
-- Returns the previous value.
|
||
-- See also 'setPromptMarker'.
|
||
enableMultilineIndent :: Bool -> IO Bool
|
||
enableMultilineIndent enable
|
||
= do uncbool $ ic_enable_multiline_indent (cbool enable)
|
||
|
||
-- | Disable or enable automatic inline hinting (enabled by default)
|
||
-- Returns the previous value.
|
||
enableHint :: Bool -> IO Bool
|
||
enableHint enable
|
||
= do uncbool $ ic_enable_hint (cbool enable)
|
||
|
||
-- | Disable or enable syntax highlighting (enabled by default).
|
||
-- Returns the previous value.
|
||
enableHighlight :: Bool -> IO Bool
|
||
enableHighlight enable
|
||
= do uncbool $ ic_enable_highlight (cbool enable)
|
||
|
||
-- | Set the delay in milliseconds before a hint is displayed (500ms by default)
|
||
-- See also 'enableHint'
|
||
setHintDelay :: Int -> IO Int
|
||
setHintDelay ms
|
||
= do cl <- ic_set_hint_delay (toEnum ms)
|
||
return (fromEnum cl)
|
||
|
||
|
||
----------------------------------------------------------------------------
|
||
-- UTF8 Strings
|
||
----------------------------------------------------------------------------
|
||
|
||
withUTF8String0 :: String -> (CString -> IO a) -> IO a
|
||
withUTF8String0 s action
|
||
= if (null s) then action nullPtr else withUTF8String s action
|
||
|
||
peekUTF8String0 :: CString -> IO String
|
||
peekUTF8String0 cstr
|
||
= if (nullPtr == cstr) then return "" else peekUTF8String cstr
|
||
|
||
peekUTF8StringMaybe :: CString -> IO (Maybe String)
|
||
peekUTF8StringMaybe cstr
|
||
= if (nullPtr == cstr) then return Nothing
|
||
else do s <- peekUTF8String cstr
|
||
return (Just s)
|
||
|
||
peekUTF8String :: CString -> IO String
|
||
peekUTF8String cstr
|
||
= do bstr <- B.packCString cstr
|
||
return (T.unpack (TE.decodeUtf8With lenientDecode bstr))
|
||
|
||
withUTF8String :: String -> (CString -> IO a) -> IO a
|
||
withUTF8String str action
|
||
= do let bstr = TE.encodeUtf8 (T.pack str)
|
||
B.useAsCString bstr action
|
||
|