local files = require 'files' local furi = require 'file-uri' local core = require 'core.hover' local config = require 'config' local catch = require 'catch' rawset(_G, 'TEST', true) local EXISTS = {} local function eq(a, b) if a == EXISTS and b ~= nil then return true end if b == EXISTS and a ~= nil then return true end local tp1, tp2 = type(a), type(b) if tp1 ~= tp2 then return false end if tp1 == 'table' then local mark = {} for k in pairs(a) do if not eq(a[k], b[k]) then return false end mark[k] = true end for k in pairs(b) do if not mark[k] then return false end end return true end return a == b end ---@diagnostic disable: await-in-sync function TEST(expect) local sourcePos, sourceUri for _, file in ipairs(expect) do local script, list = catch(file.content, '?') local uri = furi.encode(file.path) files.setText(uri, script) if #list['?'] > 0 then sourceUri = uri sourcePos = (list['?'][1][1] + list['?'][1][2]) // 2 end end local _ = function () for _, info in ipairs(expect) do files.remove(furi.encode(info.path)) end end local hover = core.byUri(sourceUri, sourcePos) assert(hover) local content = tostring(hover):gsub('\r\n', '\n') assert(eq(content, expect.hover)) end TEST { { path = 'a.lua', content = '', }, { path = 'b.lua', content = 'require ', }, hover = [[ ```lua 1 个字节 ``` --- * [a.lua](file:///a.lua) (搜索路径: `?.lua`)]], } TEST { { path = 'a.lua', content = '', }, { path = 'b.lua', content = '---@module ""', }, hover = [[ * [a.lua](file:///a.lua) (搜索路径: `?.lua`)]], } if require 'bee.platform'.OS == 'Windows' then TEST { { path = 'Folder/a.lua', content = '', }, { path = 'b.lua', content = 'require ', }, hover = [[ ```lua 1 个字节 ``` --- * [Folder\a.lua](file:///Folder/a.lua) (搜索路径: `Folder\?.lua`)]], } TEST { { path = 'Folder/a.lua', content = '', }, { path = 'b.lua', content = 'require ', }, hover = [[ ```lua 8 个字节 ``` --- * [Folder\a.lua](file:///Folder/a.lua) (搜索路径: `?.lua`)]], } TEST { { path = 'Folder/a.lua', content = '', }, { path = 'b.lua', content = 'require ', }, hover = [[ ```lua 8 个字节 ``` --- * [Folder\a.lua](file:///Folder/a.lua) (搜索路径: `?.lua`)]], } else TEST { { path = 'Folder/a.lua', content = '', }, { path = 'b.lua', content = 'require ', }, hover = [[ ```lua 1 个字节 ``` --- * [Folder/a.lua](file:///Folder/a.lua) (搜索路径: `Folder/?.lua`)]], } end TEST { { path = 'a.lua', content = [[ local function f(a, b) end return f ]], }, { path = 'b.lua', content = [[ local x = require 'a' () ]] }, hover = [[ ```lua function f(a: any, b: any) ```]] } TEST { { path = 'a.lua', content = [[ return function (a, b) end ]], }, { path = 'b.lua', content = [[ local f = require 'a' () ]] }, hover = [[ ```lua function (a: any, b: any) ```]] } TEST { { path = 'a.lua', content = [[ local mt = {} mt.__index = mt function mt:add(a, b) end return function () return setmetatable({}, mt) end ]], }, { path = 'b.lua', content = [[ local m = require 'a' local obj = m() obj:() ]] }, hover = [[ ```lua (method) mt:add(a: any, b: any) ```]] } TEST { { path = 'a.lua', content = [[ t = { [1] = 1, } ]], }, { path = 'b.lua', content = [[ [1] = 2 ]] }, hover = [[ ```lua (global) t: { [1]: integer = 1|2, } ```]], } TEST { { path = 'a.lua', content = [[ t = { [1] = 1, } ]], }, { path = 'a.lua', content = [[ [1] = 2 ]] }, hover = [[ ```lua (global) t: { [1]: integer = 2, } ```]], } TEST { { path = 'a.lua', content = [[ return { a = 1, b = 2, } ]], }, { path = 'b.lua', content = [[ local = require 'a' ]] }, hover = [[ ```lua local t: { a: integer = 1, b: integer = 2, } ```]], } TEST { { path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ --- abc ---@param x number function (x) end ]], }, hover = [[ ```lua function f(x: number) ``` --- abc]] } TEST { { path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ --- abc = 1 ]], }, hover = [[ ```lua (global) x: integer = 1 ``` --- abc]] } TEST { { path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@param x string ---| "选项1" # 注释1 ---| > "选项2" # 注释2 function (x) end ]] }, hover = [[ ```lua function f(x: string|"选项1"|"选项2") ``` --- ```lua x: | "选项1" -- 注释1 -> "选项2" -- 注释2 ```]] } TEST { { path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@alias option ---| "选项1" # 注释1 ---| > "选项2" # 注释2 ---@param x option function (x) end ]] }, hover = [[ ```lua function f(x: "选项1"|"选项2") ``` --- ```lua x: | "选项1" -- 注释1 -> "选项2" -- 注释2 ```]] } TEST { { path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@alias option ---| "选项1" # 注释1 ---| > "选项2" # 注释2 ---@return option x function () end ]] }, hover = [[ ```lua function f() -> x: "选项1"|"选项2" ``` --- ```lua x: | "选项1" -- 注释1 -> "选项2" -- 注释2 ```]] } TEST { { path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@alias option ---| "选项1" # 注释1 ---| > "选项2" # 注释2 ---@return option function () end ]] }, hover = [[ ```lua function f() -> "选项1"|"选项2" ``` --- ```lua return #1: | "选项1" -- 注释1 -> "选项2" -- 注释2 ```]] } TEST { { path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@param x string this is comment function f() end ]] }, hover = [[ ```lua (parameter) x: string ``` --- this is comment]] } TEST { { path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@param x string this is comment ---@param y table -- comment 1 ---@return boolean name #comment 2 ---@return number @comment 3 function (x, y) end ]] }, hover = [[ ```lua function f(x: string, y: table) -> name: boolean 2. number ``` --- @*param* `x` — this is comment @*param* `y` — comment 1 @*return* `name` — comment 2 @*return* — comment 3]] } TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---Comment ---@param x string ---@return string # this is comment function f() end ]] }, hover = [[ ```lua (parameter) x: string ```]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---comment1 ---@param arg1 integer ---@param arg2 integer comment2 ---@return boolean ---comment3 function (arg1, arg2) end ]] }, hover = [[ ```lua function f(arg1: integer, arg2: integer) -> boolean ``` --- comment1 @*param* `arg2` — comment2 comment3]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@param arg3 integer comment3 ---@overload fun(arg3) ---@param arg1 integer comment1 ---@param arg2 integer comment2 ---@return boolean function (arg1, arg2) end ]] }, hover = [[ ```lua function f(arg1: integer, arg2: integer) -> boolean ``` --- @*param* `arg3` — comment3 --- @*param* `arg1` — comment1 @*param* `arg2` — comment2 --- ```lua function f(arg3: any) ```]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@return boolean, string #comment function () end ]] }, hover = [[ ```lua function f() -> boolean 2. string ``` --- @*return* — comment]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@return boolean ---@return string #comment function () end ]] }, hover = [[ ```lua function f() -> boolean 2. string ``` --- @*return* @*return* — comment]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@return boolean ---@return string function () end ]] }, hover = [[ ```lua function f() -> boolean 2. string ```]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---comment1 ---comment2 function () end ]] }, hover = [[ ```lua function f() ``` --- comment1 comment2]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@param a boolean # xxx ---| true # ttt ---| false # fff local function (a) end ]] }, hover = [[ ```lua function f(a: boolean|true|false) ``` --- @*param* `a` — xxx ```lua a: | true -- ttt | false -- fff ```]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---AAA ---@class A ---@type A local ]] }, hover = [[ ```lua local x: A ``` --- AAA]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---AAA ---@class A ---BBB ---@field n string local ]] }, hover = [[ ```lua local x: A { n: string, } ``` --- AAA]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---AAA ---@class A ---BBB ---@type A local ]] }, hover = [[ ```lua local x: A ``` --- BBB --- AAA]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---AAA G. = 1 ---BBB G.A = 1 ]] }, hover = [[ ```lua (global) G.A: integer = 1 ``` --- AAA --- BBB]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---AAA G.A = 1 ---BBB G. = 1 ]] }, hover = [[ ```lua (global) G.A: integer = 1 ``` --- BBB --- AAA]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---I am the class description. ---@class Food ---@field firstField number ---@field secondField number local food = {} food. = 0 food.secondField = 2 ]] }, hover = [[ ```lua (field) Food.firstField: number ```]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ --[=[ I'm a multiline comment ]=] local ]] }, hover = [[ ```lua local food: unknown ``` --- I'm a multiline comment ]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@return string # 'this is a tab `\t`' local function () end ]] }, hover = [[ ```lua function f() -> string ``` --- @*return* — this is a tab ` `]]} TEST { { path = 'a.lua', content = [[ ---@class string ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index?: K):K, V ---@return T local function pairs(t) end return pairs ]] }, { path = 'b.lua', content = [[ local pairs = require 'a' ---@type table local t for , v in pairs(t) do end ]] }, hover = [[ ```lua local k: string ```]] } TEST { { path = 'a.lua', content = [[ ---@class string ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index?: K):K, V ---@return T function tpairs(t) end ]] }, { path = 'b.lua', content = [[ local pairs = require 'a' ---@type table local t for , v in tpairs(t) do end ]] }, hover = [[ ```lua local k: string ```]] } TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@class bthci bthci = {} ---@class adv ---@field CONN_DIR_HI integer ---@field CHAN_ALL integer bthci.adv = {} --- Sends a raw HCI command to the BlueTooth controller. function bthci.(hcibytes, callback) end --- Resets the BlueTooth controller. function bthci.reset(callback) end ]] }, hover = [[ ```lua function bthci.rawhci(hcibytes: any, callback: any) ``` --- Sends a raw HCI command to the BlueTooth controller.]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@type string | fun(): string local ]] }, hover = [[ ```lua local t: string|fun():string ``` --- ```lua function t() -> string ```]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@alias T ---comment 1 ---comment 2 ---| 'a' ---comment 3 ---comment 4 ---| 'b' ---@param p T local function (p) end ]] }, hover = [[ ```lua function f(p: 'a'|'b') ``` --- ```lua p: | 'a' -- comment 1 -- comment 2 | 'b' -- comment 3 -- comment 4 ```]]} --TEST {{ path = 'a.lua', content = '', }, { -- path = 'b.lua', -- content = [[ -----@param x number # aaa --local f -- --function f() end --]] --}, --hover = [[ --```lua --local x: number --``` -- ----- -- aaa]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---line1 --- ---line2 local function () end ]] }, hover = [[ ```lua function fn() ``` --- line1 line2]]} TEST { { path = 'a.lua', content = [[ ---@type string[] local ss for _, s in ipairs(ss) do print() end ]], }, { path = 'b.lua', content = [[ for _, x in ipairs({} and {}) do print() -- `x` is infered as `string` (fixed bug) end ]], }, hover = [[ ```lua local x: unknown ```]] } TEST { { path = 'a.lua', content = [[ ---@class A ---@field x number G = {} ]], }, { path = 'b.lua', content = [[ ]], }, hover = [[ ```lua (global) G: A { x: number, } ```]] } TEST { { path = 'a.lua', content = [[ ---@overload fun(self, a) function C:(a, b) end ]] }, hover = [[ ```lua (method) C:f(a: any, b: any) ``` --- ```lua (method) C:f(a: any) ```]] } TEST { { path = 'a.lua', content = [[ ---@overload fun(self, a) function C.(a, b) end ]] }, hover = [[ ```lua function C.f(a: any, b: any) ``` --- ```lua function C.f(self: any, a: any) ```]] } TEST { { path = 'a.lua', content = [[ ---@async ---@overload async fun(self, a) function C:(a, b) end ]] }, hover = [[ ```lua (async) (method) C:f(a: any, b: any) ``` --- ```lua (async) (method) C:f(a: any) ```]] } TEST { { path = 'a.lua', content = [[ ---@class Apple ---The color of your awesome apple! ---@field color string local Apple = {} Apple. ]] }, hover = [[ ```lua (field) Apple.color: string ``` --- The color of your awesome apple!]] } TEST { { path = 'a.lua', content = [[ ---@type fun(x: number, y: number, ...: number):(x: number, y: number, ...: number) local ]] }, hover = [[ ```lua local f: fun(x: number, y: number, ...number):(x: number, y: number, ...number) ``` --- ```lua function f(x: number, y: number, ...: number) -> x: number 2. y: number 3. ...number ```]] } TEST { { path = 'a.lua', content = [[ ---@param p 'a1' | 'a2' ---@param ... 'a3' | 'a4' ---@return 'r1' | 'r2' ret1 ---@return 'r3' | 'r4' ... local function (p, ...) end ]] }, hover = [[ ```lua function f(p: 'a1'|'a2', ...'a3'|'a4') -> ret1: 'r1'|'r2' 2. ...'r3'|'r4' ``` --- ```lua p: | 'a1' | 'a2' ...(param): | 'a3' | 'a4' ret1: | 'r1' | 'r2' ...(return): | 'r3' | 'r4' ```]] }