{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-}
module Emacs.Module.Functions
( bindFunction
, makeFunction
, withCleanup
, provide
, makeUserPtrFromStablePtr
, extractStablePtrFromUserPtr
, extractInt
, makeInt
, extractText
, makeText
, extractShortByteString
, makeShortByteString
, extractBool
, makeBool
, extractVector
, extractVectorWith
, extractUnboxedVectorWith
, makeVector
, vconcat2
, cons
, car
, cdr
, nil
, setcar
, setcdr
, makeList
, extractList
, extractListWith
, extractListRevWith
, foldlEmacsListWith
, unfoldEmacsListWith
, addFaceProp
, concat2
, valueToText
, symbolName
, MonadMask
) where
import Control.Monad.Catch
import Control.Monad.Except
import qualified Data.ByteString.Char8 as C8
import Data.ByteString.Short (ShortByteString)
import qualified Data.ByteString.Short as BSS
import Data.Foldable
import Data.Text (Text)
import qualified Data.Text.Encoding as TE
import qualified Data.Text.Encoding.Error as TE
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as U
import Foreign.Ptr (nullPtr)
import Foreign.StablePtr
import Data.Emacs.Module.Args
import qualified Data.Emacs.Module.Env as Env
import Data.Emacs.Module.SymbolName (SymbolName)
import Data.Emacs.Module.SymbolName.TH
import Emacs.Module.Assert
import Emacs.Module.Monad.Class
{-# INLINABLE bindFunction #-}
bindFunction
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> SymbolName
-> EmacsRef m s
-> m s ()
bindFunction name def = do
name' <- intern name
funcallPrimitive_ [esym|fset|] [name', def]
{-# INLINE makeFunction #-}
makeFunction
:: (WithCallStack, EmacsInvocation req opt rest, GetArities req opt rest, MonadEmacs m, Monad (m s))
=> (forall s'. EmacsFunction req opt rest s' m)
-> C8.ByteString
-> m s (EmacsRef m s)
makeFunction f doc =
makeFunctionExtra (\env _extraPtr -> f env) doc nullPtr
{-# INLINE provide #-}
provide
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> SymbolName
-> m s ()
provide sym = do
sym' <- intern sym
funcallPrimitive_ [esym|provide|] [sym']
{-# INLINE makeUserPtrFromStablePtr #-}
makeUserPtrFromStablePtr
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> StablePtr a
-> m s (EmacsRef m s)
makeUserPtrFromStablePtr =
makeUserPtr Env.freeStablePtrFinaliser . castStablePtrToPtr
{-# INLINE extractStablePtrFromUserPtr #-}
extractStablePtrFromUserPtr
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> m s (StablePtr a)
extractStablePtrFromUserPtr =
fmap castPtrToStablePtr . extractUserPtr
{-# INLINE extractInt #-}
extractInt
:: (WithCallStack, MonadEmacs m, Monad (m s)) => EmacsRef m s -> m s Int
extractInt x = do
y <- extractWideInteger x
emacsAssert
(y <= fromIntegral (maxBound :: Int))
("Integer is too wide to fit into Int: " ++ show y)
(pure (fromIntegral y))
{-# INLINE makeInt #-}
makeInt
:: (WithCallStack, MonadEmacs m, Monad (m s)) => Int -> m s (EmacsRef m s)
makeInt = makeWideInteger . fromIntegral
{-# INLINE extractText #-}
extractText
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s -> m s Text
extractText x = TE.decodeUtf8With TE.lenientDecode <$> extractString x
{-# INLINE makeText #-}
makeText
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> Text -> m s (EmacsRef m s)
makeText = makeString . TE.encodeUtf8
{-# INLINE extractShortByteString #-}
extractShortByteString
:: (WithCallStack, MonadEmacs m, Functor (m s))
=> EmacsRef m s -> m s ShortByteString
extractShortByteString = fmap BSS.toShort . extractString
{-# INLINE makeShortByteString #-}
makeShortByteString
:: (WithCallStack, MonadEmacs m)
=> ShortByteString -> m s (EmacsRef m s)
makeShortByteString = makeString . BSS.fromShort
{-# INLINE extractBool #-}
extractBool
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s -> m s Bool
extractBool = isNotNil
{-# INLINE makeBool #-}
makeBool
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> Bool -> m s (EmacsRef m s)
makeBool b = intern (if b then [esym|t|] else [esym|nil|])
{-# INLINE withCleanup #-}
withCleanup
:: (WithCallStack, MonadMask (m s), MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> (EmacsRef m s -> m s a)
-> m s a
withCleanup x f = f x `finally` freeValue x
{-# INLINABLE extractVector #-}
extractVector
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s -> m s (V.Vector (EmacsRef m s))
extractVector xs = do
n <- vecSize xs
V.generateM n $ vecGet xs
{-# INLINABLE extractVectorWith #-}
extractVectorWith
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> (EmacsRef m s -> m s a)
-> EmacsRef m s
-> m s (V.Vector a)
extractVectorWith f xs = do
n <- vecSize xs
V.generateM n $ f <=< vecGet xs
{-# INLINABLE extractUnboxedVectorWith #-}
extractUnboxedVectorWith
:: (WithCallStack, MonadEmacs m, Monad (m s), U.Unbox a)
=> (EmacsRef m s -> m s a)
-> EmacsRef m s
-> m s (U.Vector a)
extractUnboxedVectorWith f xs = do
n <- vecSize xs
U.generateM n $ f <=< vecGet xs
{-# INLINE makeVector #-}
makeVector
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> [EmacsRef m s]
-> m s (EmacsRef m s)
makeVector = funcallPrimitive [esym|vector|]
{-# INLINE vconcat2 #-}
vconcat2
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> EmacsRef m s
-> m s (EmacsRef m s)
vconcat2 x y =
funcallPrimitive [esym|vconcat|] [x, y]
{-# INLINE cons #-}
cons
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> EmacsRef m s
-> m s (EmacsRef m s)
cons x y = funcallPrimitive [esym|cons|] [x, y]
{-# INLINE car #-}
car
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> m s (EmacsRef m s)
car = funcallPrimitive [esym|car|] . (: [])
{-# INLINE cdr #-}
cdr
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> m s (EmacsRef m s)
cdr = funcallPrimitive [esym|cdr|] . (: [])
{-# INLINE nil #-}
nil
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> m s (EmacsRef m s)
nil = intern [esym|nil|]
{-# INLINE setcar #-}
setcar
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> EmacsRef m s
-> m s ()
setcar x y = funcallPrimitive_ [esym|setcar|] [x, y]
{-# INLINE setcdr #-}
setcdr
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> EmacsRef m s
-> m s ()
setcdr x y = funcallPrimitive_ [esym|setcdr|] [x, y]
{-# INLINE makeList #-}
makeList
:: (WithCallStack, MonadEmacs m, Monad (m s), Foldable f)
=> f (EmacsRef m s)
-> m s (EmacsRef m s)
makeList = unfoldEmacsListWith (pure . go) . toList
where
go = \case
[] -> Nothing
y : ys -> Just (y, ys)
{-# INLINE extractList #-}
extractList
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> m s [EmacsRef m s]
extractList = extractListWith pure
{-# INLINE extractListWith #-}
extractListWith
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> (EmacsRef m s -> m s a)
-> EmacsRef m s
-> m s [a]
extractListWith = \f -> fmap reverse . extractListRevWith f
{-# INLINE extractListRevWith #-}
extractListRevWith
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> (EmacsRef m s -> m s a)
-> EmacsRef m s
-> m s [a]
extractListRevWith f = go []
where
go acc xs = do
nonNil <- isNotNil xs
if nonNil
then do
x <- f =<< car xs
xs' <- cdr xs
go (x : acc) xs'
else pure acc
{-# INLINE foldlEmacsListWith #-}
foldlEmacsListWith
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> (a -> EmacsRef m s -> m s a)
-> a
-> EmacsRef m s
-> m s a
foldlEmacsListWith f = go
where
go acc xs = do
nonNil <- isNotNil xs
if nonNil
then do
acc' <- f acc =<< car xs
go acc' =<< cdr xs
else pure acc
{-# INLINE unfoldEmacsListWith #-}
unfoldEmacsListWith
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> (a -> m s (Maybe (EmacsRef m s, a)))
-> a
-> m s (EmacsRef m s)
unfoldEmacsListWith f accum = do
accum' <- f accum
nilVal <- nil
case accum' of
Nothing -> pure nilVal
Just (x, accum'') -> do
cell <- cons x nilVal
go nilVal accum'' cell
pure cell
where
go nilVal = go'
where
go' acc cell = do
acc' <- f acc
case acc' of
Nothing -> pure ()
Just (x, acc'') -> do
cell' <- cons x nilVal
setcdr cell cell'
go' acc'' cell'
{-# INLINE addFaceProp #-}
addFaceProp
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> SymbolName
-> m s (EmacsRef m s)
addFaceProp str face = do
faceSym <- intern [esym|face|]
face' <- intern face
funcallPrimitive [esym|propertize|] [str, faceSym, face']
{-# INLINE concat2 #-}
concat2
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> EmacsRef m s
-> m s (EmacsRef m s)
concat2 x y =
funcallPrimitive [esym|concat|] [x, y]
{-# INLINE valueToText #-}
valueToText
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> m s Text
valueToText x =
extractText =<< funcallPrimitive [esym|prin1-to-string|] [x]
{-# INLINE symbolName #-}
symbolName
:: (WithCallStack, MonadEmacs m, Monad (m s))
=> EmacsRef m s
-> m s (EmacsRef m s)
symbolName = funcallPrimitive [esym|symbol-name|] . (:[])