luttje / glua-api-snippets

Scrapes the Garry's Mod Wiki in order to build Lua Language Server comments that will provide IDE suggestions and autocompletion.
MIT License
15 stars 5 forks source link

Some improvement ideas for the future #47

Open robotboy655 opened 4 months ago

robotboy655 commented 4 months ago

Sorry to pile on these in 1 issue, I just need to write these down before I forget:

edit by luttje:

luttje commented 4 months ago

No worries, it's fine to pile it in one issue. It's workable this way, since this isn't too big a project.

You may already have considered ideas like these, but here's my thoughts on the topic of annotating multiple types and callbacks on the wiki.

With backwards compatibility

Some downsides:

Breaking changes

I guess the "markup/XML way" for this would be adding child elements that annotate more data. However I don't see a way to implement that without omitting backward compatibility for anyone scraping the wiki. Additionally it would require a bunch of work on all sides.

I do believe something like this would work best for the wiki. Since data structured like this could be used by the wiki to generate HTML for developers consulting the wiki manually.

Some mockups of how that could look:

Combination without breaking changes

The wiki could add a new element in root, e.g: <metadata> which contains elements with an id/ref. A new attribute on arg could point to that to indicate there's more information there, e.g:

...
<args>
   <arg name="callback"
        type="function"
        metadata="callback">...<arg>
   ...
</args>
...
<metadata>
   <functionParameters id="callback">...</functionParameters>
</metadata>

However this last option is really oriented on those scraping the wiki, whilst the before mentioned options could also be used by the wiki itself to generate useful html for users of the wiki.

TIMONz1535 commented 4 months ago

fields and metamethods can be easily added manually but you need to add some kind of tags to parse the wiki tables (because its just a raw text)

---@class VMatrix
---@operator add(VMatrix): VMatrix
---@operator mul(Vector): Vector
---@operator mul(VMatrix): VMatrix
---@operator sub(VMatrix): VMatrix
---@operator unm: VMatrix

---@class Angle
---@field p number
---@field y number
---@field r number
---@field pitch number
---@field yaw number
---@field roll number
---@field x number
---@field z number
---@field [1] number
---@field [2] number
---@field [3] number
---@operator add(Angle): Angle
---@operator div(number): Angle
---@operator mul(number): Angle
---@operator sub(Angle): Angle
---@operator unm: Angle

---@class Vector
---@field x number
---@field y number
---@field z number
---@field [1] number
---@field [2] number
---@field [3] number
---@operator add(Vector): Vector
---@operator div(number|Vector): Vector
---@operator mul(number|Vector): Vector
---@operator sub(Vector): Vector
---@operator unm: Vector

in general there are only Vector, Angle and VMatrix. It is unlikely that anyone will ever change them.

luttje commented 4 months ago

@TIMONz1535 Thanks for thinking with us.

I think you've got a point that the way Vector, Color, etc. work is unlikely to change. Perhaps our attention is better spent elsewhere and we can just implement your manually written up definitions in the meantime.

Parsing the markdown table like Vector has is doable, but seems like a waste of time.

robotboy655 commented 3 months ago

I have implemented a scraper friendly definitions for function callbacks: https://wiki.facepunch.com/gmod/PathFollower:Compute https://wiki.facepunch.com/gmod/concommand.Add

And for function overloads: https://wiki.facepunch.com/gmod/Global.Angle

It's currently only on these pages as a test for now.

luttje commented 3 months ago

Wow awesome, thats super useful and a clean implementation! Thanks so much for your continuous work on all this @robotboy655

robotboy655 commented 3 months ago

I have been thinking about how to deal with AccessorFunc and Entity:NetworkVar creating functions that are not being seen by the language server, and my conclusion is the following: 1) Either we open an issue/PR for this in the VSCode extension repo. I have noticed that simply doing this:

function Entity:NetworkVar(type, slot, name, extended)
    self.SetBallSize = function( s, test ) end
    self.GetBallSize = function( s ) return false end
end

Makes it detect the custom functions, but doing this:

function Entity:NetworkVar(type, slot, name, extended)
    self["Set" .. "BallSize"] = function( s, test ) end
    self["Get" .. "BallSize"] = function( s ) return false end
end

does not. It might be possible to fix on their end.

