    {-# LANGUAGE OverloadedStrings #-}
    {-|
    Module      : HsLua.Packaging.Documentation
    Copyright   : © 2020-2026 Albert Krewinkel
    License     : MIT
    Maintainer  : Albert Krewinkel <tarleb@hslua.org>

    Provides a function to print documentation if available.
    -}
    module HsLua.Packaging.Documentation
      ( -- * Setting and retrieving documentation
        getdocumentation
      , registerDocumentation
      , docsField
        -- * Documentation Types
      , ModuleDoc (..)
      , FunctionDoc (..)
      , DocumentationObject (..)
      , pushDocumentationObject
      , peekDocumentationObject
      , pushModuleDoc
      , peekModuleDoc
      , pushFunctionDoc
      , peekFunctionDoc
      , pushTypeDoc
      , peekTypeDoc
      -- * Creating documentation values
      , generateFunctionDocumentation
      , generateModuleDocumentation
      , generateTypeDocumentation
      ) where

    import Data.Version (showVersion)
    import HsLua.Core as Lua
    import HsLua.Marshalling
    import HsLua.ObjectOrientation (UDTypeGeneric (..))
    import HsLua.Packaging.Types
    import HsLua.Typing (pushTypeSpec)
    import qualified Data.Map.Strict as Map
    import qualified Data.Text as T
    import qualified HsLua.Core.Utf8 as Utf8

    -- | Pushes the documentation for the element at the given stack index.
    -- Returns the type of the documentation object.
    getdocumentation :: LuaError e => StackIndex -> LuaE e Lua.Type
    getdocumentation :: forall e. LuaError e => StackIndex -> LuaE e Type
    getdocumentation StackIndex
    idx = do
      idx' <- StackIndex -> LuaE e StackIndex
    forall e. StackIndex -> LuaE e StackIndex
    absindex StackIndex
    idx
      pushDocumentationTable
      pushvalue idx'
      rawget (nth 2) <* Lua.remove (nth 2)  -- remove documentation table

    -- | Registers the object at the top of the stack as documentation for
    -- the object at index @idx@. Pops the documentation of the stack.
    registerDocumentation :: LuaError e
                          => StackIndex  -- ^ @idx@
                          -> LuaE e ()
    registerDocumentation :: forall e. LuaError e => StackIndex -> LuaE e ()
    registerDocumentation StackIndex
    idx = do
      Int -> String -> LuaE e ()
    forall e. LuaError e => Int -> String -> LuaE e ()
    checkstack' Int
    10 String
    "registerDocumentation"  -- keep some buffer
      idx' <- StackIndex -> LuaE e StackIndex
    forall e. StackIndex -> LuaE e StackIndex
    absindex StackIndex
    idx
      pushDocumentationTable
      pushvalue idx'    -- the documented object
      pushvalue (nth 3) -- documentation object
      rawset (nth 3)    -- add to docs table
      pop 2             -- docs table and documentation object

    -- | Name of the registry field holding the documentation table. The
    -- documentation table is indexed by the documented objects, like module
    -- tables and functions, and contains documentation objects as values.
    --
    -- The table is an ephemeron table, i.e., an entry gets garbage
    -- collected if the key is no longer reachable.
    docsField :: Name
    docsField :: Name
    docsField = Name
    "HsLua docs"

    -- | Pushes the documentation table that's stored in the registry to the
    -- top of the stack, creating it if necessary. The documentation table
    -- is indexed by the documented objects, like module tables and
    -- functions, and contains documentation strings as values.
    --
    -- The table is an ephemeron table, i.e., an entry gets garbage
    -- collected if the key is no longer reachable.
    pushDocumentationTable :: LuaError e => LuaE e ()
    pushDocumentationTable :: forall e. LuaError e => LuaE e ()
    pushDocumentationTable = StackIndex -> Name -> LuaE e Type
    forall e. LuaError e => StackIndex -> Name -> LuaE e Type
    Lua.getfield StackIndex
    registryindex Name
    docsField LuaE e Type -> (Type -> LuaE e ()) -> LuaE e ()
    forall a b. LuaE e a -> (a -> LuaE e b) -> LuaE e b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \case
      Type
    Lua.TypeTable -> () -> LuaE e ()
    forall a. a -> LuaE e a
    forall (m :: * -> *) a. Monad m => a -> m a
    return () -- documentation table already initialized
      Type
    _ -> do
        Int -> LuaE e ()
    forall e. Int -> LuaE e ()
    pop Int
    1            -- pop non-table value
        LuaE e ()
    forall e. LuaE e ()
    newtable         -- create documentation table
        ByteString -> LuaE e ()
    forall e. ByteString -> LuaE e ()
    pushstring ByteString
    "k"   -- Make it an "ephemeron table" and..
        StackIndex -> Name -> LuaE e ()
    forall e. LuaError e => StackIndex -> Name -> LuaE e ()
    setfield (CInt -> StackIndex
    nth CInt
    2) Name
    "__mode"  -- collect docs if documented object is GCed
        StackIndex -> LuaE e ()
    forall e. StackIndex -> LuaE e ()
    pushvalue StackIndex
    top    -- add copy of table to registry
        StackIndex -> Name -> LuaE e ()
    forall e. LuaError e => StackIndex -> Name -> LuaE e ()
    setfield StackIndex
    registryindex Name
    docsField

    --
    -- Generating
    --

    -- | Generate documentation for a module.
    generateModuleDocumentation :: Module e -> ModuleDoc
    generateModuleDocumentation :: forall e. Module e -> ModuleDoc
    generateModuleDocumentation Module e
    mdl =
      let name :: Name
    name = Module e -> Name
    forall e. Module e -> Name
    moduleName Module e
    mdl
      in ModuleDoc
        { moduleDocName :: Text
    moduleDocName = Name -> Text
    nameToText Name
    name
        , moduleDocDescription :: Text
    moduleDocDescription = Module e -> Text
    forall e. Module e -> Text
    moduleDescription Module e
    mdl
        , moduleDocFields :: [FieldDoc]
    moduleDocFields = (Field e -> FieldDoc) -> [Field e] -> [FieldDoc]
    forall a b. (a -> b) -> [a] -> [b]
    map (Name -> Field e -> FieldDoc
    forall e. Name -> Field e -> FieldDoc
    generateFieldDocumentation Name
    name) ([Field e] -> [FieldDoc]) -> [Field e] -> [FieldDoc]
    forall a b. (a -> b) -> a -> b
    $ Module e -> [Field e]
    forall e. Module e -> [Field e]
    moduleFields Module e
    mdl
        , moduleDocFunctions :: [FunctionDoc]
    moduleDocFunctions = (DocumentedFunction e -> FunctionDoc)
    -> [DocumentedFunction e] -> [FunctionDoc]
    forall a b. (a -> b) -> [a] -> [b]
    map (Maybe Name -> DocumentedFunction e -> FunctionDoc
    forall e. Maybe Name -> DocumentedFunction e -> FunctionDoc
    generateFunctionDocumentation Maybe Name
    forall a. Maybe a
    Nothing) ([DocumentedFunction e] -> [FunctionDoc])
    -> [DocumentedFunction e] -> [FunctionDoc]
    forall a b. (a -> b) -> a -> b
    $
                                   Module e -> [DocumentedFunction e]
    forall e. Module e -> [DocumentedFunction e]
    moduleFunctions Module e
    mdl
        , moduleDocTypes :: [TypeDoc]
    moduleDocTypes = Module e -> [TypeDoc]
    forall e. Module e -> [TypeDoc]
    moduleTypeDocs Module e
    mdl
        }

    -- | Generate 'FieldDoc' documentation for a module field.
    generateFieldDocumentation :: Name     -- ^ module name
                               -> Field e  -- ^ field that's part of the module
                               -> FieldDoc
    generateFieldDocumentation :: forall e. Name -> Field e -> FieldDoc
    generateFieldDocumentation Name
    mdlName Field e
    fld =
      let doc :: FieldDoc
    doc = Field e -> FieldDoc
    forall e. Field e -> FieldDoc
    fieldDoc Field e
    fld
      in FieldDoc
    doc { fieldDocName = nameToText mdlName <> "." <> fieldDocName doc }

    -- | Generate 'FunctionDoc' documentation for module functions.
    generateFunctionDocumentation :: Maybe Name
                                  -> DocumentedFunction e
                                  -> FunctionDoc
    generateFunctionDocumentation :: forall e. Maybe Name -> DocumentedFunction e -> FunctionDoc
    generateFunctionDocumentation Maybe Name
    name DocumentedFunction e
    fn =
      let doc :: FunctionDoc
    doc = DocumentedFunction e -> FunctionDoc
    forall e. DocumentedFunction e -> FunctionDoc
    functionDoc DocumentedFunction e
    fn
          prefix :: Text
    prefix = Text -> (Name -> Text) -> Maybe Name -> Text
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe Text
    forall a. Monoid a => a
    mempty (\Name
    n -> Name -> Text
    nameToText Name
    n Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ".") Maybe Name
    name
      in FunctionDoc
    doc { funDocName = prefix <> funDocName doc }

    -- | Generate documentation for a 'UDType'.
    generateTypeDocumentation :: DocumentedType e a -> TypeDoc
    generateTypeDocumentation :: forall e a. DocumentedType e a -> TypeDoc
    generateTypeDocumentation DocumentedType e a
    ty =
      let name :: Name
    name = DocumentedType e a -> Name
    forall e fn a. UDTypeGeneric e fn a -> Name
    udName DocumentedType e a
    ty
      in TypeDoc
      { typeDocName :: Text
    typeDocName = Name -> Text
    nameToText Name
    name
      , typeDocDescription :: Text
    typeDocDescription = Text
    ""
      , typeDocOperations :: [(Operation, FunctionDoc)]
    typeDocOperations = []
      , typeDocMethods :: [FunctionDoc]
    typeDocMethods = ((Name, DocumentedFunction e) -> FunctionDoc)
    -> [(Name, DocumentedFunction e)] -> [FunctionDoc]
    forall a b. (a -> b) -> [a] -> [b]
    map (Maybe Name -> DocumentedFunction e -> FunctionDoc
    forall e. Maybe Name -> DocumentedFunction e -> FunctionDoc
    generateFunctionDocumentation (Name -> Maybe Name
    forall a. a -> Maybe a
    Just Name
    name) (DocumentedFunction e -> FunctionDoc)
    -> ((Name, DocumentedFunction e) -> DocumentedFunction e)
    -> (Name, DocumentedFunction e)
    -> FunctionDoc
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Name, DocumentedFunction e) -> DocumentedFunction e
    forall a b. (a, b) -> b
    snd) ([(Name, DocumentedFunction e)] -> [FunctionDoc])
    -> [(Name, DocumentedFunction e)] -> [FunctionDoc]
    forall a b. (a -> b) -> a -> b
    $
                             Map Name (DocumentedFunction e) -> [(Name, DocumentedFunction e)]
    forall k a. Map k a -> [(k, a)]
    Map.toList (DocumentedType e a -> Map Name (DocumentedFunction e)
    forall e fn a. UDTypeGeneric e fn a -> Map Name fn
    udMethods DocumentedType e a
    ty)
      }

    -- | Convert a Lua name to UTF-8 text.
    nameToText :: Name -> T.Text
    nameToText :: Name -> Text
    nameToText = ByteString -> Text
    Utf8.toText (ByteString -> Text) -> (Name -> ByteString) -> Name -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Name -> ByteString
    fromName

    --
    -- Retrieving and pushing documentation
    --

    -- | The metatable name of documentation objecs
    documentationObjectName :: Name
    documentationObjectName :: Name
    documentationObjectName = Name
    "HsLua DocumentationObject"

    -- | Pushes the metatable for documentation objects.
    peekDocumentationObject :: Peeker e DocumentationObject
    peekDocumentationObject :: forall e. Peeker e DocumentationObject
    peekDocumentationObject StackIndex
    idx = do
      LuaE e (Maybe DocumentationObject)
    -> Peek e (Maybe DocumentationObject)
    forall e a. LuaE e a -> Peek e a
    liftLua (StackIndex -> Name -> LuaE e (Maybe DocumentationObject)
    forall a e. StackIndex -> Name -> LuaE e (Maybe a)
    fromuserdata StackIndex
    idx Name
    documentationObjectName) Peek e (Maybe DocumentationObject)
    -> (Maybe DocumentationObject -> Peek e DocumentationObject)
    -> Peek e DocumentationObject
    forall a b. Peek e a -> (a -> Peek e b) -> Peek e b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \case
        Maybe DocumentationObject
    Nothing  -> ByteString -> Peek e DocumentationObject
    forall a e. ByteString -> Peek e a
    failPeek ByteString
    "Not a documentation object"
        Just DocumentationObject
    doc -> DocumentationObject -> Peek e DocumentationObject
    forall a. a -> Peek e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure DocumentationObject
    doc

    -- | Pushes a 'DocumentationObject' to the Lua stack.
    pushDocumentationObject :: LuaError e => Pusher e DocumentationObject
    pushDocumentationObject :: forall e. LuaError e => Pusher e DocumentationObject
    pushDocumentationObject DocumentationObject
    obj = do
      DocumentationObject -> Int -> LuaE e ()
    forall a e. a -> Int -> LuaE e ()
    newhsuserdatauv DocumentationObject
    obj Int
    0
      LuaE e ()
    forall e. LuaError e => LuaE e ()
    pushDocumentationObjectMT
      StackIndex -> LuaE e ()
    forall e. StackIndex -> LuaE e ()
    setmetatable (CInt -> StackIndex
    nth CInt
    2)

    -- | Pushes the metatable for documentation objects.
    pushDocumentationObjectMT :: LuaError e => LuaE e ()
    pushDocumentationObjectMT :: forall e. LuaError e => LuaE e ()
    pushDocumentationObjectMT = Name -> LuaE e Bool
    forall e. Name -> LuaE e Bool
    newudmetatable Name
    documentationObjectName LuaE e Bool -> (Bool -> LuaE e ()) -> LuaE e ()
    forall a b. LuaE e a -> (a -> LuaE e b) -> LuaE e b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \case
      Bool
    False -> () -> LuaE e ()
    forall a. a -> LuaE e a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
      Bool
    True -> do -- newly created metatable at the top of the stack
        -- Allow to "call" the documentation object, in which case it should
        -- return a Lua table that has all the relevant info.
        HaskellFunction e -> LuaE e ()
    forall e. LuaError e => HaskellFunction e -> LuaE e ()
    pushHaskellFunction (HaskellFunction e -> LuaE e ()) -> HaskellFunction e -> LuaE e ()
    forall a b. (a -> b) -> a -> b
    $ do
          -- object is the first argument
          Peek e DocumentationObject -> LuaE e DocumentationObject
    forall e a. LuaError e => Peek e a -> LuaE e a
    forcePeek (Peeker e DocumentationObject
    forall e. Peeker e DocumentationObject
    peekDocumentationObject (CInt -> StackIndex
    nthBottom CInt
    1)) LuaE e DocumentationObject
    -> (DocumentationObject -> LuaE e ()) -> LuaE e ()
    forall a b. LuaE e a -> (a -> LuaE e b) -> LuaE e b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \case
            DocObjectFunction FunctionDoc
    fn -> Pusher e FunctionDoc
    forall e. LuaError e => Pusher e FunctionDoc
    pushFunctionDocAsTable FunctionDoc
    fn
            DocObjectModule ModuleDoc
    mdl  -> Pusher e ModuleDoc
    forall e. LuaError e => Pusher e ModuleDoc
    pushModuleDocAsTable ModuleDoc
    mdl
            DocObjectType TypeDoc
    ty     -> Pusher e TypeDoc
    forall e. LuaError e => Pusher e TypeDoc
    pushTypeDocAsTable TypeDoc
    ty
          NumResults -> HaskellFunction e
    forall a. a -> LuaE e a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (CInt -> NumResults
    NumResults CInt
    1)
        StackIndex -> Name -> LuaE e ()
    forall e. LuaError e => StackIndex -> Name -> LuaE e ()
    setfield (CInt -> StackIndex
    nth CInt
    2) Name
    "__call"

    -- | Pushes the documentation of a module as userdata.
    pushModuleDoc :: LuaError e => Pusher e ModuleDoc
    pushModuleDoc :: forall e. LuaError e => Pusher e ModuleDoc
    pushModuleDoc = Pusher e DocumentationObject
    forall e. LuaError e => Pusher e DocumentationObject
    pushDocumentationObject Pusher e DocumentationObject
    -> (ModuleDoc -> DocumentationObject) -> ModuleDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ModuleDoc -> DocumentationObject
    DocObjectModule

    -- | Retrieves a module documentation object from the Lua stack.
    peekModuleDoc :: Peeker e ModuleDoc
    peekModuleDoc :: forall e. Peeker e ModuleDoc
    peekModuleDoc StackIndex
    idx = Peeker e DocumentationObject
    forall e. Peeker e DocumentationObject
    peekDocumentationObject StackIndex
    idx Peek e DocumentationObject
    -> (DocumentationObject -> Peek e ModuleDoc) -> Peek e ModuleDoc
    forall a b. Peek e a -> (a -> Peek e b) -> Peek e b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \case
      DocObjectModule ModuleDoc
    mdldoc -> ModuleDoc -> Peek e ModuleDoc
    forall a. a -> Peek e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure ModuleDoc
    mdldoc
      DocumentationObject
    _ -> ByteString -> Peek e ModuleDoc
    forall a e. ByteString -> Peek e a
    failPeek ByteString
    "Not a module documentation object"

    -- | Pushes function documentation as userdata.
    pushFunctionDoc :: LuaError e => Pusher e FunctionDoc
    pushFunctionDoc :: forall e. LuaError e => Pusher e FunctionDoc
    pushFunctionDoc = Pusher e DocumentationObject
    forall e. LuaError e => Pusher e DocumentationObject
    pushDocumentationObject Pusher e DocumentationObject
    -> (FunctionDoc -> DocumentationObject) -> FunctionDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FunctionDoc -> DocumentationObject
    DocObjectFunction

    -- | Retrieve function documentation from the Lua stack.
    peekFunctionDoc :: Peeker e FunctionDoc
    peekFunctionDoc :: forall e. Peeker e FunctionDoc
    peekFunctionDoc StackIndex
    idx = Peeker e DocumentationObject
    forall e. Peeker e DocumentationObject
    peekDocumentationObject StackIndex
    idx Peek e DocumentationObject
    -> (DocumentationObject -> Peek e FunctionDoc)
    -> Peek e FunctionDoc
    forall a b. Peek e a -> (a -> Peek e b) -> Peek e b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \case
      DocObjectFunction FunctionDoc
    fndoc -> FunctionDoc -> Peek e FunctionDoc
    forall a. a -> Peek e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure FunctionDoc
    fndoc
      DocumentationObject
    _ -> ByteString -> Peek e FunctionDoc
    forall a e. ByteString -> Peek e a
    failPeek ByteString
    "Not a function documentation"

    -- | Pushes documentation type documentation as userdata.
    pushTypeDoc :: LuaError e => Pusher e FunctionDoc
    pushTypeDoc :: forall e. LuaError e => Pusher e FunctionDoc
    pushTypeDoc = Pusher e DocumentationObject
    forall e. LuaError e => Pusher e DocumentationObject
    pushDocumentationObject Pusher e DocumentationObject
    -> (FunctionDoc -> DocumentationObject) -> FunctionDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FunctionDoc -> DocumentationObject
    DocObjectFunction

    -- | Retrieve function documentation from the Lua stack.
    peekTypeDoc :: Peeker e TypeDoc
    peekTypeDoc :: forall e. Peeker e TypeDoc
    peekTypeDoc StackIndex
    idx = Peeker e DocumentationObject
    forall e. Peeker e DocumentationObject
    peekDocumentationObject StackIndex
    idx Peek e DocumentationObject
    -> (DocumentationObject -> Peek e TypeDoc) -> Peek e TypeDoc
    forall a b. Peek e a -> (a -> Peek e b) -> Peek e b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \case
      DocObjectType TypeDoc
    tydoc -> TypeDoc -> Peek e TypeDoc
    forall a. a -> Peek e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure TypeDoc
    tydoc
      DocumentationObject
    _ -> ByteString -> Peek e TypeDoc
    forall a e. ByteString -> Peek e a
    failPeek ByteString
    "Not a type documentation"

    -- | Pushes the documentation of a module as a table with string fields
    -- @name@ and @description@.
    pushModuleDocAsTable :: LuaError e => Pusher e ModuleDoc
    pushModuleDocAsTable :: forall e. LuaError e => Pusher e ModuleDoc
    pushModuleDocAsTable = [(Name, ModuleDoc -> LuaE e ())] -> ModuleDoc -> LuaE e ()
    forall e a.
    LuaError e =>
    [(Name, a -> LuaE e ())] -> a -> LuaE e ()
    pushAsTable
      [ (Name
    "name", Pusher e Text
    forall e. Pusher e Text
    pushText Pusher e Text -> (ModuleDoc -> Text) -> ModuleDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ModuleDoc -> Text
    moduleDocName)
      , (Name
    "description", Pusher e Text
    forall e. Pusher e Text
    pushText Pusher e Text -> (ModuleDoc -> Text) -> ModuleDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ModuleDoc -> Text
    moduleDocDescription)
      , (Name
    "fields", Pusher e FieldDoc -> [FieldDoc] -> LuaE e ()
    forall e a. LuaError e => Pusher e a -> [a] -> LuaE e ()
    pushList Pusher e FieldDoc
    forall e. LuaError e => Pusher e FieldDoc
    pushFieldDocAsTable ([FieldDoc] -> LuaE e ())
    -> (ModuleDoc -> [FieldDoc]) -> ModuleDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ModuleDoc -> [FieldDoc]
    moduleDocFields)
      , (Name
    "functions", Pusher e FunctionDoc -> [FunctionDoc] -> LuaE e ()
    forall e a. LuaError e => Pusher e a -> [a] -> LuaE e ()
    pushList Pusher e FunctionDoc
    forall e. LuaError e => Pusher e FunctionDoc
    pushFunctionDocAsTable ([FunctionDoc] -> LuaE e ())
    -> (ModuleDoc -> [FunctionDoc]) -> ModuleDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ModuleDoc -> [FunctionDoc]
    moduleDocFunctions)
      , (Name
    "types", Pusher e TypeDoc -> [TypeDoc] -> LuaE e ()
    forall e a. LuaError e => Pusher e a -> [a] -> LuaE e ()
    pushList Pusher e TypeDoc
    forall e. LuaError e => Pusher e TypeDoc
    pushTypeDocAsTable ([TypeDoc] -> LuaE e ())
    -> (ModuleDoc -> [TypeDoc]) -> ModuleDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ModuleDoc -> [TypeDoc]
    moduleDocTypes)
      ]

    -- | Pushes the documentation of a field as a table with string fields
    -- @name@ and @description@.
    pushFieldDocAsTable :: LuaError e => Pusher e FieldDoc
    pushFieldDocAsTable :: forall e. LuaError e => Pusher e FieldDoc
    pushFieldDocAsTable = [(Name, FieldDoc -> LuaE e ())] -> FieldDoc -> LuaE e ()
    forall e a.
    LuaError e =>
    [(Name, a -> LuaE e ())] -> a -> LuaE e ()
    pushAsTable
      [ (Name
    "name", Pusher e Text
    forall e. Pusher e Text
    pushText Pusher e Text -> (FieldDoc -> Text) -> FieldDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FieldDoc -> Text
    fieldDocName)
      , (Name
    "type", TypeSpec -> LuaE e ()
    forall e. LuaError e => TypeSpec -> LuaE e ()
    pushTypeSpec (TypeSpec -> LuaE e ())
    -> (FieldDoc -> TypeSpec) -> FieldDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FieldDoc -> TypeSpec
    fieldDocType)
      , (Name
    "description", Pusher e Text
    forall e. Pusher e Text
    pushText Pusher e Text -> (FieldDoc -> Text) -> FieldDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FieldDoc -> Text
    fieldDocDescription)
      ]

    -- | Pushes the documentation of a function as a table with string
    -- fields, @name@, @description@, and @since@, sequence field
    -- @parameters@, and sequence or string field @results@.
    pushFunctionDocAsTable :: LuaError e => Pusher e FunctionDoc
    pushFunctionDocAsTable :: forall e. LuaError e => Pusher e FunctionDoc
    pushFunctionDocAsTable = [(Name, FunctionDoc -> LuaE e ())] -> FunctionDoc -> LuaE e ()
    forall e a.
    LuaError e =>
    [(Name, a -> LuaE e ())] -> a -> LuaE e ()
    pushAsTable
      [ (Name
    "name", Pusher e Text
    forall e. Pusher e Text
    pushText Pusher e Text -> (FunctionDoc -> Text) -> FunctionDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FunctionDoc -> Text
    funDocName)
      , (Name
    "description", Pusher e Text
    forall e. Pusher e Text
    pushText Pusher e Text -> (FunctionDoc -> Text) -> FunctionDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FunctionDoc -> Text
    funDocDescription)
      , (Name
    "parameters", Pusher e ParameterDoc -> [ParameterDoc] -> LuaE e ()
    forall e a. LuaError e => Pusher e a -> [a] -> LuaE e ()
    pushList Pusher e ParameterDoc
    forall e. LuaError e => Pusher e ParameterDoc
    pushParameterDocAsTable ([ParameterDoc] -> LuaE e ())
    -> (FunctionDoc -> [ParameterDoc]) -> FunctionDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FunctionDoc -> [ParameterDoc]
    funDocParameters)
      , (Name
    "results", Pusher e ResultsDoc
    forall e. LuaError e => Pusher e ResultsDoc
    pushResultsDoc Pusher e ResultsDoc
    -> (FunctionDoc -> ResultsDoc) -> FunctionDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FunctionDoc -> ResultsDoc
    funDocResults)
      , (Name
    "since", LuaE e () -> (Version -> LuaE e ()) -> Maybe Version -> LuaE e ()
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe LuaE e ()
    forall e. LuaE e ()
    pushnil (String -> LuaE e ()
    forall e. String -> LuaE e ()
    pushString (String -> LuaE e ())
    -> (Version -> String) -> Version -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Version -> String
    showVersion) (Maybe Version -> LuaE e ())
    -> (FunctionDoc -> Maybe Version) -> FunctionDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FunctionDoc -> Maybe Version
    funDocSince)
      ]

    -- | Pushes the documentation of a parameter as a table with boolean
    -- field @optional@ and string fields @name@, @type@, and @description@.
    pushParameterDocAsTable :: LuaError e => Pusher e ParameterDoc
    pushParameterDocAsTable :: forall e. LuaError e => Pusher e ParameterDoc
    pushParameterDocAsTable = [(Name, ParameterDoc -> LuaE e ())] -> ParameterDoc -> LuaE e ()
    forall e a.
    LuaError e =>
    [(Name, a -> LuaE e ())] -> a -> LuaE e ()
    pushAsTable
      [ (Name
    "name", Pusher e Text
    forall e. Pusher e Text
    pushText Pusher e Text
    -> (ParameterDoc -> Text) -> ParameterDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ParameterDoc -> Text
    parameterName)
      , (Name
    "type", TypeSpec -> LuaE e ()
    forall e. LuaError e => TypeSpec -> LuaE e ()
    pushTypeSpec (TypeSpec -> LuaE e ())
    -> (ParameterDoc -> TypeSpec) -> ParameterDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ParameterDoc -> TypeSpec
    parameterType)
      , (Name
    "description", Pusher e Text
    forall e. Pusher e Text
    pushText Pusher e Text
    -> (ParameterDoc -> Text) -> ParameterDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ParameterDoc -> Text
    parameterDescription)
      , (Name
    "optional", Pusher e Bool
    forall e. Pusher e Bool
    pushBool Pusher e Bool
    -> (ParameterDoc -> Bool) -> ParameterDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ParameterDoc -> Bool
    parameterIsOptional)
      ]

    -- | Pushes a the documentation for a function's return values as either
    -- a simple string, or as a sequence of tables with @type@ and
    -- @description@ fields.
    pushResultsDoc :: LuaError e => Pusher e ResultsDoc
    pushResultsDoc :: forall e. LuaError e => Pusher e ResultsDoc
    pushResultsDoc = \case
      ResultsDocMult Text
    desc -> Pusher e Text
    forall e. Pusher e Text
    pushText Text
    desc
      ResultsDocList [ResultValueDoc]
    resultDocs -> Pusher e ResultValueDoc -> [ResultValueDoc] -> LuaE e ()
    forall e a. LuaError e => Pusher e a -> [a] -> LuaE e ()
    pushList Pusher e ResultValueDoc
    forall e. LuaError e => Pusher e ResultValueDoc
    pushResultValueDoc [ResultValueDoc]
    resultDocs

    -- | Pushes the documentation of a single result value as a table with
    -- fields @type@ and @description@.
    pushResultValueDoc :: LuaError e => Pusher e ResultValueDoc
    pushResultValueDoc :: forall e. LuaError e => Pusher e ResultValueDoc
    pushResultValueDoc = [(Name, ResultValueDoc -> LuaE e ())]
    -> ResultValueDoc -> LuaE e ()
    forall e a.
    LuaError e =>
    [(Name, a -> LuaE e ())] -> a -> LuaE e ()
    pushAsTable
      [ (Name
    "type", TypeSpec -> LuaE e ()
    forall e. LuaError e => TypeSpec -> LuaE e ()
    pushTypeSpec (TypeSpec -> LuaE e ())
    -> (ResultValueDoc -> TypeSpec) -> ResultValueDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ResultValueDoc -> TypeSpec
    resultValueType)
      , (Name
    "description", Pusher e Text
    forall e. Pusher e Text
    pushText Pusher e Text
    -> (ResultValueDoc -> Text) -> ResultValueDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ResultValueDoc -> Text
    resultValueDescription)
      ]

    -- | Pushes the documentation of a UDType as a Lua table.
    pushTypeDocAsTable :: LuaError e => Pusher e TypeDoc
    pushTypeDocAsTable :: forall e. LuaError e => Pusher e TypeDoc
    pushTypeDocAsTable = [(Name, TypeDoc -> LuaE e ())] -> TypeDoc -> LuaE e ()
    forall e a.
    LuaError e =>
    [(Name, a -> LuaE e ())] -> a -> LuaE e ()
    pushAsTable
      [ (Name
    "name", Pusher e Text
    forall e. Pusher e Text
    pushText Pusher e Text -> (TypeDoc -> Text) -> TypeDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . TypeDoc -> Text
    typeDocName)
      , (Name
    "description", Pusher e Text
    forall e. Pusher e Text
    pushText Pusher e Text -> (TypeDoc -> Text) -> TypeDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . TypeDoc -> Text
    typeDocDescription)
      , (Name
    "methods", Pusher e FunctionDoc -> [FunctionDoc] -> LuaE e ()
    forall e a. LuaError e => Pusher e a -> [a] -> LuaE e ()
    pushList Pusher e FunctionDoc
    forall e. LuaError e => Pusher e FunctionDoc
    pushFunctionDoc ([FunctionDoc] -> LuaE e ())
    -> (TypeDoc -> [FunctionDoc]) -> TypeDoc -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . TypeDoc -> [FunctionDoc]
    typeDocMethods)
      ]