LuaLS / lua-language-server

A language server that offers Lua language support - programmed in Lua
https://luals.github.io
MIT License
3.26k stars 306 forks source link

table literals with descriptions #2128

Open max397574 opened 1 year ago

max397574 commented 1 year ago

I expect something like this or something similar to this to work but it doesn't also when I e.g. split it up onto multiple lines

---@field cat { ["name"]: string # the name of the cat, ["age"]: integer # the age of the cat }

everything is working fine without the descriptions

C3pa commented 1 year ago

Currently, you can achieve this if you create a new class for the cat field:

---@class catClass
---@field name string the name of the cat
---@field age integer the age of the cat

---@class someClass
---@field cat catClass
---@field ...
max397574 commented 1 year ago

yes but for my usecase this isn't quite a good way of doing it since the "classes" actually don't have a name they should be inline

C3pa commented 1 year ago

since the "classes" actually don't have a name

Mind elaborating a bit on this?

If I understood correctly, you don't want to define the catClass because it doesn't exist in real Lua code?

max397574 commented 1 year ago

yes

C3pa commented 1 year ago

Well, why not? That's how many do it. In the end, the annotations are just comments. These affect just your autocomplete suggestions, not code behavior.

sumneko commented 1 year ago

I haven't come up with a better syntax yet. For example, in the solution you provided, I can't decide where the comment ends.

(Translated by ChatGPT)

max397574 commented 3 months ago

could this perhaps be done when you spread the thing to multiple lines similarly to how it's possibel to add descriptions to enums? @sumneko

DrSloth commented 2 months ago

Hello, i am also very interested in this feature. In my use case there are many functions that have "named arguments" they just receive a table as first arg and take the values from there. If someone wants to give me pointers where to start i would be glad to help implement this.

tmillr commented 1 month ago

I haven't come up with a better syntax yet. For example, in the solution you provided, I can't decide where the comment ends.

(Translated by ChatGPT)

Maybe you could support syntax such as this:

-- Leading whitespace is insignificant and removed/ignored by the LSP.
-- Also if a line ends with `,` or `;`, ignore/remove it from the description as well.

---@field cat {
---  name: string the name of the cat
---  age: integer the age of the cat
---}

---@param cat {
---  name: string the name of the cat,
---  age: integer the age of the cat,
---}

Then just keep a count of balanced braces to find the end.


Or, maybe:

---@field cat {
---  @field name string the name of the cat
---  @field age integer the age of the cat
---}

---@param cat {
---  @field name string the name of the cat
---  @field age integer the age of the cat
---}
tomlau10 commented 1 month ago

Using a @class would be better. As otherwise with @alias or literal table structure annotation, it would lead to this issue: https://github.com/LuaLS/lua-language-server/issues/2785#issuecomment-2270556775, where the whole table structure get expanded during hover preview / set type inline hint 😇

I understand the argument here that the cat just a field with a specific table value, and is not a class. But in LuaLS class is just a type container. If the concern is not wanting to pollute the class types list, we may use namespaces in class names, just like what the luv library did: https://github.com/LuaCATS/luv/blob/3615eb12c94a7cfa7184b8488cf908abb5e94c9c/library/types.lua#L74-L96

---@class uv.fs_stat.result.time
---@field sec integer
---@field nsec integer

---@class uv.fs_stat.result
---
---@field dev       integer
---@field mode      integer
---@field nlink     integer
---@field uid       integer
---@field gid       integer
---@field rdev      integer
---@field ino       integer
---@field size      integer
---@field blksize   integer
---@field blocks    integer
---@field flags     integer
---@field gen       integer
---@field atime     uv.fs_stat.result.time
---@field mtime     uv.fs_stat.result.time
---@field ctime     uv.fs_stat.result.time
---@field birthtime uv.fs_stat.result.time
---@field type      string
C3pa commented 1 month ago

I totally agree with tomlau10 here. We do the same in MWSE: https://github.com/MWSE/MWSE/blob/master/misc/package/Data%20Files/MWSE/core/meta/lib/tes3.lua#L34 For a function with the following signature, we do the annotations like this:

--- @param params tes3.addArmorSlot.params
function tes3.addArmorSlot(params) end

---Table parameter definitions for `tes3.addArmorSlot`.
--- @class tes3.addArmorSlot.params
--- @field slot number Armor slot number. A number greater than 10 to configure a slot for.
--- @field name string The human-readable name for the armor slot.
--- @field key string? *Optional*. The key placed in the `tes3.armorSlot` table. If no key is provided, the name will be used.
--- @field weight number? *Default*: `0`. A stand-in for the armor base weight value, typically controlled by a GMST (e.g. iHelmWeight).
--- @field scalar number? *Default*: `0.1`. A multiplier with range 0.0-1.0 that controls how much of an item's armor value applies to a character's overall armor rating. For comparison, standard chest armor uses 0.3, helmets, greaves and pauldrons use 0.1, and gauntlets use 0.05.
tmillr commented 1 month ago

The current convention is to just come up with a name and create classes (unless you only have 2-3 fields and are sure that you don't need descriptions), but it still might be nice to have a shorthand for certain cases (mainly function args/kwargs where the table type is unique and has a small number of fields that need descriptions). It's effectively an anonymous type, or an extension of a function's type/signature. But it's not a necessary feature and we can do without it for now (since we have the @class workaround).

ChristinWhite commented 6 days ago

Is there a reason something like this wouldn't be a good idea for a small number of fields that are functioning as named arguments?

--- @param opts table ...
--- @param opts.name string ...
--- @param opts.age integer ... 

or

--- @param opts table ...
--- @field opts.name string ...
--- @field opts.age integer ... 

This would maintain nearly the same annotation conventions without adding a lot of complexity for cases when there are only 2–3 fields.

Classes certainly work, but it feels like it adds a level of visual noise and the cognitive load that may not be necessary. I'd prefer to be able to read through it linearly without bouncing between different annotation blocks

tomlau10 commented 6 days ago

Is there a reason something like this wouldn't be a good idea for a small number of fields

Here maybe the problems (just my guess)