Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Suggestions for improving our constructors?#3286

Unanswered
ipsquiggle asked this question inQ&A
Discussion options

Hello, we are using a class system which requires passing in the constructor as a lambda. This plays quite poorly with LuaLS as far as I can tell. I've made it work, sort of sometimes, but am wondering if anyone has suggestions to make this more succinct.

Here's what I've got:

---@classTestClass---@overloadfun(self:self,param:string):self   -- 2a---@overloadfun(param:string):self   -- 2blocalTestClass=Class(function(self,param)-- 1---@classTestClass          -- 3bself=self-- 3cself.field_constr=param-- 3aend)functionTestClass:Bar()self.field_meth="a"-- 4endlocaltest_inst=TestClass("b")

Okay, kind of awkward. Let's talk about what I've got here.

  1. You can see here our class factory taking the lambda for the constructor. The source of the problems.
  2. Double overload! The first overload (2a) gets applied to the function on the next line (unexpectedly?) and gives us proper type information within the constructor. The second overload (2b) gets applied when instantiating the classTestClass("b")
  3. HOWEVER: 3a creates a "fields can not be injected" warning, because it seesself as just a normal parameter. In order to fix this, I have to redeclare the class in this scope (3b) and then provide some variable to the declaration to attach to (3c). This is highly unfortunate.
  4. Unexpectedly (?) fields injected in other methods don't have any complaints.

After doing this, the autocomplete ontest_inst is.field_constr,.field_meth, and:Bar() as expected. If I leave out 3b/3c, then.field_constr is missing (because it was an illegal injection).


So my question is:Can anyone suggest any ways to clean up this mess? Ideally, 3a just works all the time, 3b and 3c are not necessary at all, and in a perfect world, 2a is not even necessary. We have tons of legacy code and so can not change the way our class system works, and are trying to find a compromise that gives us the best type information and the fewest warnings.


Edit: cleaned up unhelpful wordings.

You must be logged in to vote

Replies: 3 comments 7 replies

Comment options

In a perfect world, I feel like passingself: self in the overload should trigger the same behaviour as a method definition, in that it allows injection (opposed to passingt: TestClass would treat it as a normal parameter and prevent injection)...

You must be logged in to vote
3 replies
@ipsquiggle
Comment options

Side note to this: What is the actual behaviour of theself type? It's undocumented and I discovered it by accident.

@tomlau10
Comment options

I also noticed thisself type before.
I duno much about it, but AFAIK there are some issues with it: It doesn't follow inheritance type 🤔

There are issues reporting this:

And the workaround to that inheritance issue:#3057 (comment)

@ipsquiggle
Comment options

Ah interesting good to know. Thank you for your insights as always! 🙏

Comment options

While I don't have any good ideas atm (since you said refactoring is not a solution due to tons of legacy code 😇)
I would like to reply to some of your points:

  1. Unexpectedly (?) fields injected in other methods don't have any complaints.

You may already knew it, that you can only "inject" fields to variables that have@class attached to.
That's how you "fix" the situation of point3.
But for (4) this is actually as designed I believe

  • theTestClass local variable has marked with@class TestClass in line 4
  • then when declaring methods usingfunction TestClass:Bar(), the hidden variableself will also be marked with@class TestClass under the hood
  • soself.field_meth is auto "injected" intoTestClass
  • this is different from (1), because the server has no way to auto markself as@class TestClass
    and you also already knew that a variable marked with just@type behaves differently from a variable marked with@class
You must be logged in to vote
1 reply
@ipsquiggle
Comment options

That all makes sense, thank you for the clear explanation!

I really sympathize with the contributors on this project, lua is super weird and lets you always do weird things, creating reasonable default behavior must be such a challenge!

Comment options

I suddenly have an idea that, maybe you can use theplugin feature to achieve your need 🤔
https://luals.github.io/wiki/plugins/

  • a plugin is like a custom special script read by the server for your project
  • one of its ability isOnSetText which cantransform the text buffer of a file when viewed by the server
  • so in the script you can use some string pattern matching and replacing, to transform the code in yourideal case become the "awkward" form which is needed by the server to make it work 🤔 🤔

Here's my try

  • plugin.lua
functionOnSetText(uri,text)-- print(uri, text)-- fix custom constructorlocaldiffs= {}forstart,overload,className,finishintext:gmatch'()(---@overload[^\n]+\n)local (%w+) = Class%(func[^\n]+\n()'do-- print(start, "|", overload, "|", className, "|", finish)diffs[#diffs+1]= {start=start,finish=start-1,text=overload:gsub("fun%(","fun(self: self,"),        }diffs[#diffs+1]= {start=finish,finish=finish-1,text= ("---@class %s\n---@diagnostic disable-next-line\nlocal self = {}\n"):format(className),        }endreturn#diffs>0anddiffsornilend
  • what this plugin is doing
    • capture classname in (1) and the whole overload line (2b)
    • insert the captured overload line before (2b) but withself: self injected in it
    • finally add the---@class %s\n---@diagnostic disable-next-line\nlocal self = {}\n after location (1)

PS: duno why need to uselocal self = self instead of justself = self, otherwise the field inject won't take effect during workspace init 😕
and the---@diagnostic disable-next-line is used to silent theRedefined local `self` warning

demo

(diffed.lua is a debug file showing the transformed buffer, generated by the server when starting it with--develop, more details on official wiki page)
image

however...

I experienced some issues with this "plugin" system...

  • the auto complete triggered when you typetest_inst. doesn't containfield_constr ...
    only if you pressesc and retrigger it, this field will show up
  • and if the class definition is placed in one file, sayTestClass.lua
    and you create an instance of this class inanother file
    => thefield_constr is not recognized until you open thisTestClass.lua once ... 😇
  • I have no idea if this is a bug related to the plugin system
    or if this is the designed behavior ofOnSetText (which only process files that are opened ?!)

edited:

‼️ I seems found a workaround forfield_constr is not recognized until open thisTestClass.lua once‼️

  • somehow the trick of---@class TestClass\nself = self\n will not work on workspace init
    (somethings off in the type infer system I believe 😕 )
    because even without using plugin, the strange behavior persists
  • I then found out that I can use---@class TestClass\nlocal self = self\n 😂
    This seems to work during workspace init (without openingTestClass.lua once)
  • I have updated my demo script above 👀

edit2

I updated the string replace pattern again to"---@class %s\n---@diagnostic disable-next-line\nlocal self = {}\n"

  • this will silent theRedefined local `self` warning
  • also by usinglocal self = {}, thesetType inline hint won't show up for this virtuallocal self line
You must be logged in to vote
3 replies
@ipsquiggle
Comment options

Wow this is extremely interesting, thank you! This seems like a pretty powerful feature, I'll have to investigate further!

@ipsquiggle
Comment options

Okay! This seems to be working, I'm hugely grateful for this! The team is going to be very happy. 🙏 Thank you so much!

As an aside though, when trying to explore this, I'm unable to get adiffed.lua to show in my logs folder."misc.parameters": ["--develop=true"], should be the only thing required, right?

@tomlau10
Comment options

"misc.parameters": ["--develop=true"], should be the only thing required, right?

(I assume you use vscode)
Ah~ I forgot to mention, this flag needs to be added in.vscode/settings.json in your project folder but not.luarc.json.
Because it is read by theclient plugin (such that it can include this flag when launching the server).
If you add it in.luarc.json then no effect.

  • .vscode/settings.json
{"Lua.misc.parameters": ["--develop=true"    ],}
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Category
Q&A
Labels
None yet
2 participants
@ipsquiggle@tomlau10

[8]ページ先頭

©2009-2025 Movatter.jp