2) Or we try and see if using plugins (https://luals.github.io/wiki/plugins/) could be a solution

luttje commented 3 months ago

@robotboy655 I had been looking at the LuaLS plugins (for #50), but sadly they're very limited and only seem to output Lua to a LOGPATH/diffed.lua, from their wiki:

Introduction

Plugins allow you to create a custom syntax that will then be output to a separate file. They cannot be used to report custom diagnostics.

To me, this is a super confusing functionality in LuaLS. If the diffed file was output to a location that would be indexed for LuaLS autocomplete this would make sense, but I can't find that that's the case.

luttje commented 1 month ago

@robotboy655 I'd like to correct my previous statement. It seems I (and the LuaLS docs) were wrong about plugins not being able to customize diagnostics:

  1. I created a plugin.lua in the .vscode directory of my project:

    function OnTransformAst(uri, ast)
        if (not uri:match("gamemode/cl_init.lua$")) then
            return
        end
    
        -- I'm not sure what the structure of the AST is, so I'm simply brute forcing my way to demonstrate this is useful
        local function changeAst(ast, level)
            if (level > 3) then
                return
            end
    
            for k, v in pairs(ast) do
                if (type(v) == "table") then
                    changeAst(v, level + 1)
                elseif (v == "Xtest") then
                    ast[k] = "Changed"
                end
            end
        end
    
        for k, v in ipairs(ast) do
            changeAst(v, 0)
        end
    end
  2. I added "Lua.runtime.plugin": ".vscode/plugin.lua" to .vscode/settings.json
  3. I stop and restart the Lua Language server through the command palette (or by restarting VSCode)
  4. I added a function named 'Xtest' in my gamemode/cl_init.lua
  5. Hovering over the function results in: image

This means we can modify diagnostics with plugins.

Sadly I can't find a way to automatically load the plugin from the glua-api-snippets addon. Placing the plugin.lua in the addon folder doesn't seem to work for me (nor any of the other addons that are in the LuaLS-addons repo)

TIMONz1535 commented 1 month ago

since 3.8.0 they added

NEW plugin: add OnTransFormAst interface (@fesily)
NEW plugin: add OnNodeCompileFunctionParam interface (@fesily)
NEW plugin: add ResolveRequire interface (@Artem Dzhemesiuk)
NEW plugin: support multi plugins (@fesily)
setting: Lua.runtime.plugin can be string|string[]
setting: Lua.runtime.pluginArgs can be string[]|table<string, string>

also some interest part https://github.com/LuaLS/lua-language-server/pull/2484

NEW generic pattern (@fesily)

---@generic T
---@param t Cat.`T`
---@return T
local function f(t) end

local t = f('Smile') --> t is `Cat.Smile`

I thought this was what we needed, but it just refers to the existing syntax in the class. That is, it cannot create/modify class fields on the fly.

Well, but I realized that even before 3.8.0 it was possible to get any class through a @generic

---@generic T
---@param metaName `T` The object type to retrieve the meta table of.
---@return T # The corresponding meta table.
function _G.FindMetaTable(metaName) end

local entMeta = FindMetaTable("Entity") -- entMeta: Entity
luttje commented 1 month ago

I've added a small note to the original issue above, it's about adding a way to specify table element types. Right now functions like player.GetAll are documented to return table on the wiki instead of the more complete Player[]. The latter would greatly improve typing on subsequent loops, because lua-language-server would type the array index and elements properly.

Currently the wiki has a structure like this for the return type of player.GetAll

<rets>
    <ret name="" type="table">All <page>Player</page>s currently in the server.</ret>
</rets>

If Rubat has some time in the future and feels that this is worth it, perhaps they could add some attributes similar to this:

<rets>
    <ret name="" type="table" keytype="number" valuetype="Player">All <page>Player</page>s currently in the server.</ret>
</rets>

Perhaps even a sequential="true" so we can differentiate a sequential table like Player[] from a keyvalue table which can be unsequential like table<number, Player>

luttje commented 1 month ago

Another slightly related note, but it would be cool if functions like util.TraceLine could be documented about their structs:

<args>
    <arg name="traceConfig" type="table" struct="Trace">
        A table of data that configures the Trace.  

        For the table's format and available options see the <page>Structures/Trace</page> page.
    </arg>
</args>
<rets>
    <ret name="" type="table" struct="TraceResult">
        A table of information detailing where and what the Trace line intersected, or `nil` if the trace is being done before the <page>GM:InitPostEntity</page> hook.

        For the table's format and available options see the <page>Structures/TraceResult</page> page.
    </ret>
</rets>
TIMONz1535 commented 1 month ago

I use strict integer types "type.castNumberToInteger": false,, and noticed that some Lua builtin function differ with plugin declarations

https://wiki.facepunch.com/gmod/math.ceil

---[SHARED AND MENU] Ceils or rounds a number up.
---
---[(View on wiki)](https://wiki.facepunch.com/gmod/math.ceil)
---@param number number The number to be rounded up.
---@return number # ceiled numbers
function math.ceil(number) end

vs sumneko's

---
---Returns the smallest integral value larger than or equal to `x`.
---
---[View documents](command:extension.lua.doc?["en-us/51/manual.html/pdf-math.ceil"])
---
---@param x number
---@return integer
---@nodiscard
function math.ceil(x) end

there is no integer for wiki, but we can specify the custom type as you suggested above oh, there's also @nodiscard