diff --git a/script/core/command/reloadFFIMeta.lua b/script/core/command/reloadFFIMeta.lua new file mode 100644 index 000000000..d00929adb --- /dev/null +++ b/script/core/command/reloadFFIMeta.lua @@ -0,0 +1,56 @@ +local config = require 'config' +local ws = require 'workspace' +local fs = require 'bee.filesystem' +local scope = require 'workspace.scope' +local SDBMHash = require 'SDBMHash' +local searchCode = require 'plugins.ffi.searchCode' +local cdefRerence = require 'plugins.ffi.cdefRerence' +local ffi = require 'plugins.ffi' + +local function createDir(uri) + local dir = scope.getScope(uri).uri or 'default' + local fileDir = fs.path(METAPATH) / ('%08x'):format(SDBMHash():hash(dir)) + if fs.exists(fileDir) then + return fileDir, true + end + fs.create_directories(fileDir) + return fileDir +end + +---@async +return function (uri) + if config.get(uri, 'Lua.runtime.version') ~= 'LuaJIT' then + return + end + + ws.awaitReady(uri) + + local fileDir, exists = createDir(uri) + + local refs = cdefRerence() + if not refs or #refs == 0 then + return + end + + for i, v in ipairs(refs) do + local target_uri = v.uri + local codes = searchCode(refs, target_uri) + if not codes then + return + end + + ffi.build_single(codes, fileDir, target_uri) + end + + if not exists then + local client = require 'client' + client.setConfig { + { + key = 'Lua.workspace.library', + action = 'add', + value = tostring(fileDir), + uri = uri, + } + } + end +end diff --git a/script/plugins/ffi/cdefRerence.lua b/script/plugins/ffi/cdefRerence.lua index 819a0dd1c..14643f0ff 100644 --- a/script/plugins/ffi/cdefRerence.lua +++ b/script/plugins/ffi/cdefRerence.lua @@ -20,7 +20,7 @@ end return function () local ffi_state for uri in files.eachFile() do - if find(uri, "/ffi.lua", 0, true) then + if find(uri, "ffi.lua", 0, true) and find(uri, "lua-language-server", 0, true) then ffi_state = files.getState(uri) break end diff --git a/script/plugins/ffi/init.lua b/script/plugins/ffi/init.lua index f9162e211..f835d73fe 100644 --- a/script/plugins/ffi/init.lua +++ b/script/plugins/ffi/init.lua @@ -1,20 +1,17 @@ -local searchCode = require 'plugins.ffi.searchCode' -local cdefRerence = require 'plugins.ffi.cdefRerence' -local cdriver = require 'plugins.ffi.c-parser.cdriver' -local util = require 'plugins.ffi.c-parser.util' -local utility = require 'utility' -local SDBMHash = require 'SDBMHash' -local ws = require 'workspace' -local files = require 'files' -local await = require 'await' -local config = require 'config' -local fs = require 'bee.filesystem' -local scope = require 'workspace.scope' +local searchCode = require 'plugins.ffi.searchCode' +local cdefRerence = require 'plugins.ffi.cdefRerence' +local cdriver = require 'plugins.ffi.c-parser.cdriver' +local util = require 'plugins.ffi.c-parser.util' +local utility = require 'utility' +local SDBMHash = require 'SDBMHash' +local config = require 'config' +local fs = require 'bee.filesystem' +local scope = require 'workspace.scope' -local namespace = 'ffi.namespace*.' +local namespace = 'ffi.namespace*.' --TODO:supprot 32bit ffi, need config -local knownTypes = { +local knownTypes = { ["bool"] = 'boolean', ["char"] = 'integer', ["short"] = 'integer', @@ -63,10 +60,33 @@ local knownTypes = { ["signedlong"] = 'integer', } -local constName = 'm' +local blackKeyWord = { + ['and'] = "_and", + ['do'] = "_do", + ['elseif'] = "_elseif", + ['end'] = "_end", + ['false'] = "_false", + ['function'] = "_function", + ['in'] = "_in", + ['local'] = "_local", + ['nil'] = "_nil", + ['not'] = "_not", + ['or'] = "_or", + ['repeat'] = "_repeat", + ['then'] = "_then", + ['true'] = "_true", +} + +local invaildKeyWord = { + const = true, + restrict = true, + volatile = true, +} + +local constName = 'm' ---@class ffi.builder -local builder = { switch_ast = utility.switch() } +local builder = { switch_ast = utility.switch() } function builder:getTypeAst(name) for i, asts in ipairs(self.globalAsts) do @@ -98,14 +118,22 @@ function builder:getType(name) if type(name) == 'table' then local t = "" local isStruct + if name.type then + t = t .. name.type .. "@" + name = name.name + end for _, n in ipairs(name) do if type(n) == 'table' then n = n.full_name end + if invaildKeyWord[n] then + goto continue + end if not isStruct then isStruct = self:needDeref(self:getTypeAst(n)) end t = t .. n + ::continue:: end -- deref 一级指针 if isStruct and t:sub(#t) == '*' then @@ -145,11 +173,15 @@ local function getArrayType(arr) return res end +local function getValidName(name) + return blackKeyWord[name] or name +end + function builder:buildStructOrUnion(lines, tt, name) lines[#lines+1] = '---@class ' .. self:getType(name) for _, field in ipairs(tt.fields or {}) do if field.name and field.type then - lines[#lines+1] = ('---@field %s %s%s'):format(field.name, self:getType(field.type), + lines[#lines+1] = ('---@field %s %s%s'):format(getValidName(field.name), self:getType(field.type), getArrayType(field.isarray)) end end @@ -158,8 +190,9 @@ end function builder:buildFunction(lines, tt, name) local param_names = {} for i, param in ipairs(tt.params or {}) do - lines[#lines+1] = ('---@param %s %s%s'):format(param.name, self:getType(param.type), getArrayType(param.idxs)) - param_names[#param_names+1] = param.name + local param_name = getValidName(param.name) + lines[#lines+1] = ('---@param %s %s%s'):format(param_name, self:getType(param.type), getArrayType(param.idxs)) + param_names[#param_names+1] = param_name end if tt.vararg then param_names[#param_names+1] = '...' @@ -178,7 +211,7 @@ function builder:buildTypedef(lines, tt, name) -- 这个时候没有主类型,只有一个别名,直接创建一个别名结构体 self.switch_ast(def.type, self, lines, def, name) else - lines[#lines+1] = ('---@alias %s %s'):format(name, self:getType(def)) + lines[#lines+1] = ('---@alias %s %s'):format(self:getType(name), self:getType(def)) end end @@ -322,72 +355,18 @@ function m.compileCodes(codes) return lines end -local function createDir(uri) - local dir = scope.getScope(uri).uri or 'default' - local fileDir = fs.path(METAPATH) / ('%08x'):format(SDBMHash():hash(dir)) - fs.create_directories(fileDir) - return fileDir -end - -local builder -function m.initBuilder(fileDir) - fileDir = fileDir or createDir() - ---@async - return function (uri) - local refs = cdefRerence() - if not refs or #refs == 0 then - return - end - - local codes = searchCode(refs, uri) - if not codes then - return - end - - local texts = m.compileCodes(codes) - if not texts then - return - end - local hash = ('%08x'):format(SDBMHash():hash(uri)) - local encoding = config.get(nil, 'Lua.runtime.fileEncoding') - local filePath = fileDir / table.concat({ hash, encoding }, '_') - - utility.saveFile(tostring(filePath) .. '.d.lua', table.concat(texts, '\n')) +function m.build_single(codes, fileDir, uri) + local texts = m.compileCodes(codes) + if not texts then + return end -end -files.watch(function (ev, uri) - if ev == 'compiler' or ev == 'update' then - if builder then - await.call(function () ---@async - builder(uri) - end) - end - end -end) + local hash = ('%08x'):format(SDBMHash():hash(uri)) + local encoding = config.get(nil, 'Lua.runtime.fileEncoding') + local filePath = fileDir / table.concat({ hash, encoding }, '_') -ws.watch(function (ev, uri) - -- TODO - do return end - if ev == 'startReload' then - if config.get(uri, 'Lua.runtime.version') ~= 'LuaJIT' then - return - end - await.call(function () ---@async - ws.awaitReady(uri) - local fileDir = createDir(uri) - builder = m.initBuilder(fileDir) - local client = require 'client' - client.setConfig { - { - key = 'Lua.workspace.library', - action = 'add', - value = tostring(fileDir), - uri = uri, - } - } - end) - end -end) + utility.saveFile(tostring(filePath) .. '.d.lua', table.concat(texts, '\n')) + return true +end return m diff --git a/script/provider/provider.lua b/script/provider/provider.lua index efa627c3f..2ec429d0e 100644 --- a/script/provider/provider.lua +++ b/script/provider/provider.lua @@ -982,6 +982,11 @@ m.register 'workspace/executeCommand' { elseif command == 'lua.exportDocument' then local core = require 'core.command.exportDocument' core(params.arguments) + elseif command == 'lua.reloadFFIMeta' then + local core = require 'core.command.reloadFFIMeta' + for _, scp in ipairs(workspace.folders) do + core(scp.uri) + end end end } diff --git a/test/plugins/ffi/builder.lua b/test/plugins/ffi/builder.lua index a8fc115bd..ebbfab551 100644 --- a/test/plugins/ffi/builder.lua +++ b/test/plugins/ffi/builder.lua @@ -39,10 +39,44 @@ function TEST(wanted) end end +TEST [[ + ---@alias ffi.namespace*.EVP_MD ffi.namespace*.struct@env_md_st + + ---@return ffi.namespace*.EVP_MD + function m.EVP_md5() end +]] [[ + typedef struct env_md_st EVP_MD; + const EVP_MD *EVP_md5(void); +]] + +TEST [[ + ---@class ffi.namespace*.struct@a + ---@field _in integer +]] [[ + struct a { + int in; + }; +]] + +TEST [[ +---@param _in integer +function m.test(_in) end +]] [[ + void test(int in); +]] + +TEST [[ + ---@alias ffi.namespace*.ENGINE ffi.namespace*.struct@engine_st + ---@alias ffi.namespace*.ENGINE1 ffi.namespace*.enum@engine_st1 +]] [[ + typedef struct engine_st ENGINE; + typedef enum engine_st1 ENGINE1; +]] + TEST [[ ---@param a integer[][] function m.test(a) end -]][[ +]] [[ void test(int a[][]); ]] @@ -76,7 +110,7 @@ TEST [[ m.A = 1 m.C = 5 ---@alias ffi.namespace*.enum@a 1 | 2 | 'B' | 'A' | 5 | 'C' -]][[ +]] [[ enum a { A = 1, B = 2, @@ -165,7 +199,7 @@ TEST [[ ]] TEST [[ - ---@alias H ffi.namespace*.void + ---@alias ffi.namespace*.H ffi.namespace*.void function m.test() end ]] [[ diff --git a/test/plugins/ffi/test.lua b/test/plugins/ffi/test.lua index 2a2321923..68cec1567 100644 --- a/test/plugins/ffi/test.lua +++ b/test/plugins/ffi/test.lua @@ -8,16 +8,15 @@ local template = require 'config.template' template['Lua.runtime.version'].default = 'LuaJIT' - ---@async local function TestBuilder() - local builder = require 'plugins.ffi'.initBuilder() + local builder = require 'core.command.reloadFFIMeta' files.setText(TESTURI, [[ local ffi = require 'ffi' ffi.cdef 'void test();' ]]) - - builder(TESTURI) + local uri = ws.getFirstScope().uri + builder(uri) end ---@async