Improve typing#9861
Conversation
There was a problem hiding this comment.
I wonder if HeadlessWrapper.lua can be used instead of this file? That one is meant to mirror SimpleGraphic's API for tools that use PoB on the command line.
There was a problem hiding this comment.
Could it? I don't really know how the headless wrapper works, but doesn't this file override some functions so that the program can run headless?
The meta file doesn't do anything. It's just there for LSP info
There was a problem hiding this comment.
It does override some functions, but if the annotations can be in there instead of this .def.lua file it can pull double duty. PoB running normally also doesn't load HeadlessWrapper.lua, and those annotations could help out people running headless as well. If the LSP just sees these globally defined functions it could probably do the same for HeadlessWrapper.lua
There was a problem hiding this comment.
But HeadlessWrapper.lua is meant to actually be executed if you want to run in headless mode, right? The meta file isn't meant to be run at all.
At least with my current setup, I'd recommend explicitly ignoring the file in the LSP, exactly because it's not meant to be loaded with PoB, and the LSP reading it adds confusing things to the global namespace which then aren't actually present in PoB.
I'd also recommend ignoring the spec folder for the same reason. I think it makes sense to keep the def file separate
There was a problem hiding this comment.
HeadlessWrapper should have definitions of every global function from SimpleGraphic in there, so calling code doesn't break CLI usage with a missing global. Is there even a way to see things in the global namespace that it adds extra? I'm just trying to avoid having to add two functions every time we add one in SimpleGraphic's API. Here's the difference, for reference:
In HeadlessWrapper.lua but missing from _SimpleGraphic.def.lua:
runCallback
GetVirtualScreenSize
require
newBuild
loadBuildFromXML
loadBuildFromJSON
In _SimpleGraphic.def.lua but missing from HeadlessWrapper.lua:
NewArtHandle
GetDrawLayer
SetBlendMode
GetDrawColor
print
SetForeground
- adding a type hint for new() and newClass() - adding simple ---@Class x: y type hints for base class variables
|
There are some annotation examples in If you're getting Claude (or w/e) to help, make sure it's only considering the latest (rust) version of EmmyLua, not the old .NET or Java ones. |
… being an argument to newClass
|
The latest PR changes the way classes are constructed. Instead of the constructor being in the varargs of newClass, it's now a separately defined method, which is called by new(). Basically, class creation is now: ---@class Example
local ExampleClass = newClass("Example", "Parent1", "Parent2")
---@param whatever: WhatTypeItIs it's a good idea to type these
function ExampleClass:Example(whatever)
self.whatever = whatever
endWhich means the class type has information that self.whatever exists. In most cases this isn't super useful as everything is any, but at least now typing these isn't completely useless. It also works well for discovering fields and methods, since now you can use the IDE "go to definition" button to go to, e.g. a method added by the parent class. It's also possible to add more fields to the class with At least with Sumneko's LSP, there were also warnings about making a class with a typo in the class name. This linter PR also seems relevant, as these changes make linters much more useful: https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/1796/changes Using parent class constructors still looks a bit weird. For example
|
|
The last commit makes the proxy metatable (or whatever it's called) not supply self to the parent constructor. This means that you now use : to call it and type checking works correctly, as the LSP sees the self variable. |
|
A local Codex-assisted analysis harness, followed by a Claude review of the findings, was used to sanity-check the current PR head ( Checks covered The pass found two runtime issues.
The Export smoke fails with: The minimal fix that passed locally was to convert those classes to the named constructor style already used by the migrated local XClass = newClass("X", "Parent")
function XClass:X(...)
...
end
If function SetCallback(name, func)
__callbackTable__[name] = func
end
function GetCallback(name)
return __callbackTable__[name]
endIn the current smoke this only matters if anything calls After applying those two local changes, these checks passed: LuaLS also reports new diagnostics on existing dynamic patterns that the annotations currently model too narrowly, such as function-valued control Manual UI testing was not done. |
…f using new() and varargs
|
Commit c0a223e changes new() to not call the constructor. This is motivated by the lack of type checking on varargs. It wraps the constructor to check parent initialisation. The construction syntax would now be different: new("Control"):Control(nil, {0, 4, 0, 0})The codebase was updated using a regex to do this. This is useful, because the LSP can now see what you should be putting into the constructor, instead of only seeing any-typed varargs:
But it does change the code style more than the previous changesm as those were strictly limited to class definition. Thoughts? |

This PR contains type definitions for the SimpleGraphic API, which gets loaded to _G but makes LSPs scream, because it doesn't know about them. This also helps with development by containing type hints. The file is strictly a definition file and doesn't get loaded at runtime.
I'd also like to have a look at solving the dynamic class loading somehow. It's quite weird and is seemingly completely incompatible with type hints, as anything that
new()produces has theanytype. If anyone has ideas, please help.Modules can be typed more easily, with the following annotation:
requirewould work similarly, but doesn't require the annotation to retain type information.There would also be code quality benefits. As far as I can see, currently it is not really feasible to have linting in this project as there are way too many false errors. If these were fixed, problems such as dead code could probably be detected easily. I've noticed there have been a few code paths where code accidentally accesses an undefined global variable and which would crash. These would probably be easier to notice with linting.