diff options
Diffstat (limited to 'server/src/matcher/completion.lua')
-rw-r--r-- | server/src/matcher/completion.lua | 501 |
1 files changed, 0 insertions, 501 deletions
diff --git a/server/src/matcher/completion.lua b/server/src/matcher/completion.lua deleted file mode 100644 index 508c0320..00000000 --- a/server/src/matcher/completion.lua +++ /dev/null @@ -1,501 +0,0 @@ -local findResult = require 'matcher.find_result' -local hover = require 'matcher.hover' - -local CompletionItemKind = { - Text = 1, - Method = 2, - Function = 3, - Constructor = 4, - Field = 5, - Variable = 6, - Class = 7, - Interface = 8, - Module = 9, - Property = 10, - Unit = 11, - Value = 12, - Enum = 13, - Keyword = 14, - Snippet = 15, - Color = 16, - File = 17, - Reference = 18, - Folder = 19, - EnumMember = 20, - Constant = 21, - Struct = 22, - Event = 23, - Operator = 24, - TypeParameter = 25, -} - -local function matchKey(me, other) - if me == other then - return false - end - if me == '' then - return true - end - if #me > #other then - return false - end - local lMe = me:lower() - local lOther = other:lower() - if lMe:sub(1, 1) ~= lOther:sub(1, 1) then - return false - end - if lMe == lOther:sub(1, #lMe) then - return true - end - local used = { - [1] = true, - } - local cur = 2 - local lookup - local researched - for i = 2, #lMe do - local c = lMe:sub(i, i) - -- 1. 看当前字符是否匹配 - if c == lOther:sub(cur, cur) then - used[cur] = true - goto NEXT - end - -- 2. 看前一个字符是否匹配 - if not used[cur-1] then - if c == lOther:sub(cur-1, cur-1) then - used[cur-1] = true - goto NEXT - end - end - -- 3. 向后找这个字 - lookup = lOther:find(c, cur+1, true) - if lookup then - cur = lookup - used[cur] = true - goto NEXT - end - - -- 4. 重新搜索整个字符串,但是只允许1次,否则失败.如果找不到也失败 - if researched then - return false - else - researched = true - for j = 2, cur - 2 do - if c == lOther:sub(j, j) then - used[j] = true - goto NEXT - end - end - return false - end - -- 5. 找到下一个可用的字,如果超出长度就算成功 - ::NEXT:: - repeat - cur = cur + 1 - until not used[cur] - if cur > #lOther then - break - end - end - return true -end - -local function searchLocals(vm, pos, name, callback) - for _, loc in ipairs(vm.results.locals) do - if loc.source.start == 0 then - goto CONTINUE - end - if loc.source.start <= pos and loc.close >= pos then - if matchKey(name, loc.key) then - callback(loc) - end - end - ::CONTINUE:: - end -end - -local function searchFields(name, parent, object, callback) - if not parent or not parent.value or not parent.value.child then - return - end - for key, field in pairs(parent.value.child) do - if type(key) ~= 'string' then - goto CONTINUE - end - if object then - if not field.value or field.value.type ~= 'function' then - goto CONTINUE - end - end - if type(name) == 'string' and matchKey(name, key) then - callback(field) - end - ::CONTINUE:: - end -end - -local KEYS = {'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while', 'toclose'} -local function searchKeyWords(name, callback) - for _, key in ipairs(KEYS) do - if matchKey(name, key) then - callback(key) - end - end -end - -local function getKind(var, default) - local value = var.value - if default == CompletionItemKind.Variable then - if value.type == 'function' then - return CompletionItemKind.Function - end - end - if default == CompletionItemKind.Field then - local tp = type(value.value) - if tp == 'number' or tp == 'integer' or tp == 'string' then - return CompletionItemKind.Enum - end - if value.type == 'function' then - if var.parent and var.parent.value and var.parent.value.ENV ~= true then - return CompletionItemKind.Method - else - return CompletionItemKind.Function - end - end - end - return default -end - -local function getDetail(var) - local tp = type(var.value.value) - if tp == 'boolean' then - return ('= %q'):format(var.value.value) - elseif tp == 'number' then - if math.type(var.value.value) == 'integer' then - return ('= %q'):format(var.value.value) - else - local str = ('= %.10f'):format(var.value.value) - local dot = str:find('.', 1, true) - local suffix = str:find('[0]+$', dot+2) - if suffix then - return str:sub(1, suffix-1) - else - return str - end - end - elseif tp == 'string' then - return ('= %q'):format(var.value.value) - end - return nil -end - -local function getDocument(var, source) - if var.value.type == 'function' then - return { - kind = 'markdown', - value = hover(var, source), - } - end - return nil -end - -local function searchAsLocal(vm, pos, result, callback) - searchFields(result.key, vm.results.locals[1], nil, function (var) - callback(var, CompletionItemKind.Variable) - end) - - -- 支持 local function - if matchKey(result.key, 'function') then - callback('function', CompletionItemKind.Keyword) - end -end - -local function searchAsArg(vm, pos, result, callback) - searchFields(result.key, vm.results.locals[1], nil, function (var) - if var.value.lib then - return - end - callback(var, CompletionItemKind.Variable) - end) -end - -local function searchAsGlobal(vm, pos, result, callback) - if result.key == '' then - return - end - searchLocals(vm, pos, result.key, function (var) - callback(var, CompletionItemKind.Variable) - end) - searchFields(result.key, vm.results.locals[1], nil, function (var) - callback(var, CompletionItemKind.Field) - end) - searchKeyWords(result.key, function (name) - callback(name, CompletionItemKind.Keyword) - end) -end - -local function searchAsSuffix(result, callback) - searchFields(result.key, result.parent, result.source.object, function (var) - callback(var, CompletionItemKind.Field) - end) -end - -local function searchInArg(vm, inCall, inString, callback) - local lib = inCall.func.lib - if not lib then - return - end - - -- require列举出可以引用到的文件 - if lib.special == 'require' then - if not vm.lsp or not vm.lsp.workspace then - return - end - local results = vm.lsp.workspace:matchPath(vm.uri, inString[1]) - if not results then - return - end - for _, v in ipairs(results) do - if v ~= inString[1] then - callback(v, CompletionItemKind.File) - end - end - end - - -- 其他库函数,根据参数位置找枚举值 - if lib.args and lib.enums then - local arg = lib.args[inCall.select] - local name = arg and arg.name - for _, enum in ipairs(lib.enums) do - if enum.name == name and enum.enum then - if inString then - callback(enum.enum, CompletionItemKind.EnumMember, { - documentation = enum.description - }) - else - callback(('%q'):format(enum.enum), CompletionItemKind.EnumMember, { - documentation = enum.description - }) - end - end - end - end -end - -local function searchAsIndex(vm, pos, result, callback) - searchLocals(vm, pos, result.key, function (var) - callback(var, CompletionItemKind.Variable) - end) - for _, index in ipairs(vm.results.indexs) do - if matchKey(result.key, index) then - callback(index, CompletionItemKind.Property) - end - end - searchFields(result.key, vm.results.locals[1], nil, function (var) - callback(var, CompletionItemKind.Field) - end) -end - -local function findClosePos(vm, pos) - local curDis = math.maxinteger - local parent = nil - local function found(object, source) - local dis = pos - source.finish - if dis > 1 and dis < curDis then - curDis = dis - parent = object - end - end - for sources, object in pairs(vm.results.sources) do - if sources.type == 'multi-source' then - for _, source in ipairs(sources) do - if source.type ~= 'simple' then - found(object, source) - end - end - else - found(object, sources) - end - end - if not parent then - return nil - end - if parent.type ~= 'local' and parent.type ~= 'field' then - return nil - end - -- 造个假的 DirtyName - local source = { - type = 'name', - start = pos, - finish = pos, - [1] = '', - } - local result = { - type = 'field', - parent = parent, - key = '', - source = source, - } - return result, source -end - -local function isContainPos(obj, pos) - if obj.start <= pos and obj.finish + 1 >= pos then - return true - end - return false -end - -local function findString(vm, pos) - for _, source in ipairs(vm.results.strings) do - if isContainPos(source, pos) then - return source - end - end - return nil -end - -local function findArgCount(args, pos) - for i, arg in ipairs(args) do - if isContainPos(arg, pos) then - return i - end - end - return #args + 1 -end - --- 找出范围包含pos的call -local function findCall(vm, pos) - local results = {} - for _, call in ipairs(vm.results.calls) do - if isContainPos(call.args, pos) then - local n = findArgCount(call.args, pos) - local var = vm.results.sources[call.lastObj] - if var then - results[#results+1] = { - func = call.func, - var = var, - source = call.lastObj, - select = n, - args = call.args, - } - end - end - end - if #results == 0 then - return nil - end - -- 可能处于 'func1(func2(' 的嵌套中,因此距离越远的函数层级越低 - table.sort(results, function (a, b) - return a.args.start < b.args.start - end) - return results[#results] -end - -local function makeList(source) - local list = {} - local mark = {} - local function callback(var, defualt, data) - local key - if type(var) == 'string' then - key = var - else - key = var.key - end - if mark[key] then - return - end - mark[key] = true - data = data or {} - list[#list+1] = data - if var == key then - data.label = var - data.kind = defualt - else - data.label = var.key - data.kind = getKind(var, defualt) - data.detail = data.detail or getDetail(var) - data.documentation = data.documentation or getDocument(var, source) - end - end - return list, callback -end - -local function searchInResult(result, source, vm, pos, callback) - if result.type == 'local' then - if source.isArg then - searchAsArg(vm, pos, result, callback) - elseif source.isLocal then - searchAsLocal(vm, pos, result, callback) - else - searchAsGlobal(vm, pos, result, callback) - end - elseif result.type == 'field' then - if source.isIndex then - searchAsIndex(vm, pos, result, callback) - elseif result.parent and result.parent.value and result.parent.value.ENV == true then - searchAsGlobal(vm, pos, result, callback) - else - searchAsSuffix(result, callback) - end - end -end - -local function searchSpecial(vm, pos, callback) - -- 尝试 # - local result, source = findResult(vm, pos, 2) - if source and source.type == 'index' - and result.source and result.source.op == '#' - then - local name = {} - local var = result - while true do - var = var.parent - if not var then - break - end - if var.value and var.value.ENV then - break - end - local key = var.key - if type(key) ~= 'string' or key == '' then - return - end - table.insert(name, 1, key) - end - local label = table.concat(name, '.') .. '+1' - callback(label, CompletionItemKind.Snippet, { - textEdit = { - start = result.source.start + 1, - finish = source.finish, - newText = ('%s] = '):format(label), - } - }) - end -end - -return function (vm, pos) - local result, source = findResult(vm, pos) - if not result then - result, source = findClosePos(vm, pos) - end - - if not result then - return nil - end - - local list, callback = makeList(source) - local inCall = findCall(vm, pos) - local inString = findString(vm, pos) - if inCall then - searchInArg(vm, inCall, inString, callback) - end - searchSpecial(vm, pos, callback) - if not inString then - searchInResult(result, source, vm, pos, callback) - end - if #list == 0 then - return nil - end - return list -end |