Documenting symbols in Lua modules?

Hello,

there is something that has been bugging me since I started writing plugins in Lua: how do you mark up the “module” part of a symbol. This is not a problem with the built-in modules like vim.lsp because they are always in the global environment, but it is a problem with modules that need to be required first.

The problem

Lua does not really have a module system, but it does a good job of pretending that it does. Suppose I have a plugin foo.nvim that contains a file lua/foo.lua which returns a table. I can then reference the symbol bar as require('foo').bar. Simple enough.

But how do I document this? Popular plugins like Telescope would document it as foo.bar, but this is technically wrong because there is no global foo symbol. This becomes a real problem in Telescope with a tag like telescope.builtin.lsp_definitions(). How do I get that symbol? Is it require('telescope').builtin.lsp_defintions, require('telescope.builtin').lsp_defintions or require('telescope.builtin.lsp_defintions')? It’s the second one. I have to scroll all the way up to the beginning of the section to find the answer in prose.

Proposed solution

Here is my proposal: write the module name in quotes.

  • 'foo'.bar
  • 'telescope.builtin'.lsp_definitions()

In Lua strings cannot have fields, do there is no danger of confusion. The only problem I can foresee is that in Vim options are tagged with quotes (e.g. 'syntax'), but they don’t have any fields either.

Open questions

What is your opinion? Alternatives? And how do we communicate it to plugin authors? I would have written it in help-writing but it doesn’t look anyone reads that section.

I disagree – especially considering your audience includes new users, such “pedagogic lies” hurt more than they help.

Luckily, there’s idiomatic Lua for this, which the plugins you refer to actually use:

local telescope = require('telescope')
local builtin = require('telescope.builtin')

telescope.foo()
builtin.bar()

You can do the same in the documentation by clearly stating at the top that you will be using the following locals throughout what follows.

I disagree – especially considering your audience includes new users, such
“pedagogic lies” hurt more than they help.

Can you please elaborate what you consider a “pedagogic lie”? Vim options are
not strings, yet they are tagged with quotation marks, Vim functions can take
a number or arguments, yet they are tagged with an empty pair of parentheses.

Luckily, there’s idiomatic Lua for this, which the plugins you refer to
actually use: ```lua
local telescope = require(‘telescope’)
local builtin = require(‘telescope.builtin’)

telescope.foo()
builtin.bar()


You can do the same in the documentation by clearly stating at the top that
you will be using the following `local`s throughout what follows.

I know how to write Lua, that’s not the point here. The question is how to tag
and document Lua code. This would not be an issue in Python because Python has
an actual module system where packages have canonical names; you can do
import sys as herpderp, but the canonical name is always sys. The closest
thing we have in Lua to a canonical name is the string argument to require.

I have found one objection to my notation: it makes typing the reference on
the command-line harder. If I want to look up :h 'ftplugin' I can type :h ftplugin because ftplugin is a substring of 'ftplugin'. However, If I
want to look up 'foo'.bar I cannot type :h foo.bar, I would have to type
:h foo'.bar instead, which is quite unpleasant to type.

An alternative would be to omit the quotes in the tag but have them in the
signature. Here is an example of what it would look line in my
debugpy.nvim plugin:

                                                               *debugpy.run()*
'debugpy'.run({config})

    Callback function to run the debugger with the final configuration (see
    |dap-configuration|) which you can replace with your implementation. The
    default implementations calls |dap.run|.

The purpose of the tag is to give a unique identifier to some part of the
configuration, not to give to exact signature. Maybe I was too eager in my first
proposal.

EDIT: fixed the part that was cut off from my email

1 Like