hslua-annotations (empty) → 0.1.0
raw patch · 10 files changed
+901/−0 lines, 10 filesdep +basedep +hslua-annotationsdep +hslua-core
Dependencies added: base, hslua-annotations, hslua-core, hslua-marshalling, hslua-module-path, hslua-module-version, hslua-packaging, tasty, tasty-golden, text
Files
- CHANGELOG.md +64/−0
- LICENSE +20/−0
- LICENSE_APACHE +176/−0
- README.md +69/−0
- hslua-annotations.cabal +84/−0
- lib/HsLua/Annotations.hs +44/−0
- lib/HsLua/Annotations/Internal.hs +157/−0
- lib/HsLua/Annotations/Markdown.hs +184/−0
- lib/HsLua/Annotations/Shared.hs +51/−0
- test/Main.hs +52/−0
+ CHANGELOG.md view
@@ -0,0 +1,64 @@+## 0.1.0+> 2026-04-05++## Summary++Initial release of `hslua-annotations`, a utility library to generate Lua type+annotations and Markdown documentation for [hslua](https://github.com/hslua/hslua)+modules and functions.++### Commits+- [[`1aa9062`](https://github.com/sondr3/hslua-annotations/commit/1aa9062)] Add Hackage version badge+- [[`2cff0c6`](https://github.com/sondr3/hslua-annotations/commit/2cff0c6)] Add publishing step to CI+- [[`5c2a75a`](https://github.com/sondr3/hslua-annotations/commit/5c2a75a)] Add category+- [[`fe00c06`](https://github.com/sondr3/hslua-annotations/commit/fe00c06)] Update docs+- [[`6b045ad`](https://github.com/sondr3/hslua-annotations/commit/6b045ad)] Remove redundant function calls+- [[`a6ede1e`](https://github.com/sondr3/hslua-annotations/commit/a6ede1e)] Change return type to table, move description and version down+- [[`ec3becb`](https://github.com/sondr3/hslua-annotations/commit/ec3becb)] Handle [] as having no return type+- [[`331f5f0`](https://github.com/sondr3/hslua-annotations/commit/331f5f0)] Remove untyped return types+- [[`6913e1d`](https://github.com/sondr3/hslua-annotations/commit/6913e1d)] Add ? for optional params in lua block+- [[`7e2f9db`](https://github.com/sondr3/hslua-annotations/commit/7e2f9db)] Extract return type and create typed lua block for functions+- [[`3e3215c`](https://github.com/sondr3/hslua-annotations/commit/3e3215c)] Indent params properly+- [[`2b21218`](https://github.com/sondr3/hslua-annotations/commit/2b21218)] Take inspiration from rustdoc+- [[`fb38417`](https://github.com/sondr3/hslua-annotations/commit/fb38417)] Render parameter docs as a table+- [[`0ab42c9`](https://github.com/sondr3/hslua-annotations/commit/0ab42c9)] Change function headings to match fields+- [[`f398aee`](https://github.com/sondr3/hslua-annotations/commit/f398aee)] Refactor to not use LambdaCase+- [[`7e495ca`](https://github.com/sondr3/hslua-annotations/commit/7e495ca)] Fix multiline function descriptions+- [[`e085ef9`](https://github.com/sondr3/hslua-annotations/commit/e085ef9)] Add type annotations to fields+- [[`f8f5819`](https://github.com/sondr3/hslua-annotations/commit/f8f5819)] Refactor field rendering some more+- [[`d9826e4`](https://github.com/sondr3/hslua-annotations/commit/d9826e4)] Create utility function for filtering unlines+- [[`5bdf26c`](https://github.com/sondr3/hslua-annotations/commit/5bdf26c)] Wrap module headings with backticks+- [[`b937e35`](https://github.com/sondr3/hslua-annotations/commit/b937e35)] Remove comments, refactor things slightly+- [[`2a8b825`](https://github.com/sondr3/hslua-annotations/commit/2a8b825)] Render class fields as a table+- [[`2a0aaac`](https://github.com/sondr3/hslua-annotations/commit/2a0aaac)] Extract 'typeToText' to shared module+- [[`47c463b`](https://github.com/sondr3/hslua-annotations/commit/47c463b)] Import old, deprecated render functionality, update to latest HsLua+- [[`ed7d2f8`](https://github.com/sondr3/hslua-annotations/commit/ed7d2f8)] Extract shared functions to shared module+- [[`004b1ed`](https://github.com/sondr3/hslua-annotations/commit/004b1ed)] Rejigger tests, add annotation and markdown test trees+- [[`9d4ed09`](https://github.com/sondr3/hslua-annotations/commit/9d4ed09)] Start sketching out markdown rendering+- [[`a8b0827`](https://github.com/sondr3/hslua-annotations/commit/a8b0827)] Split out the annotation into an internal file+- [[`a071088`](https://github.com/sondr3/hslua-annotations/commit/a071088)] Implement 'typeToText' to better align with annotations+- [[`4aa5f81`](https://github.com/sondr3/hslua-annotations/commit/4aa5f81)] Slacken text package requirements+- [[`34b76cb`](https://github.com/sondr3/hslua-annotations/commit/34b76cb)] Minor README adjustments+- [[`e7b2247`](https://github.com/sondr3/hslua-annotations/commit/e7b2247)] Rename annotateMethod... not really a thing in Lua+- [[`37901ee`](https://github.com/sondr3/hslua-annotations/commit/37901ee)] Filter out empty function descriptions+- [[`285b8ed`](https://github.com/sondr3/hslua-annotations/commit/285b8ed)] Add dependabot+- [[`d86ec4c`](https://github.com/sondr3/hslua-annotations/commit/d86ec4c)] Remove unused dependencies, move test only to test package+- [[`cb45364`](https://github.com/sondr3/hslua-annotations/commit/cb45364)] Add README and document things+- [[`605f044`](https://github.com/sondr3/hslua-annotations/commit/605f044)] Remove duplicate function annotation function+- [[`6099d8e`](https://github.com/sondr3/hslua-annotations/commit/6099d8e)] Filter out empty lines+- [[`dff4da6`](https://github.com/sondr3/hslua-annotations/commit/dff4da6)] And annotate operators+- [[`fbf3e27`](https://github.com/sondr3/hslua-annotations/commit/fbf3e27)] Use T.unlines to make multilines more readable+- [[`2a5523b`](https://github.com/sondr3/hslua-annotations/commit/2a5523b)] Annotate module functions+- [[`54843d0`](https://github.com/sondr3/hslua-annotations/commit/54843d0)] Stick with GHC 9.12 for now+- [[`5700b5e`](https://github.com/sondr3/hslua-annotations/commit/5700b5e)] Fix hlint suggestions+- [[`3c9fe23`](https://github.com/sondr3/hslua-annotations/commit/3c9fe23)] Start sketching out module annotation, test on hslua-module-path+- [[`bb48b5d`](https://github.com/sondr3/hslua-annotations/commit/bb48b5d)] Move factorial to test file+- [[`99e1e70`](https://github.com/sondr3/hslua-annotations/commit/99e1e70)] Add a Justfile for running things+- [[`84a1e1b`](https://github.com/sondr3/hslua-annotations/commit/84a1e1b)] Add parameter annotations to functions+- [[`d81deb6`](https://github.com/sondr3/hslua-annotations/commit/d81deb6)] Get the function description and output it too+- [[`f9f1de5`](https://github.com/sondr3/hslua-annotations/commit/f9f1de5)] Correct pipeline name+- [[`969bf50`](https://github.com/sondr3/hslua-annotations/commit/969bf50)] Add CI pipeline+- [[`34d0bce`](https://github.com/sondr3/hslua-annotations/commit/34d0bce)] Fix unused imports+- [[`1c1c666`](https://github.com/sondr3/hslua-annotations/commit/1c1c666)] Initial pass at getting types from HsLua+- [[`1dfaa20`](https://github.com/sondr3/hslua-annotations/commit/1dfaa20)] In the beginning there was darkness...+
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2026 Sondre Aasemoen++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ LICENSE_APACHE view
@@ -0,0 +1,176 @@+ Apache License+ Version 2.0, January 2004+ http://www.apache.org/licenses/++TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++1. Definitions.++ "License" shall mean the terms and conditions for use, reproduction,+ and distribution as defined by Sections 1 through 9 of this document.++ "Licensor" shall mean the copyright owner or entity authorized by+ the copyright owner that is granting the License.++ "Legal Entity" shall mean the union of the acting entity and all+ other entities that control, are controlled by, or are under common+ control with that entity. For the purposes of this definition,+ "control" means (i) the power, direct or indirect, to cause the+ direction or management of such entity, whether by contract or+ otherwise, or (ii) ownership of fifty percent (50%) or more of the+ outstanding shares, or (iii) beneficial ownership of such entity.++ "You" (or "Your") shall mean an individual or Legal Entity+ exercising permissions granted by this License.++ "Source" form shall mean the preferred form for making modifications,+ including but not limited to software source code, documentation+ source, and configuration files.++ "Object" form shall mean any form resulting from mechanical+ transformation or translation of a Source form, including but+ not limited to compiled object code, generated documentation,+ and conversions to other media types.++ "Work" shall mean the work of authorship, whether in Source or+ Object form, made available under the License, as indicated by a+ copyright notice that is included in or attached to the work+ (an example is provided in the Appendix below).++ "Derivative Works" shall mean any work, whether in Source or Object+ form, that is based on (or derived from) the Work and for which the+ editorial revisions, annotations, elaborations, or other modifications+ represent, as a whole, an original work of authorship. For the purposes+ of this License, Derivative Works shall not include works that remain+ separable from, or merely link (or bind by name) to the interfaces of,+ the Work and Derivative Works thereof.++ "Contribution" shall mean any work of authorship, including+ the original version of the Work and any modifications or additions+ to that Work or Derivative Works thereof, that is intentionally+ submitted to Licensor for inclusion in the Work by the copyright owner+ or by an individual or Legal Entity authorized to submit on behalf of+ the copyright owner. For the purposes of this definition, "submitted"+ means any form of electronic, verbal, or written communication sent+ to the Licensor or its representatives, including but not limited to+ communication on electronic mailing lists, source code control systems,+ and issue tracking systems that are managed by, or on behalf of, the+ Licensor for the purpose of discussing and improving the Work, but+ excluding communication that is conspicuously marked or otherwise+ designated in writing by the copyright owner as "Not a Contribution."++ "Contributor" shall mean Licensor and any individual or Legal Entity+ on behalf of whom a Contribution has been received by Licensor and+ subsequently incorporated within the Work.++2. Grant of Copyright License. Subject to the terms and conditions of+ this License, each Contributor hereby grants to You a perpetual,+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable+ copyright license to reproduce, prepare Derivative Works of,+ publicly display, publicly perform, sublicense, and distribute the+ Work and such Derivative Works in Source or Object form.++3. Grant of Patent License. Subject to the terms and conditions of+ this License, each Contributor hereby grants to You a perpetual,+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable+ (except as stated in this section) patent license to make, have made,+ use, offer to sell, sell, import, and otherwise transfer the Work,+ where such license applies only to those patent claims licensable+ by such Contributor that are necessarily infringed by their+ Contribution(s) alone or by combination of their Contribution(s)+ with the Work to which such Contribution(s) was submitted. If You+ institute patent litigation against any entity (including a+ cross-claim or counterclaim in a lawsuit) alleging that the Work+ or a Contribution incorporated within the Work constitutes direct+ or contributory patent infringement, then any patent licenses+ granted to You under this License for that Work shall terminate+ as of the date such litigation is filed.++4. Redistribution. You may reproduce and distribute copies of the+ Work or Derivative Works thereof in any medium, with or without+ modifications, and in Source or Object form, provided that You+ meet the following conditions:++ (a) You must give any other recipients of the Work or+ Derivative Works a copy of this License; and++ (b) You must cause any modified files to carry prominent notices+ stating that You changed the files; and++ (c) You must retain, in the Source form of any Derivative Works+ that You distribute, all copyright, patent, trademark, and+ attribution notices from the Source form of the Work,+ excluding those notices that do not pertain to any part of+ the Derivative Works; and++ (d) If the Work includes a "NOTICE" text file as part of its+ distribution, then any Derivative Works that You distribute must+ include a readable copy of the attribution notices contained+ within such NOTICE file, excluding those notices that do not+ pertain to any part of the Derivative Works, in at least one+ of the following places: within a NOTICE text file distributed+ as part of the Derivative Works; within the Source form or+ documentation, if provided along with the Derivative Works; or,+ within a display generated by the Derivative Works, if and+ wherever such third-party notices normally appear. The contents+ of the NOTICE file are for informational purposes only and+ do not modify the License. You may add Your own attribution+ notices within Derivative Works that You distribute, alongside+ or as an addendum to the NOTICE text from the Work, provided+ that such additional attribution notices cannot be construed+ as modifying the License.++ You may add Your own copyright statement to Your modifications and+ may provide additional or different license terms and conditions+ for use, reproduction, or distribution of Your modifications, or+ for any such Derivative Works as a whole, provided Your use,+ reproduction, and distribution of the Work otherwise complies with+ the conditions stated in this License.++5. Submission of Contributions. Unless You explicitly state otherwise,+ any Contribution intentionally submitted for inclusion in the Work+ by You to the Licensor shall be under the terms and conditions of+ this License, without any additional terms or conditions.+ Notwithstanding the above, nothing herein shall supersede or modify+ the terms of any separate license agreement you may have executed+ with Licensor regarding such Contributions.++6. Trademarks. This License does not grant permission to use the trade+ names, trademarks, service marks, or product names of the Licensor,+ except as required for reasonable and customary use in describing the+ origin of the Work and reproducing the content of the NOTICE file.++7. Disclaimer of Warranty. Unless required by applicable law or+ agreed to in writing, Licensor provides the Work (and each+ Contributor provides its Contributions) on an "AS IS" BASIS,+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+ implied, including, without limitation, any warranties or conditions+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+ PARTICULAR PURPOSE. You are solely responsible for determining the+ appropriateness of using or redistributing the Work and assume any+ risks associated with Your exercise of permissions under this License.++8. Limitation of Liability. In no event and under no legal theory,+ whether in tort (including negligence), contract, or otherwise,+ unless required by applicable law (such as deliberate and grossly+ negligent acts) or agreed to in writing, shall any Contributor be+ liable to You for damages, including any direct, indirect, special,+ incidental, or consequential damages of any character arising as a+ result of this License or out of the use or inability to use the+ Work (including but not limited to damages for loss of goodwill,+ work stoppage, computer failure or malfunction, or any and all+ other commercial damages or losses), even if such Contributor+ has been advised of the possibility of such damages.++9. Accepting Warranty or Additional Liability. While redistributing+ the Work or Derivative Works thereof, You may choose to offer,+ and charge a fee for, acceptance of support, warranty, indemnity,+ or other liability obligations and/or rights consistent with this+ License. However, in accepting such obligations, You may act only+ on Your own behalf and on Your sole responsibility, not on behalf+ of any other Contributor, and only if You agree to indemnify,+ defend, and hold each Contributor harmless for any liability+ incurred by, or claims asserted against, such Contributor by reason+ of your accepting any such warranty or additional liability.++END OF TERMS AND CONDITIONS
+ README.md view
@@ -0,0 +1,69 @@+<h1 align="center">hslua-annotations</h1>++<p align="center">+ <a href="https://github.com/sondr3/hslua-annotations/actions"><img alt="GitHub Actions Status" src="https://github.com/sondr3/hslua-annotations/workflows/pipeline/badge.svg" /></a>+ <a href="https://hackage.haskell.org/package/hslua-annotations"><img alt="Hackage version" src="https://img.shields.io/hackage/v/hslua-annotations.svg?logo=haskell" /></a>+ <br />+</p>++<p align="center">+ <strong>A type annotation generator for HSLua targetting LuaLS and EmmyLua</strong>+</p>++<details>+<summary>Table of Contents</summary>+<br />++**Table of Contents**++- [Installation](#installation)+- [Usage](#usage)+- [License](#license)++</details>++This is a small library to generate [LuaLS](https://luals.github.io/wiki/annotations) and [EmmyLua](https://github.com/EmmyLuaLs/emmylua-analyzer-rust/blob/main/docs/emmylua_doc/annotations_EN/README.md) +annotations from HsLua.++## Installation++Add `hslua-annotations` to your Cabal file:++```cabal+build-depends:+ hslua-annotations ^>= 0.1+```++## Usage++```haskell+import HsLua.Annotations (annotateModule, documentModule)+import HsLua.Module.Path qualified as Path+import HsLua.Packaging+import HsLua.Core qualified as Lua+import Data.Text (Text)+import Data.Text.IO qualified as TIO++-- utility function might be needed to narrow the `Module e` type+renderDocs :: Module Lua.Exception -> Text+renderDocs mod = documentModule mod++-- utility function might be needed to narrow the `Module e` type+renderAnnotations :: Module Lua.Exception -> Text+renderAnnotations mod = annotateModule mod++main :: IO ()+main = do+ TIO.writeFile "path.md" $ renderDocs Path.documentedModule+ TIO.writeFile "path.lua" $ renderAnnotations Path.documentedModule+```++## License++This project is licensed under either of++- Apache License, Version 2.0, ([LICENSE_APACHE](LICENSE_APACHE) or+ http://www.apache.org/licenses/LICENSE-2.0)+- MIT license ([LICENSE](LICENSE) or http://opensource.org/licenses/MIT)++at your option.
+ hslua-annotations.cabal view
@@ -0,0 +1,84 @@+cabal-version: 3.0+name: hslua-annotations+version: 0.1.0+synopsis: A type annotation generator for HSLua+description: A type annotation generator for HSLua targetting LuaLS and EmmyLua+homepage: https://github.com/sondr3/hslua-annotations+bug-reports: https://github.com/sondr3/hslua-annotations/issues+license: (MIT OR Apache-2.0)+license-file: LICENSE LICENSE_APACHE+author: Sondre Aasemoen+maintainer: sondre@eons.io+category: Foreign+build-type: Simple+extra-doc-files:+ CHANGELOG.md+ README.md++tested-with:+ ghc ==9.10+ ghc ==9.12+ ghc ==9.14++source-repository head+ type: git+ location: https://github.com/sondr3/hslua-annotations.git++common config+ default-language: GHC2021+ default-extensions:+ DerivingStrategies+ DuplicateRecordFields+ LambdaCase+ MultiWayIf+ NamedFieldPuns+ OverloadedStrings+ TypeFamilies++ ghc-options:+ -Weverything+ -Wno-unsafe+ -Wno-implicit-prelude+ -Wno-missing-safe-haskell-mode+ -Wno-missing-kind-signatures+ -Wno-missing-import-lists+ -Wno-missed-specialisations+ -Wno-all-missed-specialisations+ -Werror=missing-deriving-strategies+ -fhide-source-paths+ -fwrite-ide-info++ build-depends:+ base >=4.12 && <5,+ hslua-core ^>=2.3,+ hslua-packaging ^>=2.4,+ text >=2 && <3,++library+ import: config+ hs-source-dirs: lib+ exposed-modules:+ HsLua.Annotations++ other-modules:+ HsLua.Annotations.Internal+ HsLua.Annotations.Markdown+ HsLua.Annotations.Shared++test-suite hslua-annotations-golden+ import: config+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs+ build-depends:+ hslua-annotations,+ hslua-marshalling ^>=2.3,+ hslua-module-path ^>=1.2,+ hslua-module-version ^>=1.2,+ tasty ^>=1.5,+ tasty-golden ^>=2.3,++ ghc-options:+ -threaded+ -rtsopts+ -with-rtsopts=-N
+ lib/HsLua/Annotations.hs view
@@ -0,0 +1,44 @@+{-# OPTIONS_HADDOCK show-extensions #-}++-- |+-- Module: HsLua.Annotations+-- Description: A type annotation generator for HSLua+-- Copyright: (c) Sondre Aasemoen+-- SPDX-License-Identifier: (MIT OR Apache-2.0)+--+-- Maintainer: Sondre Aasemoen <sondre@eons.io>+-- Stability: experimental+-- Portability: portable+-- Safe Haskell: Safe+--+-- A type annotation generator for HSLua targetting LuaLS and EmmyLua+--+-- == Usage+--+-- To use this library you need to add it as a dependency in your project, e.g.+-- by adding it to @build-depends@ in your @.cabal@ file.+--+-- @+-- build-depends: hslua-annotations ^>= 0.1.0+-- @+--+-- This module does not use common names and can be directly imported:+--+-- @+-- __import__ HsLua.Annotations+-- @+module HsLua.Annotations+ ( -- * Type annotation+ annotateFunction,+ annotateModule,++ -- * Markdown rendering+ documentFunction,+ documentModule,+ )+where++--++import HsLua.Annotations.Internal (annotateFunction, annotateModule)+import HsLua.Annotations.Markdown (documentFunction, documentModule)
+ lib/HsLua/Annotations/Internal.hs view
@@ -0,0 +1,157 @@+module HsLua.Annotations.Internal+ ( annotateFunction,+ annotateModule,+ )+where++import Data.Text (Text)+import Data.Text qualified as T+import HsLua.Annotations.Shared (hasReturnType, nameToText, returnType, typeToText, unlinesNonEmpty)+import HsLua.Core (Name (..))+import HsLua.Packaging++-- | Create type annotations for a documented function.+--+-- See the below example or the generated test annotations [here](https://github.com/sondr3/hslua-annotations/blob/main/test/golden/factorial.lua).+--+--+-- === __Example__+--+-- Given the example `factorial` function from HsLua:+--+-- @+-- factorial :: DocumentedFunction Lua.Exception+-- factorial =+-- defun "factorial" (liftPure $ \n -> product [1 .. n] :: Integer)+-- <#> parameter peekIntegral "integer" "n" ""+-- =#> functionResult pushIntegral "integer" "factorial"+-- #? "Calculates the factorial of a positive integer."+-- @+--+-- The output of running @annotateFunction factorial@ will be the following string:+--+-- @+-- ---Calculates the factorial of a positive integer.+-- ---\@param n integer+-- ---\@return integer+-- function factorial(n)+-- @+--+-- @since 0.1.0+annotateFunction :: DocumentedFunction a -> Text+annotateFunction = annotateFunction' Nothing++-- | Create type annotations for a documented module.+--+-- See the below example or the generated test annotations [here](https://github.com/sondr3/hslua-annotations/blob/main/test/golden/path.lua).+--+-- === __Example__+--+-- Building annotations for @HsLua.Module.Path@ using the exported @documentedModule@+-- you can generate annotations for the whole module. Note that you probably will+-- have to narrow the type of @Module a@ to for example @Module Lua.Exception@.+--+-- The output of running @annotateModule documentedModule@ will be the following string:+--+-- @+-- ---\@meta path+-- ---Module for file path manipulations.+--+-- ---\@class (exact) path+-- ---\@field separator string The character that separates directories.+-- ---\@field search_path_separator string The character that is used to separate the entries in the `PATH` environment variable.+-- local path = {}+--+-- ---Gets the directory name, i.e., removes the last directory separator and everything after from the given path.+-- ---\@param filepath string path+-- ---\@return string+-- function path.directory(filepath)+--+-- ---Get the file name.+-- ---\@param filepath string path+-- ---\@return string+-- function path.filename(filepath)+--+-- ...snip+-- @+--+-- @since 0.1.0+annotateModule :: Module a -> Text+annotateModule (Module {moduleName, moduleDescription, moduleFields, moduleFunctions, moduleOperations}) =+ unlinesNonEmpty+ [ "---@meta " <> nameToText moduleName,+ "---" <> moduleDescription <> "\n",+ "---@class (exact) " <> nameToText moduleName,+ T.intercalate "\n" (map fieldDesc moduleFields),+ T.intercalate "\n" (map opDesc moduleOperations),+ "local " <> nameToText moduleName <> " = {}\n",+ T.intercalate "\n" (map (annotateFunction' $ Just moduleName) moduleFunctions)+ ]++annotateFunction' :: Maybe Name -> DocumentedFunction a -> Text+annotateFunction' parent (DocumentedFunction {functionName, functionDoc}) = do+ let name = nameToText functionName+ desc = functionDesc functionDoc+ typ = returnType functionDoc+ paramAnn = paramsDesc functionDoc+ params = T.intercalate ", " $ paramNames functionDoc+ paren = maybe "" (\n -> nameToText n <> ".") parent+ in unlinesNonEmpty+ [ if T.null desc then mempty else desc,+ paramAnn,+ if hasReturnType (funDocResults functionDoc) then "---@return " <> typ else "",+ "function " <> paren <> name <> "(" <> params <> ")"+ ]++opDesc :: (Operation, DocumentedFunction a) -> Text+opDesc (op, DocumentedFunction {functionDoc}) = "---@operator " <> opName op <> ":" <> returnType functionDoc++fieldDesc :: Field a -> Text+fieldDesc (Field {fieldName, fieldDoc}) = "---@field " <> nameToText fieldName <> " " <> docs fieldDoc+ where+ docs (FieldDoc {fieldDocDescription, fieldDocType}) = typeToText fieldDocType <> " " <> fieldDocDescription++functionDesc :: FunctionDoc -> Text+functionDesc (FunDoc {funDocDescription}) = T.strip . unlinesNonEmpty $ map ("---" <>) $ T.lines funDocDescription++paramNames :: FunctionDoc -> [Text]+paramNames (FunDoc {funDocParameters}) = map go funDocParameters+ where+ go (ParameterDoc {parameterName}) = parameterName++paramsDesc :: FunctionDoc -> Text+paramsDesc (FunDoc {funDocParameters}) = T.intercalate "\n" $ map paramDesc funDocParameters++paramDesc :: ParameterDoc -> Text+paramDesc (ParameterDoc {parameterName, parameterType, parameterDescription, parameterIsOptional}) = do+ let isOpt = if parameterIsOptional then "?" else ""+ typ = typeToText parameterType+ in "---@param " <> parameterName <> isOpt <> " " <> typ <> " " <> parameterDescription++opName :: Operation -> Text+opName = \case+ Add -> "add"+ Sub -> "sub"+ Mul -> "mul"+ Div -> "div"+ Mod -> "mod"+ Pow -> "pow"+ Unm -> "unm"+ Idiv -> "idiv"+ Band -> "band"+ Bor -> "bor"+ Bxor -> "bxor"+ Bnot -> "bnot"+ Shl -> "shl"+ Shr -> "shr"+ Concat -> "concat"+ Len -> "len"+ Eq -> "eq"+ Lt -> "lt"+ Le -> "le"+ Index -> "index"+ Newindex -> "newindex"+ Call -> "call"+ Tostring -> "tostring"+ Pairs -> "pairs"+ CustomOperation x -> nameToText x
+ lib/HsLua/Annotations/Markdown.hs view
@@ -0,0 +1,184 @@+module HsLua.Annotations.Markdown+ ( documentFunction,+ documentModule,+ )+where++import Data.Bool (bool)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Version (showVersion)+import HsLua.Annotations.Shared (hasReturnType, nameToText, returnType, typeToText, unlinesNonEmpty)+import HsLua.Core+import HsLua.Core.Utf8 qualified as Utf8+import HsLua.Packaging++-- | Create markdown documentation for a documented function.+--+-- See the below example or the generated test annotations [here](https://github.com/sondr3/hslua-annotations/blob/main/test/golden/factorial.md).+--+-- === __Example__+--+-- Given the example `factorial` function from HsLua:+--+-- @+-- factorial :: DocumentedFunction Lua.Exception+-- factorial =+-- defun "factorial" (liftPure $ \n -> product [1 .. n] :: Integer)+-- <#> parameter peekIntegral "integer" "n" ""+-- =#> functionResult pushIntegral "integer" "factorial"+-- #? "Calculates the factorial of a positive integer."+-- @+--+-- The output of running @documentFunction factorial@ will be the following string:+--+-- @+-- `function factorial(n)`+--+-- #### Parameters+--+-- | Name | Type | Description |+-- | ---- | ---- | ----------- |+-- | `n` | `integer` | |+--+-- #### Returns+--+-- | Type | Description |+-- | ---- | ----------- |+-- | `integer` | factorial |+--+-- Calculates the factorial of a positive integer.+--+-- ```lua+-- function factorial(n: integer): integer+-- ```+--+-- *Since: 1.0.0*+-- @+--+-- @since 0.1.0+documentFunction :: DocumentedFunction e -> Text+documentFunction fn =+ let fnDoc = functionDoc fn+ fnName = Utf8.toText $ fromName (functionName fn)+ name =+ if T.null fnName+ then "<anonymous function>"+ else fnName+ in unlinesNonEmpty+ [ "`function " <> name <> "(" <> renderFunctionParams fnDoc <> ")" <> "`\n",+ renderFunctionDoc name fnDoc+ ]++-- | Create markdown documentation for a documented module.+--+-- See the below example or the generated test annotations [here](https://github.com/sondr3/hslua-annotations/blob/main/test/golden/path.md).+--+-- === __Example__+--+-- Building documentation for @HsLua.Module.Path@ using the exported @documentedModule@+-- you can generate Markdown documentation for the whole module. Note that you probably will+-- have to narrow the type of @Module a@ to for example @Module Lua.Exception@.+--+-- The output of running @documentModule documentedModule@ on it will be the+-- following string:+--+-- @+-- # `path`+-- Module for file path manipulations.+--+-- ## Fields+--+-- ### `separator`+-- The character that separates directories.+--+-- ```lua+-- path.separator: string+-- ```+--+-- ...snip+-- @+--+-- @since 0.1.0+documentModule :: Module e -> Text+documentModule (Module {moduleFields, moduleName, moduleDescription, moduleFunctions}) =+ unlinesNonEmpty+ [ "# `" <> nameToText moduleName <> "`",+ moduleDescription,+ renderFields moduleName moduleFields,+ renderFunctions moduleFunctions+ ]++renderFunctions :: [DocumentedFunction e] -> Text+renderFunctions [] = mempty+renderFunctions fs = "## Functions\n\n" <> T.intercalate "\n" (map (("### " <>) . documentFunction) fs)++renderFunctionParams :: FunctionDoc -> Text+renderFunctionParams (FunDoc {funDocParameters}) = T.intercalate ", " $ map parameterName funDocParameters++renderFields :: Name -> [Field e] -> Text+renderFields _ [] = mempty+renderFields name fs =+ unlinesNonEmpty+ [ "\n## Fields",+ T.intercalate "\n" (map (renderField name) fs)+ ]++renderField :: Name -> Field e -> Text+renderField modName (Field {fieldName, fieldDoc}) = do+ let name = nameToText fieldName+ parent = nameToText modName+ typ = typeToText $ fieldDocType fieldDoc+ unlinesNonEmpty+ [ "\n### `" <> name <> "`",+ fieldDocDescription fieldDoc,+ "\n```lua\n" <> parent <> "." <> name <> ": " <> typ <> "\n```"+ ]++renderFunctionDoc :: Text -> FunctionDoc -> Text+renderFunctionDoc name fd@(FunDoc {funDocDescription, funDocSince, funDocParameters, funDocResults}) =+ let sinceTag = case funDocSince of+ Nothing -> mempty+ Just version -> T.pack $ "*Since: " <> showVersion version <> "*"+ in unlinesNonEmpty+ [ renderParamTable funDocParameters,+ renderResultsDoc funDocResults,+ funDocDescription,+ "\n```lua\n" <> "function " <> name <> renderParamType funDocParameters <> (if hasReturnType funDocResults then ": " <> returnType fd else "") <> "\n```\n",+ sinceTag+ ]++renderParamTable :: [ParameterDoc] -> Text+renderParamTable [] = mempty+renderParamTable ps =+ T.unlines+ [ "#### Parameters\n",+ "| Name | Type | Description |",+ "| ---- | ---- | ----------- |",+ T.intercalate "\n" (map renderParamField ps)+ ]++renderParamType :: [ParameterDoc] -> Text+renderParamType [] = "()"+renderParamType ps = "(" <> T.intercalate ", " (map (\p -> parameterName p <> bool "" "?" (parameterIsOptional p) <> ": " <> typeToText (parameterType p)) ps) <> ")"++renderParamField :: ParameterDoc -> Text+renderParamField (ParameterDoc {parameterName, parameterDescription, parameterType, parameterIsOptional}) =+ "| `" <> parameterName <> "` | `" <> typeToText parameterType <> bool "" "?" parameterIsOptional <> "` | " <> parameterDescription <> " |"++renderResultsDoc :: ResultsDoc -> Text+renderResultsDoc (ResultsDocList []) = mempty+renderResultsDoc (ResultsDocList rds) =+ unlinesNonEmpty+ [ "#### Returns\n",+ "| Type | Description |",+ "| ---- | ----------- |",+ T.intercalate "\n" (map renderResultValueDoc rds)+ ]+renderResultsDoc (ResultsDocMult txt) = " - " <> indent 4 txt++renderResultValueDoc :: ResultValueDoc -> Text+renderResultValueDoc (ResultValueDoc {resultValueType, resultValueDescription}) = "| `" <> typeToText resultValueType <> "` | " <> resultValueDescription <> " |"++indent :: Int -> Text -> Text+indent n = T.replace "\n" (T.replicate n " ")
@@ -0,0 +1,51 @@+module HsLua.Annotations.Shared+ ( nameToText,+ typeToText,+ unlinesNonEmpty,+ hasReturnType,+ returnType,+ )+where++import Data.Char (toLower)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Encoding (decodeUtf8)+import HsLua.Core (Name (..), Type (..))+import HsLua.Packaging++unlinesNonEmpty :: [Text] -> Text+unlinesNonEmpty xs = T.unlines $ filter (not . T.null) xs++nameToText :: Name -> Text+nameToText = decodeUtf8 . fromName++typeToText :: TypeSpec -> Text+typeToText = \case+ BasicType t -> basicTypeName t+ NamedType nt -> decodeUtf8 $ fromName nt+ AnyType -> "any"+ FunType {} -> "function"+ RecType {} -> "table"+ SeqType t -> typeToText t <> "[]"+ SumType specs -> T.intercalate "|" (map typeToText specs)++basicTypeName :: Type -> Text+basicTypeName = \case+ TypeLightUserdata -> "light userdata"+ t -> T.pack $ map toLower . drop 4 $ show t++hasReturnType :: ResultsDoc -> Bool+hasReturnType (ResultsDocList []) = False+hasReturnType (ResultsDocList _) = True+hasReturnType _ = False++returnType :: FunctionDoc -> Text+returnType (FunDoc {funDocResults}) = go funDocResults+ where+ go (ResultsDocList [rs]) = resultValueDesc rs+ go (ResultsDocList xs) = T.intercalate "|" $ map resultValueDesc xs+ go (ResultsDocMult _) = ""++resultValueDesc :: ResultValueDoc -> Text+resultValueDesc (ResultValueDoc {resultValueType}) = typeToText resultValueType
+ test/Main.hs view
@@ -0,0 +1,52 @@+module Main (main) where++import Data.Text (Text)+import Data.Text.Lazy (fromStrict)+import Data.Text.Lazy.Encoding (encodeUtf8)+import Data.Version (makeVersion)+import HsLua.Annotations (annotateFunction, annotateModule, documentFunction, documentModule)+import HsLua.Core qualified as Lua+import HsLua.Marshalling (peekIntegral, pushIntegral)+import HsLua.Module.Path qualified as Path+import HsLua.Module.Version qualified as Version+import HsLua.Packaging+import Test.Tasty (TestTree, defaultMain, testGroup)+import Test.Tasty.Golden++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "Tests" [annotations, markdown]++annotations :: TestTree+annotations =+ testGroup+ "Annotations"+ [ goldenVsString "factorial" "test/golden/factorial.lua" (pure $ encodeUtf8 (fromStrict $ annotateFunction factorial)),+ goldenVsString "path" "test/golden/path.lua" (pure $ encodeUtf8 $ fromStrict (annModule Path.documentedModule)),+ goldenVsString "version" "test/golden/version.lua" (pure $ encodeUtf8 $ fromStrict (annModule Version.documentedModule))+ ]+ where+ annModule :: Module Lua.Exception -> Text+ annModule = annotateModule++markdown :: TestTree+markdown =+ testGroup+ "Markdown"+ [ goldenVsString "factorial" "test/golden/factorial.md" (pure $ encodeUtf8 (fromStrict $ documentFunction factorial)),+ goldenVsString "path" "test/golden/path.md" (pure $ encodeUtf8 $ fromStrict (docModule Path.documentedModule)),+ goldenVsString "version" "test/golden/version.md" (pure $ encodeUtf8 $ fromStrict (docModule Version.documentedModule))+ ]+ where+ docModule :: Module Lua.Exception -> Text+ docModule = documentModule++factorial :: DocumentedFunction Lua.Exception+factorial =+ defun "factorial" (liftPure $ \n -> product [1 .. n] :: Integer)+ <#> parameter peekIntegral "integer" "n" ""+ =#> functionResult pushIntegral "integer" "factorial"+ #? "Calculates the factorial of a positive integer."+ `since` makeVersion [1, 0, 0]