diff options
-rw-r--r-- | server-beta/src/core/diagnostics/undefined-global.lua | 41 | ||||
-rw-r--r-- | server-beta/src/library.lua | 296 | ||||
-rw-r--r-- | server-beta/src/searcher/eachField.lua | 13 | ||||
-rw-r--r-- | server-beta/src/searcher/eachGlobal.lua | 13 | ||||
-rw-r--r-- | server-beta/src/searcher/eachRef.lua | 13 | ||||
-rw-r--r-- | server-beta/src/searcher/searcher.lua | 4 |
6 files changed, 357 insertions, 23 deletions
diff --git a/server-beta/src/core/diagnostics/undefined-global.lua b/server-beta/src/core/diagnostics/undefined-global.lua index db7af133..888064dc 100644 --- a/server-beta/src/core/diagnostics/undefined-global.lua +++ b/server-beta/src/core/diagnostics/undefined-global.lua @@ -3,6 +3,8 @@ local guide = require 'parser.guide' local searcher = require 'searcher' local define = require 'proto.define' local lang = require 'language' +local library = require 'library' +local config = require 'config' return function (uri, callback) local ast = files.getAst(uri) @@ -10,26 +12,53 @@ return function (uri, callback) return end + -- 先遍历一次所有该文件中的全局变量 + -- 如果变量有 set 行为,则做标记 + -- 然后再遍历一次,对所有的行为打上标记 local hasSet = {} searcher.eachGlobal(ast.ast, function (info) - local key = info.key - if hasSet[key] ~= nil then + if hasSet[info.source] ~= nil then return end - hasSet[key] = false + local mark = false searcher.eachRef(info.source, function (info) if info.mode == 'set' then - hasSet[key] = true + mark = true end end) + searcher.eachRef(info.source, function (info) + hasSet[info.source] = mark + end) end) + -- 然后再遍历一次,检查所有标记为假的全局变量 searcher.eachGlobal(ast.ast, function (info) local source = info.source - if info.mode == 'get' and not hasSet[info.key] then + local key = info.key + local skey = key:match '^s|(.+)$' + if not skey then + return + end + if library.global[skey] then + return + end + if config.config.diagnostics.globals[skey] then + return + end + if info.mode == 'get' and not hasSet[source] then + local message + local otherVersion = library.other[key] + local customVersion = library.custom[key] + if otherVersion then + message = ('%s(%s)'):format(message, lang.script('DIAG_DEFINED_VERSION', table.concat(otherVersion, '/'), config.config.runtime.version)) + elseif customVersion then + message = ('%s(%s)'):format(message, lang.script('DIAG_DEFINED_CUSTOM', table.concat(customVersion, '/'))) + else + message = lang.script('DIAG_UNDEF_GLOBAL', info.key) + end callback { start = source.start, finish = source.finish, - message = lang.script('DIAG_UNDEF_GLOBAL', info.key), + message = message, } end end) diff --git a/server-beta/src/library.lua b/server-beta/src/library.lua new file mode 100644 index 00000000..d4dba7c9 --- /dev/null +++ b/server-beta/src/library.lua @@ -0,0 +1,296 @@ +local lni = require 'lni' +local fs = require 'bee.filesystem' +local config = require 'config' +local util = require 'utility' + +local m = {} + +local function mergeEnum(lib, locale) + if not lib or not locale then + return + end + local pack = {} + for _, enum in ipairs(lib) do + if enum.enum then + pack[enum.enum] = enum + end + if enum.code then + pack[enum.code] = enum + end + end + for _, enum in ipairs(locale) do + if pack[enum.enum] then + if enum.description then + pack[enum.enum].description = enum.description + end + end + if pack[enum.code] then + if enum.description then + pack[enum.code].description = enum.description + end + end + end +end + +local function mergeField(lib, locale) + if not lib or not locale then + return + end + local pack = {} + for _, field in ipairs(lib) do + if field.field then + pack[field.field] = field + end + end + for _, field in ipairs(locale) do + if pack[field.field] then + if field.description then + pack[field.field].description = field.description + end + end + end +end + +local function mergeLocale(libs, locale) + if not libs or not locale then + return + end + for name in pairs(locale) do + if libs[name] then + if locale[name].description then + libs[name].description = locale[name].description + end + mergeEnum(libs[name].enums, locale[name].enums) + mergeField(libs[name].fields, locale[name].fields) + end + end +end + +local function isMatchVersion(version) + if not version then + return true + end + local runtimeVersion = config.config.runtime.version + if type(version) == 'table' then + for i = 1, #version do + if version[i] == runtimeVersion then + return true + end + end + else + if version == runtimeVersion then + return true + end + end + return false +end + +local function insertGlobal(tbl, key, value) + if not isMatchVersion(value.version) then + return false + end + if not value.doc then + value.doc = key + end + tbl[key] = value + return true +end + +local function insertOther(tbl, key, value) + if not value.version then + return + end + if not tbl[key] then + tbl[key] = {} + end + if type(value.version) == 'string' then + tbl[key][#tbl[key]+1] = value.version + elseif type(value.version) == 'table' then + for _, version in ipairs(value.version) do + if type(version) == 'string' then + tbl[key][#tbl[key]+1] = version + end + end + end + table.sort(tbl[key]) +end + +local function insertCustom(tbl, key, value, libName) + if not tbl[key] then + tbl[key] = {} + end + tbl[key][#tbl[key]+1] = libName + table.sort(tbl[key]) +end + +local function isEnableGlobal(libName) + if config.config.runtime.library[libName] then + return true + end + if libName:sub(1, 1) == '@' then + return true + end + return false +end + +local function mergeSource(alllibs, name, lib, libName) + if not lib.source then + if isEnableGlobal(libName) then + local suc = insertGlobal(alllibs.global, name, lib) + if not suc then + insertOther(alllibs.other, name, lib) + end + else + insertCustom(alllibs.custom, name, lib, libName) + end + return + end + for _, source in ipairs(lib.source) do + local sourceName = source.name or name + if source.type == 'global' then + if isEnableGlobal(libName) then + local suc = insertGlobal(alllibs.global, sourceName, lib) + if not suc then + insertOther(alllibs.other, sourceName, lib) + end + else + insertCustom(alllibs.custom, sourceName, lib, libName) + end + elseif source.type == 'library' then + insertGlobal(alllibs.library, sourceName, lib) + elseif source.type == 'object' then + insertGlobal(alllibs.object, sourceName, lib) + end + end +end + +local function copy(t) + local new = {} + for k, v in pairs(t) do + new[k] = v + end + return new +end + +local function insertChild(tbl, name, key, value) + if not name or not key then + return + end + if not isMatchVersion(value.version) then + return + end + if not value.doc then + value.doc = ('%s.%s'):format(name, key) + end + if not tbl[name] then + tbl[name] = { + type = name, + name = name, + child = {}, + } + end + tbl[name].child[key] = copy(value) +end + +local function mergeParent(alllibs, name, lib, libName) + for _, parent in ipairs(lib.parent) do + if parent.type == 'global' then + if isEnableGlobal(libName) then + insertChild(alllibs.global, parent.name, name, lib) + end + elseif parent.type == 'library' then + insertChild(alllibs.library, parent.name, name, lib) + elseif parent.type == 'object' then + insertChild(alllibs.object, parent.name, name, lib) + end + end +end + +local function mergeLibs(alllibs, libs, libName) + if not libs then + return + end + for _, lib in pairs(libs) do + if lib.parent then + mergeParent(alllibs, lib.name, lib, libName) + else + mergeSource(alllibs, lib.name, lib, libName) + end + end +end + +local function loadLocale(language, relative) + local localePath = ROOT / 'locale' / language / relative + local localeBuf = util.loadFile(localePath:string()) + if localeBuf then + local locale = util.container() + xpcall(lni, log.error, localeBuf, localePath:string(), {locale}) + return locale + end + return nil +end + +local function fix(libs) + for name, lib in pairs(libs) do + lib.name = lib.name or name + lib.child = {} + end +end + +local function scan(path) + local result = {path} + local i = 0 + return function () + i = i + 1 + local current = result[i] + if not current then + return nil + end + if fs.is_directory(current) then + for path in current:list_directory() do + result[#result+1] = path + end + end + return current + end +end + +local function init() + local lang = require 'language' + local id = lang.id + m.global = util.container() + m.library = util.container() + m.object = util.container() + m.other = util.container() + m.custom = util.container() + + for libPath in (ROOT / 'libs'):list_directory() do + local libName = libPath:filename():string() + for path in scan(libPath) do + local libs + local buf = util.loadFile(path:string()) + if buf then + libs = util.container() + xpcall(lni, log.error, buf, path:string(), {libs}) + fix(libs) + end + local relative = fs.relative(path, ROOT) + + local locale = loadLocale('en-US', relative) + mergeLocale(libs, locale) + if id ~= 'en-US' then + locale = loadLocale(id, relative) + mergeLocale(libs, locale) + end + mergeLibs(m, libs, libName) + end + end +end + +function m.reload() + init() +end + +init() + +return m diff --git a/server-beta/src/searcher/eachField.lua b/server-beta/src/searcher/eachField.lua index 11ec2be2..401378b7 100644 --- a/server-beta/src/searcher/eachField.lua +++ b/server-beta/src/searcher/eachField.lua @@ -130,10 +130,6 @@ end --- 获取所有的field function searcher.eachField(source, callback) - local lock <close> = searcher.lock('eachField', source) - if not lock then - return - end local cache = searcher.cache.eachField[source] if cache then for i = 1, #cache do @@ -141,6 +137,10 @@ function searcher.eachField(source, callback) end return end + local unlock = searcher.lock('eachField', source) + if not unlock then + return + end cache = {} searcher.cache.eachField[source] = cache local mark = {} @@ -151,6 +151,9 @@ function searcher.eachField(source, callback) end mark[src] = true cache[#cache+1] = info - callback(info) end) + unlock() + for i = 1, #cache do + callback(cache[i]) + end end diff --git a/server-beta/src/searcher/eachGlobal.lua b/server-beta/src/searcher/eachGlobal.lua index 21ee2c51..49396b00 100644 --- a/server-beta/src/searcher/eachGlobal.lua +++ b/server-beta/src/searcher/eachGlobal.lua @@ -8,10 +8,6 @@ local function eachGlobal(source, callback) end function searcher.eachGlobal(source, callback) - local lock <close> = searcher.lock('eachGlobal', source) - if not lock then - return - end local cache = searcher.cache.eachGlobal[source] if cache then for i = 1, #cache do @@ -19,6 +15,10 @@ function searcher.eachGlobal(source, callback) end return end + local unlock = searcher.lock('eachGlobal', source) + if not unlock then + return + end cache = {} searcher.cache.eachGlobal[source] = cache local mark = {} @@ -29,6 +29,9 @@ function searcher.eachGlobal(source, callback) end mark[src] = true cache[#cache+1] = info - callback(info) end) + unlock() + for i = 1, #cache do + callback(cache[i]) + end end diff --git a/server-beta/src/searcher/eachRef.lua b/server-beta/src/searcher/eachRef.lua index 4cefa405..7b2e3a27 100644 --- a/server-beta/src/searcher/eachRef.lua +++ b/server-beta/src/searcher/eachRef.lua @@ -330,10 +330,6 @@ end --- 获取所有的引用 function searcher.eachRef(source, callback) - local lock <close> = searcher.lock('eachRef', source) - if not lock then - return - end local cache = searcher.cache.eachRef[source] if cache then for i = 1, #cache do @@ -341,6 +337,10 @@ function searcher.eachRef(source, callback) end return end + local unlock = searcher.lock('eachRef', source) + if not unlock then + return + end cache = {} searcher.cache.eachRef[source] = cache local mark = {} @@ -351,6 +351,9 @@ function searcher.eachRef(source, callback) end mark[src] = true cache[#cache+1] = info - callback(info) end) + unlock() + for i = 1, #cache do + callback(cache[i]) + end end diff --git a/server-beta/src/searcher/searcher.lua b/server-beta/src/searcher/searcher.lua index d09e3428..361243fc 100644 --- a/server-beta/src/searcher/searcher.lua +++ b/server-beta/src/searcher/searcher.lua @@ -24,9 +24,9 @@ function m.lock(tp, source) return nil end m.locked[tp][source] = true - return util.defer(function () + return function () m.locked[tp][source] = nil - end) + end end --- 获取特殊对象的名字 |