diff options
author | 最萌小汐 <sumneko@hotmail.com> | 2019-09-18 17:44:31 +0800 |
---|---|---|
committer | 最萌小汐 <sumneko@hotmail.com> | 2019-09-18 17:44:31 +0800 |
commit | 0a2a30c57d44357abdfdc5ee47cb19e5eba8fb4c (patch) | |
tree | 90697a0efc902ab9835e96f7eba91ea0a0f1f3ec /server-beta/src | |
parent | 23780ea2f3c6c69c7e7983541981995c0edd9017 (diff) | |
download | lua-language-server-0a2a30c57d44357abdfdc5ee47cb19e5eba8fb4c.zip |
beta
Diffstat (limited to 'server-beta/src')
-rw-r--r-- | server-beta/src/fs-utility.lua | 314 | ||||
-rw-r--r-- | server-beta/src/log.lua | 116 | ||||
-rw-r--r-- | server-beta/src/utility.lua | 336 |
3 files changed, 766 insertions, 0 deletions
diff --git a/server-beta/src/fs-utility.lua b/server-beta/src/fs-utility.lua new file mode 100644 index 00000000..14dcb08f --- /dev/null +++ b/server-beta/src/fs-utility.lua @@ -0,0 +1,314 @@ +local fs = require 'bee.filesystem' +local platform = require 'bee.platform' + +local type = type +local ioOpen = io.open +local pcall = pcall +local pairs = pairs +local setmetatable = setmetatable +local next = next + +_ENV = nil + +local m = {} +--- 读取文件 +---@param path string +function m.loadFile(path) + if type(path) ~= 'string' then + path = path:string() + end + local f, e = ioOpen(path, 'rb') + if not f then + return nil, e + end + if f:read(3) ~= '\xEF\xBB\xBF' then + f:seek("set") + end + local buf = f:read 'a' + f:close() + return buf +end + +--- 写入文件 +---@param path string +---@param content string +function m.saveFile(path, content) + if type(path) ~= 'string' then + path = path:string() + end + local f, e = ioOpen(path, "wb") + + if f then + f:write(content) + f:close() + return true + else + return false, e + end +end + +local function buildOptional(optional) + optional = optional or {} + optional.add = optional.add or {} + optional.del = optional.del or {} + optional.mod = optional.mod or {} + optional.err = optional.err or {} + return optional +end + +local function fsAbsolute(path, optional) + if type(path) == 'string' then + local suc, res = pcall(fs.path, path) + if not suc then + optional.err[#optional.err+1] = res + return nil + end + path = res + end + local suc, res = pcall(fs.absolute, path) + if not suc then + optional.err[#optional.err+1] = res + return nil + end + return res +end + +local function fsIsDirectory(path, optional) + local suc, res = pcall(fs.is_directory, path) + if not suc then + optional.err[#optional.err+1] = res + return false + end + return res +end + +local function fsRemove(path, optional) + local suc, res = pcall(fs.remove, path) + if not suc then + optional.err[#optional.err+1] = res + end + optional.del[#optional.del+1] = path:string() +end + +local function fsExists(path, optional) + local suc, res = pcall(fs.exists, path) + if not suc then + optional.err[#optional.err+1] = res + return false + end + return res +end + +local function fsCopy(source, target, optional) + local suc, res = pcall(fs.copy_file, source, target, true) + if not suc then + optional.err[#optional.err+1] = res + return false + end + return true +end + +local function fsCreateDirectories(path, optional) + local suc, res = pcall(fs.create_directories, path) + if not suc then + optional.err[#optional.err+1] = res + return false + end + return true +end + +local function fileRemove(path, optional) + if optional.onRemove and optional.onRemove(path) == false then + return + end + if fsIsDirectory(path, optional) then + for child in path:list_directory() do + fileRemove(child, optional) + end + end + if fsRemove(path, optional) then + optional.del[#optional.del+1] = path:string() + end +end + +local function fileCopy(source, target, optional) + local isDir1 = fsIsDirectory(source, optional) + local isDir2 = fsIsDirectory(target, optional) + local isExists = fsExists(target, optional) + if isDir1 then + if isDir2 or fsCreateDirectories(target) then + for filePath in source:list_directory() do + local name = filePath:filename() + fileCopy(filePath, target / name, optional) + end + end + else + if isExists and not isDir2 then + local buf1, err1 = m.loadFile(source) + local buf2, err2 = m.loadFile(target) + if buf1 and buf2 then + if buf1 ~= buf2 then + if fsCopy(source, target, optional) then + optional.mod[#optional.mod+1] = target:string() + end + end + else + if not buf1 then + optional.err[#optional.err+1] = err1 + end + if not buf2 then + optional.err[#optional.err+1] = err2 + end + end + else + if fsCopy(source, target, optional) then + optional.add[#optional.add+1] = target:string() + end + end + end +end + +local function fileSync(source, target, optional) + local isDir1 = fsIsDirectory(source, optional) + local isDir2 = fsIsDirectory(target, optional) + local isExists = fsExists(target, optional) + if isDir1 then + if isDir2 then + local fileList = m.fileList() + for filePath in target:list_directory() do + fileList[filePath] = true + end + for filePath in source:list_directory() do + local name = filePath:filename() + local targetPath = target / name + fileSync(filePath, targetPath, optional) + fileList[targetPath] = nil + end + for path in pairs(fileList) do + fileRemove(path, optional) + end + else + if isExists then + fileRemove(target, optional) + end + if fsCreateDirectories(target) then + for filePath in source:list_directory() do + local name = filePath:filename() + fileCopy(filePath, target / name, optional) + end + end + end + else + if isDir2 then + fileRemove(target, optional) + end + if isExists then + local buf1, err1 = m.loadFile(source) + local buf2, err2 = m.loadFile(target) + if buf1 and buf2 then + if buf1 ~= buf2 then + if fsCopy(source, target, optional) then + optional.mod[#optional.mod+1] = target:string() + end + end + else + if not buf1 then + optional.err[#optional.err+1] = err1 + end + if not buf2 then + optional.err[#optional.err+1] = err2 + end + end + else + if fsCopy(source, target, optional) then + optional.add[#optional.add+1] = target:string() + end + end + end +end + +--- 文件列表 +function m.fileList(optional) + optional = optional or buildOptional(optional) + local os = platform.OS + local keyMap = {} + local fileList = {} + local function computeKey(path) + path = fsAbsolute(path, optional) + if not path then + return nil + end + local key + if os == 'Windows' then + key = path:string():lower() + else + key = path:string() + end + return key + end + return setmetatable({}, { + __index = function (_, path) + local key = computeKey(path) + return fileList[key] + end, + __newindex = function (_, path, value) + local key = computeKey(path) + if not key then + return + end + if value == nil then + keyMap[key] = nil + else + keyMap[key] = path + fileList[key] = value + end + end, + __pairs = function () + local key, path + return function () + key, path = next(keyMap, key) + return path, fileList[key] + end + end, + }) +end + +--- 删除文件(夹) +function m.fileRemove(path, optional) + optional = buildOptional(optional) + path = fsAbsolute(path, optional) + + fileRemove(path, optional) + + return optional +end + +--- 复制文件(夹) +---@param source string +---@param target string +---@return table +function m.fileCopy(source, target, optional) + optional = buildOptional(optional) + source = fsAbsolute(source, optional) + target = fsAbsolute(target, optional) + + fileCopy(source, target, optional) + + return optional +end + +--- 同步文件(夹) +---@param source string +---@param target string +---@return table +function m.fileSync(source, target, optional) + optional = buildOptional(optional) + source = fsAbsolute(source, optional) + target = fsAbsolute(target, optional) + + fileSync(source, target, optional) + + return optional +end + +return m diff --git a/server-beta/src/log.lua b/server-beta/src/log.lua new file mode 100644 index 00000000..d8e782c0 --- /dev/null +++ b/server-beta/src/log.lua @@ -0,0 +1,116 @@ +local fs = require 'bee.filesystem' + +local log = {} + +log.file = nil +log.start_time = os.time() - os.clock() +log.size = 0 +log.max_size = 100 * 1024 * 1024 + +local function trim_src(src) + src = src:sub(log.prefix_len + 3, -5) + src = src:gsub('^[/\\]+', '') + src = src:gsub('[\\/]+', '.') + return src +end + +local function init_log_file() + if not log.file then + log.file = io.open(log.path, 'w') + if not log.file then + return + end + log.file:write('') + log.file:close() + log.file = io.open(log.path, 'ab') + if not log.file then + return + end + log.file:setvbuf 'no' + end +end + +local function push_log(level, ...) + if not log.path then + return + end + if log.size > log.max_size then + return + end + local t = table.pack(...) + for i = 1, t.n do + t[i] = tostring(t[i]) + end + local str = table.concat(t, '\t', 1, t.n) + if level == 'error' then + str = str .. '\n' .. debug.traceback(nil, 3) + io.stderr:write(str .. '\n') + end + init_log_file() + if not log.file then + return + end + local sec, ms = math.modf(log.start_time + os.clock()) + local timestr = os.date('%Y-%m-%d %H:%M:%S', sec) + local info = debug.getinfo(3, 'Sl') + local buf + if info and info.currentline > 0 then + buf = ('[%s.%03.f][%s]: [%s:%s]%s\n'):format(timestr, ms * 1000, level, trim_src(info.source), info.currentline, str) + else + buf = ('[%s.%03.f][%s]: %s\n'):format(timestr, ms * 1000, level, str) + end + log.file:write(buf) + log.size = log.size + #buf + if log.size > log.max_size then + log.file:write('[REACH MAX SIZE]') + end + return str +end + +function log.info(...) + push_log('info', ...) +end + +function log.debug(...) + push_log('debug', ...) +end + +function log.trace(...) + push_log('trace', ...) +end + +function log.warn(...) + push_log('warn', ...) +end + +function log.error(...) + return push_log('error', ...) +end + +function log.init(root, path) + local lastBuf + if log.file then + log.file:close() + log.file = nil + local file = io.open(log.path, 'rb') + if file then + lastBuf = file:read 'a' + file:close() + end + end + log.path = path:string() + log.prefix_len = #root:string() + log.size = 0 + if not fs.exists(path:parent_path()) then + fs.create_directories(path:parent_path()) + end + if lastBuf then + init_log_file() + if log.file then + log.file:write(lastBuf) + log.size = log.size + #lastBuf + end + end +end + +return log diff --git a/server-beta/src/utility.lua b/server-beta/src/utility.lua new file mode 100644 index 00000000..85cb4105 --- /dev/null +++ b/server-beta/src/utility.lua @@ -0,0 +1,336 @@ +local tableSort = table.sort +local stringRep = string.rep +local tableConcat = table.concat +local tostring = tostring +local type = type +local pairs = pairs +local ipairs = ipairs +local next = next +local rawset = rawset +local move = table.move +local setmetatable = setmetatable +local mathType = math.type +local mathCeil = math.ceil +local getmetatable = getmetatable +local mathAbs = math.abs +local ioOpen = io.open + +_ENV = nil + +local function formatNumber(n) + local str = ('%.10f'):format(n) + str = str:gsub('%.?0*$', '') + return str +end + +local function isInteger(n) + if mathType then + return mathType(n) == 'integer' + else + return type(n) == 'number' and n % 1 == 0 + end +end + +local TAB = setmetatable({}, { __index = function (self, n) + self[n] = stringRep(' ', n) + return self[n] +end}) + +local RESERVED = { + ['and'] = true, + ['break'] = true, + ['do'] = true, + ['else'] = true, + ['elseif'] = true, + ['end'] = true, + ['false'] = true, + ['for'] = true, + ['function'] = true, + ['goto'] = true, + ['if'] = true, + ['in'] = true, + ['local'] = true, + ['nil'] = true, + ['not'] = true, + ['or'] = true, + ['repeat'] = true, + ['return'] = true, + ['then'] = true, + ['true'] = true, + ['until'] = true, + ['while'] = true, +} + +local m = {} + +--- 打印表的结构 +---@param tbl table +---@param option table {optional = 'self'} +---@return string +function m.dump(tbl, option) + if not option then + option = {} + end + if type(tbl) ~= 'table' then + return ('%s'):format(tbl) + end + local lines = {} + local mark = {} + lines[#lines+1] = '{' + local function unpack(tbl, tab) + if mark[tbl] and mark[tbl] > 0 then + lines[#lines+1] = TAB[tab+1] .. '"<Loop>"' + return + end + mark[tbl] = (mark[tbl] or 0) + 1 + local keys = {} + local keymap = {} + local integerFormat = '[%d]' + local alignment = 0 + if #tbl >= 10 then + local width = #tostring(#tbl) + integerFormat = ('[%%0%dd]'):format(mathCeil(width)) + end + for key in pairs(tbl) do + if type(key) == 'string' then + if not key:match('^[%a_][%w_]*$') + or RESERVED[key] + or option['longStringKey'] + then + keymap[key] = ('[%q]'):format(key) + else + keymap[key] = ('%s'):format(key) + end + elseif isInteger(key) then + keymap[key] = integerFormat:format(key) + else + keymap[key] = ('["<%s>"]'):format(tostring(key)) + end + keys[#keys+1] = key + if option['alignment'] then + if #keymap[key] > alignment then + alignment = #keymap[key] + end + end + end + local mt = getmetatable(tbl) + if not mt or not mt.__pairs then + if option['sorter'] then + option['sorter'](keys, keymap) + else + tableSort(keys, function (a, b) + return keymap[a] < keymap[b] + end) + end + end + for _, key in ipairs(keys) do + local keyWord = keymap[key] + if option['noArrayKey'] + and isInteger(key) + and key <= #tbl + then + keyWord = '' + else + if #keyWord < alignment then + keyWord = keyWord .. (' '):rep(alignment - #keyWord) .. ' = ' + else + keyWord = keyWord .. ' = ' + end + end + local value = tbl[key] + local tp = type(value) + if tp == 'table' then + lines[#lines+1] = ('%s%s{'):format(TAB[tab+1], keyWord) + unpack(value, tab+1) + lines[#lines+1] = ('%s},'):format(TAB[tab+1]) + elseif tp == 'string' then + lines[#lines+1] = ('%s%s%q,'):format(TAB[tab+1], keyWord, value) + elseif tp == 'number' then + lines[#lines+1] = ('%s%s%s,'):format(TAB[tab+1], keyWord, formatNumber(value)) + elseif tp == 'nil' then + else + lines[#lines+1] = ('%s%s%s,'):format(TAB[tab+1], keyWord, tostring(value)) + end + end + mark[tbl] = mark[tbl] - 1 + end + unpack(tbl, 0) + lines[#lines+1] = '}' + return tableConcat(lines, '\r\n') +end + +--- 递归判断A与B是否相等 +---@param a any +---@param b any +---@return boolean +function m.equal(a, b) + local tp1 = type(a) + local tp2 = type(b) + if tp1 ~= tp2 then + return false + end + if tp1 == 'table' then + local mark = {} + for k, v in pairs(a) do + mark[k] = true + local res = m.equal(v, b[k]) + if not res then + return false + end + end + for k in pairs(b) do + if not mark[k] then + return false + end + end + return true + elseif tp1 == 'number' then + return mathAbs(a - b) <= 1e-10 + else + return a == b + end +end + +local function sortTable(tbl) + if not tbl then + tbl = {} + end + local mt = {} + local keys = {} + local mark = {} + local n = 0 + for key in next, tbl do + n=n+1;keys[n] = key + mark[key] = true + end + tableSort(keys) + function mt:__newindex(key, value) + rawset(self, key, value) + n=n+1;keys[n] = key + mark[key] = true + if type(value) == 'table' then + sortTable(value) + end + end + function mt:__pairs() + local list = {} + local m = 0 + for key in next, self do + if not mark[key] then + m=m+1;list[m] = key + end + end + if m > 0 then + move(keys, 1, n, m+1) + tableSort(list) + for i = 1, m do + local key = list[i] + keys[i] = key + mark[key] = true + end + n = n + m + end + local i = 0 + return function () + i = i + 1 + local key = keys[i] + return key, self[key] + end + end + + return setmetatable(tbl, mt) +end + +--- 创建一个有序表 +---@param tbl table {optional = 'self'} +---@return table +function m.container(tbl) + return sortTable(tbl) +end + +--- 读取文件 +---@param path string +function m.loadFile(path) + local f, e = ioOpen(path, 'rb') + if not f then + return nil, e + end + if f:read(3) ~= '\xEF\xBB\xBF' then + f:seek("set") + end + local buf = f:read 'a' + f:close() + return buf +end + +--- 写入文件 +---@param path string +---@param content string +function m.saveFile(path, content) + local f, e = ioOpen(path, "wb") + + if f then + f:write(content) + f:close() + return true + else + return false, e + end +end + +--- 计数器 +---@param init integer {optional = 'after'} +---@param step integer {optional = 'after'} +---@return fun():integer +function m.counter(init, step) + if not step then + step = 1 + end + local current = init and (init - 1) or -1 + return function () + current = current + step + return current + end +end + +--- 排序后遍历 +---@param t table +function m.sortPairs(t) + local keys = {} + for k in pairs(t) do + keys[#keys+1] = k + end + tableSort(keys) + local i = 0 + return function () + i = i + 1 + local k = keys[i] + return k, t[k] + end +end + +--- 深拷贝(不处理元表) +---@param source table +---@param target table {optional = 'self'} +function m.deepCopy(source, target) + local mark = {} + local function copy(a, b) + if type(a) ~= 'table' then + return a + end + if mark[a] then + return mark[a] + end + if not b then + b = {} + end + mark[a] = b + for k, v in pairs(a) do + b[copy(k)] = copy(v) + end + return b + end + return copy(source, target) +end + +return m |