Copyright | (c) Sebastian Witte |
---|---|
License | Apache-2.0 |
Maintainer | woozletoff@gmail.com |
Stability | experimental |
Portability | GHC (due to Template Haskell) |
Safe Haskell | None |
Language | Haskell2010 |
This module should contain all the things you need to write neovim plugins in
your favorite language! :-)
The documentation in this module should provide every information you need to start writing plugins.
- data Neovim r st a
- type Neovim' = Neovim () ()
- neovim :: NeovimConfig -> IO ()
- data NeovimConfig = Config {
- plugins :: [Neovim (StartupConfig NeovimConfig) () NeovimPlugin]
- logOptions :: Maybe (FilePath, Priority)
- errorMessage :: Maybe String
- defaultConfig :: NeovimConfig
- data StartupConfig cfg = StartupConfig {
- dyreParams :: Maybe (Params cfg)
- ghcEnvironmentVariables :: [(String, Maybe String)]
- def :: Default a => a
- data NeovimPlugin = NeovimPlugin (Plugin r st)
- data StatefulFunctionality r st = StatefulFunctionality {
- readOnly :: r
- writable :: st
- functionalities :: [ExportedFunctionality r st]
- data Plugin r st = Plugin {
- exports :: [ExportedFunctionality () ()]
- statefulExports :: [StatefulFunctionality r st]
- class NFData o => NvimObject o where
- (+:) :: NvimObject o => o -> [Object] -> [Object]
- type Dictionary = Map ByteString Object
- data Object :: *
- wrapPlugin :: Applicative m => Plugin r st -> m NeovimPlugin
- function :: String -> Name -> Q Exp
- function' :: Name -> Q Exp
- command :: String -> Name -> Q Exp
- command' :: Name -> Q Exp
- autocmd :: Name -> Q Exp
- data Synchronous
- data CommandOption
- data RangeSpecification
- data CommandArguments = CommandArguments {}
- data AutocmdOptions = AutocmdOptions {
- acmdPattern :: String
- acmdNested :: Bool
- acmdGroup :: Maybe String
- addAutocmd :: ByteString -> AutocmdOptions -> Neovim r st () -> Neovim r st (Maybe (Either (Neovim anyR anySt ()) ReleaseKey))
- addAutocmd' :: ByteString -> AutocmdOptions -> Neovim' () -> Neovim r st (Maybe ReleaseKey)
- ask :: MonadReader r m => m r
- asks :: MonadReader r m => (r -> a) -> m a
- put :: MonadState s m => s -> m ()
- get :: MonadState s m => m s
- gets :: MonadState s m => (s -> a) -> m a
- modify :: MonadState s m => (s -> s) -> m ()
- wait :: Neovim r st (STM result) -> Neovim r st result
- wait' :: Neovim r st (STM result) -> Neovim r st ()
- waitErr :: Pretty e => String -> Neovim r st (STM (Either e result)) -> Neovim r st result
- waitErr' :: Pretty e => String -> Neovim r st (STM (Either e result)) -> Neovim r st ()
- err :: Pretty err => err -> Neovim r st a
- data Doc :: *
- class Pretty a where
- errOnInvalidResult :: NvimObject o => Neovim r st (Either NeovimException Object) -> Neovim r st o
- text :: String -> Doc
- data NeovimException = ErrorMessage Doc
- module Neovim.API.String
- liftIO :: MonadIO m => forall a. IO a -> m a
- withCustomEnvironment :: (MonadMask io, MonadIO io) => [(String, Maybe String)] -> io a -> io a
- whenM :: Monad m => m Bool -> m () -> m ()
- unlessM :: Monad m => m Bool -> m () -> m ()
- docToObject :: Doc -> Object
- docFromObject :: Object -> Either Doc Doc
- data Priority :: *
- module Control.Monad
- module Control.Applicative
- module Data.Monoid
- module Data.Int
- module Data.Word
Installation
Installation instructions are in the README.md file that comes with the source of this package. It is also on the repositories front page.
Tutorial
tl;dr
If you are proficient with Haskell, it may be sufficient to point you at some of the
important data structures and functions. So, I will do it here. If you need more
assistance, please skip to the next section and follow the links for functions or data
types you do no understand how to use. If you think that the documentation is lacking,
please create an issue on github (or even better, a pull request with a fix ;-)
).
The code sections that describe new functionality are followed by the source code
documentation of the used functions (and possibly a few more).
The config directory location adheres to the
XDG-basedir specification.
Unless you have changed some $XDG_* environment variables, the configuration
directory on unixoid systems (e.g. MacOS X, most GNU/Linux distribution, most
BSD distributions) is $HOME/.config/nvim
.
Create a file called nvim.hs
in $XDG_CONFIG_HOME/nvim
(usually
~/.config/nvim
) with the following content:
import Neovim main =neovim
defaultConfig
Adjust the fields in defaultConfig
according to the parameters in NeovimConfig
.
Depending on how you define the parameters, you may have to add some language extensions
which GHC should point you to.
This is the environment in which all plugins are initially started.
Stateless functions use '()' for the static configuration and the mutable
state and there is another type alias for that case: Neovim'
.
Functions have to run in this transformer stack to communicate with neovim.
If parts of your own functions dont need to communicate with neovim, it is
good practice to factor them out. This allows you to write tests and spot
errors easier. Essentially, you should treat this similar to IO
in general
haskell programs.
MonadBase IO (Neovim r st) Source # | |
MonadReader r (Neovim r st) Source # | User facing instance declaration for the reader state. |
MonadState st (Neovim r st) Source # | |
Monad (Neovim r st) Source # | |
Functor (Neovim r st) Source # | |
Applicative (Neovim r st) Source # | |
MonadIO (Neovim r st) Source # | |
MonadThrow (Neovim r st) Source # | |
MonadCatch (Neovim r st) Source # | |
MonadMask (Neovim r st) Source # | |
MonadResource (Neovim r st) Source # | |
neovim :: NeovimConfig -> IO () Source #
This is essentially the main function for nvim-hs, at least if you want to use Config.Dyre for the configuration.
data NeovimConfig Source #
This data type contains information about the configuration of neovim. See the fields' documentation for what you possibly want to change. Also, the tutorial in the Neovim module should get you started.
Config | |
|
defaultConfig :: NeovimConfig Source #
Default configuration options for nvim-hs. If you want to keep the default plugins enabled, you can define your config like this:
main =neovim
defaultConfig
{ plugins = myPlugins ++ plugins defaultConfig }
data StartupConfig cfg Source #
This data type contains internal fields of nvim-hs that may
be useful for plugin authors. It is available via ask
inside
the plugin startup code.
StartupConfig | |
|
Using existing plugins
nvim-hs is all about importing and creating plugins. This is done following a
concise API. Let's start by making a given plugin available inside
our plugin provider. Assuming that we have installed a cabal package that exports
an examplePlugin
from the module TestPlugin.ExamplePlugin
. A minimal
configuration would then look like this:
import TestPlugin.ExamplePlugin (examplePlugin) main =neovim
def
{plugins
= [ examplePlugin ] ++plugins
defaultConfig
}
That's all you have to do! Multiple plugins are simply imported and put in a list.
If the plugin is not packaged, you can also put the source files of the plugin
inside $XDG_CONFIG_HOME/nvim/lib
(usually ~/.config/nvim/lib
).
Assuming the same module name and plugin name, you can use the same configuration
file. The source for the plugin must be located at
$XDG_CONFIG_HOME/nvim/lib/TestPlugin/ExamplePlugin.hs
and all source
files it depends on must follow the same structure. This is the standard way
how Haskell modules are defined in cabal projects. Having all plugins as source
files can increase the compilation times, so plugins should be put in a cabal
project once they are mature enough. This also makes them easy to share!
Creating a plugin
Creating plugins isn't difficult either. You just have to follow and survive the
compile time errors of seemingly valid code. This may sound scary, but it is not
so bad. We will cover most pitfalls in the following paragraphs and if there
isn't a solution for your error, you can always ask any friendly Haskeller in
#haskell on irc.freenode.net
!
Enough scary stuff said for now, let's write a plugin!
Due to a stage restriction in GHC when using Template Haskell, we must define
our functions in a different module than $XDG_CONFIG_HOME/nvim/nvim.hs
.
This is a bit unfortunate, but it will save you a lot of boring boilerplate and
it will present you with helpful error messages if your plugin's functions do
not work together with neovim.
So, let's write a plugin that calculates the n
th Fibonacci number. Don't we all
love those!
File ~/.config/nvim/lib/Fibonacci/Plugin.hs
:
module Fibonacci.Plugin (fibonacci) where import Neovim -- | Neovim is not really good with big numbers, so we return aString
here. fibonacci ::Int
->Neovim'
String
fibonacci n =return
.show
$ fibs !! n where fibs :: [Integer] fibs = 0:1:scanl1
(+) fibs
File ~/.config/nvim/lib/Fibonacci.hs
:
{-# LANGUAGE TemplateHaskell #-} module Fibonacci (plugin) where import Neovim import Fibonacci.Plugin (fibonacci) plugin ::Neovim
(StartupConfig
NeovimConfig
) ()NeovimPlugin
plugin =wrapPlugin
Plugin {exports
= [ $(function'
'fibonacci)Sync
] ,statefulExports
= [] }
File ~/.config/nvim/nvim.hs
:
import Neovim import qualified Fibonacci as Fibonacci main ::IO
() main =neovim
defaultConfig
{plugins
=plugins
defaultConfig
++ [ Fibonacci.plugin ] }
Let's analyze how it works. The module Fibonacci.Plugin
simply defines a function
that takes the n
th element of the infinite list of Fibonacci numbers. Even though
the definition is very concise and asthetically pleasing, the important part is the
type signature for fibonacci
. Similarly how main :: IO ()
works in normal Haskell
programs, Neovim'
is the environment we need for plugins. Internally, it stores a
few things that are needed to communicate with neovim, but that shouldn't bother you
too much. Simply remember that every plugin function must have a function signature
whose last element is of type
. The result of Neovim
r st somethingfibonacci
is String
because neovim cannot handle big numbers so well. :-)
You can use any argument or result type as long as it is an instance of NvimObject
.
The second part of of the puzzle, which is the definition of plugin
in ~/.config/nvim/lib/Fibonacci.hs
, shows what a plugin is. It is essentially two
lists of stateless and stateful functionality. A functionality can currently be one
of three things: a function, a command and an autocmd in the context of vim
terminology. In the end, all of those functionalities map to a function at the side
of nvim-hs. If you really want to know what the distinction between those is, you
have to consult the :help
pages of neovim (e.g. :help :function
, :help :command
and :help :autocmd
). What's relevant from the side of nvim-hs is the distinction
between stateful and stateless. A stateless function can be called at any
time and it does not share any of its internals with other functions. A stateful
function on the other hand can share a well-defined amount of state with other
functions and in the next section I will show you a simple example for that.
Anyhow, if you take a look at the type alias for Neovim
, you notice the two
type variables r
and st
. These can be accessed with different semantics
each. A value of type r
can only be read. It is more or less a static value
you can query with ask
or asks
if you
are inside a Neovim
environment. The value st
can be changed and those
changes will be available to other functions which run in the same environment.
You can get the current value with get
, you can replace an existing value with
put
and you can also apply a function to the current state with modify
.
Notice how Neovim'
is just a specialization of Neovim
with its r
and
st
set to ()
.
Now to the magical part: $(
. This is a so called
Template Haskell splice and this is why you need
function'
'fibonacci){-# LANGUAGE TemplateHaskell #-}
at the top of your Haskell file. This
splice simply generates Haskell code that, in this case, still needs a value
of type Synchronous
which indicates whether calling the function will make
neovim wait for its result or not. Internally, the expression
$(
creates a value that contains all the
necessary information to properly register the function with neovim. Note the
prime symbol before the function name! This would have probably caused you
some trouble if I haven't mentioned it here! Template Haskell simply requires
you to put that in front of function names that are passed in a splice.function'
'fibonacci) Sync
If you compile this (which should happen automatically if you have put those
files at the appropriate places), you can restart nvim-hs with the command
:RestartNvimhs
which is available as long as you do not remove the default
plugins from you rconfig. Afterwards, you can calculate the 2000th Fibonacci
number like as if it were a normal vim-script function:
:echo Fibonacci(2000)
You can also directly insert the result inside any text file opened with neovim by using the evaluation register by pressing the following key sequence in insert mode:
<C-r>=Fibonacci(2000)
data NeovimPlugin Source #
Plugin
values are wraped inside this data type via wrapPlugin
so that
we can put plugins in an ordinary list.
NeovimPlugin (Plugin r st) |
data StatefulFunctionality r st Source #
This datatype contains the initial state (mutable and immutable) for the functionalities defined here.
StatefulFunctionality | |
|
This data type contains meta information for the plugin manager.
Plugin | |
|
class NFData o => NvimObject o where Source #
Conversion from Object
files to Haskell types and back with respect
to neovim's interpretation.
The NFData
constraint has been added to allow forcing results of function
evaluations in order to catch exceptions from pure code. This adds more
stability to the plugin provider and seems to be a cleaner approach.
toObject :: o -> Object Source #
fromObjectUnsafe :: Object -> o Source #
(+:) :: NvimObject o => o -> [Object] -> [Object] infixr 5 Source #
Convenient operator to create a list of Object
from normal values.
type Dictionary = Map ByteString Object Source #
A generic vim dictionary is a simply a map from strings to objects. This type alias is sometimes useful as a type annotation especially if the OverloadedStrings extension is enabled.
ObjectNil | |
ObjectUInt Word64 | Unsigned integers from the MsgPack protocol: uint 8, uint 16, uint 32, uint 64 |
ObjectInt Int64 | Signed integers and fixnums from the MsgPack protocol: positive fixnum, negative fixnum, int 8, int 16, int 32, int 64 |
ObjectBool Bool | |
ObjectFloat Float | |
ObjectDouble Double | |
ObjectString ByteString | |
ObjectBinary ByteString | |
ObjectArray [Object] | |
ObjectMap (Map Object Object) | |
ObjectExt ~Int8 ByteString |
wrapPlugin :: Applicative m => Plugin r st -> m NeovimPlugin Source #
Wrap a Plugin
in some nice blankets, so that we can put them in a simple
list.
function :: String -> Name -> Q Exp Source #
Define an exported function by providing a custom name and referencing the function you want to export.
Note that the name must start with an upper case letter.
Example: $(function "MyExportedFunction" 'myDefinedFunction)
Sync
function' :: Name -> Q Exp Source #
Define an exported function. This function works exactly like function
,
but it generates the exported name automatically by converting the first
letter to upper case.
command :: String -> Name -> Q Exp Source #
Similarly to function
, this function is used to export a command with a
custom name.
Note that commands must start with an upper case letter.
Due to limitations on the side of (neo)vim, commands can only have one of the
following five signatures, where you can replace String
with ByteString
or Text
if you wish:
CommandArguments
->Neovim
r st ()CommandArguments
->Maybe
String
->Neovim
r st ()CommandArguments
->String
->Neovim
r st ()CommandArguments
-> [String
] ->Neovim
r st ()CommandArguments
->String
-> [String
] ->Neovim
r st ()
Example: $(command "RememberThePrime" 'someFunction) [
CmdBang
]
Note that the list of command options (i.e. the last argument) removes duplicate options by means of some internally convenient sorting. You should simply not define the same option twice.
command' :: Name -> Q Exp Source #
Define an exported command. This function works exactly like command
, but
it generates the command name by converting the first letter to upper case.
data Synchronous Source #
This option detemines how neovim should behave when calling some functionality on a remote host.
Async | Call the functionality entirely for its side effects and do not wait for it to finish. Calling a functionality with this flag set is completely asynchronous and nothing is really expected to happen. This is why a call like this is called notification on the neovim side of things. |
Sync | Call the function and wait for its result. This is only synchronous on the neovim side. This means that the GUI will (probably) not allow any user input until a reult is received. |
data CommandOption Source #
Options for commands.
Some command can also be described by using the OverloadedString extensions.
This means that you can write a literal String
inside your source file in
place for a CommandOption
value. See the documentation for each value on
how these strings should look like (Both versions are compile time checked.)
CmdSync Synchronous | Stringliteral "sync" or "async" |
CmdRegister | Register passed to the command. Stringliteral: |
CmdRange RangeSpecification | Determines how neovim passes the range. Stringliterals: "%" for |
CmdCount Word | Command handles a count. The argument defines the default count. Stringliteral: string of numbers (e.g. "132") |
CmdBang | Command handles a bang Stringliteral: "!" |
data RangeSpecification Source #
Specification of a range that acommand can operate on.
CurrentLine | The line the cursor is at when the command is invoked. |
WholeFile | Let the command operate on every line of the file. |
RangeCount Int | Let the command operate on each line in the given range. |
data CommandArguments Source #
You can use this type as the first argument for a function which is intended to be exported as a command. It holds information about the special attributes a command can take.
CommandArguments | |
|
data AutocmdOptions Source #
Options that can be used to register an autocmd. See :h :autocmd
or any
referenced neovim help-page from the fields of this data type.
AutocmdOptions | |
|
:: ByteString | The event to register to (e.g. BufWritePost) |
-> AutocmdOptions | |
-> Neovim r st () | Fully applied function to register |
-> Neovim r st (Maybe (Either (Neovim anyR anySt ()) ReleaseKey)) | A |
Register an autocmd in the current context. This means that, if you are
currently in a stateful plugin, the function will be called in the current
thread and has access to the configuration and state of this thread. If you
need that information, but do not want to block the other functions in this
thread, you have to manually fork a thread and make the state you need
available there. If you don't care abou the state (or your function has been
appield to all the necessary state (e.g. a TVar
to share the rusult), then
you can also call addAutocmd'
which will register a stateless function that
only interacts with other threads by means of concurrency abstractions.
Note that the function you pass must be fully applied.
Note beside: This function is equivalent to addAutocmd'
if called from a
stateless plugin thread.
addAutocmd' :: ByteString -> AutocmdOptions -> Neovim' () -> Neovim r st (Maybe ReleaseKey) Source #
Add a stateless autocmd.
See addAutocmd
for more details.
ask :: MonadReader r m => m r #
Retrieves the monad environment.
:: MonadReader r m | |
=> (r -> a) | The selector function to apply to the environment. |
-> m a |
Retrieves a function of the current environment.
put :: MonadState s m => s -> m () #
Replace the state inside the monad.
get :: MonadState s m => m s #
Return the state from the internals of the monad.
gets :: MonadState s m => (s -> a) -> m a #
Gets specific component of the state, using a projection function supplied.
modify :: MonadState s m => (s -> s) -> m () #
Monadic state transformer.
Maps an old state to a new state inside a state monad. The old state is thrown away.
Main> :t modify ((+1) :: Int -> Int) modify (...) :: (MonadState Int a) => a ()
This says that modify (+1)
acts over any
Monad that is a member of the MonadState
class,
with an Int
state.
Creating a stateful plugin
Now that we are a little bit comfortable with the interface provided by nvim-hs, we can start to write a more complicated plugin. Let's create a random number generator!
File ~/.config/nvim/lib/Random/Plugin.hs
:
module Random.Plugin (nextRandom, setNextRandom) where import Neovim -- | Neovim isn't so good with big numbers here either. nextRandom ::Neovim
r [Int16
]Int16
nextRandom = do r <-gets
head
-- get the head of the infinite random number listmodify
tail
-- set the list to its tailreturn
r setNextRandom ::Int16
->Neovim
r [Int16
] () setNextRandom n =modify
(n:) -- cons to the front of the infinite list
File ~/.config/nvim/lib/Random.hs
:
{-# LANGUAGE TemplateHaskell #-} module Random (plugin) where import Neovim import Random.Plugin (nextRandom, setNextRandom) import System.Random (newStdGen
,randoms
) plugin ::Neovim
(StartupConfig
NeovimConfig
) ()NeovimPlugin
plugin = do g <-liftIO
newStdGen
-- initialize with a random seed let randomNumbers =randoms
g -- an infinite list of random numberswrapPlugin
Plugin
{exports
= [] ,statefulExports
= [StatefulFunctionality
{ readOnly = () , writable = randomNumbers , functionalities = [ $(function'
'nextRandom)Sync
, $(function
"SetNextRandom" 'setNextRandom)Async
] }] }
File ~/.config/nvim/nvim.hs
:
import Neovim import qualified Fibonacci as Fibonacci import qualified Random as Random main ::IO
() main =neovim
defaultConfig
{plugins
=plugins
defaultConfig
++ [ Fibonacci.plugin, Random.plugin ] }
That wasn't too hard, was it? The definition is very similar to the previous
example, we just were able to mutate our state and share that with other
functions. The only slightly tedious thing was to define the statefulExports
field because it is a list of triples which has a list of exported
functionalities as its third argument. Another noteworthy detail, in case you
are not familiar with it, is the use of liftIO
in front of newStdGen
. You
have to do this, because newStdGen
has type
but the actions
inside the startup code are of type
IO
StdGen
. Neovim
(StartupConfig
NeovimConfig
) () somethingliftIO
lifts an
IO
function so that it can be run inside the Neovim
context (or more
generally, any monad that implements the MonadIO
type class).
After you have saved these files (and removed any typos :-)
), you can restart
nvim-hs with :RestartNvimhs
and insert random numbers in your text files!
<C-r>=NextRandom()
You can also cheat and pretend you know the next number:
:call SetNextRandom(42)
Calling remote functions
Calling remote functions is only possible inside a Neovim
context. There are
a few patterns of return values for the available functions. Let's start with
getting some abstract Buffer
object, test whether it is valid and then try
to rename it.
inspectBuffer ::Neovim
r st () inspectBuffer = do cb <-vim_get_current_buffer
isValid <-buffer_is_valid
cb when isValid $ do let newName = "magic" retval <-wait'
$buffer_set_name
cb newName case retval of Right cbName | cbName == newName ->return
() Right _ ->err
$ "Renaming the current buffer failed!" Left e ->err
$show
e
You may have noticed the wait'
function in there. Some functions have a return
type with STM
in it. This means that the function call is asynchronous. We can
wait
(or wait'
) for the result at the point at which we actually need it. In
this short example, we put the wait'
directly in front of the remote function
call because we want to inspect the result immediately, though. The other
functions either returned a result directly or they returned
whose result we inspected ourselves. The Either
Object
somethingerr
function directly terminates the current thread and sends the given error
message to neovim which the user immediately notices. Since it is not unusual to
not know what to do if the remote function call failed, the functions waitErr
and waitErr'
can save you from some typing and deeply nested case expressions.
That's pretty much all there is to it.
wait :: Neovim r st (STM result) -> Neovim r st result Source #
Wait for the result of the STM action.
This action possibly blocks as it is an alias for
ioSTM -> ioSTM >>= liftIO . atomically
.
wait' :: Neovim r st (STM result) -> Neovim r st () Source #
Variant of wait
that discards the result.
waitErr' :: Pretty e => String -> Neovim r st (STM (Either e result)) -> Neovim r st () Source #
waitErr
that discards the result.
The abstract data type Doc
represents pretty documents.
Doc
is an instance of the Show
class. (show doc)
pretty
prints document doc
with a page width of 100 characters and a
ribbon width of 40 characters.
show (text "hello" <$> text "world")
Which would return the string "hello\nworld", i.e.
hello world
The member prettyList
is only used to define the instance Pretty
a => Pretty [a]
. In normal circumstances only the pretty
function
is used.
Pretty Bool | |
Pretty Char | |
Pretty Double | |
Pretty Float | |
Pretty Int | |
Pretty Integer | |
Pretty () | |
Pretty Doc | |
Pretty NeovimException # | |
Pretty AutocmdOptions # | |
Pretty CommandArguments # | |
Pretty RangeSpecification # | |
Pretty CommandOptions # | |
Pretty CommandOption # | |
Pretty Synchronous # | |
Pretty FunctionalityDescription # | |
Pretty FunctionName # | |
Pretty Notification # | |
Pretty Request # | |
Pretty FunctionCall # | |
Pretty FunctionType # | |
Pretty Message # | |
Pretty a => Pretty [a] | |
Pretty a => Pretty (Maybe a) | |
(Pretty a, Pretty b) => Pretty (a, b) | |
(Pretty a, Pretty b, Pretty c) => Pretty (a, b, c) | |
errOnInvalidResult :: NvimObject o => Neovim r st (Either NeovimException Object) -> Neovim r st o Source #
The document (text s)
contains the literal string s
. The
string shouldn't contain any newline ('\n'
) characters. If the
string contains newline characters, the function string
should be
used.
data NeovimException Source #
Exceptions specific to nvim-hs.
ErrorMessage Doc | Simply error message that is passed to neovim. It should currently only contain one line of text. |
Generated functions for neovim interaction
module Neovim.API.String
Unsorted exports
withCustomEnvironment :: (MonadMask io, MonadIO io) => [(String, Maybe String)] -> io a -> io a Source #
Execute the given action with a changed set of environment variables and restore the original state of the environment afterwards.
docToObject :: Doc -> Object Source #
docFromObject :: Object -> Either Doc Doc Source #
See docToObject
.
Priorities are used to define how important a log message is. Users can filter log messages based on priorities.
These have their roots on the traditional syslog system. The standard definitions are given below, but you are free to interpret them however you like. They are listed here in ascending importance order.
module Control.Monad
module Control.Applicative
module Data.Monoid
module Data.Int
module Data.Word