2024-03-10 13:53:47 +00:00
|
|
|
# Suggest-require
|
|
|
|
|
|
|
|
**Suggest-require** is a tiny Lua library, for discovering importable
|
|
|
|
modules for Lua's `require` function. The intended purpose is
|
|
|
|
as a part of an auto-complete system for Lua written in Lua. The
|
|
|
|
script can, when run as a standalone program, list all of the
|
|
|
|
available modules in the current environment.
|
|
|
|
|
|
|
|
The library has been tested with LuaJIT and Lua 5.1 under Linux.
|
|
|
|
Compatibility with Windows, MacOS and other Lua versions is possible,
|
|
|
|
but not assured.
|
|
|
|
|
|
|
|
Source code on [Gitfub](https://gitfub.space/Jmaa/suggest-require). Usage and examples are available in the source file itself. License is Beerware.
|
|
|
|
|
|
|
|
## Why suggest-require? ##
|
|
|
|
|
|
|
|
I have been writing a debugging shell in LÖVE for a while now. The
|
|
|
|
shell itself, called Xenoterm, is pretty powerful at this point, with complex
|
|
|
|
interactions and autocompletions, inspired by [Fish shell](https://fishshell.com/).
|
|
|
|
One of Xenoterm most powerful features is argument auto-completion,
|
|
|
|
where the shell detects which function is being tab-completed on, and
|
|
|
|
suggests several possible arguments. This is similar to when Fish
|
|
|
|
provides suggestions for command-line options, listing files in the
|
|
|
|
local directory, or password files for
|
|
|
|
[pass](https://www.passwordstore.org/). This allows the user to
|
|
|
|
offload the need to remember specific names or files, and allows them
|
|
|
|
to focus on more important things, like writing code.
|
|
|
|
|
|
|
|
![Using suggest-require within Xenoterm to autocomplete the Posix
|
2024-03-10 14:19:08 +00:00
|
|
|
module.](images/suggest-require-2.png)
|
2024-03-10 13:53:47 +00:00
|
|
|
|
|
|
|
Argument completion <abbr title="reason for being" lang="fr">raison d'être</abbr>
|
|
|
|
was to make life easier for me and
|
|
|
|
[Sketchwhale](http://takunomi.space) while working on our games, as we
|
|
|
|
had a habit of forgetting the ids of items and enemies.
|
|
|
|
|
|
|
|
For obvious reasons, argument autocompletion makes most sense for
|
|
|
|
functions with a single string argument, and a limited number of
|
|
|
|
inputs. Giving autocompletions for `string.lower` would not make any
|
|
|
|
sense. A prime candidate from Lua's standard library was `require`,
|
|
|
|
Lua's module importing functionality, which is why I wrote the
|
|
|
|
suggest-require library written back in 2017.
|
|
|
|
|
|
|
|
## How is suggest-require implemented? ##
|
|
|
|
|
|
|
|
A curious thing about Lua is that it aims to be [POSIX
|
|
|
|
compatible](https://en.wikipedia.org/wiki/POSIX) in order to be as
|
|
|
|
portable as possible. This means pretty much no built-in knowledge of
|
|
|
|
filesystems. Instead, Lua stores any information about the file system
|
|
|
|
configuration in the `package` global: Directory separators, library
|
|
|
|
locations, and so on. Depending upon which platform your Lua is
|
|
|
|
compiled for the proper values are fitted in, but all of this can be
|
|
|
|
inspected and modified at runtime. `require` will update its behaviour
|
|
|
|
based upon the values in the table. So if you wanted to figure out the
|
|
|
|
importable modules, you have all the information you want.
|
|
|
|
|
|
|
|
Unfortunately, Lua is __very__ filesystem agnostic, to the point of
|
|
|
|
having no API for looking up files in a directory. The
|
|
|
|
[Lua Filesystem Module](https://keplerproject.github.io/luafilesystem/)
|
|
|
|
exists for filesystem lookup, but I don't like dependencies for a
|
|
|
|
simple script. Lua does have the `io.popen` function for
|
|
|
|
interacting with the system's shell, and this can be used to query the
|
|
|
|
filesystem. On Unixes, we use the `find` program to find files, and
|
|
|
|
its builtin pattern matching allowed a bit of a speedup.
|
|
|
|
|
|
|
|
![Using suggest-require along with grep to find all importable posix
|
2024-03-10 14:19:08 +00:00
|
|
|
modules](images/suggest-require-1.png)
|
2024-03-10 13:53:47 +00:00
|
|
|
|
|
|
|
So the entire process is roughly:
|
|
|
|
|
|
|
|
1. Produce glob patterns, based on information in the global `package` table.
|
|
|
|
2. Use `find` to execute those globs, retrieving a list of possible
|
|
|
|
libraries.
|
|
|
|
3. The library paths in the `package` table may contain multiple
|
|
|
|
wildcards (marked as `?` in the path), which, when using `require`
|
|
|
|
is replaced with the requested module. Thus our glob approach finds
|
|
|
|
too many files, and we need to prune some of them, (so `./?/?.lua`
|
|
|
|
matches `./a/a.lua`, but not `./a/b/.lua`.)
|
|
|
|
4. Add the names of the already loaded libraries into the mix; these
|
|
|
|
are easily found by looking in the global `package.loaded` table.
|
|
|
|
|
|
|
|
Sugggest-require will thus most likely only work on desktop computers,
|
|
|
|
and other full featured environments. These are also the environments
|
|
|
|
where I could see the script being used; a script deep in a game
|
|
|
|
engine or on an embedded device will probably know exactly which
|
|
|
|
modules to import.
|