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['?'] 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) hover = tostring(hover):gsub('\r\n', '\n') assert(eq(hover, 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: string | '选项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: option | '选项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: option | '选项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: option | '选项1' -- 注释1 -> '选项2' -- 注释2 ```]] } TEST { { path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ ---@param x string this is comment function f() end ]] }, hover = [[ ```lua local 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 local 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: boolean | 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 = 0 ```]]} TEST {{ path = 'a.lua', content = '', }, { path = 'b.lua', content = [[ --[=[ I'm a multiline comment ]=] local ]] }, hover = [[ ```lua local food: any ``` --- 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: T | 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` end ]], }, hover = [[ ```lua local x: any ```]] } 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) ```]] }