Hi, i tried to edit script files by neovim,but lua-language-server need lua library files to complete code,how can i get those lua files?
There are a couple of annotation projects out there but I believe this project is the most maintained.
I recently updated the way we extract documentation from the engine and it is easier to also add support for Lua annotations now. I’d be happy to add support for this and include it in the ref-doc.zip file we generate for each release.
This issues covers the request at least partially:
If someone can share exact format you require of the annotations I’ll try to make it happen.
Great news!
Literally this is a kind of the expected final result.
Also there are docs about the annotations format.
It would be great if there was an automated test dedicated to finding inconsistencies in the type data in the API docs.
Off the top of my head, I can think of these problems:
- boolean type is sometimes
boolean
and sometimesbool
- number type is sometimes
number
and sometimesint
and sometimesinteger
- quaternion type is sometimes
quaternion
and sometimesquat
- there’s one single instance of
array
which is probably better astable
in the lua context
Unfortunately there are many of them.
- unknown types:
command
,route
,tiles
,stream
,handle
,texture
,buffer
, etc. - wrong types:
vecto4
,component[
,transaction_step[
,...any
,table<string,string>
, etc.
The issue mentioned above is excactly about this problem.
I think creating the pull requests by community to fix docs are okay but that’s not an efficient way to solve this problem for every release until Defold team doesn’t see these warnings anywhere.
As a solution, if @britzl will add the automatic generation for annotations to the pipeline, it should pass tests to ensure that there are no warnings from the Lua Language Server.
Is that not the way Defold is currently accepting contributions to improve the docs? I have wondered on and off about it.
Yep, I’ve noticed. I have added type validation in a local branch and man oh man there’s a lot! Missing types and wrong types… I’m going to fix all wrong types, and perhaps leave missing types as warnings for now.
I mean that it would be more efficient to have some kind of system with tests that would keep track of the validity of the documentation data. Now the main result of generation is the documentation on the website, and it forgives many mistakes, unlike annotations.
I’ve tried doing PRs in the past, but it takes time and also many things just can’t be resolved through fixes in the documentation (for example, new unknown types that are userdata). You can fix some obvious things like mistypes via PR in the documentation after release A, but they won’t get into the documentation files (which used to generate annotations) until the next release B, where there will be new things that can only be updated in release C, etc.
So when I see a new release and some problems in the annotations, I just make quick patches in the config so that end users get the correct annotations in the Defold Kit as soon as possible. It’s not the smartest way to handle it, so it would be great if the Defold team comes up with something to keep annotations in the perfect condition for Lua Language Server.
@astrochili Your annotations resource works well – it adapted seamlessly to the LSP plugin for Lite-XL, putting it in a folder in ~/.config/lite-xl/libraries/
Open question for the thread: what do you think is the best way to handle the types currently named constant
?
I understand most of them are numbers, but they are typically used differently from a number literal. I think you’d lose some nuance if they were all annotated as the number
type.
In TypeScript, they’re a great candidate for branded types. I’m not sure if there’s a Lua equivalent.
(For those who don’t want to read the above article: branded types are an intersection of a base type and custom defined type that contains no real properties and is only used to ensure the type is considered unique by the type checker. The article shows how this technique can be used to flag problems such as passing parameters into a function in the wrong order, even if the types would otherwise be identical.)
thinks, it’s a great help to me.
If you are sure that all the constants
are numbers
I think that’s good idea, why not? On the downside, it allows to pass any numbers as parameters, but again, why not.
I’ve declared constant
as userdata
because I’m not sure that there is any guarantee that constant
is always a number
and that it will be the same in the future. Also userdata
is something more mystical that the user never thinks to change, manipulate or modify and only uses as is, i.e. there is no reason for Defold API users to know which constants are of type and value, as it will return them back in the same shape.
So it doesn’t really matter what type constants are, as long as it doesn’t confuse you, motivate you to do something wrong with them, or cause LLS warnings.
I’m doing a cleanup of the API docs and I agree that “constants” aren’t great. In most cases they are indeed numbers and I think the type should be that, or that we introduce some kind of typedef for the Lua docs so that you can reference a named type and do a lookup somehow.
It’s not possbile to bring all Defold types to known Lua types, because it still requires to separate many userdata
types among themselves, such as node
, texture
, buffer_stream
and others. The aproach with references is good here.
Ok, so in the 1.10.3 beta we now generate *.lua files with annotations. The annotation files are stored together with the other ref doc files in ref-doc.zip:
It would be great if someone could check them out and provide feedback.
Also, in this version all typos and missing types have been fixed throughout the documentation. There’s still some uncertainty how to treat custom types, and while typing this I see that at least in the Lua annotations they should be handled using @alias
.
Looks good, I will check it asap!
Yeap, most of the userdata
types should be marked with with @alias. But not all of them. Types that are directly modified in the code must be declared as classes. Operators for vectors must also be declared.
Check the bottom of the meta.lua for examples.
UPDATE: Moved details to the next post.
Okay, here’s a list of what else needs to be done to the current output for the annotations to be correct:
-
Skip everything related to
lua_*
because Lua Language Server already includes annotations for Lua base standard library. -
Skip all the
proto-*
files because they just create dublicated empty namespaces. -
Fix some namespaces :
---@class defold_api.game object
game object = {} -- > go = {}
---@class defold_api.vector math
vector math = {} --> vmath = {}
---@class defold_api.bitop
bitop = {} --> bit = {}
---@class defold_api.message
message = {} --> msg = {}
---@class defold_api.system
system = {} --> sys = {}
---@class defold_api.collection factory
collection factory = {} --> collectionfactory = {}
---@class defold_api.collection proxy
collection proxy = {} --> collectionproxy = {}
---@class defold_api.particle effects
particle effects = {} --> particlefx = {}
---@class defold_api.collision object
collision object = {} --> physics = {}
---@class defold_api.luasocket
luasocket = {} --> socket = {}
- Don’t create empty namespaceslike
built_ins
:
---@class defold_api.built_ins
built_ins = {}
-
Skip empty namespace declarations if they already exist:
script_sys.cpp_doc.lua
→scripts-script_sys_gamesys.cpp_doc.lua
scripts-script_camera.cpp_doc.lua
→render-render_script_camera.cpp_doc.lua
script-sys_ddf.proto_doc.lua
→scripts-script_sys_gamesys.cpp_doc.lua
-
Fix redundant namespace prefixes like
editor.*
andjson.*
:
editor.editor.platform = nil
editor.http.server.local_url = nil
editor.zip.METHOD.DEFLATED = nil
json.json.null = nil
...
- Automatically create all the folded namespaces:
---@class editor.bundle
editor.bundle = {}
---@class editor.prefs
editor.prefs = {}
---@class editor.prefs.SCOPE
editor.prefs.SCOPE = {}
---@class editor.prefs.schema
editor.prefs.schema = {}
---@class editor.tx
editor.tx = {}
---@class editor.ui
editor.ui = {}
---@class editor.ui.ALIGNMENT
editor.ui.ALIGNMENT = {}
---@class editor.ui.COLOR
editor.ui.COLOR = {}
---@class editor.ui.HEADING_STYLE
editor.ui.HEADING_STYLE = {}
---@class editor.ui.ICON
editor.ui.ICON = {}
---@class editor.ui.ISSUE_SEVERITY
editor.ui.ISSUE_SEVERITY = {}
---@class editor.ui.ORIENTATION
editor.ui.ORIENTATION = {}
---@class editor.ui.PADDING
editor.ui.PADDING = {}
---@class editor.ui.SPACING
editor.ui.SPACING = {}
---@class editor.ui.TEXT_ALIGNMENT
editor.ui.TEXT_ALIGNMENT = {}
---@class http.server
http.server = {}
- Replace all the constant types in @param with the
constant
type. Because it doesn’t work by this way:
---@param easing vector|go.EASING_INBACK|go.EASING_INBOUNCE|go.EASING_INCIRC|go.EASING_INCUBIC|go.EASING_INELASTIC|go.EASING_INEXPO|go.EASING_INOUTBACK|go.EASING_INOUTBOUNCE|go.EASING_INOUTCIRC|go.EASING_INOUTCUBIC|go.EASING_INOUTELASTIC|go.EASING_INOUTEXPO|go.EASING_INOUTQUAD|go.EASING_INOUTQUART|go.EASING_INOUTQUINT|go.EASING_INOUTSINE|go.EASING_INQUAD|go.EASING_INQUART|go.EASING_INQUINT|go.EASING_INSINE|go.EASING_LINEAR|go.EASING_OUTBACK|go.EASING_OUTBOUNCE|go.EASING_OUTCIRC|go.EASING_OUTCUBIC|go.EASING_OUTELASTIC|go.EASING_OUTEXPO|go.EASING_OUTINBACK|go.EASING_OUTINBOUNCE|go.EASING_OUTINCIRC|go.EASING_OUTINCUBIC|go.EASING_OUTINELASTIC|go.EASING_OUTINEXPO|go.EASING_OUTINQUAD|go.EASING_OUTINQUART|go.EASING_OUTINQUINT|go.EASING_OUTINSINE|go.EASING_OUTQUAD|go.EASING_OUTQUART|go.EASING_OUTQUINT|go.EASING_OUTSINE
-->
---@param easing vector|constant description...
- Mark optional params as optionals with the
?
mark or with the|nil
if it’s more suitable:
---@param delay number delay before the animation starts in seconds
---@param complete_function function(self, url, property) optional function to call when the animation has completed
-->
---@param delay? number delay before the animation starts in seconds
---@param complete_function? fun(self, url, property) optional function to call when the animation has completed
- Use
fun()
instead offunction()
in params. It allows to declare a function with params as a param. Otherwise function’s params will be ignored by LLS:
---@param callback function(self, event_type)|nil
-->
---@param callback? fun(self, event_type)
- Avoid types like
type...
, LLS doesn’t understand it:
---@param ...commands string bob commands, e.g. <code>"resolve"</code> or <code>"build"</code>
function editor.bob(options, ...commands) end
-->
---@param ...? string bob commands, e.g. "resolve" or "build"
function editor.bob(options, ...) end
---@param x2... number number(s)
function bit.bor(x1, x2...) end
-->
---@param ...? number number(s)
function bit.bor(x1, ...) end
---@param x2... number number(s)
function bit.band(x1, x2...) end
-->
---@param ...? number number(s)
function bit.band(x1, ...) end
---@param x2... number number(s)
function bit.bxor(x1, x2...) end
-->
---@param ...? number number(s)
function bit.bxor(x1, ...) end
- Skip all the script functions from the
gameobject_script.cpp_doc.lua
because there is no need to annotate them as global functions:
'init'
'update'
'fixed_update'
'on_input'
'on_message'
'on_reload'
'final'
- Also remove these socket
self:
functions from global space because we don’t have these global namespaces. They should be declared in the classes liketcp_client
,udp_unconnected
instead (may be later).
'client:*'
'server:*'
'master:*'
'connected:*'
'unconnected:*
- Add aliases. Example:
---@alias array table
---@alias b2Body userdata
---@alias b2BodyType number
---@alias b2World userdata
---@alias bool boolean
---@alias buffer_data userdata
---@alias buffer_stream userdata
---@alias constant number
---@alias constant_buffer userdata
---@alias editor.command userdata
---@alias editor.component userdata
---@alias editor.schema userdata
---@alias editor.tiles userdata
---@alias editor.transaction_step userdata
---@alias float number
---@alias hash userdata
---@alias http.response userdata
---@alias http.route userdata
---@alias node userdata
---@alias quaternion vector4
---@alias render_predicate userdata
---@alias render_target string|userdata
---@alias resource_data userdata
---@alias resource_handle number|userdata
---@alias socket_client userdata
---@alias socket_master userdata
---@alias socket_unconnected userdata
---@alias vector userdata
- Add basic classes and operators for vectors. Example:
---@class url
---@field fragment hash
---@field path hash
---@field socket hash
---@class vector3
---@field x number
---@field y number
---@field z number
---@operator add(vector3): vector3
---@operator mul(number): vector3
---@operator sub(vector3): vector3
---@operator unm: vector3
---@class vector4
---@field w number
---@field x number
---@field y number
---@field z number
---@operator add(vector4): vector4
---@operator mul(number): vector4
---@operator sub(vector4): vector4
---@operator unm: vector4
---@class matrix4
---@field c0 vector4
---@field c1 vector4
---@field c2 vector4
---@field c3 vector4
---@field m00 number
---@field m01 number
---@field m02 number
---@field m03 number
---@field m10 number
---@field m11 number
---@field m12 number
---@field m13 number
---@field m20 number
---@field m21 number
---@field m22 number
---@field m23 number
---@field m30 number
---@field m31 number
---@field m32 number
---@field m33 number
- [To think] It would also be cool to have table descriptions that are passed as parameters or returned as results, but there’s a lot of work involved, but it’s nice to have. To begin with, of course, it could all be just a
table
. Some examples:
---@class on_input.action
---@field dx? number The change in x value of a pointer device, if present.
---@field dy? number The change in y value of a pointer device, if present.
---@field gamepad? integer The change in screen space y value of a pointer device, if present.
---@field pressed? boolean If the input was pressed this frame. This is not present for mouse movement.
---@field released? boolean If the input was released this frame. This is not present for mouse movement.
---@field repeated? boolean If the input was repeated this frame. This is similar to how a key on a keyboard is repeated when you hold it down. This is not present for mouse movement.
---@field screen_dx? number The change in screen space x value of a pointer device, if present.
---@field screen_dy? number The index of the gamepad device that provided the input.
---@field screen_x? number The screen space x value of a pointer device, if present.
---@field screen_y? number The screen space y value of a pointer device, if present.
---@field text? string The text entered with the `text` action, if present
---@field touch? on_input.touch[] List of touch input, one element per finger, if present.
---@field value? number The amount of input given by the user. This is usually 1 for buttons and 0-1 for analogue inputs. This is not present for mouse movement.
---@field x? number The x value of a pointer device, if present.
---@field y? number The y value of a pointer device, if present.
---@class on_input.touch
---@field acc_x? number Accelerometer x value (if present).
---@field acc_y? number Accelerometer y value (if present).
---@field acc_z? number Accelerometer z value (if present).
---@field dx number The change in x value.
---@field dy number The change in y value.
---@field id number A number identifying the touch input during its duration.
---@field pressed boolean True if the finger was pressed this frame.
---@field released boolean True if the finger was released this frame.
---@field screen_dx? number The change in screen space x value of a pointer device, if present.
---@field screen_dy? number The index of the gamepad device that provided the input.
---@field screen_x? number The screen space x value of a pointer device, if present.
---@field screen_y? number The screen space y value of a pointer device, if present.
---@field tap_count integer Number of taps, one for single, two for double-tap, etc
---@field x number The x touch location.
---@field y number The y touch location.
---@class physics.raycast_response
---@field fraction number The fraction of the hit measured along the ray, where 0 is the start of the ray and 1 is the end
---@field group hash The collision group of the hit collision object as a hashed name
---@field id hash The instance id of the hit collision object
---@field normal vector3 The normal of the surface of the collision object where it was hit
---@field position vector3 The world position of the hit
---@field request_id number The id supplied when the ray cast was requested
---@class resource.animation
---@field flip_horizontal? boolean Optional flip the animation horizontally, the default value is false
---@field flip_vertical? boolean Optional flip the animation vertically, the default value is false
---@field fps? integer Optional fps of the animation, the default value is 30
---@field frame_end integer Index to the last geometry of the animation (non-inclusive). Indices are lua based and must be in the range of 1 .. in atlas.
---@field frame_start integer Index to the first geometry of the animation. Indices are lua based and must be in the range of 1 .. in atlas.
---@field height integer The height of the animation
---@field id string The id of the animation, used in e.g sprite.play_animation
---@field playback? constant Optional playback mode of the animation, the default value is go.PLAYBACK_ONCE_FORWARD
---@field width integer The width of the animation
---@class resource.atlas
---@field animations resource.animation[] A list of the animations in the atlas
---@field geometries resource.geometry[] A list of the geometries that should map to the texture data
---@field texture string|hash The path to the texture resource, e.g "/main/my_texture.texturec"
---@class resource.geometry
---@field id string The name of the geometry. Used when matching animations between multiple atlases
---@field indices number[] A list of the indices of the geometry in the form { i0, i1, i2, ..., in }. Each tripe in the list represents a triangle.
---@field uvs number[] A list of the uv coordinates in texture space of the geometry in the form of { u0, v0, u1, v1, ..., un, vn }
---@field vertices number[] A list of the vertices in texture space of the geometry in the form { px0, py0, px1, py1, ..., pxn, pyn }
- [To think] Remove all the HTML tags or replace them with Markdown:
---@return nil|string result If <code>out</code> option is set to <code>"capture"</code>, returns the output as string with trimmed trailing newlines. Otherwise, returns <code>nil</code>.
-->
---@return nil|string result If `out` option is set to `capture`, returns the output as string with trimmed trailing newlines. Otherwise, returns `nil`.
- [To think] Isolate too rare to use but too general aliases to avoid conflicts with the developer local annotations, for example:
command -> editor.command
resource -> defold.resource
master -> socket.master
... etc ...
At the same time don’t isolate vector3
, vector4
, hash
, url
, node
types because they are used everywhere otherwise it will be uncomfortable to use them in local annotations.
OFF-TOPIC:
By the way, this is why I don’t like the idea of the globaltilemap
variable, which exists only for the Editor, but from the Lua Language Server point of view it ends up taking up namespace and adds confusion for the developer if he uses his owntilemap
variable. It’s would be better top wraptilemap
toeditor.tilemap
Validation is easy.
Just open the annotations folder in VSCode with Lua Language Server extension installed and wait for LLS to finish analyzing all files.
Right now it looks like this:
After manual edits according the list above it looks valid:
The same siutuation with the last defold-annotations release:
Moved all the information to the separated issue.