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
|
|||
|
|