mlua-rs / mlua

High level Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau bindings to Rust with async/await support
Other
1.54k stars 132 forks source link

Automatically generate definition files #392

Open viperML opened 4 months ago

viperML commented 4 months ago

It may be interesting to have some mechanism to autogenerate definition files to be consumed by lua-language-server from rust code:

https://luals.github.io/wiki/definition-files

A usecase for example: exposed a function from rust, and get type hints when editing lua code that uses this function.

mikavilpas commented 3 months ago

I started playing with an implementation for writing lua language server definition files. Maybe it could be used here.

I was thinking it could be an opt-in crate separate from this crate, but it would be interesting to discuss various options!

mikavilpas commented 3 months ago

Unfortunately my attempt, while possible to do, proved to perhaps not provide enough value as it does not have a way to type check the annotations (or to generate them from rust code).

Such an approach came up in discussion here: https://github.com/wez/wezterm/issues/3132#issuecomment-2130041325

The idea, as I understand it, is to write a new feature to mlua that would allow exposing metadata about modules, functions, variables, etc. I think the best thing would be to be able to generate this at compile time with e.g. macros, so no runtime cost would have to be made. Then the feature could maybe be completely opt-in.

Does this sound like a possible approach?

CodeDoctorDE commented 3 months ago

I can only speak for myself but generating on compile time sounds like a good idea. If you make modding support for a game and want to export definition files, there is no really need to do this on runtime

mikavilpas commented 3 months ago

I ran into what look like promising existing toolkits fit for the purpose of generating some sort of FFI (foreign function interface) bindings from rust to other languages:

  1. UniFFI (https://github.com/mozilla/uniffi-rs) which seems to be the most popular (according to github stars 😄 ). What is notable is that it says it's used at Mozilla in the Firefox project.
  2. Diplomat (https://github.com/rust-diplomat/diplomat) is another option but seems to focus heavily on C language bindings as the shared interface. I don't know much about mlua and C, but maybe someone else might know if this is a viable option.
  3. Interoptopus (https://github.com/ralfbiedert/interoptopus) is a "polyglot bindings generator for your library"

Here are some suggestions for possible solutions to this problem:

  1. Pick option 1 (UniFFI), and add a feature to mlua (e.g. using proc macros) where a UniFFI Interface Definition file can be generated. If this is successful, a decoupled project could be built (e.g. the one I have started) to process these Interface Definition Files into LuaLS type annotation files. In theory, any other possible lua type information system could be supported without requiring changes to mlua.
  2. Option 3 (Interoptopus) might also work with the same idea.
Shelim commented 1 month ago

Run into the very same question :)

I am looking to integrate lua via mlua into game framework and my strict requirement is to provide vscode intellisense and (less strictly) debugging capabilities. The first one is supplied by Lua Definition Files, so I am having my eyes open here.

As of API design, I would recommend looking into serde's schemars - it is designed around macros, and docs-comments around actual struct- and methods definitions and it allows generating schema via macro, which can be then deployed into specific file as part of build- or runtime process. We actually use that in runtime, as we can supplement it with additional data from the current project files.

Any progress on this feature?

mikavilpas commented 1 month ago

From my side, I looked at all the options I listed in my previous comment, and it seems they could kind of work. All of them are designed for languages that are compiled into runtime code, which is not the case here (types are not executed at runtime). This is likely not a big issue, but in some cases it means we have to conform to a common api that is not designed for lua.

Ref: https://github.com/mozilla/uniffi-rs/issues/2144

Another interesting finding is that these FFI generation packages are designed to work based on either rust functions and data types (with some special restrictions), or an additional schema definition that's separate from rust source code. I felt my goal of having easy type definitions with minimal code changes by users (developers) cannot be achieved so easily.

Some more random notes:


I also have some ideas towards potential solutions:

Option 1: "fancy solution"

In this option, the types are automatically generated from the rust code. @Shelim, your idea about a schemars like api seems like the best option. In addition to data, functions would have to be supported.

I think this could be supported in a backward compatible manner if the user opts in to using it. It might work if they move their mlua invocations under a new trait whose methods have a signature that is compatible with whatever code generation tool is picked. Maybe the method calls are optimized away automatically to prevent a runtime cost, or maybe this can be done by inlining - I'm not sure.

Option 2: "simple solution"

This option is extremely simple:

Shelim commented 1 month ago

@mikavilpas

Option 1 is a dream-like scenario. I believe it can be implemented as separate crate, atop of mlua (and just call some imperative mlua api); The cost of indirect function calls would remain through (or maybe not, if for some clever usage of Rust macros...?)