Open Luke100000 opened 6 months ago
We don't have a way to do this currently. You may be interested in writing a short plugin that would do that for you.
I looked into plugins and I can e.g. replace ---@override
with a ---@see parent.method
, but I don't know how I can retrieve the parents function definition directly.
Oh, looks like the Wiki docs aren't up to date anymore. There is more info on plugins on the project's GitHub pages: https://luals.github.io/wiki/plugins/. I can't really help you from here, I haven't used plugin api.
Alright thank you for the links, I made a plugin which injects ---@alias
at definitions, and ---@type
and ---@see
at overrides. Only param/return comments disappear but otherwise seems to be working fine for me.
However I struggle to understand another concept, maybe you can help me here :)
---@class (exact) A
---@field ox number
local a = {}
function a:init()
--This is fine
self.ox = 0
end
---@class (exact) B : A
local b = {}
function b:init()
--This is an injection and thus invalid
self.ox = 0
end
---@type B
local x
--But this is fine again?
x.ox = 0
Why does it want to inject a new field into B in the B's init? How can I specify that its fine to inject when its already inherited?
Another viewpoint would be: I don't want inheritance, but composition. Right now I have A.ox
AND B.ox
, but in my case it doesn't matter who introduced that value, its now just ox
.
In my opinion, inject field diagnostic isn't smart enough yet and has its false positives. I suggest opening an issue or looking if an issue about this was already reported.
同求相关支持。
使用场景: 项目中有很多角色脚本通过外部的通知进行驱动(击中\玩家输入之类),每个脚本针对相同通知的响应逻辑都不同,因此创建了一个父类用来定义一共有多少个通知响应接口以及每个接口的参数类型,具体的实现就交由各个脚本。
现有问题: 角色重写父类函数时会识别为一个在局部新建的函数,并默认将所有参数类型全部设置为 any,导致父类函数定义的参数类型在重写的函数体内全部丢失。
这里写定义的时候还是可以正确识别的。
进入函数体内之后参数类型就丢失了。
期待效果: 希望子类在重写父类函数时,除非子类手动重新指定函数的参数类型或者增加新的参数,否则子类函数直接采用父类函数定义的参数类型。
Alright thank you for the links, I made a plugin which injects
---@alias
at definitions, and---@type
and---@see
at overrides. Only param/return comments disappear but otherwise seems to be working fine for me.However I struggle to understand another concept, maybe you can help me here :)
---@class (exact) A ---@field ox number local a = {} function a:init() --This is fine self.ox = 0 end ---@class (exact) B : A local b = {} function b:init() --This is an injection and thus invalid self.ox = 0 end ---@type B local x --But this is fine again? x.ox = 0
Why does it want to inject a new field into B in the B's init? How can I specify that its fine to inject when its already inherited?
Another viewpoint would be: I don't want inheritance, but composition. Right now I have
A.ox
ANDB.ox
, but in my case it doesn't matter who introduced that value, its now justox
.
Could you please consider sharing your plug-in? I'm stuck with then same issue.
Appologise if you've already done publishing it. I've tried and failed to find it.
Could you please consider sharing your plug-in? I'm stuck with then same issue.
Appologise if you've already done publishing it. I've tried and failed to find it.
Here: https://pastebin.com/fmWLcjjw It works for me but I'm certain that there are edge cases which breaks it.
Annotate overridden functions with ---@override
and it will output something like this:
---@class (exact) A
local a = {}
---Test Comment
---@param test string Some argument
---@return string @ Some return
---@alias def.A.Method fun(self: A, test : string) : string
function a:Method(test) return "" end
---@class (exact) B : A
local b = {}
---@see A.Method
---@type def.A.Method
function b:Method(test)
return "hi"
end
---@type B
local test
test:Method("") -- Annotations are there!
Thanks so much!
Partially implemented in https://github.com/LuaLS/lua-language-server/commit/7c481f57c407f7e4d0b359a3cfbce66add99ec2f now, arguments are inferred.
Thanks for the work and the notification, but there's still some distance to what I need.
In my use case, I have lots of objs which are only generated at runtime but customized at coding stage. They're still not getting any auto completion inside their overridden function bodies.
For example, I need to define the logic of diferent character skills, but skill information is only fetched at runtime and it's the same with skill objs. Thus, the code will end up like this: To make it more complicated, the definition of skill, generate_skill_objs, list, list.skill1.onBegin all belongs to different files.
Could someone please look into this? Can't be more grateful.
If I understand the recent change correctly, it's for child class overriding parent class's method.
In the code you provided, the list
is table<string, skill>
, which means list.skill1
is still skill
class (not a child class). Therefore luals will think you are redefining the skill:onBegin()
in the skill
class, so it will instead throw a duplicate-set-field
warning.
---@class skill
local skill = {}
---@param casterID integer
---@param targetID integer
function skill:onBegin(casterID, targetID) end
---@param casterID integer
---@param targetID integer
function skill:onEnd(casterID, targetID) end
---@return table<string, skill>
local generate_skill_objs = function ()
return {}
end
local list = generate_skill_objs()
function list.skill1:onBegin(casterID, targetID) end --< duplicate-set-field
function list.skill1:onEnd(casterID, targetID) end --< duplicate-set-field
In order to make use of the recent change, you have to create new class for each of your skill, and extending the base skill class, like the following
-- same code to generate `list` as above
---@class skill1: skill
local skill1 = list.skill1
function skill1:onBegin(casterID, targetID)
print(casterID, targetID) --< integer, integer
end
function skill1:onEnd(casterID, targetID)
print(casterID, targetID) --< integer, integer
end
It can be interpreted as skill1
is implementing the skill
interface 🤔
But as you can see, this requires an extra @class
annotation for each of your skill, ~and you said the definitions belong to different files which makes things more complicated~, so it may not suit you need... 😇 @NicameOurister
edit: Seems it doesn't matter if all the definitions are in different files 🤔 Because right after using:
---@class skills1: skill
local skill1 = list.skill1
list.skill1
and cast it to the new skills1
typelocal skill1
class variable, while still getting the parent method's params' typeIf I understand the recent change correctly, it's for child class overriding parent class's method. In the code you provided, the
list
istable<string, skill>
, which meanslist.skill1
is stillskill
class (not a child class). Therefore luals will think you are redefining theskill:onBegin()
in theskill
class, so it will instead throw aduplicate-set-field
warning.---@class skill local skill = {} ---@param casterID integer ---@param targetID integer function skill:onBegin(casterID, targetID) end ---@param casterID integer ---@param targetID integer function skill:onEnd(casterID, targetID) end ---@return table<string, skill> local generate_skill_objs = function () return {} end local list = generate_skill_objs() function list.skill1:onBegin(casterID, targetID) end --< duplicate-set-field function list.skill1:onEnd(casterID, targetID) end --< duplicate-set-field
In order to make use of the recent change, you have to create new class for each of your skill, and extending the base skill class, like the following
-- same code to generate `list` as above ---@class skill1: skill local skill1 = list.skill1 function skill1:onBegin(casterID, targetID) print(casterID, targetID) --< integer, integer end function skill1:onEnd(casterID, targetID) print(casterID, targetID) --< integer, integer end
It can be interpreted as
skill1
is implementing theskill
interface 🤔 But as you can see, this requires an extra@class
annotation for each of your skill, ~and you said the definitions belong to different files which makes things more complicated~, so it may not suit you need... 😇 @NicameOuristeredit: Seems it doesn't matter if all the definitions are in different files 🤔 Because right after using:
---@class skills1: skill local skill1 = list.skill1
- luals will ignore the original type in
list.skill1
and cast it to the newskills1
type- and you can start overriding methods in the
local skill1
class variable, while still getting the parent method's params' type
Thanks for comment.
The recent change does cover part of my need, but not all of it. I know that I can do it this way. but there's still reason for the need of a more powerful implement.
In my case, there are tens of skill objs in every character's script, and the number of character script is more. It will be considerable lines of redundant code and makes it easier to cause name conflict.
Thus, I'm still hoping that we can just skip all those repeat annotations. Hopefully this enhancement could solve all the same related issues in #477, #1779 and #1757.
btw your link for #1779
and #1757
are incorrect, they all points to #477
🙈
Actually you can just write #1779
or paste the link https://github.com/LuaLS/lua-language-server/issues/1779
when you write comment, and github will be smart enough to turns it into [#1779](https://github.com/LuaLS/lua-language-server/issues/1779)
, which can be verified in Preview
tab. No need to write the url markdown in full form 😄
btw your link for
#1779
and#1757
are incorrect, they all points to#477
🙈Actually you can just write
#1779
or paste the linkhttps://github.com/LuaLS/lua-language-server/issues/1779
when you write comment, and github will be smart enough to turns it into[#1779](https://github.com/LuaLS/lua-language-server/issues/1779)
, which can be verified inPreview
tab. No need to write the url markdown in full form 😄
Thank you!
Strange, if I define the interface using @type fun()
style, then when overriding it, params' types are there ‼️ 🤔
(although it will still trigger duplicate-set-field
warnings)
---@class skill
local skill = {}
---@type fun(self: skill, casterID: integer, targetID: integer)
function skill:onBegin(casterID, targetID) end
---@type fun(self: skill, casterID: integer, targetID: integer)
function skill:onEnd(casterID, targetID) end
---@return table<string, skill>
local generate_skill_objs = function ()
return {}
end
local list = generate_skill_objs()
function list.skill1:onBegin(casterID, targetID) end --< integer, integer, but with `duplicate-set-field` warning
function list.skill1:onEnd(casterID, targetID) end --< integer, integer, but with `duplicate-set-field` warning
I think I have found a way!! 😄 @NicameOurister
@field
first, then all the methods will have params type infer && no duplicate-set-field warningsfun()
syntax do not support multi line)
---@class skill
---@field onBegin fun(self: skill, casterID: integer, targetID: integer)
---@field onEnd fun(self: skill, casterID: integer, targetID: integer)
local skill = {}
function skill:onBegin(casterID, targetID) print(casterID, targetID) -- integer, integer -- your default implementation end
function skill:onEnd(casterID, targetID) print(casterID, targetID) -- integer, integer -- your default implementation end
---@return table<string, skill> local generate_skill_objs = function () return {} end local list = generate_skill_objs()
function list.skill1:onBegin(casterID, targetID) print(casterID, targetID) -- integer, integer -- no duplicate-set-field end function list.skill2:onBegin(casterID, targetID) print(casterID, targetID) -- integer, integer -- no duplicate-set-field end
---
I'm still trying to find out why `@param` style won't work 🤔
Thanks for the solution!
It works as needed and does really solved the biggest problem, but it produces some little new problems. The most obvious problem is that the ---field definition will mask out the real code and becomes the only target of that function's definition jump.
I can totally accept those extra ---field definitions . But I'm still kind of bothered with the definition jump mask problem. After all, the whole purpose of mine is to make my code library as accessible as possible for others.
I'm going to settle with the current solution while keep looking for a better way. Thanks again for the help.
Strange, if I define the interface using @type fun() style, then when overriding it, params' types are there ... I'm still trying to find out why
@param
style won't work
I think I have successfully made it working without breaking anything 🎉 (at least all existing tests passed).
I believe the function param infer logic resides in here: https://github.com/LuaLS/lua-language-server/blob/b9639f6e135b9ab5340b5e64c64f3643a9618a74/script/vm/compiler.lua#L1110-L1124
@type fun()
works but not @param
, is because the former one is of doc.type.function
type. Here the logic will only consider the arguments' node types in a doc.type.function
(the fun()
annotation)if
conditions in line 1124 to include function
type, then it works!
But with some side effects and breaking the tests... 🙁 function
infer type. And this causes problems when trying to infer its own params' type by its own definition => loop => causing the types to be unknown
instead of any
source
:
if (n.type == 'doc.type.function' or n.type == 'function')
and n.args[aindex] and n.args[aindex] ~= source
then
note the n.args[aindex] ~= source
added here => then all existing tests passed 🙂
We still have to deal with the duplicate-set-field
warning. The related logic is here: https://github.com/LuaLS/lua-language-server/blob/b9639f6e135b9ab5340b5e64c64f3643a9618a74/script/core/diagnostics/duplicate-set-field.lua#L54-L70
So we have to find a way to skip the warning of this overriding class function / method use case. The one that I can think of is that:
whenever the function definition (def
in the above logic) is defined within a class
namespace, we should allow overriding it (src
in the above logic) in a non-class
namespace.
---@class A; local A = {}
, here A
has a class
attached. If there are duplicated function A:f() end; function A:f() end
=> should throw warnings
---@class A
local A = {}
function A:f() end
function A:f() end -- should throw warnings
---@type A; local a = {}
, here a
has no class
attached. Then we should allow function a:f() end
for overriding
---@class A
local A = {}
function A:f() end
---@type A local a = {} function a:f() end -- allow this
if vm.getDefinedClass(guide.getUri(def), def.node)
and not vm.getDefinedClass(guide.getUri(src), src.node)
then
-- allow type variable to override function defined in class variable
goto CONTINUE
end
With the above proposed change, now we can have param type inference in your original annotation style, and without duplicate-set-field
warnings 👀
---@class skill
local skill = {}
---@param casterID integer
---@param targetID integer
function skill:onBegin(casterID, targetID) end
---@type table<string, skill>
local list = {}
function list.skill1:onBegin(casterID, targetID)
print(casterID, targetID) -- integer, integer
-- no `duplicate-set-field` as well
end
function list.skill2:onBegin(casterID, targetID)
print(casterID, targetID) -- integer, integer
-- no `duplicate-set-field` as well
end
I will need some time to do a more in-depth test before I create a PR. Meanwhile you may try to patch this in your local copy of luals to see the actual effect 😄@NicameOurister
Roger that. Thanks for all these work.
My related PR is merged recently, so this use case: https://github.com/LuaLS/lua-language-server/issues/2569#issuecomment-2342854384 will be supported in the next release 👀
It would be great if I can override fields, not just shadow them, to retain their signature and doc.
The signature wont change for overridden methods, and thus I would like to avoid copy pasting the whole doc for each method. How would I resolve this efficiently?