diff options
Diffstat (limited to 'script/core')
27 files changed, 648 insertions, 528 deletions
diff --git a/script/core/code-action.lua b/script/core/code-action.lua index 64f862f9..8256107e 100644 --- a/script/core/code-action.lua +++ b/script/core/code-action.lua @@ -1,63 +1,49 @@ -local files = require 'files' -local lang = require 'language' -local util = require 'utility' -local sp = require 'bee.subprocess' -local guide = require "parser.guide" +local files = require 'files' +local lang = require 'language' +local util = require 'utility' +local sp = require 'bee.subprocess' +local guide = require "parser.guide" +local converter = require 'proto.converter' local function checkDisableByLuaDocExits(uri, row, mode, code) - local lines = files.getLines(uri) - local ast = files.getState(uri) - local text = files.getOriginText(uri) - local line = lines[row] - if ast.ast.docs and line then - for _, doc in ipairs(ast.ast.docs) do - if doc.start >= line.start - and doc.finish <= line.finish then - if doc.type == 'doc.diagnostic' then - if doc.mode == mode then - if doc.names then - return { - start = doc.finish, - finish = doc.finish, - newText = text:sub(doc.finish, doc.finish) - .. ', ' - .. code - } - else - return { - start = doc.finish, - finish = doc.finish, - newText = text:sub(doc.finish, doc.finish) - .. ': ' - .. code - } - end - end + if row < 0 then + return nil + end + local state = files.getState(uri) + local lines = state.lines + if state.ast.docs and lines then + return guide.eachSourceBetween(state.ast.docs, guide.positionOf(row, 0), guide.positionOf(row + 1, 0), function (doc) + if doc.type == 'doc.diagnostic' + and doc.mode == mode then + if doc.names then + return { + start = doc.finish, + finish = doc.finish, + newText = ', ' .. code, + } + else + return { + start = doc.finish, + finish = doc.finish, + newText = ': ' .. code, + } end end - end + end) end return nil end local function checkDisableByLuaDocInsert(uri, row, mode, code) - local lines = files.getLines(uri) - local ast = files.getState(uri) - local text = files.getOriginText(uri) - -- 先看看上一行是不是已经有了 - -- 没有的话就插入一行 - local line = lines[row] return { - start = line.start, - finish = line.start, - newText = '---@diagnostic ' .. mode .. ': ' .. code .. '\n' - .. text:sub(line.start, line.start) + start = guide.positionOf(row, 0), + finish = guide.positionOf(row, 0), + newText = '---@diagnostic ' .. mode .. ': ' .. code .. '\n', } end local function disableDiagnostic(uri, code, start, results) - local lines = files.getLines(uri) - local row = guide.positionOf(lines, start) + local row = guide.rowColOf(start) results[#results+1] = { title = lang.script('ACTION_DISABLE_DIAG', code), kind = 'quickfix', @@ -90,8 +76,8 @@ local function disableDiagnostic(uri, code, start, results) checkDisableByLuaDocExits (uri, row - 1, 'disable-next-line', code) or checkDisableByLuaDocInsert(uri, row, 'disable-next-line', code)) pushEdit(lang.script('ACTION_DISABLE_DIAG_FILE', code), - checkDisableByLuaDocExits (uri, 1, 'disable', code) - or checkDisableByLuaDocInsert(uri, 1, 'disable', code)) + checkDisableByLuaDocExits (uri, 0, 'disable', code) + or checkDisableByLuaDocInsert(uri, 0, 'disable', code)) end local function markGlobal(uri, name, results) @@ -134,8 +120,8 @@ end local function solveUndefinedGlobal(uri, diag, results) local ast = files.getState(uri) - local offset = files.offsetOfWord(uri, diag.range.start) - guide.eachSourceContain(ast.ast, offset, function (source) + local start = converter.unpackRange(uri, diag.range) + guide.eachSourceContain(ast.ast, start, function (source) if source.type ~= 'getglobal' then return end @@ -153,8 +139,8 @@ end local function solveLowercaseGlobal(uri, diag, results) local ast = files.getState(uri) - local offset = files.offsetOfWord(uri, diag.range.start) - guide.eachSourceContain(ast.ast, offset, function (source) + local start = converter.unpackRange(uri, diag.range) + guide.eachSourceContain(ast.ast, start, function (source) if source.type ~= 'setglobal' then return end @@ -168,7 +154,7 @@ local function findSyntax(uri, diag) local ast = files.getState(uri) for _, err in ipairs(ast.errs) do if err.type:lower():gsub('_', '-') == diag.code then - local range = files.range(uri, err.start, err.finish) + local range = converter.packRange(uri, err.start, err.finish) if util.equal(range, diag.range) then return err end @@ -197,8 +183,13 @@ local function solveSyntaxByAddDoEnd(uri, err, results) [uri] = { { start = err.start, + finish = err.start, + newText = 'do ', + }, + { + start = err.finish, finish = err.finish, - newText = ('do %s end'):format(text:sub(err.start, err.finish)), + newText = ' end', }, } } @@ -265,7 +256,7 @@ local function solveSyntax(uri, diag, results) end local function solveNewlineCall(uri, diag, results) - local start = files.unrange(uri, diag.range) + local start = converter.unpackRange(uri, diag.range) results[#results+1] = { title = lang.script.ACTION_ADD_SEMICOLON, kind = 'quickfix', @@ -349,18 +340,18 @@ local function checkQuickFix(results, uri, start, diagnostics) end local function checkSwapParams(results, uri, start, finish) - local ast = files.getState(uri) - local text = files.getText(uri) - if not ast then + local state = files.getState(uri) + local text = files.getText(uri) + if not state then return end local args = {} - guide.eachSourceBetween(ast.ast, start, finish, function (source) + guide.eachSourceBetween(state.ast, start, finish, function (source) if source.type == 'callargs' or source.type == 'funcargs' then local targetIndex for index, arg in ipairs(source) do - if arg.start - 1 <= finish and arg.finish >= start then + if arg.start <= finish and arg.finish >= start then -- should select only one param if targetIndex then return @@ -373,11 +364,17 @@ local function checkSwapParams(results, uri, start, finish) end local node if source.type == 'callargs' then - node = text:sub(source.parent.node.start, source.parent.node.finish) + node = text:sub( + guide.positionToOffset(state, source.parent.node.start) + 1, + guide.positionToOffset(state, source.parent.node.finish) + ) elseif source.type == 'funcargs' then local var = source.parent.parent if guide.isSet(var) then - node = text:sub(var.start, var.finish) + node = text:sub( + guide.positionToOffset(state, var.start) + 1, + guide.positionToOffset(state, var.finish) + ) else node = lang.script.SYMBOL_ANONYMOUS end @@ -411,12 +408,18 @@ local function checkSwapParams(results, uri, start, finish) { start = myArg.start, finish = myArg.finish, - newText = text:sub(targetArg.start, targetArg.finish), + newText = text:sub( + guide.positionToOffset(state, targetArg.start) + 1, + guide.positionToOffset(state, targetArg.finish) + ), }, { start = targetArg.start, finish = targetArg.finish, - newText = text:sub(myArg.start, myArg.finish), + newText = text:sub( + guide.positionToOffset(state, myArg.start) + 1, + guide.positionToOffset(state, myArg.finish) + ), }, } } @@ -499,13 +502,16 @@ end --end local function checkJsonToLua(results, uri, start, finish) - local text = files.getText(uri) - local jsonStart = text:match ('()[%{%[]', start) + local text = files.getText(uri) + local state = files.getState(uri) + local startOffset = guide.positionToOffset(state, start) + local finishOffset = guide.positionToOffset(state, finish) + local jsonStart = text:match ('()[%{%[]', startOffset + 1) if not jsonStart then return end local jsonFinish - for i = math.min(finish, #text), jsonStart + 1, -1 do + for i = math.min(finishOffset, #text), jsonStart + 1, -1 do local char = text:sub(i, i) if char == ']' or char == '}' then @@ -528,8 +534,8 @@ local function checkJsonToLua(results, uri, start, finish) arguments = { { uri = uri, - start = jsonStart, - finish = jsonFinish, + start = guide.offsetToPosition(state, jsonStart) - 1, + finish = guide.offsetToPosition(state, jsonFinish), } } }, diff --git a/script/core/command/autoRequire.lua b/script/core/command/autoRequire.lua index 2cb6a8f8..d711fb16 100644 --- a/script/core/command/autoRequire.lua +++ b/script/core/command/autoRequire.lua @@ -4,20 +4,22 @@ local config = require 'config' local rpath = require 'workspace.require-path' local client = require 'client' local lang = require 'language' +local guide = require 'parser.guide' -local function findInsertOffset(uri) - local lines = files.getLines(uri) +local function findInsertRow(uri) local text = files.getText(uri) + local state = files.getState(uri) + local lines = state.lines local fmt = { pair = false, quot = '"', col = nil, } - for i = 1, #lines do + for i = 0, #lines do local ln = lines[i] - local lnText = text:sub(ln.start, ln.finish) + local lnText = text:match('[^\r\n]*', ln) if not lnText:find('require', 1, true) then - return ln.start, fmt + return i, fmt else local lpPos = lnText:find '%(' if lpPos then @@ -33,7 +35,7 @@ local function findInsertOffset(uri) end end end - return 1, fmt + return 0, fmt end local function askAutoRequire(visiblePaths) @@ -70,7 +72,7 @@ local function askAutoRequire(visiblePaths) return nameMap[result] end -local function applyAutoRequire(uri, offset, name, result, fmt) +local function applyAutoRequire(uri, row, name, result, fmt) local quotedResult = ('%q'):format(result) if fmt.quot == "'" then quotedResult = ([['%s']]):format(quotedResult:sub(2, -2) @@ -88,11 +90,11 @@ local function applyAutoRequire(uri, offset, name, result, fmt) if fmt.col and fmt.col > #text then sp = (' '):rep(fmt.col - #text - 1) end - text = ('\nlocal %s%s= require%s\n'):format(name, sp, quotedResult) + text = ('local %s%s= require%s\n'):format(name, sp, quotedResult) client.editText(uri, { { - start = offset, - finish = offset - 1, + start = guide.positionOf(row, 0), + finish = guide.positionOf(row, 0), text = text, } }) @@ -121,6 +123,6 @@ return function (data) return end - local offset, fmt = findInsertOffset(uri) + local offset, fmt = findInsertRow(uri) applyAutoRequire(uri, offset, name, result, fmt) end diff --git a/script/core/command/jsonToLua.lua b/script/core/command/jsonToLua.lua index c4f001ff..8a493b5e 100644 --- a/script/core/command/jsonToLua.lua +++ b/script/core/command/jsonToLua.lua @@ -1,9 +1,10 @@ -local files = require 'files' -local json = require 'json' -local util = require 'utility' -local proto = require 'proto' -local define = require 'proto.define' -local lang = require 'language' +local files = require 'files' +local json = require 'json' +local util = require 'utility' +local proto = require 'proto' +local define = require 'proto.define' +local lang = require 'language' +local converter = require 'proto.converter' return function (data) local text = files.getText(data.uri) @@ -26,7 +27,7 @@ return function (data) changes = { [data.uri] = { { - range = files.range(data.uri, data.start, data.finish), + range = converter.packRange(data.uri, data.start, data.finish), newText = luaStr, } } diff --git a/script/core/command/removeSpace.lua b/script/core/command/removeSpace.lua index b94f9788..3021d4a4 100644 --- a/script/core/command/removeSpace.lua +++ b/script/core/command/removeSpace.lua @@ -1,7 +1,8 @@ -local files = require 'files' -local guide = require 'parser.guide' -local proto = require 'proto' -local lang = require 'language' +local files = require 'files' +local guide = require 'parser.guide' +local proto = require 'proto' +local lang = require 'language' +local converter = require 'proto.converter' local function isInString(ast, offset) return guide.eachSourceContain(ast.ast, offset, function (source) @@ -13,29 +14,39 @@ end return function (data) local uri = data.uri - local lines = files.getLines(uri) local text = files.getText(uri) - local ast = files.getState(uri) - if not lines then + local state = files.getState(uri) + if not state then return end + local lines = state.lines local textEdit = {} - for i = 1, #lines do - local line = guide.lineContent(lines, text, i, true) - local pos = line:find '[ \t]+$' - if pos then - local start, finish = guide.lineRange(lines, i, true) - start = start + pos - if isInString(ast, start) then - goto NEXT_LINE - end - textEdit[#textEdit+1] = { - range = files.range(uri, start, finish), - newText = '', - } + for i = 0, #lines do + local startOffset = lines[i] + local finishOffset = text:find('[\r\n]', startOffset) or (#text + 1) + local lastOffset = finishOffset - 1 + local lastChar = text:sub(lastOffset, lastOffset) + if lastChar ~= ' ' and lastChar ~= '\t' then + goto NEXT_LINE + end + local lastPos = guide.offsetToPosition(state, lastOffset) + if isInString(state.ast, lastPos) then goto NEXT_LINE end + local firstOffset = startOffset + for n = lastOffset - 1, startOffset, -1 do + local char = text:sub(n, n) + if char ~= ' ' and char ~= '\t' then + firstOffset = n + 1 + break + end + end + local firstPos = guide.offsetToPosition(state, firstOffset) - 1 + textEdit[#textEdit+1] = { + range = converter.packRange(uri, firstPos, lastPos), + newText = '', + } ::NEXT_LINE:: end diff --git a/script/core/command/solve.lua b/script/core/command/solve.lua index a493de24..9428d065 100644 --- a/script/core/command/solve.lua +++ b/script/core/command/solve.lua @@ -1,7 +1,8 @@ -local files = require 'files' -local guide = require 'parser.guide' -local proto = require 'proto' -local lang = require 'language' +local files = require 'files' +local guide = require 'parser.guide' +local proto = require 'proto' +local lang = require 'language' +local converter = require 'proto.converter' local opMap = { ['+'] = true, @@ -34,8 +35,7 @@ return function (data) return end - local start = files.offsetOfWord(uri, data.range.start) - local finish = files.offsetOfWord(uri, data.range['end']) + local start, finish = converter.unpackRange(uri, data.range) local result = guide.eachSourceContain(ast.ast, start, function (source) if source.start ~= start @@ -85,7 +85,7 @@ return function (data) changes = { [uri] = { { - range = files.range(uri, result.start, result.finish), + range = converter.packRange(uri, result.start, result.finish), newText = ('(%s)'):format(text:sub(result.start, result.finish)), } }, diff --git a/script/core/completion.lua b/script/core/completion.lua index fb7b2eb4..bb55506a 100644 --- a/script/core/completion.lua +++ b/script/core/completion.lua @@ -53,17 +53,17 @@ local function trim(str) return str:match '^%s*(%S+)%s*$' end -local function findNearestSource(ast, offset) +local function findNearestSource(state, position) local source - guide.eachSourceContain(ast.ast, offset, function (src) + guide.eachSourceContain(state.ast, position, function (src) source = src end) return source end -local function findNearestTableField(ast, offset) +local function findNearestTableField(state, position) local source - guide.eachSourceContain(ast.ast, offset, function (src) + guide.eachSourceContain(state.ast, position, function (src) if src.type == 'table' or src.type == 'tablefield' or src.type == 'tableindex' @@ -74,7 +74,8 @@ local function findNearestTableField(ast, offset) return source end -local function findParent(ast, text, offset) +local function findParent(state, text, position) + local offset = guide.positionToOffset(state, position) for i = offset, 1, -1 do local char = text:sub(i, i) if lookBackward.isSpace(char) then @@ -92,11 +93,12 @@ local function findParent(ast, text, offset) else return nil, nil end - local anyPos = lookBackward.findAnyPos(text, i-1) - if not anyPos then + local anyOffset = lookBackward.findAnyOffset(text, i-1) + if not anyOffset then return nil, nil end - local parent = guide.eachSourceContain(ast.ast, anyPos, function (source) + local anyPos = guide.offsetToPosition(state, anyOffset) + local parent = guide.eachSourceContain(state.ast, anyPos, function (source) if source.finish == anyPos then return source end @@ -109,9 +111,9 @@ local function findParent(ast, text, offset) return nil, nil end -local function findParentInStringIndex(ast, text, offset) +local function findParentInStringIndex(state, text, position) local near, nearStart - guide.eachSourceContain(ast.ast, offset, function (source) + guide.eachSourceContain(state.ast, position, function (source) local start = guide.getStartFinish(source) if not start then return @@ -169,17 +171,18 @@ local function getSnip(source) if def ~= source and def.type == 'function' then local uri = guide.getUri(def) local text = files.getText(uri) - local lines = files.getLines(uri) + local state = files.getState(uri) + local lines = state.lines if not text then goto CONTINUE end if vm.isMetaFile(uri) then goto CONTINUE end - local row = guide.positionOf(lines, def.start) - local firstRow = lines[row] - local lastRow = lines[math.min(row + context - 1, #lines)] - local snip = text:sub(firstRow.start, lastRow.finish) + local firstRow = guide.rowColOf(def.start) + local lastRow = firstRow + context + local lastOffset = lines[lastRow] and (lines[lastRow] - 1) or #text + local snip = text:sub(lines[firstRow], lastOffset) return snip end ::CONTINUE:: @@ -222,38 +225,8 @@ local function buildFunction(results, source, value, oop, data) end end -local function buildInsertRequire(ast, targetUri, stemName) - local uri = guide.getUri(ast.ast) - local lines = files.getLines(uri) - local text = files.getText(uri) - local start = 1 - for i = 1, #lines do - local ln = lines[i] - local lnText = text:sub(ln.start, ln.finish) - if not lnText:find('require', 1, true) then - start = ln.start - break - end - end - local path = furi.decode(targetUri) - local visiblePaths = rpath.getVisiblePath(path, config.get 'Lua.runtime.path', true) - if not visiblePaths or #visiblePaths == 0 then - return nil - end - table.sort(visiblePaths, function (a, b) - return #a.expect < #b.expect - end) - return { - { - start = start, - finish = start - 1, - newText = ('local %s = require %q\n'):format(stemName, visiblePaths[1].expect) - } - } -end - -local function isSameSource(ast, source, pos) - if guide.getUri(source) ~= guide.getUri(ast.ast) then +local function isSameSource(state, source, pos) + if guide.getUri(source) ~= guide.getUri(state.ast) then return false end if source.type == 'field' @@ -283,10 +256,10 @@ local function getParams(func, oop) return '(' .. table.concat(args, ', ') .. ')' end -local function checkLocal(ast, word, offset, results) - local locals = guide.getVisibleLocals(ast.ast, offset) +local function checkLocal(state, word, position, results) + local locals = guide.getVisibleLocals(state.ast, position) for name, source in util.sortPairs(locals) do - if isSameSource(ast, source, offset) then + if isSameSource(state, source, position) then goto CONTINUE end if not matchKey(word, name) then @@ -330,13 +303,13 @@ local function checkLocal(ast, word, offset, results) end end -local function checkModule(ast, word, offset, results) +local function checkModule(state, word, position, results) if not config.get 'Lua.completion.autoRequire' then return end - local locals = guide.getVisibleLocals(ast.ast, offset) + local locals = guide.getVisibleLocals(state.ast, position) for uri in files.eachFile() do - if uri == guide.getUri(ast.ast) then + if uri == guide.getUri(state.ast) then goto CONTINUE end local path = furi.decode(uri) @@ -347,11 +320,11 @@ local function checkModule(ast, word, offset, results) and not config.get 'Lua.diagnostics.globals'[stemName] and stemName:match '^[%a_][%w_]*$' and matchKey(word, stemName) then - local targetAst = files.getState(uri) - if not targetAst then + local targetState = files.getState(uri) + if not targetState then goto CONTINUE end - local targetReturns = targetAst.ast.returns + local targetReturns = targetState.ast.returns if not targetReturns then goto CONTINUE end @@ -377,7 +350,7 @@ local function checkModule(ast, word, offset, results) command = 'lua.autoRequire:' .. sp:get_id(), arguments = { { - uri = guide.getUri(ast.ast), + uri = guide.getUri(state.ast), target = uri, name = stemName, }, @@ -393,7 +366,7 @@ local function checkModule(ast, word, offset, results) return { detail = buildDetail(targetSource), description = md, - --additionalTextEdits = buildInsertRequire(ast, originUri, stemName), + --additionalTextEdits = buildInsertRequire(state, originUri, stemName), } end) } @@ -402,19 +375,28 @@ local function checkModule(ast, word, offset, results) end end -local function checkFieldFromFieldToIndex(name, src, parent, word, start, offset) +local function checkFieldFromFieldToIndex(name, src, parent, word, startPos, position) if name:match '^[%a_][%w_]*$' then return nil end local textEdit, additionalTextEdits - local uri = guide.getUri(parent) - local text = files.getText(uri) - local wordStart + local uri = guide.getUri(parent) + local text = files.getText(uri) + local state = files.getState(uri) + local startOffset = guide.positionToOffset(state, startPos) + local offset = guide.positionToOffset(state, position) + local wordStartOffset if word == '' then - wordStart = text:match('()%S', start + 1) or (offset + 1) + wordStartOffset = text:match('()%S', startOffset + 1) + if wordStartOffset then + wordStartOffset = wordStartOffset - 1 + else + wordStartOffset = offset + end else - wordStart = offset - #word + 1 + wordStartOffset = offset - #word end + local wordStartPos = guide.offsetToPosition(state, wordStartOffset) local newText if vm.getKeyType(src) == 'string' then newText = ('[%q]'):format(name) @@ -422,26 +404,28 @@ local function checkFieldFromFieldToIndex(name, src, parent, word, start, offset newText = ('[%s]'):format(name) end textEdit = { - start = wordStart, - finish = offset, + start = wordStartPos, + finish = position, newText = newText, } local nxt = parent.next if nxt then - local dotStart + local dotStart, dotFinish if nxt.type == 'setfield' or nxt.type == 'getfield' or nxt.type == 'tablefield' then dotStart = nxt.dot.start + dotFinish = nxt.dot.finish elseif nxt.type == 'setmethod' or nxt.type == 'getmethod' then dotStart = nxt.colon.start + dotFinish = nxt.colon.finish end if dotStart then additionalTextEdits = { { start = dotStart, - finish = dotStart, + finish = dotFinish, newText = '', } } @@ -457,7 +441,7 @@ local function checkFieldFromFieldToIndex(name, src, parent, word, start, offset return textEdit, additionalTextEdits end -local function checkFieldThen(name, src, word, start, offset, parent, oop, results) +local function checkFieldThen(name, src, word, startPos, position, parent, oop, results) local value = searcher.getObjectValue(src) or src local kind = define.CompletionItemKind.Field if value.type == 'function' @@ -494,11 +478,11 @@ local function checkFieldThen(name, src, word, start, offset, parent, oop, resul local str = parent.next.index textEdit = { start = str.start + #str[2], - finish = offset, + finish = position, newText = name, } else - textEdit, additionalTextEdits = checkFieldFromFieldToIndex(name, src, parent, word, start, offset) + textEdit, additionalTextEdits = checkFieldFromFieldToIndex(name, src, parent, word, startPos, position) end results[#results+1] = { label = name, @@ -515,7 +499,7 @@ local function checkFieldThen(name, src, word, start, offset, parent, oop, resul } end -local function checkFieldOfRefs(refs, ast, word, start, offset, parent, oop, results, locals, isGlobal) +local function checkFieldOfRefs(refs, state, word, startPos, position, parent, oop, results, locals, isGlobal) local fields = {} local funcs = {} local count = 0 @@ -524,7 +508,7 @@ local function checkFieldOfRefs(refs, ast, word, start, offset, parent, oop, res if not name then goto CONTINUE end - if isSameSource(ast, src, start) then + if isSameSource(state, src, startPos) then goto CONTINUE end if isGlobal and locals and locals[name] then @@ -573,29 +557,29 @@ local function checkFieldOfRefs(refs, ast, word, start, offset, parent, oop, res end for name, src in util.sortPairs(fields) do if src then - checkFieldThen(name, src, word, start, offset, parent, oop, results) + checkFieldThen(name, src, word, startPos, position, parent, oop, results) end end end -local function checkGlobal(ast, word, start, offset, parent, oop, results) - local locals = guide.getVisibleLocals(ast.ast, offset) +local function checkGlobal(state, word, startPos, position, parent, oop, results) + local locals = guide.getVisibleLocals(state.ast, position) local globals = vm.getGlobalSets '*' - checkFieldOfRefs(globals, ast, word, start, offset, parent, oop, results, locals, 'global') + checkFieldOfRefs(globals, state, word, startPos, position, parent, oop, results, locals, 'global') end -local function checkField(ast, word, start, offset, parent, oop, results) +local function checkField(state, word, start, position, parent, oop, results) if parent.tag == '_ENV' or parent.special == '_G' then local globals = vm.getGlobalSets '*' - checkFieldOfRefs(globals, ast, word, start, offset, parent, oop, results) + checkFieldOfRefs(globals, state, word, start, position, parent, oop, results) else local refs = vm.getRefs(parent, '*') - checkFieldOfRefs(refs, ast, word, start, offset, parent, oop, results) + checkFieldOfRefs(refs, state, word, start, position, parent, oop, results) end end -local function checkTableField(ast, word, start, results) - local source = guide.eachSourceContain(ast.ast, start, function (source) +local function checkTableField(state, word, start, results) + local source = guide.eachSourceContain(state.ast, start, function (source) if source.start == start and source.parent and source.parent.type == 'table' then @@ -606,7 +590,7 @@ local function checkTableField(ast, word, start, results) return end local used = {} - guide.eachSourceType(ast.ast, 'tablefield', function (src) + guide.eachSourceType(state.ast, 'tablefield', function (src) if not src.field then return end @@ -623,7 +607,7 @@ local function checkTableField(ast, word, start, results) end) end -local function checkCommon(myUri, word, text, offset, results) +local function checkCommon(myUri, word, text, position, results) local showWord = config.get 'Lua.completion.showWord' if showWord == 'Disable' then return @@ -691,11 +675,14 @@ local function checkCommon(myUri, word, text, offset, results) end end end - for str, pos in text:gmatch '([%a_][%w_]+)()' do + local state = files.getState(myUri) + for str, offset in text:gmatch '([%a_][%w_]+)()' do if #results >= 100 then break end - if #str >= 3 and not used[str] and pos - 1 ~= offset then + if #str >= 3 + and not used[str] + and guide.offsetToPosition(state, offset - 1) ~= position then used[str] = true if matchKey(word, str) then results[#results+1] = { @@ -710,26 +697,26 @@ local function checkCommon(myUri, word, text, offset, results) end end -local function isInString(ast, offset) - return guide.eachSourceContain(ast.ast, offset, function (source) +local function isInString(state, position) + return guide.eachSourceContain(state.ast, position, function (source) if source.type == 'string' then return true end end) end -local function checkKeyWord(ast, text, start, offset, word, hasSpace, afterLocal, results) +local function checkKeyWord(state, text, start, position, word, hasSpace, afterLocal, results) local snipType = config.get 'Lua.completion.keywordSnippet' - local symbol = lookBackward.findSymbol(text, start - 1) + local symbol = lookBackward.findSymbol(text, guide.positionToOffset(state, start)) local isExp = symbol == '(' or symbol == ',' or symbol == '=' local info = { hasSpace = hasSpace, isExp = isExp, text = text, start = start, - uri = guide.getUri(ast.ast), - offset = offset, - ast = ast, + uri = guide.getUri(state.ast), + position = position, + state = state, } for _, data in ipairs(keyWordMap) do local key = data[1] @@ -789,9 +776,9 @@ local function checkKeyWord(ast, text, start, offset, word, hasSpace, afterLocal end end -local function checkProvideLocal(ast, word, start, results) +local function checkProvideLocal(state, word, start, results) local block - guide.eachSourceContain(ast.ast, start, function (source) + guide.eachSourceContain(state.ast, start, function (source) if source.type == 'function' or source.type == 'main' then block = source @@ -825,8 +812,8 @@ local function checkProvideLocal(ast, word, start, results) end) end -local function checkFunctionArgByDocParam(ast, word, start, results) - local func = guide.eachSourceContain(ast.ast, start, function (source) +local function checkFunctionArgByDocParam(state, word, startPos, results) + local func = guide.eachSourceContain(state.ast, startPos, function (source) if source.type == 'function' then return source end @@ -846,7 +833,7 @@ local function checkFunctionArgByDocParam(ast, word, start, results) end local firstArg = func.args and func.args[1] if not firstArg - or firstArg.start <= start and firstArg.finish >= start then + or firstArg.start <= startPos and firstArg.finish >= startPos then local firstParam = params[1] if firstParam and matchKey(word, firstParam.param[1]) then local label = {} @@ -870,16 +857,17 @@ local function checkFunctionArgByDocParam(ast, word, start, results) end end -local function isAfterLocal(text, start) - local pos = lookBackward.skipSpace(text, start-1) - local word = lookBackward.findWord(text, pos) +local function isAfterLocal(state, text, startPos) + local offset = guide.positionToOffset(state, startPos) + local pos = lookBackward.skipSpace(text, offset) + local word = lookBackward.findWord(text, pos) return word == 'local' end -local function checkUri(ast, text, offset, results) +local function checkUri(state, text, position, results) local collect = {} - local myUri = guide.getUri(ast.ast) - guide.eachSourceContain(ast.ast, offset, function (source) + local myUri = guide.getUri(state.ast) + guide.eachSourceContain(state.ast, position, function (source) if source.type ~= 'string' then return end @@ -910,7 +898,7 @@ local function checkUri(ast, text, offset, results) collect[info.expect] = { textEdit = { start = source.start + #source[2], - finish = offset, + finish = position, newText = info.expect, } } @@ -996,23 +984,27 @@ local function checkUri(ast, text, offset, results) end end -local function checkLenPlusOne(ast, text, offset, results) - guide.eachSourceContain(ast.ast, offset, function (source) +local function checkLenPlusOne(state, text, position, results) + guide.eachSourceContain(state.ast, position, function (source) if source.type == 'getindex' or source.type == 'setindex' then - local _, pos = text:find('%s*%[%s*%#', source.node.finish) - if not pos then + local finish = guide.positionToOffset(state, source.node.finish) + local _, offset = text:find('%s*%[%s*%#', finish) + if not offset then return end - local nodeText = text:sub(source.node.start, source.node.finish) - local writingText = trim(text:sub(pos + 1, offset - 1)) or '' + local start = guide.positionToOffset(state, source.node.start) + 1 + local nodeText = text:sub(start, finish) + local writingText = trim(text:sub(offset + 1, guide.positionToOffset(state, position))) or '' if not matchKey(writingText, nodeText) then return end + local offsetPos = guide.offsetToPosition(state, offset) - 1 if source.parent == guide.getParentBlock(source) then + local sourceFinish = guide.positionToOffset(state, source.finish) -- state - local label = text:match('%#[ \t]*', pos) .. nodeText .. '+1' - local eq = text:find('^%s*%]?%s*%=', source.finish) + local label = text:match('%#[ \t]*', offset) .. nodeText .. '+1' + local eq = text:find('^%s*%]?%s*%=', sourceFinish) local newText = label .. ']' if not eq then newText = newText .. ' = ' @@ -1022,20 +1014,20 @@ local function checkLenPlusOne(ast, text, offset, results) match = nodeText, kind = define.CompletionItemKind.Snippet, textEdit = { - start = pos, + start = offsetPos, finish = source.finish, newText = newText, }, } else -- exp - local label = text:match('%#[ \t]*', pos) .. nodeText + local label = text:match('%#[ \t]*', offset) .. nodeText local newText = label .. ']' results[#results+1] = { label = label, kind = define.CompletionItemKind.Snippet, textEdit = { - start = pos, + start = offsetPos, finish = source.finish, newText = newText, }, @@ -1049,7 +1041,7 @@ local function tryLabelInString(label, source) if not source or source.type ~= 'string' then return label end - local str = parser:grammar(label, 'String') + local str = parser.grammar(label, 'String') if not str then return label end @@ -1084,7 +1076,7 @@ local function mergeEnums(a, b, source) end end -local function checkTypingEnum(ast, text, offset, defs, str, results) +local function checkTypingEnum(state, text, position, defs, str, results) local enums = {} for _, def in ipairs(defs) do if def.type == 'doc.type.enum' @@ -1106,21 +1098,21 @@ local function checkTypingEnum(ast, text, offset, defs, str, results) end end -local function checkEqualEnumLeft(ast, text, offset, source, results) +local function checkEqualEnumLeft(state, text, position, source, results) if not source then return end - local str = guide.eachSourceContain(ast.ast, offset, function (src) + local str = guide.eachSourceContain(state.ast, position, function (src) if src.type == 'string' then return src end end) local defs = vm.getDefs(source) - checkTypingEnum(ast, text, offset, defs, str, results) + checkTypingEnum(state, text, position, defs, str, results) end -local function checkEqualEnum(ast, text, offset, results) - local start = lookBackward.findTargetSymbol(text, offset, '=') +local function checkEqualEnum(state, text, position, results) + local start = lookBackward.findTargetSymbol(text, guide.positionToOffset(state, position), '=') if not start then return end @@ -1131,7 +1123,7 @@ local function checkEqualEnum(ast, text, offset, results) eqOrNeq = true end start = lookBackward.skipSpace(text, start - 1) - local source = findNearestSource(ast, start) + local source = findNearestSource(state, guide.offsetToPosition(state, start)) if not source then return end @@ -1141,11 +1133,11 @@ local function checkEqualEnum(ast, text, offset, results) if source.type == 'call' and not eqOrNeq then return end - checkEqualEnumLeft(ast, text, offset, source, results) + checkEqualEnumLeft(state, text, position, source, results) end -local function checkEqualEnumInString(ast, text, offset, results) - local source = findNearestSource(ast, offset) +local function checkEqualEnumInString(state, text, position, results) + local source = findNearestSource(state, position) local parent = source.parent if parent.type == 'binary' then if source ~= parent[2] then @@ -1157,118 +1149,123 @@ local function checkEqualEnumInString(ast, text, offset, results) if parent.op.type ~= '==' and parent.op.type ~= '~=' then return end - checkEqualEnumLeft(ast, text, offset, parent[1], results) + checkEqualEnumLeft(state, text, position, parent[1], results) end if parent.type == 'local' then - checkEqualEnumLeft(ast, text, offset, parent, results) + checkEqualEnumLeft(state, text, position, parent, results) end if parent.type == 'setlocal' or parent.type == 'setglobal' or parent.type == 'setfield' or parent.type == 'setindex' then - checkEqualEnumLeft(ast, text, offset, parent.node, results) + checkEqualEnumLeft(state, text, position, parent.node, results) end end -local function isFuncArg(ast, offset) - return guide.eachSourceContain(ast.ast, offset, function (source) +local function isFuncArg(state, position) + return guide.eachSourceContain(state.ast, position, function (source) if source.type == 'funcargs' then return true end end) end -local function trySpecial(ast, text, offset, results) - if isInString(ast, offset) then - checkUri(ast, text, offset, results) - checkEqualEnumInString(ast, text, offset, results) +local function trySpecial(state, text, position, results) + if isInString(state, position) then + checkUri(state, text, position, results) + checkEqualEnumInString(state, text, position, results) return end -- x[#x+1] - checkLenPlusOne(ast, text, offset, results) + checkLenPlusOne(state, text, position, results) -- type(o) == - checkEqualEnum(ast, text, offset, results) + checkEqualEnum(state, text, position, results) end -local function tryIndex(ast, text, offset, results) - local parent, oop = findParentInStringIndex(ast, text, offset) +local function tryIndex(state, text, position, results) + local parent, oop = findParentInStringIndex(state, text, position) if not parent then return end local word = parent.next.index[1] - checkField(ast, word, offset, offset, parent, oop, results) + checkField(state, word, position, position, parent, oop, results) end -local function tryWord(ast, text, offset, triggerCharacter, results) +local function tryWord(state, text, position, triggerCharacter, results) + local offset = guide.positionToOffset(state, position) local finish = lookBackward.skipSpace(text, offset) local word, start = lookBackward.findWord(text, offset) + local startPos if not word then if triggerCharacter == nil then word = '' - start = offset + 1 + startPos = position + 1 else return nil end + else + startPos = guide.offsetToPosition(state, start - 1) end local hasSpace = triggerCharacter ~= nil and finish ~= offset - if isInString(ast, offset) then + if isInString(state, position) then if not hasSpace then if #results == 0 then - checkCommon(ast.uri, word, text, offset, results) + checkCommon(state.uri, word, text, position, results) end end else - local parent, oop = findParent(ast, text, start - 1) + local parent, oop = findParent(state, text, startPos) if parent then if not hasSpace then - checkField(ast, word, start, offset, parent, oop, results) + checkField(state, word, startPos, position, parent, oop, results) end - elseif isFuncArg(ast, offset) then - checkProvideLocal(ast, word, start, results) - checkFunctionArgByDocParam(ast, word, start, results) + elseif isFuncArg(state, position) then + checkProvideLocal(state, word, startPos, results) + checkFunctionArgByDocParam(state, word, startPos, results) else - local afterLocal = isAfterLocal(text, start) - local stop = checkKeyWord(ast, text, start, offset, word, hasSpace, afterLocal, results) + local afterLocal = isAfterLocal(state, text, startPos) + local stop = checkKeyWord(state, text, startPos, position, word, hasSpace, afterLocal, results) if stop then return end if not hasSpace then if afterLocal then - checkProvideLocal(ast, word, start, results) + checkProvideLocal(state, word, startPos, results) else - checkLocal(ast, word, start, results) - checkTableField(ast, word, start, results) - local env = guide.getENV(ast.ast, start) - checkGlobal(ast, word, start, offset, env, false, results) - checkModule(ast, word, start, results) + checkLocal(state, word, startPos, results) + checkTableField(state, word, startPos, results) + local env = guide.getENV(state.ast, startPos) + checkGlobal(state, word, startPos, position, env, false, results) + checkModule(state, word, startPos, results) end end end if not hasSpace then - checkCommon(ast.uri, word, text, offset, results) + checkCommon(state.uri, word, text, position, results) end end end -local function trySymbol(ast, text, offset, results) - local symbol, start = lookBackward.findSymbol(text, offset) +local function trySymbol(state, text, position, results) + local symbol, start = lookBackward.findSymbol(text, guide.positionToOffset(state, position)) if not symbol then return nil end - if isInString(ast, offset) then + if isInString(state, position) then return nil end + local startPos = guide.offsetToPosition(state, start) if symbol == '.' or symbol == ':' then - local parent, oop = findParent(ast, text, start) + local parent, oop = findParent(state, text, startPos) if parent then tracy.ZoneBeginN 'completion.trySymbol' - checkField(ast, '', start, offset, parent, oop, results) + checkField(state, '', startPos, position, parent, oop, results) tracy.ZoneEnd() end end if symbol == '(' then - checkFunctionArgByDocParam(ast, '', start, results) + checkFunctionArgByDocParam(state, '', startPos, results) end end @@ -1354,9 +1351,9 @@ local function getCallEnumsAndFuncs(source, index, oop) end end -local function findCall(ast, text, offset) +local function findCall(state, text, position) local call - guide.eachSourceContain(ast.ast, offset, function (src) + guide.eachSourceContain(state.ast, position, function (src) if src.type == 'call' then if not call or call.start < src.start then call = src @@ -1366,13 +1363,13 @@ local function findCall(ast, text, offset) return call end -local function getCallArgInfo(call, text, offset) +local function getCallArgInfo(call, text, position) if not call.args then return 1, nil, nil end local oop = call.node.type == 'getmethod' for index, arg in ipairs(call.args) do - if arg.start <= offset and arg.finish >= offset then + if arg.start <= position and arg.finish >= position then return index, arg, oop end end @@ -1391,7 +1388,7 @@ local function getFuncParamByCallIndex(func, index) return func.args[index] end -local function checkTableLiteralField(ast, text, offset, tbl, fields, results) +local function checkTableLiteralField(state, text, position, tbl, fields, results) local mark = {} for _, field in ipairs(tbl) do if field.type == 'tablefield' @@ -1407,9 +1404,9 @@ local function checkTableLiteralField(ast, text, offset, tbl, fields, results) return guide.getKeyName(a) < guide.getKeyName(b) end) -- {$} - local left = lookBackward.findWord(text, offset) + local left = lookBackward.findWord(text, guide.positionToOffset(state, position)) if not left then - local pos = lookBackward.findAnyPos(text, offset) + local pos = lookBackward.findAnyOffset(text, guide.positionToOffset(state, position)) local char = text:sub(pos, pos) if char == '{' or char == ',' or char == ';' then left = '' @@ -1435,8 +1432,8 @@ local function checkTableLiteralField(ast, text, offset, tbl, fields, results) end end -local function checkTableLiteralFieldByCall(ast, text, offset, call, defs, index, results) - local source = findNearestTableField(ast, offset) +local function checkTableLiteralFieldByCall(state, text, position, call, defs, index, results) + local source = findNearestTableField(state, position) if not source then return end @@ -1469,16 +1466,16 @@ local function checkTableLiteralFieldByCall(ast, text, offset, call, defs, index end ::CONTINUE:: end - checkTableLiteralField(ast, text, offset, tbl, fields, results) + checkTableLiteralField(state, text, position, tbl, fields, results) end -local function tryCallArg(ast, text, offset, results) - local call = findCall(ast, text, offset) +local function tryCallArg(state, text, position, results) + local call = findCall(state, text, position) if not call then return end local myResults = {} - local argIndex, arg, oop = getCallArgInfo(call, text, offset) + local argIndex, arg, oop = getCallArgInfo(call, text, position) if arg and arg.type == 'function' then return end @@ -1493,12 +1490,11 @@ local function tryCallArg(ast, text, offset, results) for _, enum in ipairs(myResults) do results[#results+1] = enum end - checkTableLiteralFieldByCall(ast, text, offset, call, defs, argIndex, results) + checkTableLiteralFieldByCall(state, text, position, call, defs, argIndex, results) end -local function tryTable(ast, text, offset, results) - offset = lookBackward.skipSpace(text, offset) - local source = findNearestTableField(ast, offset) +local function tryTable(state, text, position, results) + local source = findNearestTableField(state, position) if not source then return end @@ -1521,21 +1517,21 @@ local function tryTable(ast, text, offset, results) fields[#fields+1] = field end end - checkTableLiteralField(ast, text, offset, tbl, fields, results) + checkTableLiteralField(state, text, position, tbl, fields, results) end -local function getComment(ast, offset) - for _, comm in ipairs(ast.comms) do - if offset >= comm.start - 2 and offset <= comm.finish then +local function getComment(state, position) + for _, comm in ipairs(state.comms) do + if position > comm.start and position <= comm.finish then return comm end end return nil end -local function getLuaDoc(ast, offset) - for _, doc in ipairs(ast.ast.docs) do - if offset >= doc.start and offset <= doc.range then +local function getLuaDoc(state, position) + for _, doc in ipairs(state.ast.docs) do + if position >= doc.start and position <= doc.range then return doc end end @@ -1568,28 +1564,28 @@ local function tryLuaDocCate(word, results) end end -local function getLuaDocByContain(ast, offset) +local function getLuaDocByContain(state, position) local result local range = math.huge - guide.eachSourceContain(ast.ast.docs, offset, function (src) + guide.eachSourceContain(state.ast.docs, position, function (src) if not src.start then return end - if range >= offset - src.start - and offset <= src.finish then - range = offset - src.start + if range >= position - src.start + and position <= src.finish then + range = position - src.start result = src end end) return result end -local function getLuaDocByErr(ast, text, start, offset) +local function getLuaDocByErr(state, text, start, position) local targetError - for _, err in ipairs(ast.errs) do - if err.finish <= offset + for _, err in ipairs(state.errs) do + if err.finish <= position and err.start >= start then - if not text:sub(err.finish + 1, offset):find '%S' then + if not text:sub(err.finish + 1, position):find '%S' then targetError = err break end @@ -1599,8 +1595,8 @@ local function getLuaDocByErr(ast, text, start, offset) return nil end local targetDoc - for i = #ast.ast.docs, 1, -1 do - local doc = ast.ast.docs[i] + for i = #state.ast.docs, 1, -1 do + local doc = state.ast.docs[i] if doc.finish <= targetError.start then targetDoc = doc break @@ -1609,7 +1605,7 @@ local function getLuaDocByErr(ast, text, start, offset) return targetError, targetDoc end -local function tryLuaDocBySource(ast, offset, source, results) +local function tryLuaDocBySource(state, position, source, results) if source.type == 'doc.extends.name' then if source.parent.type == 'doc.class' then for _, doc in ipairs(vm.getDocDefines '*') do @@ -1621,7 +1617,7 @@ local function tryLuaDocBySource(ast, offset, source, results) kind = define.CompletionItemKind.Class, textEdit = doc[1]:find '[^%w_]' and { start = source.start, - finish = offset, + finish = position, newText = doc[1], }, } @@ -1639,7 +1635,7 @@ local function tryLuaDocBySource(ast, offset, source, results) kind = define.CompletionItemKind.Class, textEdit = doc[1]:find '[^%w_]' and { start = source.start, - finish = offset, + finish = position, newText = doc[1], }, } @@ -1648,8 +1644,8 @@ local function tryLuaDocBySource(ast, offset, source, results) return true elseif source.type == 'doc.param.name' then local funcs = {} - guide.eachSourceBetween(ast.ast, offset, math.huge, function (src) - if src.type == 'function' and src.start > offset then + guide.eachSourceBetween(state.ast, position, math.huge, function (src) + if src.type == 'function' and src.start > position then funcs[#funcs+1] = src end end) @@ -1702,7 +1698,7 @@ local function tryLuaDocBySource(ast, offset, source, results) return false end -local function tryLuaDocByErr(ast, offset, err, docState, results) +local function tryLuaDocByErr(state, position, err, docState, results) if err.type == 'LUADOC_MISS_CLASS_EXTENDS_NAME' then for _, doc in ipairs(vm.getDocDefines '*') do if doc.type == 'doc.class.name' @@ -1724,8 +1720,8 @@ local function tryLuaDocByErr(ast, offset, err, docState, results) end elseif err.type == 'LUADOC_MISS_PARAM_NAME' then local funcs = {} - guide.eachSourceBetween(ast.ast, offset, math.huge, function (src) - if src.type == 'function' and src.start > offset then + guide.eachSourceBetween(state.ast, position, math.huge, function (src) + if src.type == 'function' and src.start > position then funcs[#funcs+1] = src end end) @@ -1851,49 +1847,49 @@ local function tryLuaDocOfFunction(doc, results) } end -local function tryLuaDoc(ast, text, offset, results) - local doc = getLuaDoc(ast, offset) +local function tryLuaDoc(state, text, position, results) + local doc = getLuaDoc(state, position) if not doc then return end if doc.type == 'doc.comment' then - local line = text:sub(doc.start, doc.range) + local line = doc.originalComment.text -- 尝试 ---$ if line == '-' then tryLuaDocOfFunction(doc, results) return end -- 尝试 ---@$ - local cate = line:match('^-%s*@(%a*)$') + local cate = line:match('^-+%s*@(%a*)$') if cate then tryLuaDocCate(cate, results) return end end -- 根据输入中的source来补全 - local source = getLuaDocByContain(ast, offset) + local source = getLuaDocByContain(state, position) if source then - local suc = tryLuaDocBySource(ast, offset, source, results) + local suc = tryLuaDocBySource(state, position, source, results) if suc then return end end -- 根据附近的错误消息来补全 - local err, expectDoc = getLuaDocByErr(ast, text, doc.start, offset) + local err, expectDoc = getLuaDocByErr(state, text, doc.start, position) if err then - tryLuaDocByErr(ast, offset, err, expectDoc, results) + tryLuaDocByErr(state, position, err, expectDoc, results) return end end -local function tryComment(ast, text, offset, results) +local function tryComment(state, text, position, results) if #results > 0 then return end - local word = lookBackward.findWord(text, offset) - local doc = getLuaDoc(ast, offset) + local word = lookBackward.findWord(text, guide.positionToOffset(state, position)) + local doc = getLuaDoc(state, position) if not word then - local comment = getComment(ast, offset) + local comment = getComment(state, position) if comment.type == 'comment.short' or comment.type == 'comment.cshort' then if comment.text == '' then @@ -1912,23 +1908,24 @@ local function tryComment(ast, text, offset, results) if doc and doc.type ~= 'doc.comment' then return end - checkCommon(ast.uri, word, text, offset, results) + checkCommon(state.uri, word, text, position, results) end -local function makeCache(uri, offset, results) +local function makeCache(uri, position, results) local cache = workspace.getCache 'completion' if not uri then cache.results = nil return end local text = files.getText(uri) - local word = lookBackward.findWord(text, offset) + local state = files.getState(uri) + local word = lookBackward.findWord(text, guide.positionToOffset(state, position)) if not word or #word < 2 then cache.results = nil return end cache.results = results - cache.offset = offset + cache.position= position cache.word = word:lower() cache.length = #word end @@ -1950,13 +1947,14 @@ local function isValidCache(word, result) return false end -local function getCache(uri, offset) +local function getCache(uri, position) local cache = workspace.getCache 'completion' if not cache.results then return nil end local text = files.getText(uri) - local word = lookBackward.findWord(text, offset) + local state = files.getState(uri) + local word = lookBackward.findWord(text, guide.positionToOffset(state, position)) if not word then return nil end @@ -1980,7 +1978,7 @@ local function getCache(uri, offset) end if results.enableCommon then - checkCommon(uri, word, text, offset, results) + checkCommon(uri, word, text, position, results) end return cache.results @@ -1991,36 +1989,36 @@ local function clearCache() cache.results = nil end -local function completion(uri, offset, triggerCharacter) +local function completion(uri, position, triggerCharacter) tracy.ZoneBeginN 'completion cache' - local results = getCache(uri, offset) + local results = getCache(uri, position) tracy.ZoneEnd() if results then return results end tracy.ZoneBeginN 'completion #1' - local ast = files.getState(uri) + local state = files.getState(uri) local text = files.getText(uri) results = {} clearStack() tracy.ZoneEnd() tracy.ZoneBeginN 'completion #2' - if ast then - if getComment(ast, offset) then - tryLuaDoc(ast, text, offset, results) - tryComment(ast, text, offset, results) + if state then + if getComment(state, position) then + tryLuaDoc(state, text, position, results) + tryComment(state, text, position, results) else - trySpecial(ast, text, offset, results) - tryCallArg(ast, text, offset, results) - tryTable(ast, text, offset, results) - tryWord(ast, text, offset, triggerCharacter, results) - tryIndex(ast, text, offset, results) - trySymbol(ast, text, offset, results) + trySpecial(state, text, position, results) + tryCallArg(state, text, position, results) + tryTable(state, text, position, results) + tryWord(state, text, position, triggerCharacter, results) + tryIndex(state, text, position, results) + trySymbol(state, text, position, results) end else - local word = lookBackward.findWord(text, offset) + local word = lookBackward.findWord(text, guide.positionToOffset(state, position)) if word then - checkCommon(nil, word, text, offset, results) + checkCommon(nil, word, text, position, results) end end tracy.ZoneEnd() @@ -2031,7 +2029,7 @@ local function completion(uri, offset, triggerCharacter) end tracy.ZoneBeginN 'completion #3' - makeCache(uri, offset, results) + makeCache(uri, position, results) tracy.ZoneEnd() return results end diff --git a/script/core/diagnostics/count-down-loop.lua b/script/core/diagnostics/count-down-loop.lua index 1a7dcf7d..49c48880 100644 --- a/script/core/diagnostics/count-down-loop.lua +++ b/script/core/diagnostics/count-down-loop.lua @@ -10,30 +10,39 @@ return function (uri, callback) end guide.eachSourceType(state.ast, 'loop', function (source) - if not source.loc or not source.loc.value then - return - end local maxNumer = source.max and tonumber(source.max[1]) if maxNumer ~= 1 then return end - local minNumber = source.loc and source.loc.value and tonumber(source.loc.value[1]) + local minNumber = source.init and tonumber(source.init[1]) if minNumber and minNumber <= 1 then return end if not source.step then callback { - start = source.loc.value.start, + start = source.init.start, finish = source.max.finish, - message = lang.script('DIAG_COUNT_DOWN_LOOP', ('%s, %s'):format(text:sub(source.loc.value.start, source.max.finish), '-1')) + message = lang.script('DIAG_COUNT_DOWN_LOOP' + , ('%s, %s'):format(text:sub( + guide.positionToOffset(state, source.init.start), + guide.positionToOffset(state, source.max.finish) + ) + , '-1') + ) } else local stepNumber = tonumber(source.step[1]) if stepNumber and stepNumber > 0 then callback { - start = source.loc.value.start, + start = source.init.start, finish = source.step.finish, - message = lang.script('DIAG_COUNT_DOWN_LOOP', ('%s, -%s'):format(text:sub(source.loc.value.start, source.max.finish), source.step[1])) + message = lang.script('DIAG_COUNT_DOWN_LOOP' + , ('%s, -%s'):format(text:sub( + guide.positionToOffset(state, source.init.start), + guide.positionToOffset(state, source.max.finish) + ) + , source.step[1]) + ) } end end diff --git a/script/core/diagnostics/different-requires.lua b/script/core/diagnostics/different-requires.lua index 909342f4..fd7415b6 100644 --- a/script/core/diagnostics/different-requires.lua +++ b/script/core/diagnostics/different-requires.lua @@ -12,7 +12,7 @@ return function (uri, callback) end local cache = vm.getCache 'different-requires' guide.eachSpecialOf(state.ast, 'require', function (source) - local call = source.next + local call = source.parent if not call or call.type ~= 'call' then return end diff --git a/script/core/diagnostics/init.lua b/script/core/diagnostics/init.lua index 09688f6e..63a1bcf0 100644 --- a/script/core/diagnostics/init.lua +++ b/script/core/diagnostics/init.lua @@ -44,7 +44,7 @@ local function check(uri, name, results) if vm.isDiagDisabledAt(uri, result.start, name) then return end - if result.start == 0 then + if result.start < 0 then return end if mark[result.start] then diff --git a/script/core/diagnostics/newfield-call.lua b/script/core/diagnostics/newfield-call.lua index fe86ad66..669ed2bb 100644 --- a/script/core/diagnostics/newfield-call.lua +++ b/script/core/diagnostics/newfield-call.lua @@ -8,7 +8,6 @@ return function (uri, callback) return end - local lines = files.getLines(uri) local text = files.getText(uri) guide.eachSourceType(ast.ast, 'table', function (source) @@ -27,8 +26,8 @@ return function (uri, callback) local func = call.node local args = call.args if args then - local funcLine = guide.positionOf(lines, func.finish) - local argsLine = guide.positionOf(lines, args.start) + local funcLine = guide.rowColOf(func.finish) + local argsLine = guide.rowColOf(args.start) if argsLine > funcLine then callback { start = call.start, diff --git a/script/core/diagnostics/newline-call.lua b/script/core/diagnostics/newline-call.lua index 71dc33e2..dbb8c690 100644 --- a/script/core/diagnostics/newline-call.lua +++ b/script/core/diagnostics/newline-call.lua @@ -3,14 +3,13 @@ local guide = require 'parser.guide' local lang = require 'language' return function (uri, callback) - local ast = files.getState(uri) - local lines = files.getLines(uri) + local state = files.getState(uri) local text = files.getText(uri) - if not ast or not lines then + if not state then return end - guide.eachSourceType(ast.ast, 'call', function (source) + guide.eachSourceType(state.ast, 'call', function (source) local node = source.node local args = source.args if not args then @@ -21,13 +20,15 @@ return function (uri, callback) if not source.next then return end - if text:sub(args.start, args.start) ~= '(' - or text:sub(args.finish, args.finish) ~= ')' then + local startOffset = guide.positionToOffset(state, args.start) + 1 + local finishOffset = guide.positionToOffset(state, args.finish) + if text:sub(startOffset, startOffset) ~= '(' + or text:sub(finishOffset, finishOffset) ~= ')' then return end - local nodeRow = guide.positionOf(lines, node.finish) - local argRow = guide.positionOf(lines, args.start) + local nodeRow = guide.rowColOf(node.finish) + local argRow = guide.rowColOf(args.start) if nodeRow == argRow then return end diff --git a/script/core/diagnostics/redundant-value.lua b/script/core/diagnostics/redundant-value.lua index d6cd97a7..4c913330 100644 --- a/script/core/diagnostics/redundant-value.lua +++ b/script/core/diagnostics/redundant-value.lua @@ -1,24 +1,24 @@ local files = require 'files' local define = require 'proto.define' local lang = require 'language' +local guide = require 'parser.guide' +local await = require 'await' -return function (uri, callback, code) - local ast = files.getState(uri) - if not ast then +return function (uri, callback) + local state = files.getState(uri) + if not state then return end - local diags = ast.diags[code] - if not diags then - return - end - - for _, info in ipairs(diags) do - callback { - start = info.start, - finish = info.finish, - tags = { define.DiagnosticTag.Unnecessary }, - message = lang.script('DIAG_OVER_MAX_VALUES', info.max, info.passed) - } - end + guide.eachSource(state.ast, function (src) + await.delay() + if src.redundant then + callback { + start = src.start, + finish = src.finish, + tags = { define.DiagnosticTag.Unnecessary }, + message = lang.script('DIAG_OVER_MAX_VALUES', src.redundant.max, src.redundant.passed) + } + end + end) end diff --git a/script/core/diagnostics/trailing-space.lua b/script/core/diagnostics/trailing-space.lua index 824eb83f..cc51cf77 100644 --- a/script/core/diagnostics/trailing-space.lua +++ b/script/core/diagnostics/trailing-space.lua @@ -13,40 +13,43 @@ local function isInString(ast, offset) end return function (uri, callback) - local ast = files.getState(uri) - if not ast then + local state = files.getState(uri) + if not state then return end local text = files.getText(uri) - local lines = files.getLines(uri) - for i = 1, #lines do - local start = lines[i].start - local range = lines[i].range - local lastChar = text:sub(range, range) + local lines = state.lines + for i = 0, #lines do + local startOffset = lines[i] + local finishOffset = text:find('[\r\n]', startOffset) or (#text + 1) + local lastOffset = finishOffset - 1 + local lastChar = text:sub(lastOffset, lastOffset) if lastChar ~= ' ' and lastChar ~= '\t' then goto NEXT_LINE end - if isInString(ast.ast, range) then + local lastPos = guide.offsetToPosition(state, lastOffset) + if isInString(state.ast, lastPos) then goto NEXT_LINE end - local first = start - for n = range - 1, start, -1 do + local firstOffset = startOffset + for n = lastOffset - 1, startOffset, -1 do local char = text:sub(n, n) if char ~= ' ' and char ~= '\t' then - first = n + 1 + firstOffset = n + 1 break end end - if first == start then + local firstPos = guide.offsetToPosition(state, firstOffset) - 1 + if firstOffset == startOffset then callback { - start = first, - finish = range, + start = firstPos, + finish = lastPos, message = lang.script.DIAG_LINE_ONLY_SPACE, } else callback { - start = first, - finish = range, + start = firstPos, + finish = lastPos, message = lang.script.DIAG_LINE_POST_SPACE, } end diff --git a/script/core/diagnostics/undefined-global.lua b/script/core/diagnostics/undefined-global.lua index c7ddeac2..14754c16 100644 --- a/script/core/diagnostics/undefined-global.lua +++ b/script/core/diagnostics/undefined-global.lua @@ -5,6 +5,7 @@ local config = require 'config' local guide = require 'parser.guide' local noder = require 'core.noder' local collector = require 'core.collector' +local await = require 'await' local requireLike = { ['include'] = true, @@ -35,6 +36,7 @@ return function (uri, callback) if node.tag ~= '_ENV' then return end + await.delay() local id = 'def:' .. noder.getID(src) if not collector.has(id) then local message = lang.script('DIAG_UNDEF_GLOBAL', key) diff --git a/script/core/document-symbol.lua b/script/core/document-symbol.lua index 9f950d25..cfabedab 100644 --- a/script/core/document-symbol.lua +++ b/script/core/document-symbol.lua @@ -5,20 +5,26 @@ local define = require 'proto.define' local util = require 'utility' local function buildName(source, text) + local uri = guide.getUri(source) + local state = files.getState(uri) + local startOffset = guide.positionToOffset(state, source.start) if source.type == 'setmethod' or source.type == 'getmethod' then if source.method then - return text:sub(source.start, source.method.finish) + local finishOffset = guide.positionToOffset(state, source.method.finish) + return text:sub(startOffset + 1, finishOffset) end end if source.type == 'setfield' or source.type == 'tablefield' or source.type == 'getfield' then if source.field then - return text:sub(source.start, source.field.finish) + local finishOffset = guide.positionToOffset(state, source.field.finish) + return text:sub(startOffset + 1, finishOffset) end end - return text:sub(source.start, source.finish) + local finishOffset = guide.positionToOffset(state, source.finish) + return text:sub(startOffset + 1, finishOffset) end local function buildFunctionParams(func) @@ -208,7 +214,7 @@ local function buildAnonymousFunction(source, text, used, symbols) detail = ('%sfunction (%s)'):format(head, buildFunctionParams(source)), kind = define.SymbolKind.Function, range = { source.start, source.finish }, - selectionRange = { source.start, source.start }, + selectionRange = { source.keyword[1], source.keyword[2] }, valueRange = { source.start, source.finish }, } end diff --git a/script/core/find-source.lua b/script/core/find-source.lua index edbb1e2c..26a411e5 100644 --- a/script/core/find-source.lua +++ b/script/core/find-source.lua @@ -11,12 +11,12 @@ local function isValidFunctionPos(source, offset) return false end -return function (ast, offset, accept) +return function (ast, position, accept) local len = math.huge local result - guide.eachSourceContain(ast.ast, offset, function (source) + guide.eachSourceContain(ast.ast, position, function (source) if source.type == 'function' then - if not isValidFunctionPos(source, offset) then + if not isValidFunctionPos(source, position) then return end end diff --git a/script/core/highlight.lua b/script/core/highlight.lua index 47b482d5..02f3c07f 100644 --- a/script/core/highlight.lua +++ b/script/core/highlight.lua @@ -54,12 +54,12 @@ local function find(source, uri, callback) end end -local function checkInIf(source, text, offset) +local function checkInIf(state, source, text, position) -- 检查 end - local endA = source.finish - #'end' + 1 - local endB = source.finish - if offset >= endA - and offset <= endB + local endB = guide.positionToOffset(state, source.finish) + local endA = endB - #'end' + 1 + if position >= source.finish - #'end' + and position <= source.finish and text:sub(endA, endB) == 'end' then return true end @@ -68,7 +68,7 @@ local function checkInIf(source, text, offset) for i = 1, #block.keyword, 2 do local start = block.keyword[i] local finish = block.keyword[i+1] - if offset >= start and offset <= finish then + if position >= start and position <= finish then return true end end @@ -76,12 +76,12 @@ local function checkInIf(source, text, offset) return false end -local function makeIf(source, text, callback) +local function makeIf(state, source, text, callback) -- end - local endA = source.finish - #'end' + 1 - local endB = source.finish + local endB = guide.positionToOffset(state, source.finish) + local endA = endB - #'end' + 1 if text:sub(endA, endB) == 'end' then - callback(endA, endB) + callback(source.finish - #'end', source.finish) end -- 每个子模块 for _, block in ipairs(source) do @@ -94,8 +94,8 @@ local function makeIf(source, text, callback) return false end -local function findKeyWord(ast, text, offset, callback) - guide.eachSourceContain(ast.ast, offset, function (source) +local function findKeyWord(state, text, position, callback) + guide.eachSourceContain(state.ast, position, function (source) if source.type == 'do' or source.type == 'function' or source.type == 'loop' @@ -106,7 +106,7 @@ local function findKeyWord(ast, text, offset, callback) for i = 1, #source.keyword, 2 do local start = source.keyword[i] local finish = source.keyword[i+1] - if offset >= start and offset <= finish then + if position >= start and position <= finish then ok = true break end @@ -119,9 +119,9 @@ local function findKeyWord(ast, text, offset, callback) end end elseif source.type == 'if' then - local ok = checkInIf(source, text, offset) + local ok = checkInIf(state, source, text, position) if ok then - makeIf(source, text, callback) + makeIf(state, source, text, callback) end end end) @@ -238,15 +238,15 @@ local function isLiteralValue(source) end return function (uri, offset) - local ast = files.getState(uri) - if not ast then + local state = files.getState(uri) + if not state then return nil end local text = files.getText(uri) local results = {} local mark = {} - local source = findSource(ast, offset, accept) + local source = findSource(state, offset, accept) if source then local isGlobal = guide.isGlobal(source) local isLiteral = isLiteralValue(source) @@ -344,7 +344,7 @@ return function (uri, offset) end) end - findKeyWord(ast, text, offset, function (start, finish) + findKeyWord(state, text, offset, function (start, finish) results[#results+1] = { start = start, finish = finish, @@ -352,7 +352,7 @@ return function (uri, offset) } end) - checkRegion(ast, text, offset, function (start, finish) + checkRegion(state, text, offset, function (start, finish) results[#results+1] = { start = start, finish = finish, diff --git a/script/core/hover/label.lua b/script/core/hover/label.lua index a29cf672..3322e0d3 100644 --- a/script/core/hover/label.lua +++ b/script/core/hover/label.lua @@ -50,8 +50,7 @@ local function asValue(source, title) local literal = infer.searchAndViewLiterals(source) local cont if not infer.hasType(source, 'string') - and not type:find('%[%]$') - and not type:find('%w%<') then + and not type:find('%[%]$') then if #vm.getRefs(source, '*') > 0 or infer.hasType(source, 'table') then cont = buildTable(source) diff --git a/script/core/infer.lua b/script/core/infer.lua index d6784c67..2915f7f5 100644 --- a/script/core/infer.lua +++ b/script/core/infer.lua @@ -371,9 +371,10 @@ function m.getDocName(doc) return nodeName .. '[]' end if doc.type == 'doc.type.table' then - local key = m.viewDocName(doc.tkey) or '?' + local node = m.viewDocName(doc.node) or '?' + local key = m.viewDocName(doc.tkey) or '?' local value = m.viewDocName(doc.tvalue) or '?' - return ('table<%s, %s>'):format(key, value) + return ('%s<%s, %s>'):format(node, key, value) end if doc.type == 'doc.type.function' then return m.viewDocFunction(doc) diff --git a/script/core/keyword.lua b/script/core/keyword.lua index b8e37605..295026d7 100644 --- a/script/core/keyword.lua +++ b/script/core/keyword.lua @@ -24,14 +24,12 @@ end", end return true end, function (info) - return guide.eachSourceContain(info.ast.ast, info.start, function (source) + return guide.eachSourceContain(info.state.ast, info.start, function (source) if source.type == 'while' or source.type == 'in' or source.type == 'loop' then - for i = 1, #source.keyword do - if info.start == source.keyword[i] then - return true - end + if source.finish - info.start <= 2 then + return true end end end) @@ -40,8 +38,9 @@ end", {'break'}, {'else'}, {'elseif', function (info, results) - if info.text:find('^%s*then', info.offset + 1) - or info.text:find('^%s*do', info.offset + 1) then + local offset = guide.positionToOffset(info.state, info.position) + if info.text:find('^%s*then', offset + 1) + or info.text:find('^%s*do', offset + 1) then return false end if info.hasSpace then @@ -155,8 +154,9 @@ end" end}, {'goto'}, {'if', function (info, results) - if info.text:find('^%s*then', info.offset + 1) - or info.text:find('^%s*do', info.offset + 1) then + local offset = guide.positionToOffset(info.state, info.position) + if info.text:find('^%s*then', offset + 1) + or info.text:find('^%s*do', offset + 1) then return false end if info.hasSpace then @@ -183,8 +183,9 @@ end" return true end}, {'in', function (info, results) - if info.text:find('^%s*then', info.offset + 1) - or info.text:find('^%s*do', info.offset + 1) then + local offset = guide.positionToOffset(info.state, info.position) + if info.text:find('^%s*then', offset + 1) + or info.text:find('^%s*do', offset + 1) then return false end if info.hasSpace then @@ -270,15 +271,16 @@ until $1" return false end}, {'then', function (info, results) - local lines = files.getLines(info.uri) - local pos, first = info.text:match('%S+%s+()(%S+)', info.start) + local startOffset = guide.positionToOffset(info.state, info.start) + local pos, first = info.text:match('%S+%s+()(%S+)', startOffset + 1) if first == 'end' or first == 'else' or first == 'elseif' then - local startRow = guide.positionOf(lines, info.start) - local finishRow = guide.positionOf(lines, pos) - local startSp = info.text:match('^%s*', lines[startRow].start + 1) - local finishSp = info.text:match('^%s*', lines[finishRow].start + 1) + local startRow = guide.rowColOf(info.start) + local finishPosition = guide.offsetToPosition(info.state, pos) + local finishRow = guide.rowColOf(finishPosition) + local startSp = info.text:match('^%s*', info.state.lines[startRow]) + local finishSp = info.text:match('^%s*', info.state.lines[finishRow]) if startSp == finishSp then return false end diff --git a/script/core/look-backward.lua b/script/core/look-backward.lua index ee89078f..2f90b768 100644 --- a/script/core/look-backward.lua +++ b/script/core/look-backward.lua @@ -77,7 +77,7 @@ function m.findTargetSymbol(text, offset, symbol) return nil end -function m.findAnyPos(text, offset) +function m.findAnyOffset(text, offset) for i = offset, 1, -1 do if not m.isSpace(text:sub(i, i)) then return i diff --git a/script/core/noder.lua b/script/core/noder.lua index 3fd316eb..f364f458 100644 --- a/script/core/noder.lua +++ b/script/core/noder.lua @@ -905,6 +905,9 @@ compileNodeMap = util.switch() end) : case 'doc.type.table' : call(function (noders, id, source) + if source.node then + pushForward(noders, id, getID(source.node), INFO_CLASS_TO_EXNTENDS) + end if source.tkey then local keyID = id .. TABLE_KEY pushForward(noders, keyID, getID(source.tkey)) @@ -1231,6 +1234,55 @@ compileNodeMap = util.switch() end end end) + : case 'in' + : call(function (noders, id, source) + local keys = source.keys + local exps = source.exps + if not keys or not exps then + return + end + local node = exps[1] + local param1 = exps[2] + local param2 = exps[3] + if node.type == 'call' then + if not param1 then + param1 = { + type = 'select', + dummy = true, + sindex = 2, + start = node.start, + finish = node.finish, + vararg = node, + parent = source, + } + compileCallReturn(noders, node, getID(param1), 2) + if not param2 then + param2 = { + type = 'select', + dummy = true, + sindex = 3, + start = node.start, + finish = node.finish, + vararg = node, + parent = source, + } + compileCallReturn(noders, node, getID(param2), 3) + end + end + end + local call = { + type = 'call', + dummy = true, + start = source.keyword[3], + finish = exps[#exps].finish, + node = node, + args = { param1, param2 }, + parent = source, + } + for i = 1, #keys do + compileCallReturn(noders, call, getID(keys[i]), i) + end + end) : case 'main' : call(function (noders, id, source) if source.returns then @@ -1288,7 +1340,7 @@ function m.compileNode(noders, source) local id = getID(source) bindValue(noders, source, id) - if specialMap[source.special] then + if id and specialMap[source.special] then noders.skip[id] = true end @@ -1534,11 +1586,6 @@ local partNodersMap = util.switch() m.compilePartNodes(noders, ref) end end - - local nxt = source.next - if nxt then - m.compilePartNodes(noders, nxt) - end end) : case 'setlocal' : case 'getlocal' @@ -1554,6 +1601,14 @@ local partNodersMap = util.switch() if parent.value == source then m.compilePartNodes(noders, parent) end + + if parent.type == 'call' then + local node = parent.node + if node.special == 'rawset' + or node.special == 'rawget' then + m.compilePartNodes(noders, parent) + end + end end) : case 'setfield' : case 'getfield' @@ -1585,6 +1640,14 @@ local partNodersMap = util.switch() if parent.value == source then m.compilePartNodes(noders, parent) end + + if parent.type == 'call' then + local node = parent.node + if node.special == 'rawset' + or node.special == 'rawget' then + m.compilePartNodes(noders, parent) + end + end end) : case 'label' : call(function (noders, source) diff --git a/script/core/reference.lua b/script/core/reference.lua index 5f5831c6..067d2e23 100644 --- a/script/core/reference.lua +++ b/script/core/reference.lua @@ -52,13 +52,13 @@ local accept = { ['doc.alias.name'] = true, } -return function (uri, offset) +return function (uri, position) local ast = files.getState(uri) if not ast then return nil end - local source = findSource(ast, offset, accept) + local source = findSource(ast, position, accept) if not source then return nil end diff --git a/script/core/rename.lua b/script/core/rename.lua index 0ab7a055..0c48dbc4 100644 --- a/script/core/rename.lua +++ b/script/core/rename.lua @@ -36,12 +36,12 @@ local function isValidFunctionName(str) if isValidGlobal(str) then return true end - local pos = str:find(':', 1, true) - if not pos then + local offset = str:find(':', 1, true) + if not offset then return false end - return isValidGlobal(trim(str:sub(1, pos-1))) - and isValidName(trim(str:sub(pos+1))) + return isValidGlobal(trim(str:sub(1, offset-1))) + and isValidName(trim(str:sub(offset+1))) end local function isFunctionGlobalName(source) @@ -81,21 +81,26 @@ local function renameField(source, newname, callback) elseif parent.type == 'getmethod' then callback(source, source.start, source.finish, newname) elseif parent.type == 'setmethod' then - local uri = guide.getUri(source) - local text = files.getText(uri) + local uri = guide.getUri(source) + local text = files.getText(uri) + local state = files.getState(uri) local func = parent.value -- function mt:name () end --> mt['newname'] = function (self) end + local startOffset = guide.positionToOffset(state, parent.start) + 1 + local finishOffset = guide.positionToOffset(state, parent.node.finish) local newstr = string.format('%s[%s] = function ' - , text:sub(parent.start, parent.node.finish) + , text:sub(startOffset, finishOffset) , util.viewString(newname) ) callback(source, func.start, parent.finish, newstr) - local pl = text:find('(', parent.finish, true) + local finishOffset = guide.positionToOffset(state, parent.finish) + local pl = text:find('(', finishOffset, true) if pl then + local insertPos = guide.offsetToPosition(state, pl) if text:find('^%s*%)', pl + 1) then - callback(source, pl + 1, pl, 'self') + callback(source, insertPos, insertPos, 'self') else - callback(source, pl + 1, pl, 'self, ') + callback(source, insertPos, insertPos, 'self, ') end end end diff --git a/script/core/semantic-tokens.lua b/script/core/semantic-tokens.lua index 96cbc5e1..405f2735 100644 --- a/script/core/semantic-tokens.lua +++ b/script/core/semantic-tokens.lua @@ -5,6 +5,7 @@ local define = require 'proto.define' local vm = require 'vm' local util = require 'utility' local guide = require 'parser.guide' +local converter = require 'proto.converter' local Care = {} Care['setglobal'] = function (source, results) @@ -188,8 +189,8 @@ local function buildTokens(uri, results) local lastLine = 0 local lastStartChar = 0 for i, source in ipairs(results) do - local startPos = files.position(uri, source.start, 'left') - local finishPos = files.position(uri, source.finish, 'right') + local startPos = converter.packPosition(uri, source.start) + local finishPos = converter.packPosition(uri, source.finish) local line = startPos.line local startChar = startPos.character local deltaLine = line - lastLine @@ -214,7 +215,6 @@ end return function (uri, start, finish) local ast = files.getState(uri) - local lines = files.getLines(uri) local text = files.getText(uri) if not ast then return nil diff --git a/script/core/signature.lua b/script/core/signature.lua index 26d9867c..007a3787 100644 --- a/script/core/signature.lua +++ b/script/core/signature.lua @@ -7,20 +7,22 @@ local guide = require 'parser.guide' local lookback = require 'core.look-backward' local function findNearCall(uri, ast, pos) - local text = files.getText(uri) + local text = files.getText(uri) + local state = files.getState(uri) local nearCall guide.eachSourceContain(ast.ast, pos, function (src) if src.type == 'call' or src.type == 'table' or src.type == 'function' then + local finishOffset = guide.positionToOffset(state, src.finish) -- call(),$ if src.finish <= pos - and text:sub(src.finish, src.finish) == ')' then + and text:sub(finishOffset, finishOffset) == ')' then return end -- {},$ if src.finish <= pos - and text:sub(src.finish, src.finish) == '}' then + and text:sub(finishOffset, finishOffset) == '}' then return end if not nearCall or nearCall.start <= src.start then @@ -56,13 +58,13 @@ local function makeOneSignature(source, oop, index) for start, finish in converted:gmatch '%s*()[^,]+()' do i = i + 1 params[i] = { - label = {start + argStart, finish - 1 + argStart}, + label = {start + argStart - 1, finish - 1 + argStart}, } end -- 不定参数 if index > i and i > 0 then local lastLabel = params[i].label - local text = label:sub(lastLabel[1], lastLabel[2]) + local text = label:sub(lastLabel[1] + 1, lastLabel[2]) if text == '...' then index = i end @@ -88,11 +90,15 @@ local function makeSignatures(text, call, pos) args[#args+1] = arg end end + local uri = guide.getUri(call) + local state = files.getState(uri) for i, arg in ipairs(args) do - local start = lookback.findTargetSymbol(text, arg.start - 1, '(') - or lookback.findTargetSymbol(text, arg.start - 1, ',') - or arg.start - if start > pos then + local startOffset = guide.positionToOffset(state, arg.start) + startOffset = lookback.findTargetSymbol(text, startOffset, '(') + or lookback.findTargetSymbol(text, startOffset, ',') + or startOffset + local startPos = guide.offsetToPosition(state, startOffset) + if startPos > pos then index = i - 1 break end @@ -102,7 +108,8 @@ local function makeSignatures(text, call, pos) end end if not index then - local backSymbol = lookback.findSymbol(text, pos) + local offset = guide.positionToOffset(state, pos) + local backSymbol = lookback.findSymbol(text, offset) if backSymbol == ',' or backSymbol == '(' then index = #args + 1 @@ -130,13 +137,14 @@ local function makeSignatures(text, call, pos) end return function (uri, pos) - local ast = files.getState(uri) - if not ast then + local state = files.getState(uri) + if not state then return nil end local text = files.getText(uri) - pos = lookback.skipSpace(text, pos) - local call = findNearCall(uri, ast, pos) + local offset = guide.positionToOffset(state, pos) + pos = guide.offsetToPosition(state, lookback.skipSpace(text, offset)) + local call = findNearCall(uri, state, pos) if not call then return nil end diff --git a/script/core/type-formatting.lua b/script/core/type-formatting.lua index a225d9d7..b946184b 100644 --- a/script/core/type-formatting.lua +++ b/script/core/type-formatting.lua @@ -2,85 +2,89 @@ local files = require 'files' local lookBackward = require 'core.look-backward' local guide = require "parser.guide" -local function insertIndentation(uri, offset, edits) - local lines = files.getLines(uri) - local text = files.getOriginText(uri) - local row = guide.positionOf(lines, offset) - local line = lines[row] - local indent = text:sub(line.start, line.finish):match '^%s*' +local function insertIndentation(uri, position, edits) + local text = files.getText(uri) + local state = files.getState(uri) + local row = guide.rowColOf(position) + local offset = state.lines[row] + local indent = text:match('^%s*', offset) for _, edit in ipairs(edits) do edit.text = edit.text:gsub('\n', '\n' .. indent) end end -local function findForward(text, offset, ...) - local pos = text:match('^[ \t]*()', offset) - if not pos then +local function findForward(uri, position, ...) + local text = files.getText(uri) + local state = files.getState(uri) + local offset = guide.positionToOffset(state, position) + local firstOffset = text:match('^[ \t]*()', offset + 1) + if not firstOffset then return nil end for _, symbol in ipairs { ... } do - if text:sub(pos, pos + #symbol - 1) == symbol then - return pos, symbol + if text:sub(firstOffset, firstOffset + #symbol - 1) == symbol then + return guide.offsetToPosition(state, firstOffset - 1), symbol end end return nil end -local function findBackward(text, offset, ...) - local pos = lookBackward.findAnyPos(text, offset) +local function findBackward(uri, position, ...) + local text = files.getText(uri) + local state = files.getState(uri) + local offset = guide.positionToOffset(state, position) + local lastOffset = lookBackward.findAnyOffset(text, offset) for _, symbol in ipairs { ... } do - if text:sub(pos - #symbol + 1, pos) == symbol then - return pos - #symbol + 1, symbol + if text:sub(lastOffset - #symbol + 1, lastOffset) == symbol then + return guide.offsetToPosition(state, lastOffset) end end return nil end -local function checkSplitOneLine(results, uri, offset, ch) +local function checkSplitOneLine(results, uri, position, ch) if ch ~= '\n' then return end - local text = files.getOriginText(uri) - local fOffset, fSymbol = findForward(text, offset + 1, 'end', '}') - if not fOffset then + local fPosition, fSymbol = findForward(uri, position, 'end', '}') + if not fPosition then return end - local bOffset, bSymbol = findBackward(text, offset, 'then', 'do', ')', '{') - if not bOffset then + local bPosition = findBackward(uri, position, 'then', 'do', ')', '{') + if not bPosition then return end local edits = {} edits[#edits+1] = { - start = bOffset + #bSymbol, - finish = offset, + start = bPosition, + finish = position, text = '\n\t', } edits[#edits+1] = { - start = offset + 1, - finish = fOffset + #fSymbol - 1, - text = '' + start = position, + finish = fPosition + 1, + text = '', } edits[#edits+1] = { - start = fOffset + #fSymbol, - finish = fOffset + #fSymbol - 1, - text = '\n' .. fSymbol + start = fPosition + 1, + finish = fPosition + 1, + text = '\n' .. fSymbol:sub(1, 1) } - insertIndentation(uri, bOffset, edits) + insertIndentation(uri, bPosition, edits) for _, edit in ipairs(edits) do results[#results+1] = edit end end -return function (uri, offset, ch) +return function (uri, position, ch) local ast = files.getState(uri) - local text = files.getOriginText(uri) - if not ast or not text then + if not ast then return nil end local results = {} -- split `function () $ end` - checkSplitOneLine(results, uri, offset, ch) + checkSplitOneLine(results, uri, position, ch) return results end |