Sphinx-LuaLS#
Sphinx-LuaLS features domain for Lua and automatic documentation generator based on Lua Language Server.
See an example output: logging
.
Installation#
You’ll need a Python installation and a Sphinx project to start with Sphinx-LuaLS.
If you’re new to Python and Sphinx
Installing Python
We recommend using PyEnv to manage python installations on your system.
Similar to LuaRocks, it creates executables for Python, Pip (Package Installer for Python), and other tools. When run, these executables determine which Python environment to use, and invoke the appropriate command.
Following its installation guide, install PyEnv, configure your shell, and install build dependencies.
Make sure that PyEnv shims are in your path by checking them with
which
:$ which python /home/user/.pyenv/shims/python $ which pip /home/user/.pyenv/shims/pip
Once ready, you can install Python 3.12:
$ pyenv install 3.12.2
To avoid installing Python packages globally, we recommend using virtual environments. It is a good practice to create a separate environment for each project:
$ pyenv virtualenv 3.12.2 my-project
Once a new environment is created, you’ll need to activate it. Navigate to your project directory and run the following command:
$ pyenv local my-project
This will create a file called
.python-version
. Every time you run an executable that was installed by PyEnv, it will look for a.python-version
file to determine which environment to use. Thus, when running Python from your project’s directory, it will always use the right virtual environment.Installing Sphinx
You can now install Sphinx using Pip:
$ pip install sphinx
To make your life easier, you can create a file named
requirements.txt
, and list all of the dependencies there:$ echo "sphinx~=8.0" > requirements.txt
Now, you can install all dependencies at once:
$ pip install -r requirements.txt
Creating a Sphinx project
Sphinx comes with a tool for creating new projects. Make a directory for your documentation and run the
sphinx-quickstart
command in it:$ mkdir docs/ $ cd docs/ $ sphinx-quickstart
Once finished, you’ll see several files generated.
Makefile
contains commands to build documentation,conf.py
contains configuration, andindex.rst
is the main document.Try building documentation and see the results:
$ make html $ open build/html/index.html
Install sphinx-lua-ls
using Pip:
$ pip install sphinx-lua-ls
Add it to the extensions
list in your conf.py
,
and specify the location of your Lua project:
extensions = [
"sphinx_lua_ls",
]
# Path to the folder containing the `.luarc.json` file,
# relative to the directory with `conf.py`.
lua_ls_project_root = "../"
If you plan to use Markdown in code comments, install the MySt plugin for Sphinx.
Quickstart#
Use lua:module
to indicate which module you’re documenting.
After it, use lua:data
, lua:function
, lua:class
,
and others to document module’s contents.
.. lua:module:: soundboard
.. lua:class:: Sound
A sound that can be played by the sound board.
.. lua:staticmethod:: new(id: string) -> sound: Sound
Create a new sound.
:param string id: id of a sound.
:return Sound sound: a new class instance.
.. lua:method:: play(self: Sound)
Plays the sound.
Example output
Reference documented entities using the lua:data
, lua:func
,
and lua:class
roles:
Here's a reference to the :lua:class:`soundboard.Sound` class.
Example output
Here’s a reference to the soundboard.Sound
class.
Use lua:autoobject
to extract documentation from source code.
Its options are similar to the ones used by python autodoc
:
.. lua:autoobject:: logging.Logger
Example output
- class logging.Logger
An object for logging messages.
Declaring objects#
- .. lua:data:: name: type#
- .. lua:const:: name: type#
- .. lua:attribute:: name: type#
Directives for documenting variables. Accepts name of the variable, and an optional type:
.. lua:data:: name: string Person's name.
Example output
-
name:
string
Person’s name.
-
name:
- .. lua:function:: name(param: type) -> type#
- .. lua:method:: name(param: type) -> type#
- .. lua:classmethod:: name(param: type) -> type#
- .. lua:staticmethod:: name(param: type) -> type#
Directives for documenting functions and class methods. Accepts function name, optional parenthesized list of parameters, and an optional return type:
.. lua:function:: doABarrelRoll(times: integer) -> success: boolean Does a barrel roll given amount of times. Returns ``true`` if successful.
Example output
-
doABarrelRoll(times:
integer
) success:boolean
Does a barrel roll given amount of times. Returns
true
if successful.
-
doABarrelRoll(times:
- .. lua:class:: name: bases#
For documenting classes and metatables. Accepts a class name and an optional list of base classes:
.. lua:class:: Logger: LogFilter, LogSink The user-facing interface for logging messages.
Example output
-
class Logger :
LogFilter
,LogSink
The user-facing interface for logging messages.
-
class Logger :
- .. lua:alias:: name = type#
For documenting type aliases. Accepts name of the alias and its type:
.. lua:alias:: LogLevel = integer Verbosity level of a log message.
Example output
-
alias LogLevel =
integer
Verbosity level of a log message.
-
alias LogLevel =
- .. lua:module:: name#
Specifies beginning of a module. Other objects declared after this directive will be automatically attached to this module.
This directive doesn’t accept any content, it just creates an anchor.
- .. lua:currentmodule:: name#
Switches current module without making an index entry or an anchor. If
name
isNone
, sets current module to be the global namespace.
Note
Setting the default domain
You can avoid prefixing directives and roles with lua:
if you set Lua
as your default domain. For this, declare primary_domain
in your conf.py
:
primary_domain = "lua"
All directives that document Lua objects accept the standard parameters:
- :no-index:#
Render the documentation, but don’t add it to the index and don’t create anchors. You will not be able to reference un-indexed objects.
- :private:#
- :protected:#
- :package:#
- :virtual:#
- :abstract:#
- :async:#
- :global:#
Adds a corresponding annotation before object’s name:
.. lua:function:: fetch(url: string) -> code: integer, content: string? :async: Fetches content from the given url.
Example output
-
async fetch(url:
string
) code:integer
, content?:string
Fetches content from the given url.
-
async fetch(url:
- :annotation:#
Allows adding custom short annotations.
- :deprecated:#
Marks object as deprecated in index and when cross-referencing. This will not add any text to the documented object, you’ll need to use the
deprecated
directive for this:.. lua:data:: fullname: string :deprecated: Person's full name. .. deprecated:: 3.2 Use ``name`` and ``surname`` instead.
Example output
-
fullname:
string
Person’s full name.
Deprecated since version 3.2: Use
name
andsurname
instead.
-
fullname:
- :module:#
Allows overriding current module for a single object. This is useful for documenting global variables that are declared in a module.
This option should not be used inside of a class or an alias.
Cross-referencing objects#
- :lua:obj:#
You can reference any documented object through the
lua:obj
role.Given an object path, Lua domain will first search for an object with this path in the outer-most class, then in the current module, and finally in the global namespace.
So, if you reference an object
Sound.id
from documentation of a classSoundBoard.Helper
located in the modulesoundboard
, Lua domain will first checksoundboard.SoundBoard.Helper.Sound.id
, thensoundboard.Sound.id
, and finallySound.id
.If you specify a fully qualified object name, and would like to hide its prefix, you can add a tilde (
~
) to the object’s path:Reference to a :lua:obj:`~logging.Logger`.
Example output
Reference to a
Logger
.
- :lua:func:#
- :lua:data:#
- :lua:const:#
- :lua:class:#
- :lua:alias:#
- :lua:meth:#
- :lua:attr:#
- :lua:mod:#
These are additional roles that you can use to reference a Lua object.
Lua domain does not allow having multiple objects with the same full name. Thus, all of these roles work exactly the same as
lua:obj
. The only difference is that they will warn you if the type of the referenced object doesn’t match the role’s type.
Note
Setting the default role
When you use backticks without explicitly specifying a role, Sphinx uses the default
role to resolve it. Setting lua:obj
as the default
role can reduce boilerplate in documentation.
In conf.py
, declare default_role
:
default_role = "lua:obj"
Now, you can reference any object with just backticks:
Reference to a `logging.Logger.info`.
Example output
Reference to a logging.Logger:info()
.
Autodoc directive#
- .. lua:autoobject:: name#
You can automatically generate documentation for any object by invoking the
lua:autoobject
directive.Tables are exported as
data
by default, meaning that their contents are not documented.To enable documentation within a table, document is as a class. If you inherit it from
table
, autodoc will treat it as a module. Thus, a typical Lua module will look like this:--- This is a module. Notice that we've declared it as a class --- inherited from :lua:obj:`table`. --- --- @class library: table local library = {} --- Submodules should also be declared as classes. --- --- @class library.submodule: table library.submodule = {} --- Other objects are documented as usual. function library.foo() end return library
Note
By default, autodoc will parse object comments as ReStructured Text, not as MarkDown. If you plan to use Markdown in code comments, install the MySt plugin for Sphinx and invoke include
lua:autoobject
from a markdown file.Make sure you separate separate comment markers from documentation by a space. Otherwise, autodoc will not be able to tell your comments apart from content automatically generated by Lua Language Server:
--- This is OK: separated by a space. local x = 0; ---This is NOT OK: no separation. local x = 0;
Warning
Currently, Lua Language Server does not export all available information.
@see
markers can sometimes be broken. We recommend using theseealso
directive instead.@deprecated
markers do not add any note to the documentation. We recommend providing an explicit message with thedeprecated
directive.@nodiscard
and@operator
markers are not exported.Export of enums (
@enum
) is completely broken. We recommend using@alias
instead:--- Instead of enums, we use aliases. --- --- @alias LogLevel integer LogLevel = {} --- Alias members are declared as usual. LogLevel.Debug = 1 --- And so on...
lua:autoobject
supports same settings as other lua directives, as well as some additional ones:- :members:#
If enabled, autodoc will also document object’s members. You can pass a list of coma-separated names to specify which members should be documented. Otherwise, this option will document all public non-special members which have a description.
- :undoc-members:#
Include undocumented members to the object’s description. By default, they are skipped even if
members
is passed.
- :private-members:#
- :protected-members:#
- :package-members:#
Include non-public members to the object’s description.
- :special-members:#
Include special members to the object’s description. That is, generate documentation for members whose names start with double underscore.
- :inherited-members:#
For classes, includes members inherited from base classes.
- :exclude-members:#
A coma-separated list of members that should not be documented.
- :recursive:#
If enabled, autodoc will recursively generate documentation for all objects nested within the root. That is, object’s members, their members, and so on.
Settings for :rst:dir:undoc-members, :rst:dir:private-members, :rst:dir:special-members, and :rst:dir:inherited-members are applied to all documented objects.
- :member-order:#
Controls how items are sorted. There are three options available:
alphabetical
: members are sorted in lexicographical order of their names;groupwise
: members are grouped by their type. Within each group, they are ordered by name;bysource
: members are sorted in the same order as they appear in code. This is the default option.
Warning
Currently, Lua Language Server does not export position information for enums (
@enum
). If ordering by source, enums will be placed at the end of the documentation.
Controlling generation from code comments#
When using lua:autoobject
in recursive mode, it is sometimes necessary
to override its options for some objects. To do this, you can include specially
formatted comments to your documentation.
To override any lua:autoobject
setting for a particular object,
use !doc
comments. For example, here we enable lua:autoobject:special-members
and exclude __tostring
for class Foo
:
--- Some class documentation...
---
--- !doc special-members
--- !doc exclude-members: __tostring
--- @class Foo
You can also specify which type of object is being documented by using
a !doctype
comment. For example, here we use !doctype const
to indicate
that a certain variable should be documented as lua:const
:
--- Some const documentation...
---
--- !doctype const
--- @type string
foo = "bar!"
Settings#
- lua_ls_project_root: str
Path to a directory with
.luarc.json
file, relative to the location ofconf.py
. Lua Language Server will be launched from here.
- lua_ls_project_directories: list[str]
By default, Lua Language Server documents all files from
lua_ls_project_root
. You can change that by providing a list or directories that should be documented. Autodoc will launch Lua Language Server using each of these directories as a target. The path is relative tolua_ls_project_root
.
- lua_ls_auto_install: bool
Controls whether autodoc should try downloading Lua Language Server from github if it isn’t installed already. This setting is enabled by default.
- lua_ls_auto_install_location: str
Controls where the Lua Language Server will be installed. By default, autodoc uses a folder in the temporary directory provided by the os. For unix, it is
/tmp/python_lua_ls_cache
.
- lua_ls_min_version: str
Controls the minimal version of Lua Language Server used.
- lua_ls_default_options: dict[str, str]
Default values for directive options. You can override member ordering or enable documentation for undocumented or private members from here. For example:
lua_ls_default_options = { # Enable documentation for object's members. # Empty string means documenting all members with non-empty description. "members": "", # Set ordering of automatically generated content to alphabetical. "member-order": "alphabetical", # And so on... }
- lua_ls_lua_version: str
Controls which documentation version is used when linking to standard library functions. Does not otherwise affect parsing or generation.
Example output#
This output is generated with the following directive:
.. lua:autoobject:: logging
:members:
:recursive:
A generic logging module, just to demonstrate you documentation.
-
global LOG_LEVEL:
integer
# Default log level.
-
alias logging.Level =
integer
# Represents message severity.
-
Debug:
integer
# For debug messages, hidden by default.
-
Info:
integer
# For info messages.
-
Warning:
integer
# For warnings, when behavior may be different from what users expect.
-
Error:
integer
# For errors, when the system stops working.
-
Debug:
- class logging.Logger#
An object for logging messages.
-
staticmethod new(name:
string
, level?:integer
)logging.Logger
# Create a new logger.
- Parameters:
name (
string
) – name of the logger, will be added to every message.level? (
integer
) – level of the logger, equals toLOG_LEVEL
by default.
-
debug(self:
logging.Logger
, msg:string
, ...:any
)# Print a debug message.
- Parameters:
msg (
string
) – message format string, will be processed bystring.format
.... (
any
) – parameters for message formatting.
-
info(self:
logging.Logger
, msg:string
, ...:any
)# Print an info message.
- Parameters:
msg (
string
) – message format string, will be processed bystring.format
.... (
any
) – parameters for message formatting.
-
warning(self:
logging.Logger
, msg:string
, ...:any
)# Print a warning message.
- Parameters:
msg (
string
) – message format string, will be processed bystring.format
.... (
any
) – parameters for message formatting.
-
error(self:
logging.Logger
, msg:string
, ...:any
)# Print an error message.
- Parameters:
msg (
string
) – message format string, will be processed bystring.format
.... (
any
) – parameters for message formatting.
-
staticmethod new(name: