emacs-module 0.1 → 0.1.1
raw patch · 6 files changed
+120/−44 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- emacs-module.cabal +7/−2
- src/Data/Emacs/Module/Args.hs +20/−28
- src/Emacs/Module.hs +64/−11
- src/Emacs/Module/Assert.hs +3/−0
- src/Emacs/Module/Monad.hs +12/−0
- src/Emacs/Module/Monad/Class.hs +14/−3
emacs-module.cabal view
@@ -1,7 +1,7 @@ name: emacs-module version:- 0.1+ 0.1.1 category: Foreign, Foreign binding synopsis:@@ -9,7 +9,12 @@ description: This package provides a full set of bindings to emacs-module.h that- allows to develop Emacs modules in Haskell.+ allows to develop Emacs modules in Haskell. Bindings are based on+ Emacs 25 version of the interface and thus should work in all+ subsequent versions of Emacs.++ For pointers on how to write minimal Emacs module, please refer+ to https://github.com/sergv/emacs-module/blob/master/test/src/Emacs/TestsInit.hs license: BSD3
src/Data/Emacs/Module/Args.hs view
@@ -6,11 +6,7 @@ -- Maintainer : serg.foo@gmail.com ---------------------------------------------------------------------------- -{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveFoldable #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-}@@ -19,7 +15,6 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilyDependencies #-}@@ -48,6 +43,9 @@ import Data.Emacs.Module.Raw.Env (variadicFunctionArgs) import Data.Emacs.Module.Raw.Value +-- | Type-level Peano numbers.+--+-- Indented to be used with @DataKinds@ extension enabled. data Nat = Z | S Nat class NatValue (n :: Nat) where@@ -61,43 +59,36 @@ {-# INLINE natValue #-} natValue _ = 1 + natValue (Proxy @n) --- type family EmacsArgs' (req :: Nat) (m :: Type -> Type) (a :: Type) = (r :: Type) | r -> req m a where--- EmacsArgs' ('S n) m a = RawValue -> EmacsArgs' n m a--- -- EmacsArgs 'Z m a = Maybe RawValue -> EmacsArgs' 'Z k rest m a--- -- EmacsArgs 'Z m a = [RawValue] -> m a--- EmacsArgs' 'Z m a = m a---- type family EmacsArgs' (req :: Nat) (a :: Type) = (r :: Type) | r -> req------ type instance EmacsArgs' ('S n) a = RawValue -> EmacsArgs' n a--- -- EmacsArgs 'Z m a = Maybe RawValue -> EmacsArgs' 'Z k rest m a--- -- EmacsArgs 'Z m a = [RawValue] -> m a--- type instance EmacsArgs' 'Z a = IO a---- | Required argument.+-- | Required argument of an exported function. data R a b = R !a !b --- | Optional argument.+-- | Optional argument of an exported function. data O a b = O !(Maybe a) !b --- | All other arguments as a list.+-- | All other arguments of an exported function as a list. newtype Rest a = Rest [a] --- | No more arguments.+-- | End of argument list of an exported funciton. data Stop a = Stop +-- | Specification of the arguments that exposed functions can receive from Emacs.+--+-- This type family allows to declaratively specify how many required and+-- optional arguments a function can take and whether it accepts rest arguments.+-- It's a direct translation of argument lists in Emacs lisp, e.g.+--+-- > (defun foo (x y z &optional w t &rest quux)+-- > (+ (* x y z) (* (or w 1) (or t 2)) (length quux)))+--+-- The function above has 3 required arguments, 2 optional and also has+-- rest arguments. The type family below has two 'Nat's and one 'Bool'+-- to provide that info. type family EmacsArgs (req :: Nat) (opt :: Nat) (rest :: Bool) (a :: Type) = (r :: Type) | r -> req opt rest a where EmacsArgs ('S n) opt rest a = R a (EmacsArgs n opt rest a) EmacsArgs 'Z ('S k) rest a = O a (EmacsArgs 'Z k rest a) EmacsArgs 'Z 'Z 'True a = Rest a EmacsArgs 'Z 'Z 'False a = Stop a --- type family EmacsArgs (req :: Nat) (opt :: Nat) (rest :: Bool) (s :: Type) (a :: Type) = (r :: Type) | r -> req opt rest where--- EmacsArgs ('S n) opt rest s a = Value s -> EmacsArgs n opt rest s a--- EmacsArgs 'Z ('S k) rest s a = Maybe (Value s) -> EmacsArgs 'Z k rest s a--- EmacsArgs 'Z 'Z 'True s a = [Value s] -> IO a--- EmacsArgs 'Z 'Z 'False s a = IO a- class EmacsInvocation req opt rest where supplyEmacsArgs :: MonadBase IO m@@ -164,6 +155,7 @@ supplyEmacsArgs (nargs - 1) (advanceEmacsValuePtr startPtr) mkArg (f . R arg) +-- | Helper to retrieve number of arguments a function takes for Emacs. class GetArities (req :: Nat) (opt :: Nat) (rest :: Bool) where arities :: Proxy req -> Proxy opt -> Proxy rest -> (CPtrdiff, CPtrdiff)
src/Emacs/Module.hs view
@@ -4,33 +4,86 @@ -- Copyright : (c) Sergey Vinokurov 2018 -- License : BSD3-style (see LICENSE) -- Maintainer : serg.foo@gmail.com+--+-- This module is the entry point for writing Emacs extensions in+-- Haskell.+--+-- This package, though provides a lot of wrapping around Emacs's bare+-- C interface, still presumes some familiarity with said interface.+-- Thus, when developnig Emacs modules it's recommended to keep a+-- reference of the C interface around. One such reference is+-- <https://phst.github.io/emacs-modules.html>.+--+-- = Minimalistic example+--+-- Consider Emacs function+--+-- > (defun foo (f x y z &optional w t &rest quux)+-- > (+ (funcall f (* x y z)) (* (or w 1) (or t 2)) (length quux)))+--+-- With help of this package, it may be defined as+--+-- @+-- {-# LANGUAGE DataKinds #-}+-- {-# LANGUAGE QuasiQuotes #-}+--+-- import Data.Maybe+-- import Data.Emacs.Module.SymbolName.TH+-- import Emacs.Module+--+-- foo+-- :: (MonadEmacs m, Monad (m s))+-- => EmacsFunction ('S ('S ('S ('S 'Z)))) ('S ('S 'Z)) 'True s m+-- foo (R f (R x (R y (R z (O w (O t (Rest quux))))))) = do+-- x' <- extractInt x+-- y' <- extractInt y+-- z' <- extractInt z+-- w' <- traverse extractInt w+-- t' <- traverse extractInt t+--+-- tmp <- makeInt (x' * y' * z')+-- tmp' <- extractInt =<< funcall [esym|funcall|] [f, tmp]+--+-- produceRef =<< makeInt (tmp' + fromMaybe 1 w' * fromMaybe 2 t' + length quux)+-- @+--+-- = Creating Emacs dynamic module+-- In order to make shared object or dll callable from Emacs,+-- a cabal project with foreign-library section has to be created.+-- Please refer to <https://github.com/sergv/emacs-module/tree/master/test>+-- for such a project.+--+-- Please note that this project will need a small C file for initialising+-- Haskell runtime. In the project mentioned before it's present as+-- <https://github.com/sergv/emacs-module/blob/master/test/cbits/emacs_wrapper.c> ---------------------------------------------------------------------------- module Emacs.Module- ( -- * Generic interface- MonadEmacs(..)+ (+ -- * EmacsM+ EmacsM+ , runEmacsM - -- * Type synonyms+ -- * Basic bindings+ , MonadEmacs(..)++ -- ** Define functions callable by Emacs , EmacsFunction , EmacsFunctionExtra- , Raw.UserPtrFinaliserType- , Raw.UserPtrFinaliser-- -- * Types for defining functions , Nat(..) , R(..) , O(..) , Rest(..) , Stop(..) - -- * Errors+ -- ** Error types , EmacsError(..) , EmacsInternalError(..) , reportAllErrorsToEmacs - -- * EmacsM- , EmacsM- , runEmacsM+ -- ** Other types+ , Raw.UserPtrFinaliserType+ , Raw.UserPtrFinaliser -- * Reexports , module Emacs.Module.Functions
src/Emacs/Module/Assert.hs view
@@ -21,6 +21,9 @@ import GHC.Stack (HasCallStack) #endif +-- | Call stacks for all emacs-related functions in Haskell.+-- Will be disabled unless this package was build with 'assertions'+-- flag enabled. #ifdef ASSERTIONS type WithCallStack = (HasCallStack :: Constraint) #else
src/Emacs/Module/Monad.hs view
@@ -75,6 +75,17 @@ , eResourceState :: !Resource.InternalState } +-- | Concrete monad for interacting with Emacs. It provides:+--+-- 1. Ability to call Emacs C functions and automatically rethrows any+-- errors (non-local exits) from elisp as Haskell exceptions.+-- 2. Tracks ownership of any produced Emacs values and communicates+-- that to Emacs, so that GC on Emacs side will not make any+-- values in Haskell invalid (funnily enough, this can happen!).+--+-- Parameter 's' serves to make ownership-tracking capabilities possible.+-- It's use is the same as in 'Control.Monad.ST' monad. That is, it creates+-- local threads so that no produced Emacs values can leave past 'runEmacsM'. newtype EmacsM s a = EmacsM { unEmacsM :: ReaderT Environment IO a } deriving ( Functor@@ -100,6 +111,7 @@ {-# INLINE restoreM #-} restoreM x = EmacsM (restoreM x) +-- | Execute emacs interaction session using an environment supplied by Emacs. runEmacsM :: Env -> (forall s. EmacsM s a)
src/Emacs/Module/Monad/Class.hs view
@@ -34,14 +34,25 @@ import Emacs.Module.Assert import Emacs.Module.Errors +-- | Basic Haskell function that can be called by Emacs. type EmacsFunction req opt rest (s :: k) (m :: k -> Type -> Type) = (Throws EmacsThrow, Throws EmacsError, Throws EmacsInternalError, Throws UserError) => EmacsArgs req opt rest (EmacsRef m s) -> m s (EmacsReturn m s) +-- | A Haskell functions that is callable by Emacs.+--+-- This type differs from 'EmacsFunction' in that it has an extra+-- parameter which will result in an additional pointer being passed+-- to this function when it's called by Emacs. Contents of the pointer is+-- specified when function is exported to Emacs. type EmacsFunctionExtra req opt rest extra (s :: k) (m :: k -> Type -> Type) = (Throws EmacsThrow, Throws EmacsError, Throws EmacsInternalError, Throws UserError) => EmacsArgs req opt rest (EmacsRef m s) -> Ptr extra -> m s (EmacsReturn m s) +-- | A mtl-style typeclass for interacting with Emacs. Typeclass functions+-- are mostly direct translations of emacs interface provided by 'emacs-module.h'.+--+-- For more functions please refer to "Emacs.Module.Functions" module. class MonadEmacs (m :: k -> Type -> Type) where -- | Emacs value that is managed by the 'm' monad. Will be cleaned up@@ -66,7 +77,7 @@ -- | Equivalent to Emacs's @signal@ function. -- -- NB if a non-local exit is alredy pending, this function will not- -- overwrite it. In order to do that, use nonLocalExitClear.+ -- overwrite it. In order to do that, use 'nonLocalExitClear'. nonLocalExitSignal :: WithCallStack => EmacsRef m s -- ^ Error symbol@@ -76,7 +87,7 @@ -- | Equivalent to Emacs's @throw@ function. -- -- NB if a non-local exit is alredy pending, this function will not- -- overwrite it. In order to do that, use nonLocalExitClear.+ -- overwrite it. In order to do that, use 'nonLocalExitClear'. nonLocalExitThrow :: WithCallStack => EmacsRef m s -- ^ Tag@@ -147,7 +158,7 @@ -- characters are the equal, but not much more. For more complete -- equality comparison do --- -- > funcall [esym|equal|] [x, y]+ -- > funcallPrimitive [esym|equal|] [x, y] eq :: WithCallStack => EmacsRef m s -> EmacsRef m s -> m s Bool